6 Commits

Author SHA1 Message Date
Tom Alexander
26f41b83aa Publish version 0.1.7.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-09-12 16:15:53 -04:00
Tom Alexander
e4c0e32536 Change public interface to return boxed dynamic error instead of String.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-09-12 15:52:01 -04:00
Tom Alexander
37e85158ea Reduce the exposed functions when compare feature is enabled. 2023-09-12 15:48:37 -04:00
Tom Alexander
6589a755a6 Merge branch 'reduce_pub'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-09-11 15:37:33 -04:00
Tom Alexander
a651b79e77 Move sexp into compare. 2023-09-11 15:37:20 -04:00
Tom Alexander
98de5e4ec5 Remove unused sexp parser entry point. 2023-09-11 15:07:52 -04:00
14 changed files with 129 additions and 146 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "organic" name = "organic"
version = "0.1.6" version = "0.1.7"
authors = ["Tom Alexander <tom@fizz.buzz>"] authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser." description = "An org-mode parser."
edition = "2021" edition = "2021"

View File

@@ -71,10 +71,6 @@ fn write_header(test_file: &mut File) {
test_file, test_file,
r#" r#"
#[feature(exit_status_error)] #[feature(exit_status_error)]
use organic::compare_document;
use organic::parser::parse;
use organic::emacs_parse_anonymous_org_document;
use organic::parser::sexp::sexp_with_padding;
"# "#
) )

View File

@@ -1,18 +1,9 @@
#![feature(round_char_boundary)] #![feature(round_char_boundary)]
#![feature(exact_size_is_empty)] #![feature(exact_size_is_empty)]
use std::io::Read; use std::io::Read;
use std::path::Path;
use ::organic::parser::parse; use organic::compare::run_anonymous_compare;
use organic::compare_document; use organic::compare::run_compare_on_file;
use organic::emacs_parse_anonymous_org_document;
use organic::emacs_parse_file_org_document;
use organic::get_emacs_version;
use organic::get_org_mode_version;
use organic::parser::parse_with_settings;
use organic::parser::sexp::sexp_with_padding;
use organic::GlobalSettings;
use organic::LocalFileAccessInterface;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
use crate::init_tracing::init_telemetry; use crate::init_tracing::init_telemetry;
@@ -43,10 +34,10 @@ fn main_body() -> Result<(), Box<dyn std::error::Error>> {
let args = std::env::args().skip(1); let args = std::env::args().skip(1);
if args.is_empty() { if args.is_empty() {
let org_contents = read_stdin_to_string()?; let org_contents = read_stdin_to_string()?;
run_anonymous_parse(org_contents) run_anonymous_compare(org_contents)
} else { } else {
for arg in args { for arg in args {
run_parse_on_file(arg)? run_compare_on_file(arg)?
} }
Ok(()) Ok(())
} }
@@ -59,64 +50,3 @@ fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
.read_to_string(&mut stdin_contents)?; .read_to_string(&mut stdin_contents)?;
Ok(stdin_contents) Ok(stdin_contents)
} }
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
let org_contents = org_contents.as_ref();
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
let rust_parsed = parse(org_contents)?;
let org_sexp = emacs_parse_anonymous_org_document(org_contents)?;
let (_remaining, parsed_sexp) =
sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
// We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?;
if diff_result.is_bad() {
Err("Diff results do not match.")?;
}
Ok(())
}
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
let org_path = org_path.as_ref();
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
let parent_directory = org_path
.parent()
.ok_or("Should be contained inside a directory.")?;
let org_contents = std::fs::read_to_string(org_path)?;
let org_contents = org_contents.as_str();
let file_access_interface = LocalFileAccessInterface {
working_directory: Some(parent_directory.to_path_buf()),
};
let global_settings = {
let mut global_settings = GlobalSettings::default();
global_settings.file_access = &file_access_interface;
global_settings
};
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
let org_sexp = emacs_parse_file_org_document(org_path)?;
let (_remaining, parsed_sexp) =
sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
// We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?;
if diff_result.is_bad() {
Err("Diff results do not match.")?;
}
Ok(())
}

73
src/compare/compare.rs Normal file
View File

