26 Commits

Author SHA1 Message Date
Tom Alexander
31406fd520 Fix clippy.
Some checks failed
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-10-31 22:19:39 -04:00
Tom Alexander
49bc51ba89 Compare post-blank. 2023-10-31 22:18:28 -04:00
Tom Alexander
92592104a4 Compare contents begin/end. 2023-10-31 22:11:38 -04:00
Tom Alexander
33f4614d28 Make get_rust_byte_offsets more generic so it can be used for contents.
Some checks failed
clippy Build clippy has failed
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-31 21:59:58 -04:00
Tom Alexander
6c197c376a Add todo implementations of the new standard property functions. 2023-10-31 21:49:33 -04:00
Tom Alexander
bcf1b49db2 Remove the GetStandardProperties trait.
This was using dynamic dispatch to deal with enums to avoid the repetitive typing.
2023-10-31 21:26:00 -04:00
Tom Alexander
49f6e70a19 Use RPIT to get static dispatch GetStandardProperties. 2023-10-31 21:20:46 -04:00
Tom Alexander
31fb815681 Add a function for getting the post blank. 2023-10-31 21:20:46 -04:00
Tom Alexander
7dfe24ff98 Merge branch 'lazy_parse_lesser_block_contents'
All checks were successful
clippy Build clippy has succeeded
rustfmt Build rustfmt has succeeded
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
2023-10-31 20:54:01 -04:00
Tom Alexander
a5627d0cee Do not parse the lesser block contents during parsing, but rather only if the contents are requested.
Some checks failed
rust-test Build rust-test has failed
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
This seemed like an unnecessary allocation during parsing, especially considering we throw away some parses based on whether or not we found radio targets in the source.
2023-10-31 20:43:08 -04:00
Tom Alexander
93cfa71df2 Merge branch 'foreign_document_literate_build_emacs'
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
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
2023-10-31 19:49:15 -04:00
Tom Alexander
78320d3265 Fix clippy errors.
All checks were successful
clippy Build clippy has succeeded
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
2023-10-31 18:32:01 -04:00
Tom Alexander
9e908935f8 Add special case to delete invalid org-mode file.
Some checks failed
clippy Build clippy has failed
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-31 17:26:13 -04:00
Tom Alexander
b18a703529 Handle nil values for compare_property_object_tree. 2023-10-31 17:20:35 -04:00
Tom Alexander
ea52dc60be Add a literate tutorial for building emacs to the foreign documents test. 2023-10-31 16:33:11 -04:00
Tom Alexander
f5699ce830 Remove PartialEq from Object.
Some checks failed
rustfmt Build rustfmt has succeeded
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-10-31 16:33:10 -04:00
Tom Alexander
10aa0956ee Merge branch 'lesser_block_memory_optimization'
All checks were successful
clippy Build clippy has succeeded
rustfmt Build rustfmt 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-27 22:13:25 -04:00
Tom Alexander
816c164996 Only allocate memory if removing text for lesser blocks.
All checks were successful
clippy Build clippy has succeeded
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
2023-10-27 21:50:08 -04:00
Tom Alexander
ee201e1336 Merge branch 'explicit_all_node_iter'
All checks were successful
rustfmt Build rustfmt has succeeded
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 18:39:16 -04:00
Tom Alexander
4897952330 Make creating AllAstNodeIter explicit.
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
This is to remove the ambiguity between calling iter on the specific structs like Document and calling iter on an AstNode by having an explicitly-named function to create the iterator.
2023-10-23 18:25:59 -04:00
Tom Alexander
e1d85c6dc2 Merge branch 'remove_set_source'
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
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
2023-10-23 18:06:56 -04:00
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
37 changed files with 1419 additions and 528 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

@@ -93,6 +93,12 @@ ARG WORG_PATH=/foreign_documents/worg
ARG WORG_REPO=https://git.sr.ht/~bzg/worg
RUN mkdir -p $WORG_PATH && git -C $WORG_PATH init --initial-branch=main && git -C $WORG_PATH remote add origin $WORG_REPO && git -C $WORG_PATH fetch origin $WORG_VERSION && git -C $WORG_PATH checkout FETCH_HEAD
ARG LITERATE_BUILD_EMACS_VERSION=e3ac1afe1e40af601be7af12c1d13d96308ab209
ARG LITERATE_BUILD_EMACS_PATH=/foreign_documents/literate_build_emacs
ARG LITERATE_BUILD_EMACS_REPO=https://gitlab.com/spudlyo/orgdemo2.git
RUN mkdir -p $LITERATE_BUILD_EMACS_PATH && git -C $LITERATE_BUILD_EMACS_PATH init --initial-branch=main && git -C $LITERATE_BUILD_EMACS_PATH remote add origin $LITERATE_BUILD_EMACS_REPO && git -C $LITERATE_BUILD_EMACS_PATH fetch origin $LITERATE_BUILD_EMACS_VERSION && git -C $LITERATE_BUILD_EMACS_PATH checkout FETCH_HEAD
# unused/aws.org contains invalid paths for setupfile which causes both upstream org-mode and Organic to error out.
RUN rm $LITERATE_BUILD_EMACS_PATH/unused/aws.org
FROM tester as foreign-document-test
RUN apk add --no-cache bash coreutils
@@ -100,6 +106,7 @@ RUN mkdir /foreign_documents
COPY --from=foreign-document-gather /foreign_documents/howardabrams /foreign_documents/howardabrams
COPY --from=foreign-document-gather /foreign_documents/doomemacs /foreign_documents/doomemacs
COPY --from=foreign-document-gather /foreign_documents/worg /foreign_documents/worg
COPY --from=foreign-document-gather /foreign_documents/literate_build_emacs /foreign_documents/literate_build_emacs
COPY --from=build-org-mode /root/org-mode /foreign_documents/org-mode
COPY --from=build-emacs /root/emacs /foreign_documents/emacs
ENTRYPOINT ["cargo", "run", "--bin", "foreign_document_test", "--features", "compare,foreign_document_test", "--profile", "release-lto"]

View File

@@ -0,0 +1,5 @@
#+caption:
#+caption: *foo*
#+caption[bar]:
#+begin_src bash
#+end_src

View File

@@ -1,3 +0,0 @@
foo <<bar>> baz
lorem << ipsum >> dolar

View File

