use crate::parser::sexp::Token; use crate::parser::Source; /// 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 the offset into source that the rust object exists at. /// /// These offsets are zero-based unlike the elisp ones. pub fn get_offsets<'s, S: Source<'s>>(source: &'s str, rust_object: &'s S) -> (usize, usize) { let rust_object_source = rust_object.get_source(); assert!(is_slice_of(source, rust_object_source)); let offset = rust_object_source.as_ptr() as usize - source.as_ptr() as usize; let end = offset + rust_object_source.len(); (offset, end) } pub fn assert_name<'s>(emacs: &'s Token<'s>, name: &str) -> Result<(), Box> { let children = emacs.as_list()?; let first_child = children .first() .ok_or("Should have at least one child.")? .as_atom()?; if first_child != name { Err(format!( "Expected a {expected} cell, but found a {found} cell.", expected = name, found = first_child ))?; } Ok(()) } pub fn assert_bounds<'s, S: Source<'s>>( source: &'s str, emacs: &'s Token<'s>, rust: &'s S, ) -> Result<(), Box> { let children = emacs.as_list()?; let attributes_child = children .iter() .nth(1) .ok_or("Should have an attributes child.")?; let attributes_map = attributes_child.as_map()?; let standard_properties = attributes_map.get(":standard-properties"); let (begin, end) = if standard_properties.is_some() { let std_props = standard_properties .expect("if statement proves its Some") .as_vector()?; let begin = std_props .get(0) .ok_or("Missing first element in standard properties")? .as_atom()?; let end = std_props .get(1) .ok_or("Missing first element in standard properties")? .as_atom()?; (begin, end) } else { let begin = attributes_map .get(":begin") .ok_or("Missing :begin attribute.")? .as_atom()?; let end = attributes_map .get(":end") .ok_or("Missing :end attribute.")? .as_atom()?; (begin, end) }; let (rust_begin, rust_end) = get_offsets(source, rust); if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end { Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?; } Ok(()) }