5 Commits

Author SHA1 Message Date
Tom Alexander
c420ccd029 Fix clippy errors.
All checks were successful
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-23 17:43:43 -04:00
Tom Alexander
a880629831 Make clippy not write to the host git repo. 2023-10-23 17:43:32 -04:00
Tom Alexander
5e2dea1f28 Remove the SetSource trait.
It was only being used for creating paragraphs of specific text, so I just adjusted the of_text function to handle it.
2023-10-23 17:43:32 -04:00
Tom Alexander
f47d688be4 Remove owned String from CustomError.
Some checks failed
rustfmt Build rustfmt has failed
rust-test Build rust-test has failed
clippy Build clippy has failed
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has succeeded
This is a 15% performance improvement.
2023-10-21 14:29:37 -04:00
Tom Alexander
acfc5e5e68 Only allocate memory when unquoting sexp string that contains escapes.
All checks were successful
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
clippy Build clippy has succeeded
rustfmt Build rustfmt has succeeded
If the quoted string contains no escape sequences, then unquoting the string can be done by simply shaving off the leading and trailing quotation marks which can be a slice operation. By returning Cow, we can return either a borrowed slice or an owned String.
2023-10-20 12:53:27 -04:00
17 changed files with 163 additions and 123 deletions

View File

@@ -45,10 +45,6 @@ dockerclippy:
clippy:
> cargo clippy --no-deps --all-targets --all-features -- -D warnings
.PHONY: clippyfix
clippyfix:
> cargo clippy --fix --lib -p organic --all-features
.PHONY: test
test:
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)

View File

@@ -25,13 +25,13 @@ ifdef REMOTE_REPO
else
@echo "REMOTE_REPO not defined, not removing from remote repo."
endif
docker volume rm cargo-cache
docker volume rm rust-cache cargo-cache
# NOTE: This target will write to folders underneath the git-root
.PHONY: run
run: build
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
.PHONY: shell
shell: build
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)

View File