@@ -53,6 +53,9 @@ async fn main_body() -> Result<ExitCode, Box<dyn std::error::Error>> {
let layer = layer.chain(compare_group("doomemacs", || {
compare_all_org_document("/foreign_documents/doomemacs")
}));
let layer = layer.chain(compare_group("literate_build_emacs", || {
compare_all_org_document("/foreign_documents/literate_build_emacs")
}));
let running_tests: Vec<_> = layer.map(|c| tokio::spawn(c.run_test())).collect();
let mut any_failed = false;

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;
@@ -546,6 +548,21 @@ where
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
for (kw_e, kw_r) in outer_emacs_list.iter().zip(outer_rust_list) {
match (kw_e.as_atom(), kw_r) {
(Ok("nil"), (None, mandatory_value)) if mandatory_value.is_empty() => {
// If its an empty keyword then it becomes nil in the elisp.
continue;
}
(Ok("nil"), _) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
_ => {}
}
let kw_e = kw_e.as_list()?;
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
@@ -554,6 +571,17 @@ where
let mut kw_e = kw_e.iter();
// First element is a list representing the mandatory value.
if let Some(val_e) = kw_e.next() {
match (val_e.as_atom(), kw_r) {
(Ok("nil"), (_, mandatory_value)) if mandatory_value.is_empty() => {}
(Ok("nil"), _) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
_ => {
let el = val_e.as_list()?;
if el.len() != kw_r.1.len() {
let this_status = DiffStatus::Bad;
@@ -566,6 +594,8 @@ where
for (e, r) in el.iter().zip(kw_r.1.iter()) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
}
};
} else {
let this_status = DiffStatus::Bad;
let message = Some(format!(
@@ -653,7 +683,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

@@ -57,7 +57,6 @@ use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::FootnoteReferenceType;
use crate::types::GetStandardProperties;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::Hour;
@@ -128,7 +127,7 @@ pub struct DiffResult<'b, 's> {
emacs_token: &'b Token<'s>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub(crate) enum DiffStatus {
Good,
Bad,
@@ -164,7 +163,7 @@ impl<'b, 's> DiffEntry<'b, 's> {
fn is_immediately_bad(&self) -> bool {
match self {
DiffEntry::DiffResult(diff) => diff.status == DiffStatus::Bad,
DiffEntry::DiffResult(diff) => matches!(diff.status, DiffStatus::Bad),
DiffEntry::DiffLayer(_) => false,
}
}
@@ -413,7 +412,7 @@ pub(crate) fn compare_ast_node<'b, 's>(
name: rust.get_elisp_fact().get_elisp_name(),
message: Some(e.to_string()),
children: Vec::new(),
rust_source: rust.get_standard_properties().get_source(),
rust_source: rust.get_source(),
emacs_token: emacs,
}
.into()
@@ -1576,7 +1575,7 @@ fn compare_example_block<'b, 's>(
[],
(
EmacsField::Required(":value"),
|r| Some(r.contents.as_str()),
|r| Some(r.get_value()),
compare_property_quoted_string
),
(
@@ -1654,7 +1653,7 @@ fn compare_export_block<'b, 's>(
),
(
EmacsField::Required(":value"),
|r| Some(r.contents.as_str()),
|r| Some(r.get_value()),
compare_property_quoted_string
)
) {
@@ -1702,7 +1701,7 @@ fn compare_src_block<'b, 's>(
),
(
EmacsField::Required(":value"),
|r| Some(r.contents.as_str()),
|r| Some(r.get_value()),
compare_property_quoted_string
),
(
@@ -2153,7 +2152,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;
@@ -14,7 +15,6 @@ use crate::compare::sexp::unquote;
use crate::types::AffiliatedKeywordValue;
use crate::types::AstNode;
use crate::types::GetAffiliatedKeywords;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
/// Check if the child string slice is a slice of the parent string slice.
@@ -29,32 +29,29 @@ fn is_slice_of(parent: &str, child: &str) -> bool {
/// Get the byte offset into source that the rust object exists at.
///
/// These offsets are zero-based unlike the elisp ones.
fn get_rust_byte_offsets<'b, 's, S: StandardProperties<'s> + ?Sized>(
original_document: &'s str,
rust_ast_node: &'b S,
) -> (usize, usize) {
let rust_object_source = rust_ast_node.get_source();
debug_assert!(is_slice_of(original_document, rust_object_source));
let offset = rust_object_source.as_ptr() as usize - original_document.as_ptr() as usize;
let end = offset + rust_object_source.len();
fn get_rust_byte_offsets(original_document: &str, subset: &str) -> (usize, usize) {
debug_assert!(is_slice_of(original_document, subset));
let offset = subset.as_ptr() as usize - original_document.as_ptr() as usize;
let end = offset + subset.len();
(offset, end)
}
pub(crate) fn compare_standard_properties<
'b,
's,
S: GetStandardProperties<'s> + GetElispFact<'s> + ?Sized,
S: StandardProperties<'s> + GetElispFact<'s> + ?Sized,
>(
original_document: &'s str,
emacs: &'b Token<'s>,
rust: &'b S,
) -> Result<(), Box<dyn std::error::Error>> {
assert_name(emacs, rust.get_elisp_fact().get_elisp_name())?;
assert_bounds(original_document, emacs, rust.get_standard_properties())?;
assert_bounds(original_document, emacs, rust)?;
assert_post_blank(emacs, rust)?;
Ok(())
}
pub(crate) fn assert_name<S: AsRef<str>>(
fn assert_name<S: AsRef<str>>(
emacs: &Token<'_>,
name: S,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -77,25 +74,67 @@ pub(crate) fn assert_name<S: AsRef<str>>(
/// Assert that the character ranges defined by upstream org-mode's :standard-properties match the slices in Organic's StandardProperties.
///
/// This does **not** handle plain text because plain text is a special case.
pub(crate) fn assert_bounds<'b, 's, S: StandardProperties<'s> + ?Sized>(
fn assert_bounds<'b, 's, S: StandardProperties<'s> + ?Sized>(
original_document: &'s str,
emacs: &'b Token<'s>,
rust: &'b S,
) -> Result<(), Box<dyn std::error::Error>> {
let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
// Check begin/end
{
let (begin, end) = (
standard_properties
.begin
.ok_or("Token should have a begin.")?,
standard_properties.end.ok_or("Token should have an end.")?,
);
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust); // 0-based
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust.get_source()); // 0-based
let rust_begin_char_offset = original_document[..rust_begin].chars().count() + 1; // 1-based
let rust_end_char_offset =
rust_begin_char_offset + original_document[rust_begin..rust_end].chars().count(); // 1-based
if rust_begin_char_offset != begin || rust_end_char_offset != end {
Err(format!("Rust bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=end))?;
}
}
// Check contents-begin/contents-end
{
let (begin, end) = (
standard_properties
.contents_begin
.ok_or("Token should have a begin.")?,
standard_properties
.contents_end
.ok_or("Token should have an end.")?,
);
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust.get_contents()); // 0-based
let rust_begin_char_offset = original_document[..rust_begin].chars().count() + 1; // 1-based
let rust_end_char_offset =
rust_begin_char_offset + original_document[rust_begin..rust_end].chars().count(); // 1-based
if rust_begin_char_offset != begin || rust_end_char_offset != end {
Err(format!("Rust contents bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs contents bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=end))?;
}
}
Ok(())
}
/// Assert that the post blank matches between emacs and organic.
///
/// This does **not** handle plain text because plain text is a special case.
fn assert_post_blank<'b, 's, S: StandardProperties<'s> + ?Sized>(
emacs: &'b Token<'s>,
rust: &'b S,
) -> Result<(), Box<dyn std::error::Error>> {
let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
let rust_post_blank = rust.get_post_blank().chars().count();
let emacs_post_blank = standard_properties
.post_blank
.ok_or("Token should have a post-blank.")?;
if rust_post_blank != emacs_post_blank {
Err(format!("Rust post-blank (in chars) {rust_post_blank} does not match emacs post-blank ({emacs_post_blank})", rust_post_blank = rust_post_blank, emacs_post_blank = emacs_post_blank))?;
}
Ok(())
}
@@ -206,10 +245,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

@@ -90,12 +90,11 @@ impl<'r, 's> Iterator for AllAstNodeIter<'r, 's> {
}
}
impl<'r, 's> IntoIterator for AstNode<'r, 's> {
type Item = AstNode<'r, 's>;
type IntoIter = AllAstNodeIter<'r, 's>;
fn into_iter(self) -> Self::IntoIter {
impl<'r, 's> AstNode<'r, 's> {
/// Iterate all AST nodes.
///
/// This is different from the iter/into_iter functions which iterate a single level of the children. This iterates the entire tree including returning the root node itself.
pub fn iter_all_ast_nodes(self) -> AllAstNodeIter<'r, 's> {
AllAstNodeIter {
root: Some(self),
queue: VecDeque::new(),

View File

@@ -225,7 +225,7 @@ fn impl_balanced_bracket<
let contents_end = remaining;
let (remaining, _) = end_parser(remaining)?;
let contents = if contents_start != contents_end {
let contents = if Into::<&str>::into(contents_start) != Into::<&str>::into(contents_end) {
Some(contents_start.get_until(contents_end))
} else {
None
@@ -244,7 +244,7 @@ mod tests {
let input = OrgSource::new("()");
let (remaining, call) = opt(babel_call_call)(input)?;
assert_eq!(Into::<&str>::into(remaining), "()");
assert_eq!(call, None);
assert!(call.is_none());
Ok(())
}
}

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

@@ -210,12 +210,11 @@ mod tests {
use crate::context::GlobalSettings;
use crate::context::List;
use crate::parser::element_parser::element;
use crate::types::CitationReference;
use crate::types::Element;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
#[test]
fn citation_simple() {
fn citation_simple() -> Result<(), Box<dyn std::error::Error>> {
let input = OrgSource::new("[cite:@foo]");
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
@@ -227,28 +226,33 @@ mod tests {
_ => panic!("Should be a paragraph!"),
};
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"[cite:@foo]"
);
assert_eq!(first_paragraph.get_source(), "[cite:@foo]");
assert_eq!(first_paragraph.children.len(), 1);
assert_eq!(
first_paragraph
match first_paragraph
.children
.get(0)
.expect("Len already asserted to be 1"),
&Object::Citation(Citation {
source: "[cite:@foo]",
style: None,
prefix: vec![],
suffix: vec![],
children: vec![CitationReference {
source: "@foo",
key: "foo",
prefix: vec![],
suffix: vec![]
}]
})
);
.first()
.expect("Len already asserted to be 1.")
{
Object::Citation(inner) => {
assert_eq!(inner.get_source(), "[cite:@foo]");
assert_eq!(inner.children.len(), 1);
assert!(inner.prefix.is_empty());
assert!(inner.suffix.is_empty());
assert!(inner.style.is_none());
let citation_reference = inner
.children
.first()
.expect("Len already asserted to be 1.");
assert_eq!(citation_reference.get_source(), "@foo");
assert_eq!(citation_reference.key, "foo");
assert!(citation_reference.prefix.is_empty());
assert!(citation_reference.suffix.is_empty());
}
_ => {
return Err("Child should be a citation.".into());
}
};
Ok(())
}
}

View File

@@ -143,7 +143,7 @@ fn document_org_source<'b, 'g, 'r, 's>(
{
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
let all_radio_targets: Vec<&Vec<Object<'_>>> = Into::<AstNode>::into(&document)
.into_iter()
.iter_all_ast_nodes()
.filter_map(|ast_node| {
if let AstNode::RadioTarget(ast_node) = ast_node {
Some(ast_node)

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

@@ -160,7 +160,7 @@ mod tests {
use crate::context::Context;
use crate::context::GlobalSettings;
use crate::context::List;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
#[test]
fn two_paragraphs() {
@@ -181,17 +181,13 @@ line footnote.",
footnote_definition_matcher(remaining).expect("Parse second footnote_definition.");
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(
first_footnote_definition
.get_standard_properties()
.get_source(),
first_footnote_definition.get_source(),
"[fn:1] A footnote.
"
);
assert_eq!(
second_footnote_definition
.get_standard_properties()
.get_source(),
second_footnote_definition.get_source(),
"[fn:2] A multi-
line footnote."
@@ -216,9 +212,7 @@ not in the footnote.",
footnote_definition_matcher(input).expect("Parse first footnote_definition");
assert_eq!(Into::<&str>::into(remaining), "not in the footnote.");
assert_eq!(
first_footnote_definition
.get_standard_properties()
.get_source(),
first_footnote_definition.get_source(),
"[fn:2] A multi-
line footnote.

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,
})?;
@@ -157,7 +157,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) {
document.category = Into::<AstNode>::into(&*document)
.into_iter()
.iter_all_ast_nodes()
.filter_map(|ast_node| {
if let AstNode::Keyword(ast_node) = ast_node {
if ast_node.key.eq_ignore_ascii_case("category") {

View File

@@ -202,7 +202,7 @@ where
let parser_context = parser_context.with_additional_node(&contexts[1]);
let parser_context = parser_context.with_additional_node(&contexts[2]);
let (remaining, contents) = content(&parser_context, remaining)?;
let (remaining, contents) = text_until_exit(&parser_context, remaining)?;
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
let (remaining, _trailing_ws) =
@@ -236,7 +236,7 @@ where
retain_labels,
use_labels,
label_format,
contents,
value: Into::<&str>::into(contents),
},
))
}
@@ -276,7 +276,7 @@ where
let parser_context = parser_context.with_additional_node(&contexts[1]);
let parser_context = parser_context.with_additional_node(&contexts[2]);
let (remaining, contents) = content(&parser_context, remaining)?;
let (remaining, contents) = text_until_exit(&parser_context, remaining)?;
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
let (remaining, _trailing_ws) =
@@ -292,7 +292,7 @@ where
),
export_type: export_type.map(Into::<&str>::into),
data: parameters.map(Into::<&str>::into),
contents,
value: Into::<&str>::into(contents),
},
))
}
@@ -331,7 +331,7 @@ where
let parser_context = context.with_additional_node(&contexts[0]);
let parser_context = parser_context.with_additional_node(&contexts[1]);
let parser_context = parser_context.with_additional_node(&contexts[2]);
let (remaining, contents) = content(&parser_context, remaining)?;
let (remaining, contents) = text_until_exit(&parser_context, remaining)?;
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
let (remaining, _trailing_ws) =
@@ -371,7 +371,7 @@ where
retain_labels,
use_labels,
label_format,
contents,
value: Into::<&str>::into(contents),
},
))
}
@@ -650,51 +650,3 @@ fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not(" \t\r\n"),
))(input)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn content<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, String> {
let mut ret = String::new();
let mut remaining = input;
let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context);
loop {
if exit_matcher_parser(remaining).is_ok() {
break;
}
let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?;
if let Some(val) = pre_escape_whitespace {
ret.push_str(Into::<&str>::into(val));
}
ret.push_str(line.into());
remaining = remain;
}
Ok((remaining, ret))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn content_line<'s>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, (Option<OrgSource<'s>>, OrgSource<'s>)> {
let (remaining, pre_escape_whitespace) = opt(map(
tuple((
recognize(tuple((
space0,
many_till(
tag(","),
peek(tuple((tag(","), alt((tag("#+"), tag("*")))))),
),
))),
tag(","),
)),
|(pre_comma, _)| pre_comma,
))(input)?;
let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?;
Ok((remaining, (pre_escape_whitespace, line_post_escape)))
}

View File

@@ -12,7 +12,7 @@ use nom::Slice;
pub(crate) type BracketDepth = i16;
#[derive(Copy, Clone, PartialEq)]
#[derive(Copy, Clone)]
pub(crate) struct OrgSource<'s> {
full_source: &'s str,
start: usize,

View File

@@ -96,7 +96,7 @@ mod tests {
use crate::context::List;
use crate::parser::element_parser::element;
use crate::parser::org_source::OrgSource;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
#[test]
fn two_paragraphs() {
@@ -109,13 +109,7 @@ mod tests {
let (remaining, second_paragraph) =
paragraph_matcher(remaining).expect("Parse second paragraph.");
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"foo bar baz\n\n"
);
assert_eq!(
second_paragraph.get_standard_properties().get_source(),
"lorem ipsum"
);
assert_eq!(first_paragraph.get_source(), "foo bar baz\n\n");
assert_eq!(second_paragraph.get_source(), "lorem ipsum");
}
}

View File

@@ -629,7 +629,7 @@ mod tests {
use crate::context::Context;
use crate::context::GlobalSettings;
use crate::context::List;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
#[test]
fn plain_list_item_empty() {
@@ -640,7 +640,7 @@ mod tests {
let plain_list_item_matcher = bind_context!(plain_list_item, &initial_context);
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_standard_properties().get_source(), "1.");
assert_eq!(result.get_source(), "1.");
}
#[test]
@@ -652,7 +652,7 @@ mod tests {
let plain_list_item_matcher = bind_context!(plain_list_item, &initial_context);
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
assert_eq!(result.get_source(), "1. foo");
}
#[test]
@@ -664,7 +664,7 @@ mod tests {
let (remaining, result) =
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_standard_properties().get_source(), "1.");
assert_eq!(result.get_source(), "1.");
}
#[test]
@@ -676,7 +676,7 @@ mod tests {
let (remaining, result) =
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
assert_eq!(result.get_source(), "1. foo");
}
#[test]
@@ -721,7 +721,7 @@ mod tests {
plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(Into::<&str>::into(remaining), " ipsum\n");
assert_eq!(
result.get_standard_properties().get_source(),
result.get_source(),
r#"1. foo
2. bar
baz
@@ -749,7 +749,7 @@ baz"#,
plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(Into::<&str>::into(remaining), "baz");
assert_eq!(
result.get_standard_properties().get_source(),
result.get_source(),
r#"1. foo
1. bar
@@ -782,7 +782,7 @@ dolar"#,
plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(Into::<&str>::into(remaining), "dolar");
assert_eq!(
result.get_standard_properties().get_source(),
result.get_source(),
r#"1. foo
bar

View File

@@ -143,7 +143,7 @@ mod tests {
use crate::context::GlobalSettings;
use crate::context::List;
use crate::parser::object_parser::detect_standard_set_object_sans_plain_text;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
#[test]
fn plain_text_simple() {
@@ -160,9 +160,6 @@ mod tests {
)(input)
.unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(
result.get_standard_properties().get_source(),
Into::<&str>::into(input)
);
assert_eq!(result.get_source(), Into::<&str>::into(input));
}
}

View File

@@ -175,11 +175,11 @@ mod tests {
use crate::parser::element_parser::element;
use crate::types::Bold;
use crate::types::Element;
use crate::types::GetStandardProperties;
use crate::types::PlainText;
use crate::types::StandardProperties;
#[test]
fn plain_text_radio_target() {
fn plain_text_radio_target() -> Result<(), Box<dyn std::error::Error>> {
let input = OrgSource::new("foo bar baz");
let radio_target_match = vec![Object::PlainText(PlainText { source: "bar" })];
let global_settings = GlobalSettings {
@@ -195,26 +195,33 @@ mod tests {
_ => panic!("Should be a paragraph!"),
};
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"foo bar baz"
);
assert_eq!(first_paragraph.get_source(), "foo bar baz");
assert_eq!(first_paragraph.children.len(), 3);
assert_eq!(
first_paragraph
match first_paragraph
.children
.get(1)
.expect("Len already asserted to be 3"),
&Object::RadioLink(RadioLink {
source: "bar ",
children: vec![Object::PlainText(PlainText { source: "bar" })],
path: "bar"
})
);
.expect("Len already asserted to be 3.")
{
Object::RadioLink(inner) => {
assert_eq!(inner.get_source(), "bar ");
assert_eq!(inner.path, "bar");
assert_eq!(inner.children.len(), 1);
let child = inner
.children
.first()
.expect("Length already asserted to be 1.");
assert!(matches!(child, Object::PlainText(_)));
assert_eq!(child.get_source(), "bar");
}
_ => {
return Err("Child should be a radio link.".into());
}
};
Ok(())
}
#[test]
fn bold_radio_target() {
fn bold_radio_target() -> Result<(), Box<dyn std::error::Error>> {
let input = OrgSource::new("foo *bar* baz");
let radio_target_match = vec![Object::Bold(Bold {
source: "*bar*",
@@ -234,24 +241,28 @@ mod tests {
_ => panic!("Should be a paragraph!"),
};
assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"foo *bar* baz"
);
assert_eq!(first_paragraph.get_source(), "foo *bar* baz");
assert_eq!(first_paragraph.children.len(), 3);
assert_eq!(
first_paragraph
match first_paragraph
.children
.get(1)
.expect("Len already asserted to be 3"),
&Object::RadioLink(RadioLink {
source: "*bar* ",
children: vec![Object::Bold(Bold {
source: "*bar* ",
children: vec![Object::PlainText(PlainText { source: "bar" })]
})],
path: "*bar* "
})
);
.expect("Len already asserted to be 3.")
{
Object::RadioLink(inner) => {
assert_eq!(inner.get_source(), "*bar* ");
assert_eq!(inner.path, "*bar* ");
assert_eq!(inner.children.len(), 1);
let child = inner
.children
.first()
.expect("Length already asserted to be 1.");
assert!(matches!(child, Object::Bold(_)));
assert_eq!(child.get_source(), "*bar* ");
}
_ => {
return Err("Child should be a radio link.".into());
}
};
Ok(())
}
}

View File

@@ -2,6 +2,7 @@ use super::macros::to_ast_node;
use super::CenterBlock;
use super::QuoteBlock;
use super::SpecialBlock;
use super::StandardProperties;
use crate::types::AngleLink;
use crate::types::BabelCall;
use crate::types::Bold;
@@ -24,7 +25,6 @@ use crate::types::ExportSnippet;
use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::GetStandardProperties;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::InlineBabelCall;
@@ -259,67 +259,193 @@ to_ast_node!(&'r Superscript<'s>, AstNode::Superscript);
to_ast_node!(&'r TableCell<'s>, AstNode::TableCell);
to_ast_node!(&'r Timestamp<'s>, AstNode::Timestamp);
impl<'r, 's> GetStandardProperties<'s> for AstNode<'r, 's> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn crate::types::StandardProperties<'s> {
impl<'r, 's> StandardProperties<'s> for AstNode<'r, 's> {
fn get_source<'b>(&'b self) -> &'s str {
match self {
AstNode::Document(inner) => *inner,
AstNode::Heading(inner) => *inner,
AstNode::Section(inner) => *inner,
AstNode::Paragraph(inner) => *inner,
AstNode::PlainList(inner) => *inner,
AstNode::PlainListItem(inner) => *inner,
AstNode::CenterBlock(inner) => *inner,
AstNode::QuoteBlock(inner) => *inner,
AstNode::SpecialBlock(inner) => *inner,
AstNode::DynamicBlock(inner) => *inner,
AstNode::FootnoteDefinition(inner) => *inner,
AstNode::Comment(inner) => *inner,
AstNode::Drawer(inner) => *inner,
AstNode::PropertyDrawer(inner) => *inner,
AstNode::NodeProperty(inner) => *inner,
AstNode::Table(inner) => *inner,
AstNode::TableRow(inner) => *inner,
AstNode::VerseBlock(inner) => *inner,
AstNode::CommentBlock(inner) => *inner,
AstNode::ExampleBlock(inner) => *inner,
AstNode::ExportBlock(inner) => *inner,
AstNode::SrcBlock(inner) => *inner,
AstNode::Clock(inner) => *inner,
AstNode::DiarySexp(inner) => *inner,
AstNode::Planning(inner) => *inner,
AstNode::FixedWidthArea(inner) => *inner,
AstNode::HorizontalRule(inner) => *inner,
AstNode::Keyword(inner) => *inner,
AstNode::BabelCall(inner) => *inner,
AstNode::LatexEnvironment(inner) => *inner,
AstNode::Bold(inner) => *inner,
AstNode::Italic(inner) => *inner,
AstNode::Underline(inner) => *inner,
AstNode::StrikeThrough(inner) => *inner,
AstNode::Code(inner) => *inner,
AstNode::Verbatim(inner) => *inner,
AstNode::PlainText(inner) => *inner,
AstNode::RegularLink(inner) => *inner,
AstNode::RadioLink(inner) => *inner,
AstNode::RadioTarget(inner) => *inner,
AstNode::PlainLink(inner) => *inner,
AstNode::AngleLink(inner) => *inner,
AstNode::OrgMacro(inner) => *inner,
AstNode::Entity(inner) => *inner,
AstNode::LatexFragment(inner) => *inner,
AstNode::ExportSnippet(inner) => *inner,
AstNode::FootnoteReference(inner) => *inner,
AstNode::Citation(inner) => *inner,
AstNode::CitationReference(inner) => *inner,
AstNode::InlineBabelCall(inner) => *inner,
AstNode::InlineSourceBlock(inner) => *inner,
AstNode::LineBreak(inner) => *inner,
AstNode::Target(inner) => *inner,
AstNode::StatisticsCookie(inner) => *inner,
AstNode::Subscript(inner) => *inner,
AstNode::Superscript(inner) => *inner,
AstNode::TableCell(inner) => *inner,
AstNode::Timestamp(inner) => *inner,
AstNode::Document(inner) => inner.get_source(),
AstNode::Heading(inner) => inner.get_source(),
AstNode::Section(inner) => inner.get_source(),
AstNode::Paragraph(inner) => inner.get_source(),
AstNode::PlainList(inner) => inner.get_source(),
AstNode::PlainListItem(inner) => inner.get_source(),
AstNode::CenterBlock(inner) => inner.get_source(),
AstNode::QuoteBlock(inner) => inner.get_source(),
AstNode::SpecialBlock(inner) => inner.get_source(),
AstNode::DynamicBlock(inner) => inner.get_source(),
AstNode::FootnoteDefinition(inner) => inner.get_source(),
AstNode::Comment(inner) => inner.get_source(),
AstNode::Drawer(inner) => inner.get_source(),
AstNode::PropertyDrawer(inner) => inner.get_source(),
AstNode::NodeProperty(inner) => inner.get_source(),
AstNode::Table(inner) => inner.get_source(),
AstNode::TableRow(inner) => inner.get_source(),
AstNode::VerseBlock(inner) => inner.get_source(),
AstNode::CommentBlock(inner) => inner.get_source(),
AstNode::ExampleBlock(inner) => inner.get_source(),
AstNode::ExportBlock(inner) => inner.get_source(),
AstNode::SrcBlock(inner) => inner.get_source(),
AstNode::Clock(inner) => inner.get_source(),
AstNode::DiarySexp(inner) => inner.get_source(),
AstNode::Planning(inner) => inner.get_source(),
AstNode::FixedWidthArea(inner) => inner.get_source(),
AstNode::HorizontalRule(inner) => inner.get_source(),
AstNode::Keyword(inner) => inner.get_source(),
AstNode::BabelCall(inner) => inner.get_source(),
AstNode::LatexEnvironment(inner) => inner.get_source(),
AstNode::Bold(inner) => inner.get_source(),
AstNode::Italic(inner) => inner.get_source(),
AstNode::Underline(inner) => inner.get_source(),
AstNode::StrikeThrough(inner) => inner.get_source(),
AstNode::Code(inner) => inner.get_source(),
AstNode::Verbatim(inner) => inner.get_source(),
AstNode::PlainText(inner) => inner.get_source(),
AstNode::RegularLink(inner) => inner.get_source(),
AstNode::RadioLink(inner) => inner.get_source(),
AstNode::RadioTarget(inner) => inner.get_source(),
AstNode::PlainLink(inner) => inner.get_source(),
AstNode::AngleLink(inner) => inner.get_source(),
AstNode::OrgMacro(inner) => inner.get_source(),
AstNode::Entity(inner) => inner.get_source(),
AstNode::LatexFragment(inner) => inner.get_source(),
AstNode::ExportSnippet(inner) => inner.get_source(),
AstNode::FootnoteReference(inner) => inner.get_source(),
AstNode::Citation(inner) => inner.get_source(),
AstNode::CitationReference(inner) => inner.get_source(),
AstNode::InlineBabelCall(inner) => inner.get_source(),
AstNode::InlineSourceBlock(inner) => inner.get_source(),
AstNode::LineBreak(inner) => inner.get_source(),
AstNode::Target(inner) => inner.get_source(),
AstNode::StatisticsCookie(inner) => inner.get_source(),
AstNode::Subscript(inner) => inner.get_source(),
AstNode::Superscript(inner) => inner.get_source(),
AstNode::TableCell(inner) => inner.get_source(),
AstNode::Timestamp(inner) => inner.get_source(),
}
}
fn get_contents<'b>(&'b self) -> &'s str {
match self {
AstNode::Document(inner) => inner.get_contents(),
AstNode::Heading(inner) => inner.get_contents(),
AstNode::Section(inner) => inner.get_contents(),
AstNode::Paragraph(inner) => inner.get_contents(),
AstNode::PlainList(inner) => inner.get_contents(),
AstNode::PlainListItem(inner) => inner.get_contents(),
AstNode::CenterBlock(inner) => inner.get_contents(),
AstNode::QuoteBlock(inner) => inner.get_contents(),
AstNode::SpecialBlock(inner) => inner.get_contents(),
AstNode::DynamicBlock(inner) => inner.get_contents(),
AstNode::FootnoteDefinition(inner) => inner.get_contents(),
AstNode::Comment(inner) => inner.get_contents(),
AstNode::Drawer(inner) => inner.get_contents(),
AstNode::PropertyDrawer(inner) => inner.get_contents(),
AstNode::NodeProperty(inner) => inner.get_contents(),
AstNode::Table(inner) => inner.get_contents(),
AstNode::TableRow(inner) => inner.get_contents(),
AstNode::VerseBlock(inner) => inner.get_contents(),
AstNode::CommentBlock(inner) => inner.get_contents(),
AstNode::ExampleBlock(inner) => inner.get_contents(),
AstNode::ExportBlock(inner) => inner.get_contents(),
AstNode::SrcBlock(inner) => inner.get_contents(),
AstNode::Clock(inner) => inner.get_contents(),
AstNode::DiarySexp(inner) => inner.get_contents(),
AstNode::Planning(inner) => inner.get_contents(),
AstNode::FixedWidthArea(inner) => inner.get_contents(),
AstNode::HorizontalRule(inner) => inner.get_contents(),
AstNode::Keyword(inner) => inner.get_contents(),
AstNode::BabelCall(inner) => inner.get_contents(),
AstNode::LatexEnvironment(inner) => inner.get_contents(),
AstNode::Bold(inner) => inner.get_contents(),
AstNode::Italic(inner) => inner.get_contents(),
AstNode::Underline(inner) => inner.get_contents(),
AstNode::StrikeThrough(inner) => inner.get_contents(),
AstNode::Code(inner) => inner.get_contents(),
AstNode::Verbatim(inner) => inner.get_contents(),
AstNode::PlainText(inner) => inner.get_contents(),
AstNode::RegularLink(inner) => inner.get_contents(),
AstNode::RadioLink(inner) => inner.get_contents(),
AstNode::RadioTarget(inner) => inner.get_contents(),
AstNode::PlainLink(inner) => inner.get_contents(),
AstNode::AngleLink(inner) => inner.get_contents(),
AstNode::OrgMacro(inner) => inner.get_contents(),
AstNode::Entity(inner) => inner.get_contents(),
AstNode::LatexFragment(inner) => inner.get_contents(),
AstNode::ExportSnippet(inner) => inner.get_contents(),
AstNode::FootnoteReference(inner) => inner.get_contents(),
AstNode::Citation(inner) => inner.get_contents(),
AstNode::CitationReference(inner) => inner.get_contents(),
AstNode::InlineBabelCall(inner) => inner.get_contents(),
AstNode::InlineSourceBlock(inner) => inner.get_contents(),
AstNode::LineBreak(inner) => inner.get_contents(),
AstNode::Target(inner) => inner.get_contents(),
AstNode::StatisticsCookie(inner) => inner.get_contents(),
AstNode::Subscript(inner) => inner.get_contents(),
AstNode::Superscript(inner) => inner.get_contents(),
AstNode::TableCell(inner) => inner.get_contents(),
AstNode::Timestamp(inner) => inner.get_contents(),
}
}
fn get_post_blank<'b>(&'b self) -> &'s str {
match self {
AstNode::Document(inner) => inner.get_post_blank(),
AstNode::Heading(inner) => inner.get_post_blank(),
AstNode::Section(inner) => inner.get_post_blank(),
AstNode::Paragraph(inner) => inner.get_post_blank(),
AstNode::PlainList(inner) => inner.get_post_blank(),
AstNode::PlainListItem(inner) => inner.get_post_blank(),
AstNode::CenterBlock(inner) => inner.get_post_blank(),
AstNode::QuoteBlock(inner) => inner.get_post_blank(),
AstNode::SpecialBlock(inner) => inner.get_post_blank(),
AstNode::DynamicBlock(inner) => inner.get_post_blank(),
AstNode::FootnoteDefinition(inner) => inner.get_post_blank(),
AstNode::Comment(inner) => inner.get_post_blank(),
AstNode::Drawer(inner) => inner.get_post_blank(),
AstNode::PropertyDrawer(inner) => inner.get_post_blank(),
AstNode::NodeProperty(inner) => inner.get_post_blank(),
AstNode::Table(inner) => inner.get_post_blank(),
AstNode::TableRow(inner) => inner.get_post_blank(),
AstNode::VerseBlock(inner) => inner.get_post_blank(),
AstNode::CommentBlock(inner) => inner.get_post_blank(),
AstNode::ExampleBlock(inner) => inner.get_post_blank(),
AstNode::ExportBlock(inner) => inner.get_post_blank(),
AstNode::SrcBlock(inner) => inner.get_post_blank(),
AstNode::Clock(inner) => inner.get_post_blank(),
AstNode::DiarySexp(inner) => inner.get_post_blank(),
AstNode::Planning(inner) => inner.get_post_blank(),
AstNode::FixedWidthArea(inner) => inner.get_post_blank(),
AstNode::HorizontalRule(inner) => inner.get_post_blank(),
AstNode::Keyword(inner) => inner.get_post_blank(),
AstNode::BabelCall(inner) => inner.get_post_blank(),
AstNode::LatexEnvironment(inner) => inner.get_post_blank(),
AstNode::Bold(inner) => inner.get_post_blank(),
AstNode::Italic(inner) => inner.get_post_blank(),
AstNode::Underline(inner) => inner.get_post_blank(),
AstNode::StrikeThrough(inner) => inner.get_post_blank(),
AstNode::Code(inner) => inner.get_post_blank(),
AstNode::Verbatim(inner) => inner.get_post_blank(),
AstNode::PlainText(inner) => inner.get_post_blank(),
AstNode::RegularLink(inner) => inner.get_post_blank(),
AstNode::RadioLink(inner) => inner.get_post_blank(),
AstNode::RadioTarget(inner) => inner.get_post_blank(),
AstNode::PlainLink(inner) => inner.get_post_blank(),
AstNode::AngleLink(inner) => inner.get_post_blank(),
AstNode::OrgMacro(inner) => inner.get_post_blank(),
AstNode::Entity(inner) => inner.get_post_blank(),
AstNode::LatexFragment(inner) => inner.get_post_blank(),
AstNode::ExportSnippet(inner) => inner.get_post_blank(),
AstNode::FootnoteReference(inner) => inner.get_post_blank(),
AstNode::Citation(inner) => inner.get_post_blank(),
AstNode::CitationReference(inner) => inner.get_post_blank(),
AstNode::InlineBabelCall(inner) => inner.get_post_blank(),
AstNode::InlineSourceBlock(inner) => inner.get_post_blank(),
AstNode::LineBreak(inner) => inner.get_post_blank(),
AstNode::Target(inner) => inner.get_post_blank(),
AstNode::StatisticsCookie(inner) => inner.get_post_blank(),
AstNode::Subscript(inner) => inner.get_post_blank(),
AstNode::Superscript(inner) => inner.get_post_blank(),
AstNode::TableCell(inner) => inner.get_post_blank(),
AstNode::Timestamp(inner) => inner.get_post_blank(),
}
}
}

View File

@@ -1,7 +1,6 @@
use std::path::PathBuf;
use super::Element;
use super::GetStandardProperties;
use super::NodeProperty;
use super::Object;
use super::StandardProperties;
@@ -54,41 +53,52 @@ pub enum TodoKeywordType {
Done,
}
impl<'s> GetStandardProperties<'s> for DocumentElement<'s> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self {
DocumentElement::Heading(inner) => inner,
DocumentElement::Section(inner) => inner,
}
}
}
impl<'s> StandardProperties<'s> for Document<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Section<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Heading<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> Heading<'s> {
pub fn get_raw_value(&self) -> String {
// TODO: I think this could just return a string slice instead of an owned string.
let title_source: String = self
.title
.iter()
.map(|obj| obj.get_standard_properties().get_source())
.collect();
let title_source: String = self.title.iter().map(|obj| obj.get_source()).collect();
title_source
}
@@ -132,3 +142,26 @@ impl<'s> Document<'s> {
.flat_map(|property_drawer| property_drawer.children.iter())
}
}
impl<'s> StandardProperties<'s> for DocumentElement<'s> {
fn get_source<'b>(&'b self) -> &'s str {
match self {
DocumentElement::Heading(inner) => inner.get_source(),
DocumentElement::Section(inner) => inner.get_source(),
}
}
fn get_contents<'b>(&'b self) -> &'s str {
match self {
DocumentElement::Heading(inner) => inner.get_contents(),
DocumentElement::Section(inner) => inner.get_contents(),
}
}
fn get_post_blank<'b>(&'b self) -> &'s str {
match self {
DocumentElement::Heading(inner) => inner.get_post_blank(),
DocumentElement::Section(inner) => inner.get_post_blank(),
}
}
}

View File

@@ -20,9 +20,7 @@ use super::lesser_element::SrcBlock;
use super::lesser_element::VerseBlock;
use super::CenterBlock;
use super::Drawer;
use super::GetStandardProperties;
use super::QuoteBlock;
use super::SetSource;
use super::SpecialBlock;
use super::StandardProperties;
@@ -55,65 +53,91 @@ 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) {
impl<'s> StandardProperties<'s> for Element<'s> {
fn get_source<'b>(&'b self) -> &'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,
Element::Paragraph(inner) => inner.get_source(),
Element::PlainList(inner) => inner.get_source(),
Element::CenterBlock(inner) => inner.get_source(),
Element::QuoteBlock(inner) => inner.get_source(),
Element::SpecialBlock(inner) => inner.get_source(),
Element::DynamicBlock(inner) => inner.get_source(),
Element::FootnoteDefinition(inner) => inner.get_source(),
Element::Comment(inner) => inner.get_source(),
Element::Drawer(inner) => inner.get_source(),
Element::PropertyDrawer(inner) => inner.get_source(),
Element::Table(inner) => inner.get_source(),
Element::VerseBlock(inner) => inner.get_source(),
Element::CommentBlock(inner) => inner.get_source(),
Element::ExampleBlock(inner) => inner.get_source(),
Element::ExportBlock(inner) => inner.get_source(),
Element::SrcBlock(inner) => inner.get_source(),
Element::Clock(inner) => inner.get_source(),
Element::DiarySexp(inner) => inner.get_source(),
Element::Planning(inner) => inner.get_source(),
Element::FixedWidthArea(inner) => inner.get_source(),
Element::HorizontalRule(inner) => inner.get_source(),
Element::Keyword(inner) => inner.get_source(),
Element::BabelCall(inner) => inner.get_source(),
Element::LatexEnvironment(inner) => inner.get_source(),
}
}
}
impl<'s> GetStandardProperties<'s> for Element<'s> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
fn get_contents<'b>(&'b self) -> &'s str {
match self {
Element::Paragraph(inner) => inner,
Element::PlainList(inner) => inner,
Element::CenterBlock(inner) => inner,
Element::QuoteBlock(inner) => inner,
Element::SpecialBlock(inner) => inner,
Element::DynamicBlock(inner) => inner,
Element::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner,
Element::Drawer(inner) => inner,
Element::PropertyDrawer(inner) => inner,
Element::Table(inner) => inner,
Element::VerseBlock(inner) => inner,
Element::CommentBlock(inner) => inner,
Element::ExampleBlock(inner) => inner,
Element::ExportBlock(inner) => inner,
Element::SrcBlock(inner) => inner,
Element::Clock(inner) => inner,
Element::DiarySexp(inner) => inner,
Element::Planning(inner) => inner,
Element::FixedWidthArea(inner) => inner,
Element::HorizontalRule(inner) => inner,
Element::Keyword(inner) => inner,
Element::BabelCall(inner) => inner,
Element::LatexEnvironment(inner) => inner,
Element::Paragraph(inner) => inner.get_contents(),
Element::PlainList(inner) => inner.get_contents(),
Element::CenterBlock(inner) => inner.get_contents(),
Element::QuoteBlock(inner) => inner.get_contents(),
Element::SpecialBlock(inner) => inner.get_contents(),
Element::DynamicBlock(inner) => inner.get_contents(),
Element::FootnoteDefinition(inner) => inner.get_contents(),
Element::Comment(inner) => inner.get_contents(),
Element::Drawer(inner) => inner.get_contents(),
Element::PropertyDrawer(inner) => inner.get_contents(),
Element::Table(inner) => inner.get_contents(),
Element::VerseBlock(inner) => inner.get_contents(),
Element::CommentBlock(inner) => inner.get_contents(),
Element::ExampleBlock(inner) => inner.get_contents(),
Element::ExportBlock(inner) => inner.get_contents(),
Element::SrcBlock(inner) => inner.get_contents(),
Element::Clock(inner) => inner.get_contents(),
Element::DiarySexp(inner) => inner.get_contents(),
Element::Planning(inner) => inner.get_contents(),
Element::FixedWidthArea(inner) => inner.get_contents(),
Element::HorizontalRule(inner) => inner.get_contents(),
Element::Keyword(inner) => inner.get_contents(),
Element::BabelCall(inner) => inner.get_contents(),
Element::LatexEnvironment(inner) => inner.get_contents(),
}
}
fn get_post_blank<'b>(&'b self) -> &'s str {
match self {
Element::Paragraph(inner) => inner.get_post_blank(),
Element::PlainList(inner) => inner.get_post_blank(),
Element::CenterBlock(inner) => inner.get_post_blank(),
Element::QuoteBlock(inner) => inner.get_post_blank(),
Element::SpecialBlock(inner) => inner.get_post_blank(),
Element::DynamicBlock(inner) => inner.get_post_blank(),
Element::FootnoteDefinition(inner) => inner.get_post_blank(),
Element::Comment(inner) => inner.get_post_blank(),
Element::Drawer(inner) => inner.get_post_blank(),
Element::PropertyDrawer(inner) => inner.get_post_blank(),
Element::Table(inner) => inner.get_post_blank(),
Element::VerseBlock(inner) => inner.get_post_blank(),
Element::CommentBlock(inner) => inner.get_post_blank(),
Element::ExampleBlock(inner) => inner.get_post_blank(),
Element::ExportBlock(inner) => inner.get_post_blank(),
Element::SrcBlock(inner) => inner.get_post_blank(),
Element::Clock(inner) => inner.get_post_blank(),
Element::DiarySexp(inner) => inner.get_post_blank(),
Element::Planning(inner) => inner.get_post_blank(),
Element::FixedWidthArea(inner) => inner.get_post_blank(),
Element::HorizontalRule(inner) => inner.get_post_blank(),
Element::Keyword(inner) => inner.get_post_blank(),
Element::BabelCall(inner) => inner.get_post_blank(),
Element::LatexEnvironment(inner) => inner.get_post_blank(),
}
}
}

