diff --git a/src/bin_wasm.rs b/src/bin_wasm.rs index 242c235f..4a2212c4 100644 --- a/src/bin_wasm.rs +++ b/src/bin_wasm.rs @@ -3,15 +3,25 @@ use organic::parser::parse_with_settings; use organic::settings::GlobalSettings; use wasm::ParseResult; +use wasm::ToWasm; +use wasm::ToWasmContext; use wasm_bindgen::prelude::wasm_bindgen; +mod error; mod wasm; #[wasm_bindgen] pub fn parse_org(org_contents: &str) -> wasm_bindgen::JsValue { let global_settings = GlobalSettings::default(); - let rust_parsed = parse_with_settings(org_contents, &global_settings) - .map(|document| ParseResult::Success((org_contents, document).into())) - .unwrap_or_else(|err| ParseResult::Error(format!("{:?}", err))); + let to_wasm_context = ToWasmContext::new(org_contents); + let rust_parsed = match parse_with_settings(org_contents, &global_settings) + .map(|document| document.to_wasm(to_wasm_context)) + .map(|wasm_document| match wasm_document { + Ok(wasm_document) => ParseResult::Success(wasm_document), + Err(err) => ParseResult::Error(format!("{:?}", err)), + }) { + Ok(wasm_document) => wasm_document, + Err(err) => ParseResult::Error(format!("{:?}", err)), + }; serde_wasm_bindgen::to_value(&rust_parsed).unwrap() } diff --git a/src/wasm/document.rs b/src/wasm/document.rs index abd73e28..9e80c88a 100644 --- a/src/wasm/document.rs +++ b/src/wasm/document.rs @@ -3,16 +3,27 @@ use serde::Deserialize; use serde::Serialize; use super::macros::to_wasm; +use super::standard_properties::WasmStandardProperties; +use super::to_wasm::ToWasm; +use crate::wasm::to_wasm::ToWasmStandardProperties; #[derive(Serialize, Deserialize)] #[serde(tag = "ast_node")] #[serde(rename = "org-data")] pub(crate) struct WasmDocument { + standard_properties: WasmStandardProperties, children: Vec<()>, } -to_wasm!(WasmDocument, Document<'s>, original, document, { - WasmDocument { - children: Vec::new(), +to_wasm!( + WasmDocument, + Document<'s>, + wasm_context, + standard_properties, + { + Ok(WasmDocument { + standard_properties, + children: Vec::new(), + }) } -}); +); diff --git a/src/wasm/macros.rs b/src/wasm/macros.rs index 8d02399b..76f5c272 100644 --- a/src/wasm/macros.rs +++ b/src/wasm/macros.rs @@ -2,10 +2,15 @@ /// /// This exists to make changing the type signature easier. macro_rules! to_wasm { - ($ostruct:ty, $istruct:ty, $original:ident, $document:ident, $fnbody:tt) => { - impl<'s> From<(&'s str, $istruct)> for $ostruct { - fn from(value: (&'s str, $istruct)) -> $ostruct { - let ($original, $document) = value; + ($ostruct:ty, $istruct:ty, $wasm_context:ident, $standard_properties:ident, $fnbody:tt) => { + impl<'s> ToWasm for $istruct { + type Output = $ostruct; + + fn to_wasm( + &self, + $wasm_context: crate::wasm::to_wasm::ToWasmContext<'_>, + ) -> Result { + let $standard_properties = self.to_wasm_standard_properties($wasm_context)?; $fnbody } } diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index ad43a2dc..d0e568ff 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -2,5 +2,8 @@ mod document; mod macros; mod parse_result; mod standard_properties; +mod to_wasm; pub(crate) use parse_result::ParseResult; +pub(crate) use to_wasm::ToWasm; +pub(crate) use to_wasm::ToWasmContext; diff --git a/src/wasm/standard_properties.rs b/src/wasm/standard_properties.rs index f0b162bc..92db1e7f 100644 --- a/src/wasm/standard_properties.rs +++ b/src/wasm/standard_properties.rs @@ -1,19 +1,71 @@ use organic::types::PostBlank; +use organic::types::StandardProperties; use serde::Deserialize; use serde::Serialize; -use super::macros::to_wasm; +use super::to_wasm::ToWasmContext; +use super::to_wasm::ToWasmStandardProperties; #[derive(Serialize, Deserialize)] pub(crate) struct WasmStandardProperties { - begin: Option, - end: Option, + begin: usize, + end: usize, contents_begin: Option, contents_end: Option, - post_blank: Option, + post_blank: PostBlank, } -to_wasm!(WasmStandardProperties, Document<'s>, original, document, { - // foo - todo!() -}); +impl<'s, SP: StandardProperties<'s>> ToWasmStandardProperties for SP { + type Output = WasmStandardProperties; + + fn to_wasm_standard_properties( + &self, + wasm_context: ToWasmContext<'_>, + ) -> Result { + let (begin, end) = get_char_indices(wasm_context.full_document, self.get_source()); + let (contents_begin, contents_end) = match self.get_contents() { + Some(contents) => { + let (begin, end) = get_char_indices(wasm_context.full_document, contents); + (Some(begin), Some(end)) + } + None => (None, None), + }; + + let post_blank = self.get_post_blank(); + Ok(WasmStandardProperties { + begin, + end, + contents_begin, + contents_end, + post_blank, + }) + } +} + +fn get_char_indices(original_document: &str, subset: &str) -> (usize, usize) { + let (begin_byte, end_byte) = get_rust_byte_offsets(original_document, subset); + // Add 1 to make this 1-based to match emacs. + let begin_char = original_document[..begin_byte].chars().count() + 1; + // Also 1-based: + let end_char = begin_char + original_document[begin_byte..end_byte].chars().count(); + (begin_char, end_char) +} + +/// Get the byte offset into source that the string slice exists at. +/// +/// These offsets are zero-based. +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) +} + +/// 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 +} diff --git a/src/wasm/to_wasm.rs b/src/wasm/to_wasm.rs new file mode 100644 index 00000000..cd3f6924 --- /dev/null +++ b/src/wasm/to_wasm.rs @@ -0,0 +1,27 @@ +use crate::error::CustomError; + +pub(crate) trait ToWasm { + type Output; + + fn to_wasm(&self, full_document: ToWasmContext<'_>) -> Result; +} + +pub(crate) trait ToWasmStandardProperties { + type Output; + + fn to_wasm_standard_properties( + &self, + wasm_context: ToWasmContext<'_>, + ) -> Result; +} + +#[derive(Debug, Clone)] +pub(crate) struct ToWasmContext<'s> { + pub(crate) full_document: &'s str, +} + +impl<'s> ToWasmContext<'s> { + pub(crate) fn new(full_document: &'s str) -> ToWasmContext<'s> { + ToWasmContext { full_document } + } +}