@@ -0,0 +1,73 @@
use std::path::Path;
use crate::compare::diff::compare_document;
use crate::compare::parse::emacs_parse_anonymous_org_document;
use crate::compare::parse::emacs_parse_file_org_document;
use crate::compare::parse::get_emacs_version;
use crate::compare::parse::get_org_mode_version;
use crate::compare::sexp::sexp;
use crate::parser::parse;
use crate::parser::parse_with_settings;
use crate::GlobalSettings;
use crate::LocalFileAccessInterface;
pub fn run_anonymous_compare<P: AsRef<str>>(
org_contents: P,
) -> Result<(), Box<dyn std::error::Error>> {
let org_contents = org_contents.as_ref();
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
let rust_parsed = parse(org_contents)?;
let org_sexp = emacs_parse_anonymous_org_document(org_contents)?;
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
// We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?;
if diff_result.is_bad() {
Err("Diff results do not match.")?;
}
Ok(())
}
pub fn run_compare_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
let org_path = org_path.as_ref();
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
let parent_directory = org_path
.parent()
.ok_or("Should be contained inside a directory.")?;
let org_contents = std::fs::read_to_string(org_path)?;
let org_contents = org_contents.as_str();
let file_access_interface = LocalFileAccessInterface {
working_directory: Some(parent_directory.to_path_buf()),
};
let global_settings = {
let mut global_settings = GlobalSettings::default();
global_settings.file_access = &file_access_interface;
global_settings
};
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
let org_sexp = emacs_parse_file_org_document(org_path)?;
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
// We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?;
if diff_result.is_bad() {
Err("Diff results do not match.")?;
}
Ok(())
}

View File

@@ -1,11 +1,11 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::collections::HashSet; use std::collections::HashSet;
use super::sexp::unquote;
use super::sexp::Token;
use super::util::assert_bounds; use super::util::assert_bounds;
use super::util::assert_name; use super::util::assert_name;
use super::util::get_property; use super::util::get_property;
use crate::parser::sexp::unquote;
use crate::parser::sexp::Token;
use crate::types::AngleLink; use crate::types::AngleLink;
use crate::types::Bold; use crate::types::Bold;
use crate::types::Citation; use crate::types::Citation;

View File

@@ -1,8 +1,7 @@
mod compare;
mod diff; mod diff;
mod parse; mod parse;
mod sexp;
mod util; mod util;
pub use diff::compare_document; pub use compare::run_anonymous_compare;
pub use parse::emacs_parse_anonymous_org_document; pub use compare::run_compare_on_file;
pub use parse::emacs_parse_file_org_document;
pub use parse::get_emacs_version;
pub use parse::get_org_mode_version;

View File