View File

@@ -1,12 +0,0 @@
use super::StandardProperties;
pub trait GetStandardProperties<'s> {
// TODO: Can I eliminate this dynamic dispatch, perhaps using nominal generic structs? Low prioritiy since this is not used during parsing.
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s>;
}
impl<'s, I: StandardProperties<'s>> GetStandardProperties<'s> for I {
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
self
}
}

View File

@@ -131,72 +131,168 @@ impl<'s> StandardProperties<'s> for PlainList<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for PlainListItem<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for CenterBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for QuoteBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for SpecialBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for DynamicBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for FootnoteDefinition<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Drawer<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for PropertyDrawer<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for NodeProperty<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Table<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for TableRow<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> PlainListItem<'s> {

View File

@@ -1,9 +1,25 @@
use std::borrow::Cow;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::space0;
use nom::combinator::map;
use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::multi::many_till;
use nom::sequence::tuple;
use super::object::Object;
use super::AffiliatedKeywords;
use super::GetAffiliatedKeywords;
use super::PlainText;
use super::StandardProperties;
use super::Timestamp;
use crate::error::CustomError;
use crate::error::Res;
#[derive(Debug)]
pub struct Paragraph<'s> {
@@ -59,7 +75,7 @@ pub struct ExampleBlock<'s> {
pub retain_labels: RetainLabels,
pub use_labels: bool,
pub label_format: Option<&'s str>,
pub contents: String,
pub value: &'s str,
}
#[derive(Debug)]
@@ -68,7 +84,7 @@ pub struct ExportBlock<'s> {
pub affiliated_keywords: AffiliatedKeywords<'s>,
pub export_type: Option<&'s str>,
pub data: Option<&'s str>,
pub contents: String,
pub value: &'s str,
}
#[derive(Debug)]
@@ -83,7 +99,7 @@ pub struct SrcBlock<'s> {
pub retain_labels: RetainLabels,
pub use_labels: bool,
pub label_format: Option<&'s str>,
pub contents: String,
pub value: &'s str,
}
#[derive(Debug)]
@@ -169,11 +185,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 })],
}
}
}
@@ -182,92 +198,220 @@ impl<'s> StandardProperties<'s> for Paragraph<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for TableCell<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Comment<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for VerseBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for CommentBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for ExampleBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for ExportBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for SrcBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Clock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for DiarySexp<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Planning<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for FixedWidthArea<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for HorizontalRule<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Keyword<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for BabelCall<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> Comment<'s> {
@@ -294,6 +438,13 @@ impl<'s> FixedWidthArea<'s> {
}
}
impl<'s> ExampleBlock<'s> {
/// Gets the contents of the lesser block, handling the escaping of lines with leading commas.
pub fn get_value(&self) -> Cow<'s, str> {
lesser_block_content(self.value).expect("This parser should never fail.")
}
}
impl<'s> ExportBlock<'s> {
/// Gets the export type capitalized.
///
@@ -301,6 +452,18 @@ impl<'s> ExportBlock<'s> {
pub fn get_export_type(&self) -> Option<String> {
self.export_type.map(|s| s.to_uppercase())
}
/// Gets the contents of the lesser block, handling the escaping of lines with leading commas.
pub fn get_value(&self) -> Cow<'s, str> {
lesser_block_content(self.value).expect("This parser should never fail.")
}
}
impl<'s> SrcBlock<'s> {
/// Gets the contents of the lesser block, handling the escaping of lines with leading commas.
pub fn get_value(&self) -> Cow<'s, str> {
lesser_block_content(self.value).expect("This parser should never fail.")
}
}
impl<'s> GetAffiliatedKeywords<'s> for Paragraph<'s> {
@@ -374,3 +537,82 @@ impl<'s> GetAffiliatedKeywords<'s> for VerseBlock<'s> {
&self.affiliated_keywords
}
}
enum ContentState {
Normal,
Modified(String),
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn lesser_block_content<'s>(input: &'s str) -> Result<Cow<'s, str>, CustomError> {
let mut state = ContentState::Normal;
let mut remaining = input;
loop {
if remaining.is_empty() {
break;
}
let (remain, (pre_escape_whitespace, line)) =
content_line(remaining).map_err(|err| match err {
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;
if let Some(val) = pre_escape_whitespace {
if let ContentState::Modified(ref mut ret) = state {
ret.push_str(val);
} else {
let mut ret = String::new();
ret.push_str(get_str_until(input, remaining));
ret.push_str(val);
state = ContentState::Modified(ret);
}
}
if let ContentState::Modified(ref mut ret) = state {
ret.push_str(line);
}
remaining = remain;
}
match state {
ContentState::Normal => Ok(Cow::Borrowed(get_str_until(input, remaining))),
ContentState::Modified(ret) => Ok(Cow::Owned(ret)),
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn content_line<'s>(input: &'s str) -> Res<&'s str, (Option<&'s str>, &'s str)> {
let (remaining, pre_escape_whitespace) = opt(map(
tuple((
recognize(tuple((
space0,
many_till(
tag(","),
peek(tuple((tag(","), alt((tag("#+"), tag("*")))))),
),
))),
tag(","),
)),
|(pre_comma, _)| pre_comma,
))(input)?;
let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?;
Ok((remaining, (pre_escape_whitespace, line_post_escape)))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
/// Check if the child string slice is a slice of the parent string slice.
fn is_slice_of(parent: &str, child: &str) -> bool {
let parent_start = parent.as_ptr() as usize;
let parent_end = parent_start + parent.len();
let child_start = child.as_ptr() as usize;
let child_end = child_start + child.len();
child_start >= parent_start && child_end <= parent_end
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn get_str_until<'s>(parent: &'s str, child: &'s str) -> &'s str {
debug_assert!(is_slice_of(parent, child));
let parent_start = parent.as_ptr() as usize;
let child_start = child.as_ptr() as usize;
&parent[..(child_start - parent_start)]
}

View File

@@ -2,19 +2,17 @@ mod affiliated_keyword;
mod ast_node;
mod document;
mod element;
mod get_standard_properties;
mod greater_element;
mod lesser_element;
mod macros;
mod object;
mod source;
mod standard_properties;
mod util;
pub use affiliated_keyword::AffiliatedKeyword;
pub use affiliated_keyword::AffiliatedKeywordValue;
pub use affiliated_keyword::AffiliatedKeywords;
pub use affiliated_keyword::GetAffiliatedKeywords;
pub(crate) use ast_node::AstNode;
pub use ast_node::AstNode;
pub use document::Document;
pub use document::DocumentElement;
pub use document::Heading;
@@ -23,7 +21,6 @@ pub use document::PriorityCookie;
pub use document::Section;
pub use document::TodoKeywordType;
pub use element::Element;
pub use get_standard_properties::GetStandardProperties;
pub use greater_element::CenterBlock;
pub use greater_element::CheckboxType;
pub use greater_element::Drawer;
@@ -113,5 +110,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

@@ -6,11 +6,9 @@ use super::util::coalesce_whitespace_if_line_break;
use super::util::remove_line_break;
use super::util::remove_whitespace_if_line_break;
use super::util::to_lowercase;
use super::GetStandardProperties;
use super::StandardProperties;
// TODO: Why did we make Object implement PartialEq again? Was it just for tests?
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub enum Object<'s> {
Bold(Bold<'s>),
Italic(Italic<'s>),
@@ -41,48 +39,48 @@ pub enum Object<'s> {
Timestamp(Timestamp<'s>),
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Bold<'s> {
pub source: &'s str,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Italic<'s> {
pub source: &'s str,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Underline<'s> {
pub source: &'s str,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct StrikeThrough<'s> {
pub source: &'s str,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Code<'s> {
pub source: &'s str,
pub contents: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Verbatim<'s> {
pub source: &'s str,
pub contents: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct PlainText<'s> {
pub source: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct RegularLink<'s> {
pub source: &'s str,
pub link_type: LinkType<'s>,
@@ -105,21 +103,21 @@ pub struct RegularLink<'s> {
pub application: Option<Cow<'s, str>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct RadioTarget<'s> {
pub source: &'s str,
pub value: &'s str,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct RadioLink<'s> {
pub source: &'s str,
pub path: &'s str,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct PlainLink<'s> {
pub source: &'s str,
pub link_type: LinkType<'s>,
@@ -129,7 +127,7 @@ pub struct PlainLink<'s> {
pub application: Option<&'s str>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct AngleLink<'s> {
pub source: &'s str,
pub link_type: LinkType<'s>,
@@ -147,7 +145,7 @@ pub struct AngleLink<'s> {
pub application: Option<&'s str>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct OrgMacro<'s> {
pub source: &'s str,
@@ -164,7 +162,7 @@ pub struct OrgMacro<'s> {
pub value: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Entity<'s> {
pub source: &'s str,
pub name: &'s str,
@@ -177,27 +175,27 @@ pub struct Entity<'s> {
pub use_brackets: bool,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct LatexFragment<'s> {
pub source: &'s str,
pub value: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct ExportSnippet<'s> {
pub source: &'s str,
pub backend: &'s str,
pub contents: Option<&'s str>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct FootnoteReference<'s> {
pub source: &'s str,
pub label: Option<&'s str>,
pub definition: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Citation<'s> {
pub source: &'s str,
pub style: Option<&'s str>,
@@ -206,7 +204,7 @@ pub struct Citation<'s> {
pub children: Vec<CitationReference<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct CitationReference<'s> {
pub source: &'s str,
pub key: &'s str,
@@ -214,7 +212,7 @@ pub struct CitationReference<'s> {
pub suffix: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct InlineBabelCall<'s> {
pub source: &'s str,
pub value: &'s str,
@@ -224,7 +222,7 @@ pub struct InlineBabelCall<'s> {
pub end_header: Option<&'s str>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct InlineSourceBlock<'s> {
pub source: &'s str,
pub language: &'s str,
@@ -232,31 +230,31 @@ pub struct InlineSourceBlock<'s> {
pub value: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct LineBreak<'s> {
pub source: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Target<'s> {
pub source: &'s str,
pub value: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct StatisticsCookie<'s> {
pub source: &'s str,
pub value: &'s str,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Subscript<'s> {
pub source: &'s str,
pub use_brackets: bool,
pub children: Vec<Object<'s>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Superscript<'s> {
pub source: &'s str,
pub use_brackets: bool,
@@ -264,7 +262,7 @@ pub struct Superscript<'s> {
}
// TODO: Perhaps there is an optimization of converting to unix time we can do to shrink this struct. (ref: clippy::large_enum_variant on Element)
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Timestamp<'s> {
pub source: &'s str,
pub timestamp_type: TimestampType,
@@ -277,7 +275,7 @@ pub struct Timestamp<'s> {
pub warning_delay: Option<WarningDelay>,
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub enum TimestampType {
Diary,
Active,
@@ -286,7 +284,7 @@ pub enum TimestampType {
InactiveRange,
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub enum TimestampRangeType {
None,
DateRange,
@@ -299,19 +297,19 @@ pub type DayOfMonthInner = u8;
pub type HourInner = u8;
pub type MinuteInner = u8;
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Year(YearInner);
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Month(MonthInner);
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct DayOfMonth(DayOfMonthInner);
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Hour(HourInner);
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Minute(MinuteInner);
impl Year {
@@ -386,7 +384,7 @@ impl Minute {
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Date<'s> {
year: Year,
month: Month,
@@ -444,7 +442,7 @@ impl<'s> Date<'s> {
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Time<'s> {
hour: Hour,
minute: Minute,
@@ -478,20 +476,20 @@ impl<'s> Time<'s> {
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub enum RepeaterType {
Cumulative,
CatchUp,
Restart,
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub enum WarningDelayType {
All,
First,
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub enum TimeUnit {
Hour,
Day,
@@ -502,214 +500,396 @@ pub enum TimeUnit {
pub type RepeaterWarningDelayValueType = u16;
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Repeater {
pub repeater_type: RepeaterType,
pub value: RepeaterWarningDelayValueType,
pub unit: TimeUnit,
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct WarningDelay {
pub warning_delay_type: WarningDelayType,
pub value: RepeaterWarningDelayValueType,
pub unit: TimeUnit,
}
impl<'s> GetStandardProperties<'s> for Object<'s> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self {
Object::Bold(inner) => inner,
Object::Italic(inner) => inner,
Object::Underline(inner) => inner,
Object::StrikeThrough(inner) => inner,
Object::Code(inner) => inner,
Object::Verbatim(inner) => inner,
Object::PlainText(inner) => inner,
Object::RegularLink(inner) => inner,
Object::RadioLink(inner) => inner,
Object::RadioTarget(inner) => inner,
Object::PlainLink(inner) => inner,
Object::AngleLink(inner) => inner,
Object::OrgMacro(inner) => inner,
Object::Entity(inner) => inner,
Object::LatexFragment(inner) => inner,
Object::ExportSnippet(inner) => inner,
Object::FootnoteReference(inner) => inner,
Object::Citation(inner) => inner,
Object::CitationReference(inner) => inner,
Object::InlineBabelCall(inner) => inner,
Object::InlineSourceBlock(inner) => inner,
Object::LineBreak(inner) => inner,
Object::Target(inner) => inner,
Object::StatisticsCookie(inner) => inner,
Object::Subscript(inner) => inner,
Object::Superscript(inner) => inner,
Object::Timestamp(inner) => inner,
}
}
}
impl<'s> StandardProperties<'s> for Bold<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Italic<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Underline<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for StrikeThrough<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Code<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Verbatim<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for RegularLink<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for RadioLink<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for RadioTarget<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for PlainLink<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for AngleLink<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for OrgMacro<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Entity<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for LatexFragment<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for ExportSnippet<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for FootnoteReference<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Citation<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for CitationReference<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for InlineBabelCall<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for InlineSourceBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for LineBreak<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Target<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for StatisticsCookie<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Subscript<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Superscript<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for Timestamp<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> StandardProperties<'s> for PlainText<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
fn get_contents<'b>(&'b self) -> &'s str {
todo!()
}
fn get_post_blank<'b>(&'b self) -> &'s str {
todo!()
}
}
impl<'s> Timestamp<'s> {
@@ -718,7 +898,7 @@ impl<'s> Timestamp<'s> {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub enum LinkType<'s> {
File,
Protocol(Cow<'s, str>),
@@ -787,7 +967,7 @@ impl<'s> OrgMacro<'s> {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub enum FootnoteReferenceType {
Standard,
Inline,
@@ -802,3 +982,101 @@ impl<'s> FootnoteReference<'s> {
}
}
}
impl<'s> StandardProperties<'s> for Object<'s> {
fn get_source<'b>(&'b self) -> &'s str {
match self {
Object::Bold(inner) => inner.get_source(),
Object::Italic(inner) => inner.get_source(),
Object::Underline(inner) => inner.get_source(),
Object::StrikeThrough(inner) => inner.get_source(),
Object::Code(inner) => inner.get_source(),
Object::Verbatim(inner) => inner.get_source(),
Object::PlainText(inner) => inner.get_source(),
Object::RegularLink(inner) => inner.get_source(),
Object::RadioLink(inner) => inner.get_source(),
Object::RadioTarget(inner) => inner.get_source(),
Object::PlainLink(inner) => inner.get_source(),
Object::AngleLink(inner) => inner.get_source(),
Object::OrgMacro(inner) => inner.get_source(),
Object::Entity(inner) => inner.get_source(),
Object::LatexFragment(inner) => inner.get_source(),
Object::ExportSnippet(inner) => inner.get_source(),
Object::FootnoteReference(inner) => inner.get_source(),
Object::Citation(inner) => inner.get_source(),
Object::CitationReference(inner) => inner.get_source(),
Object::InlineBabelCall(inner) => inner.get_source(),
Object::InlineSourceBlock(inner) => inner.get_source(),
Object::LineBreak(inner) => inner.get_source(),
Object::Target(inner) => inner.get_source(),
Object::StatisticsCookie(inner) => inner.get_source(),
Object::Subscript(inner) => inner.get_source(),
Object::Superscript(inner) => inner.get_source(),
Object::Timestamp(inner) => inner.get_source(),
}
}
fn get_contents<'b>(&'b self) -> &'s str {
match self {
Object::Bold(inner) => inner.get_contents(),
Object::Italic(inner) => inner.get_contents(),
Object::Underline(inner) => inner.get_contents(),
Object::StrikeThrough(inner) => inner.get_contents(),
Object::Code(inner) => inner.get_contents(),
Object::Verbatim(inner) => inner.get_contents(),
Object::PlainText(inner) => inner.get_contents(),
Object::RegularLink(inner) => inner.get_contents(),
Object::RadioLink(inner) => inner.get_contents(),
Object::RadioTarget(inner) => inner.get_contents(),
Object::PlainLink(inner) => inner.get_contents(),
Object::AngleLink(inner) => inner.get_contents(),
Object::OrgMacro(inner) => inner.get_contents(),
Object::Entity(inner) => inner.get_contents(),
Object::LatexFragment(inner) => inner.get_contents(),
Object::ExportSnippet(inner) => inner.get_contents(),
Object::FootnoteReference(inner) => inner.get_contents(),
Object::Citation(inner) => inner.get_contents(),
Object::CitationReference(inner) => inner.get_contents(),
Object::InlineBabelCall(inner) => inner.get_contents(),
Object::InlineSourceBlock(inner) => inner.get_contents(),
Object::LineBreak(inner) => inner.get_contents(),
Object::Target(inner) => inner.get_contents(),
Object::StatisticsCookie(inner) => inner.get_contents(),
Object::Subscript(inner) => inner.get_contents(),
Object::Superscript(inner) => inner.get_contents(),
Object::Timestamp(inner) => inner.get_contents(),
}
}
fn get_post_blank<'b>(&'b self) -> &'s str {
match self {
Object::Bold(inner) => inner.get_post_blank(),
Object::Italic(inner) => inner.get_post_blank(),
Object::Underline(inner) => inner.get_post_blank(),
Object::StrikeThrough(inner) => inner.get_post_blank(),
Object::Code(inner) => inner.get_post_blank(),
Object::Verbatim(inner) => inner.get_post_blank(),
Object::PlainText(inner) => inner.get_post_blank(),
Object::RegularLink(inner) => inner.get_post_blank(),
Object::RadioLink(inner) => inner.get_post_blank(),
Object::RadioTarget(inner) => inner.get_post_blank(),
Object::PlainLink(inner) => inner.get_post_blank(),
Object::AngleLink(inner) => inner.get_post_blank(),
Object::OrgMacro(inner) => inner.get_post_blank(),
Object::Entity(inner) => inner.get_post_blank(),
Object::LatexFragment(inner) => inner.get_post_blank(),
Object::ExportSnippet(inner) => inner.get_post_blank(),
Object::FootnoteReference(inner) => inner.get_post_blank(),
Object::Citation(inner) => inner.get_post_blank(),
Object::CitationReference(inner) => inner.get_post_blank(),
Object::InlineBabelCall(inner) => inner.get_post_blank(),
Object::InlineSourceBlock(inner) => inner.get_post_blank(),
Object::LineBreak(inner) => inner.get_post_blank(),
Object::Target(inner) => inner.get_post_blank(),
Object::StatisticsCookie(inner) => inner.get_post_blank(),
Object::Subscript(inner) => inner.get_post_blank(),
Object::Superscript(inner) => inner.get_post_blank(),
Object::Timestamp(inner) => inner.get_post_blank(),
}
}
}

View File

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

View File

@@ -5,10 +5,15 @@ pub trait StandardProperties<'s> {
/// This corresponds to :begin to :end in upstream org-mode's standard properties.
fn get_source<'b>(&'b self) -> &'s str;
// Get the slice of the AST node's contents.
//
// This corresponds to :contents-begin to :contents-end
// fn get_contents(&'s self) -> &'s str;
/// Get the slice of the AST node's contents.
///
/// This corresponds to :contents-begin to :contents-end
fn get_contents<'b>(&'b self) -> &'s str;
/// Get the slice of the AST node's post-blank text.
///
/// This is optional whitespace following the node.
fn get_post_blank<'b>(&'b self) -> &'s str;
}
// TODO: Write some debugging code to alert when any of the unknown fields below are non-nil in our test data so we can see what these fields represent.