@@ -1,3 +1,5 @@
use std::borrow::Borrow;
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::fmt::Debug;
use std::str::FromStr;
@@ -262,11 +264,11 @@ pub(crate) fn compare_property_set_of_quoted_string<
.iter()
.map(|e| e.as_atom())
.collect::<Result<Vec<_>, _>>()?;
let value: Vec<String> = value
let value: Vec<Cow<'_, str>> = value
.into_iter()
.map(unquote)
.collect::<Result<Vec<_>, _>>()?;
let value: BTreeSet<&str> = value.iter().map(|e| e.as_str()).collect();
let value: BTreeSet<&str> = value.iter().map(|e| e.borrow()).collect();
let mismatched: Vec<_> = value.symmetric_difference(&rust_value).copied().collect();
if !mismatched.is_empty() {
let this_status = DiffStatus::Bad;
@@ -653,7 +655,7 @@ pub(crate) fn compare_property_number_lines<
(Some(number_lines), Some(rust_number_lines)) => {
let token_list = number_lines.as_list()?;
let number_type = token_list
.get(0)
.first()
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.ok_or(":number-lines should have a type.")?;

View File

@@ -2153,7 +2153,7 @@ fn compare_plain_text<'b, 's>(
let text = emacs.as_text()?;
let start_ind: usize = text
.properties
.get(0)
.first()
.expect("Should have start index.")
.as_atom()?
.parse()?;

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::collections::HashMap;
use nom::branch::alt;
@@ -36,12 +37,6 @@ pub struct TextWithProperties<'s> {
pub(crate) properties: Vec<Token<'s>>,
}
enum ParseState {
Normal,
Escape,
Octal(Vec<u8>),
}
impl<'s> Token<'s> {
pub(crate) fn as_vector<'p>(
&'p self,
@@ -117,8 +112,27 @@ fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
&input[..offset]
}
pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut out: Vec<u8> = Vec::with_capacity(text.len());
#[derive(Debug)]
enum UnquoteState {
Normal,
Escape,
HasEscape {
out: Vec<u8>,
},
HasEscapeEscape {
out: Vec<u8>,
},
Octal {
octal_begin_offset: usize,
octal: Vec<u8>,
},
HasEscapeOctal {
out: Vec<u8>,
octal: Vec<u8>,
},
}
pub(crate) fn unquote(text: &str) -> Result<Cow<'_, str>, Box<dyn std::error::Error>> {
if !text.starts_with('"') {
return Err("Quoted text does not start with quote.".into());
}
@@ -126,54 +140,143 @@ pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>>
return Err("Quoted text does not end with quote.".into());
}
let interior_text = &text[1..(text.len() - 1)];
let mut state = ParseState::Normal;
for current_char in interior_text.bytes() {
let mut state = UnquoteState::Normal;
for (offset, current_char) in interior_text.bytes().enumerate() {
// Check to see if octal finished
state = match (state, current_char) {
(ParseState::Octal(octal), b'0'..=b'7') if octal.len() < MAX_OCTAL_LENGTH => {
ParseState::Octal(octal)
(
UnquoteState::Octal {
octal_begin_offset,
octal,
},
b'0'..=b'7',
) if octal.len() < MAX_OCTAL_LENGTH => UnquoteState::Octal {
octal_begin_offset,
octal,
},
(
UnquoteState::Octal {
octal_begin_offset,
octal,
},
_,
) => {
let octal_number_string = String::from_utf8(octal)?;
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
out.extend_from_slice(&interior_text.as_bytes()[..octal_begin_offset]);
out.push(decoded_byte);
UnquoteState::HasEscape { out }
}
(ParseState::Octal(octal), _) => {
(UnquoteState::HasEscapeOctal { out, octal }, b'0'..=b'7')
if octal.len() < MAX_OCTAL_LENGTH =>
{
UnquoteState::HasEscapeOctal { out, octal }
}
(UnquoteState::HasEscapeOctal { mut out, octal }, _) => {
let octal_number_string = String::from_utf8(octal)?;
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
out.push(decoded_byte);
ParseState::Normal
UnquoteState::HasEscape { out }
}
(state, _) => state,
};
state = match (state, current_char) {
(ParseState::Normal, b'\\') => ParseState::Escape,
(ParseState::Normal, _) => {
(UnquoteState::Normal, b'\\') => UnquoteState::Escape,
(UnquoteState::Normal, _) => UnquoteState::Normal,
(UnquoteState::HasEscape { out }, b'\\') => UnquoteState::HasEscapeEscape { out },
(UnquoteState::HasEscape { mut out }, _) => {
out.push(current_char);
ParseState::Normal
UnquoteState::HasEscape { out }
}
(ParseState::Escape, b'n') => {
(UnquoteState::Escape, b'n') => {
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
// Subtract 1 from offset to account for backslash.
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
out.push(b'\n');
ParseState::Normal
UnquoteState::HasEscape { out }
}
(ParseState::Escape, b'\\') => {
(UnquoteState::HasEscapeEscape { mut out }, b'n') => {
out.push(b'\n');
UnquoteState::HasEscape { out }
}
(UnquoteState::Escape, b'\\') => {
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
// Subtract 1 from offset to account for backslash.
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
out.push(b'\\');
ParseState::Normal
UnquoteState::HasEscape { out }
}
(ParseState::Escape, b'"') => {
(UnquoteState::HasEscapeEscape { mut out }, b'\\') => {
out.push(b'\\');
UnquoteState::HasEscape { out }
}
(UnquoteState::Escape, b'"') => {
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
// Subtract 1 from offset to account for backslash.
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
out.push(b'"');
ParseState::Normal
UnquoteState::HasEscape { out }
}
(ParseState::Escape, b'0'..=b'7') => {
(UnquoteState::HasEscapeEscape { mut out }, b'"') => {
out.push(b'"');
UnquoteState::HasEscape { out }
}
(UnquoteState::Escape, b'0'..=b'7') => {
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
octal.push(current_char);
ParseState::Octal(octal)
// Substract 1 from offset to account for backslash
UnquoteState::Octal {
octal_begin_offset: offset - 1,
octal,
}
}
(ParseState::Octal(mut octal), b'0'..=b'7') => {
(UnquoteState::HasEscapeEscape { out }, b'0'..=b'7') => {
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
octal.push(current_char);
ParseState::Octal(octal)
// Substract 1 from offset to account for backslash
UnquoteState::HasEscapeOctal { out, octal }
}
_ => panic!("Invalid state unquoting string."),
(
UnquoteState::Octal {
octal_begin_offset,
mut octal,
},
b'0'..=b'7',
) => {
octal.push(current_char);
UnquoteState::Octal {
octal_begin_offset,
octal,
}
}
(UnquoteState::HasEscapeOctal { out, mut octal }, b'0'..=b'7') => {
octal.push(current_char);
UnquoteState::HasEscapeOctal { out, octal }
}
(state, _) => panic!(
"Invalid state unquoting string: {:?} | {} | {:?}",
state, offset, interior_text
),
};
}
Ok(String::from_utf8(out)?)
match state {
UnquoteState::Normal | UnquoteState::Escape | UnquoteState::Octal { .. } => {
Ok(Cow::Borrowed(interior_text))
}
UnquoteState::HasEscape { out } => Ok(Cow::Owned(String::from_utf8(out)?)),
UnquoteState::HasEscapeEscape { mut out } => {
out.push(b'\\');
Ok(Cow::Owned(String::from_utf8(out)?))
}
UnquoteState::HasEscapeOctal { mut out, octal } => {
out.push(b'\\');
out.extend(octal);
Ok(Cow::Owned(String::from_utf8(out)?))
}
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::str::FromStr;
use super::compare_field::compare_property_list_of_quoted_string;
@@ -206,10 +207,10 @@ pub(crate) fn get_property_unquoted_atom<'s>(
/// Get a named property containing an quoted string from the emacs token.
///
/// Returns None if key is not found.
pub(crate) fn get_property_quoted_string(
emacs: &Token<'_>,
pub(crate) fn get_property_quoted_string<'s>(
emacs: &Token<'s>,
key: &str,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
) -> Result<Option<Cow<'s, str>>, Box<dyn std::error::Error>> {
get_property(emacs, key)?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?

View File

@@ -6,8 +6,6 @@ pub(crate) type Res<T, U> = IResult<T, U, CustomError>;
#[derive(Debug)]
pub enum CustomError {
#[allow(dead_code)]
Text(String),
Static(&'static str),
IO(std::io::Error),
Parser(ErrorKind),
@@ -35,9 +33,3 @@ impl From<&'static str> for CustomError {
CustomError::Static(value)
}
}
impl From<String> for CustomError {
fn from(value: String) -> Self {
CustomError::Text(value)
}
}

View File

@@ -15,10 +15,8 @@ use crate::context::RefContext;
use crate::error::CustomError;
use crate::error::Res;
use crate::parser::macros::element;
use crate::types::AffiliatedKeywords;
use crate::types::Object;
use crate::types::Paragraph;
use crate::types::PlainText;
#[cfg_attr(
feature = "tracing",
@@ -80,13 +78,10 @@ pub(crate) fn broken_end<'b, 'g, 'r, 's>(
Ok((
remaining,
Paragraph {
source: input.get_until(remaining).into(),
affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText {
source: input.get_until(lead_in_remaining).into(),
})],
},
Paragraph::of_text(
input.get_until(remaining).into(),
input.get_until(lead_in_remaining).into(),
),
))
}
}
@@ -139,13 +134,10 @@ pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
Ok((
remaining,
Paragraph {
source: input.get_until(remaining).into(),
affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText {
source: input.get_until(lead_in_remaining).into(),
})],
},
Paragraph::of_text(
input.get_until(remaining).into(),
input.get_until(lead_in_remaining).into(),
),
))
}
}

View File

@@ -235,7 +235,7 @@ mod tests {
assert_eq!(
first_paragraph
.children
.get(0)
.first()
.expect("Len already asserted to be 1"),
&Object::Citation(Citation {
source: "[cite:@foo]",

View File

@@ -31,7 +31,6 @@ use crate::types::Drawer;
use crate::types::Element;
use crate::types::Keyword;
use crate::types::Paragraph;
use crate::types::SetSource;
#[cfg_attr(
feature = "tracing",
@@ -81,9 +80,8 @@ where
))(remaining)
{
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain);
element.set_source(source.into());
let element = Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()));
(remain, vec![element])
}
Err(_) => {

View File

@@ -37,7 +37,6 @@ use crate::types::DynamicBlock;
use crate::types::Element;
use crate::types::Keyword;
use crate::types::Paragraph;
use crate::types::SetSource;
#[cfg_attr(
feature = "tracing",
@@ -88,9 +87,7 @@ where
))))(remaining)?;
let leading_blank_lines =
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
element.set_source(source.into());
element
Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()))
});
let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?;