@@ -16,9 +16,6 @@ use nom::sequence::delimited;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::convert_error;
use super::org_source::OrgSource;
use super::util::get_consumed;
use crate::error::Res; use crate::error::Res;
#[derive(Debug)] #[derive(Debug)]
@@ -99,6 +96,25 @@ impl<'s> Token<'s> {
} }
} }
/// 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
}
/// Get a slice of the string that was consumed in a parser using the original input to the parser and the remaining input after the parser.
pub fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
assert!(is_slice_of(input, remaining));
let source = {
let offset = remaining.as_ptr() as usize - input.as_ptr() as usize;
&input[..offset]
};
source.into()
}
pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> { pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut out = String::with_capacity(text.len()); let mut out = String::with_capacity(text.len());
if !text.starts_with(r#"""#) { if !text.starts_with(r#"""#) {
@@ -136,29 +152,20 @@ pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>>
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { pub fn sexp<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = multispace0(input)?; let (remaining, _) = multispace0(input)?;
let remaining = OrgSource::new(remaining); let (remaining, tkn) = token(remaining).map(|(rem, out)| (Into::<&str>::into(rem), out))?;
let (remaining, tkn) = token(remaining)
.map(|(rem, out)| (Into::<&str>::into(rem), out))
.map_err(convert_error)?;
let (remaining, _) = multispace0(remaining)?; let (remaining, _) = multispace0(remaining)?;
Ok((remaining, tkn)) Ok((remaining, tkn))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn sexp<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn token<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, tkn) = token(input)?;
Ok((remaining, tkn))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn token<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
alt((list, vector, atom))(input) alt((list, vector, atom))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn list<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag("(")(input)?; let (remaining, _) = tag("(")(input)?;
let (remaining, children) = delimited( let (remaining, children) = delimited(
multispace0, multispace0,
@@ -170,7 +177,7 @@ fn list<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn vector<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn vector<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let (remaining, children) = delimited( let (remaining, children) = delimited(
multispace0, multispace0,
@@ -182,7 +189,7 @@ fn vector<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
not(peek(one_of(")]")))(input)?; not(peek(one_of(")]")))(input)?;
alt(( alt((
text_with_properties, text_with_properties,
@@ -193,7 +200,7 @@ fn atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn unquoted_atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn unquoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, body) = take_till1(|c| match c { let (remaining, body) = take_till1(|c| match c {
' ' | '\t' | '\r' | '\n' | ')' | ']' => true, ' ' | '\t' | '\r' | '\n' | ')' | ']' => true,
_ => false, _ => false,
@@ -202,7 +209,7 @@ fn unquoted_atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn quoted_atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn quoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag(r#"""#)(input)?; let (remaining, _) = tag(r#"""#)(input)?;
let (remaining, _) = escaped( let (remaining, _) = escaped(
take_till1(|c| match c { take_till1(|c| match c {
@@ -218,7 +225,7 @@ fn quoted_atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn hash_notation<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn hash_notation<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag("#<")(input)?; let (remaining, _) = tag("#<")(input)?;
let (remaining, _body) = take_till1(|c| match c { let (remaining, _body) = take_till1(|c| match c {
'>' => true, '>' => true,
@@ -229,7 +236,7 @@ fn hash_notation<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
Ok((remaining, Token::Atom(source.into()))) Ok((remaining, Token::Atom(source.into())))
} }
fn text_with_properties<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> { fn text_with_properties<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag("#(")(input)?; let (remaining, _) = tag("#(")(input)?;
let (remaining, (text, props)) = delimited( let (remaining, (text, props)) = delimited(
multispace0, multispace0,
@@ -259,7 +266,7 @@ mod tests {
#[test] #[test]
fn simple() { fn simple() {
let input = " (foo bar baz ) "; let input = " (foo bar baz ) ";
let (remaining, parsed) = sexp_with_padding(input).expect("Parse the input"); let (remaining, parsed) = sexp(input).expect("Parse the input");
assert_eq!(remaining, ""); assert_eq!(remaining, "");
assert!(match parsed { assert!(match parsed {
Token::Atom(_) => false, Token::Atom(_) => false,
@@ -272,7 +279,7 @@ mod tests {
#[test] #[test]
fn quoted() { fn quoted() {
let input = r#" ("foo" bar baz ) "#; let input = r#" ("foo" bar baz ) "#;
let (remaining, parsed) = sexp_with_padding(input).expect("Parse the input"); let (remaining, parsed) = sexp(input).expect("Parse the input");
assert_eq!(remaining, ""); assert_eq!(remaining, "");
assert!(match parsed { assert!(match parsed {
Token::Atom(_) => false, Token::Atom(_) => false,
@@ -296,7 +303,7 @@ mod tests {
#[test] #[test]
fn quoted_containing_paren() { fn quoted_containing_paren() {
let input = r#" (foo "b(a)r" baz ) "#; let input = r#" (foo "b(a)r" baz ) "#;
let (remaining, parsed) = sexp_with_padding(input).expect("Parse the input"); let (remaining, parsed) = sexp(input).expect("Parse the input");
assert_eq!(remaining, ""); assert_eq!(remaining, "");
assert!(match parsed { assert!(match parsed {
Token::List(_) => true, Token::List(_) => true,
@@ -332,7 +339,7 @@ mod tests {
#[test] #[test]
fn string_containing_escaped_characters() { fn string_containing_escaped_characters() {
let input = r#" (foo "\\( x=2 \\)" bar) "#; let input = r#" (foo "\\( x=2 \\)" bar) "#;
let (remaining, parsed) = sexp_with_padding(input).expect("Parse the input"); let (remaining, parsed) = sexp(input).expect("Parse the input");
assert_eq!(remaining, ""); assert_eq!(remaining, "");
assert!(match parsed { assert!(match parsed {
Token::Atom(_) => false, Token::Atom(_) => false,

View File

@@ -1,4 +1,4 @@
use crate::parser::sexp::Token; use super::sexp::Token;
use crate::types::Source; use crate::types::Source;
/// Check if the child string slice is a slice of the parent string slice. /// Check if the child string slice is a slice of the parent string slice.

View File

@@ -4,17 +4,7 @@
// TODO: #![warn(missing_docs)] // TODO: #![warn(missing_docs)]
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
mod compare; pub mod compare;
#[cfg(feature = "compare")]
pub use compare::compare_document;
#[cfg(feature = "compare")]
pub use compare::emacs_parse_anonymous_org_document;
#[cfg(feature = "compare")]
pub use compare::emacs_parse_file_org_document;
#[cfg(feature = "compare")]
pub use compare::get_emacs_version;
#[cfg(feature = "compare")]
pub use compare::get_org_mode_version;
mod context; mod context;
mod error; mod error;

View File

@@ -28,7 +28,7 @@ use crate::types::Object;
/// ///
/// This is the main entry point for Organic. It will parse the full contents of the input string as an org-mode document. /// This is the main entry point for Organic. It will parse the full contents of the input string as an org-mode document.
#[allow(dead_code)] #[allow(dead_code)]
pub fn parse<'s>(input: &'s str) -> Result<Document<'s>, String> { pub fn parse<'s>(input: &'s str) -> Result<Document<'s>, Box<dyn std::error::Error>> {
parse_with_settings(input, &GlobalSettings::default()) parse_with_settings(input, &GlobalSettings::default())
} }
@@ -41,7 +41,7 @@ pub fn parse<'s>(input: &'s str) -> Result<Document<'s>, String> {
pub fn parse_with_settings<'g, 's>( pub fn parse_with_settings<'g, 's>(
input: &'s str, input: &'s str,
global_settings: &'g GlobalSettings<'g, 's>, global_settings: &'g GlobalSettings<'g, 's>,
) -> Result<Document<'s>, String> { ) -> Result<Document<'s>, Box<dyn std::error::Error>> {
let initial_context = ContextElement::document_context(); let initial_context = ContextElement::document_context();
let initial_context = Context::new(global_settings, List::new(&initial_context)); let initial_context = Context::new(global_settings, List::new(&initial_context));
let wrapped_input = OrgSource::new(input); let wrapped_input = OrgSource::new(input);
@@ -49,7 +49,7 @@ pub fn parse_with_settings<'g, 's>(
all_consuming(parser_with_context!(document_org_source)(&initial_context))(wrapped_input) all_consuming(parser_with_context!(document_org_source)(&initial_context))(wrapped_input)
.map_err(|err| err.to_string()) .map_err(|err| err.to_string())
.map(|(_remaining, parsed_document)| parsed_document); .map(|(_remaining, parsed_document)| parsed_document);
ret Ok(ret?)
} }
/// Parse a full org-mode document. /// Parse a full org-mode document.

View File

@@ -37,8 +37,6 @@ mod property_drawer;
mod radio_link; mod radio_link;
mod regular_link; mod regular_link;
mod section; mod section;
#[cfg(feature = "compare")]
pub mod sexp;
mod statistics_cookie; mod statistics_cookie;
mod subscript_and_superscript; mod subscript_and_superscript;
mod table; mod table;

View File

@@ -64,5 +64,5 @@ pub use object::Target;
pub use object::Timestamp; pub use object::Timestamp;
pub use object::Underline; pub use object::Underline;
pub use object::Verbatim; pub use object::Verbatim;
pub use source::SetSource; pub(crate) use source::SetSource;
pub use source::Source; pub use source::Source;

View File

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

View File

@@ -1,17 +1,7 @@
#[test] #[test]
fn {name}() {{ fn {name}() -> Result<(), Box<dyn std::error::Error>> {{
let todo_org_path = "{path}"; let org_path = "{path}";
let org_contents = std::fs::read_to_string(todo_org_path).expect("Read org file."); let org_contents = std::fs::read_to_string(org_path).expect("Read org file.");
println!("{{}}", org_contents); organic::compare::run_anonymous_compare(org_contents.as_str())?;
let org_sexp = emacs_parse_anonymous_org_document(org_contents.as_str()).expect("Use emacs to parse org file."); Ok(())
println!("{{}}", org_sexp);
let (_remaining, parsed_sexp) = sexp_with_padding(org_sexp.as_str()).expect("Sexp Parse failure");
let rust_parsed = parse(org_contents.as_str()).expect("Org Parse failure");
println!("{{:#?}}", rust_parsed);
let diff_result =
compare_document(&parsed_sexp, &rust_parsed).expect("Compare parsed documents.");
diff_result
.print(org_contents.as_str())
.expect("Print document parse tree diff.");
assert!(!diff_result.is_bad());
}} }}