View File

@@ -39,7 +39,6 @@ use crate::types::Element;
use crate::types::Keyword;
use crate::types::Paragraph;
use crate::types::QuoteBlock;
use crate::types::SetSource;
use crate::types::SpecialBlock;
#[cfg_attr(
@@ -257,9 +256,7 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
))))(remaining)?;
let leading_blank_lines =
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
element.set_source(source.into());
element
Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()))
});
let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?;

View File

@@ -102,7 +102,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
}) {
let (_, (in_progress_words, complete_words)) =
todo_keywords(kw.value).map_err(|err| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;
@@ -123,7 +123,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
kw.value,
)
.map_err(|err: nom::Err<_>| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;
@@ -141,7 +141,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
.filter(|kw| kw.key.eq_ignore_ascii_case("link"))
{
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;

View File

@@ -22,7 +22,6 @@ use super::CenterBlock;
use super::Drawer;
use super::GetStandardProperties;
use super::QuoteBlock;
use super::SetSource;
use super::SpecialBlock;
use super::StandardProperties;
@@ -55,38 +54,6 @@ pub enum Element<'s> {
LatexEnvironment(LatexEnvironment<'s>),
}
impl<'s> SetSource<'s> for Element<'s> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn set_source(&mut self, source: &'s str) {
match self {
Element::Paragraph(obj) => obj.source = source,
Element::PlainList(obj) => obj.source = source,
Element::CenterBlock(obj) => obj.source = source,
Element::QuoteBlock(obj) => obj.source = source,
Element::SpecialBlock(obj) => obj.source = source,
Element::DynamicBlock(obj) => obj.source = source,
Element::FootnoteDefinition(obj) => obj.source = source,
Element::Comment(obj) => obj.source = source,
Element::Drawer(obj) => obj.source = source,
Element::PropertyDrawer(obj) => obj.source = source,
Element::Table(obj) => obj.source = source,
Element::VerseBlock(obj) => obj.source = source,
Element::CommentBlock(obj) => obj.source = source,
Element::ExampleBlock(obj) => obj.source = source,
Element::ExportBlock(obj) => obj.source = source,
Element::SrcBlock(obj) => obj.source = source,
Element::Clock(obj) => obj.source = source,
Element::DiarySexp(obj) => obj.source = source,
Element::Planning(obj) => obj.source = source,
Element::FixedWidthArea(obj) => obj.source = source,
Element::HorizontalRule(obj) => obj.source = source,
Element::Keyword(obj) => obj.source = source,
Element::BabelCall(obj) => obj.source = source,
Element::LatexEnvironment(obj) => obj.source = source,
}
}
}
impl<'s> GetStandardProperties<'s> for Element<'s> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self {

View File

@@ -169,11 +169,11 @@ impl<'s> Paragraph<'s> {
/// Generate a paragraph of the passed in text with no additional properties.
///
/// This is used for elements that support an "empty" content like greater blocks.
pub(crate) fn of_text(input: &'s str) -> Self {
pub(crate) fn of_text(source: &'s str, body: &'s str) -> Self {
Paragraph {
source: input,
source,
affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText { source: input })],
children: vec![Object::PlainText(PlainText { source: body })],
}
}
}

View File

@@ -7,7 +7,6 @@ mod greater_element;
mod lesser_element;
mod macros;
mod object;
mod source;
mod standard_properties;
mod util;
pub use affiliated_keyword::AffiliatedKeyword;
@@ -113,5 +112,4 @@ pub use object::WarningDelay;
pub use object::WarningDelayType;
pub use object::Year;
pub use object::YearInner;
pub(crate) use source::SetSource;
pub use standard_properties::StandardProperties;

View File

@@ -1,3 +0,0 @@
pub(crate) trait SetSource<'s> {
fn set_source(&mut self, source: &'s str);
}