2023-10-20 12:44:24 -04:00
use std ::borrow ::Cow ;
2023-10-02 15:49:51 -04:00
use std ::str ::FromStr ;
2023-10-11 13:04:33 -04:00
use super ::compare_field ::compare_property_list_of_quoted_string ;
2023-10-16 12:50:53 -04:00
use super ::compare_field ::compare_property_object_tree ;
2023-10-16 12:05:36 -04:00
use super ::compare_field ::compare_property_optional_pair ;
2023-10-11 13:00:21 -04:00
use super ::compare_field ::compare_property_quoted_string ;
2023-10-10 17:40:27 -04:00
use super ::compare_field ::ComparePropertiesResult ;
2023-10-09 13:00:47 -04:00
use super ::diff ::DiffEntry ;
use super ::diff ::DiffStatus ;
2023-09-23 21:03:12 -04:00
use super ::elisp_fact ::GetElispFact ;
2023-09-11 15:31:48 -04:00
use super ::sexp ::Token ;
2023-10-09 13:00:47 -04:00
use crate ::compare ::diff ::compare_ast_node ;
2023-09-29 17:28:50 -04:00
use crate ::compare ::sexp ::unquote ;
2023-10-11 13:00:21 -04:00
use crate ::types ::AffiliatedKeywordValue ;
2023-10-09 13:00:47 -04:00
use crate ::types ::AstNode ;
2023-10-11 12:42:42 -04:00
use crate ::types ::GetAffiliatedKeywords ;
2023-09-23 17:59:13 -04:00
use crate ::types ::StandardProperties ;
2023-04-11 19:16:04 -04:00
/// 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
}
2023-09-23 17:59:13 -04:00
/// Get the byte offset into source that the rust object exists at.
2023-04-11 19:16:04 -04:00
///
/// These offsets are zero-based unlike the elisp ones.
2023-10-31 22:19:39 -04:00
fn get_rust_byte_offsets ( original_document : & str , subset : & str ) -> ( usize , usize ) {
2023-10-31 21:59:58 -04:00
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 ( ) ;
2023-04-11 19:16:04 -04:00
( offset , end )
}
2023-04-19 15:19:21 -04:00
2023-09-23 21:03:12 -04:00
pub ( crate ) fn compare_standard_properties <
2023-10-02 12:36:09 -04:00
' b ,
2023-09-23 21:03:12 -04:00
' s ,
2023-10-31 21:20:39 -04:00
S : StandardProperties < ' s > + GetElispFact < ' s > + ? Sized ,
2023-09-23 21:03:12 -04:00
> (
original_document : & ' s str ,
2023-10-02 12:36:09 -04:00
emacs : & ' b Token < ' s > ,
rust : & ' b S ,
2023-09-23 21:03:12 -04:00
) -> Result < ( ) , Box < dyn std ::error ::Error > > {
assert_name ( emacs , rust . get_elisp_fact ( ) . get_elisp_name ( ) ) ? ;
2023-10-31 21:20:39 -04:00
assert_bounds ( original_document , emacs , rust ) ? ;
2023-10-31 22:18:28 -04:00
assert_post_blank ( emacs , rust ) ? ;
2023-09-23 21:03:12 -04:00
Ok ( ( ) )
}
2023-10-31 21:59:58 -04:00
fn assert_name < S : AsRef < str > > (
2023-10-16 18:54:41 -04:00
emacs : & Token < '_ > ,
2023-09-23 21:03:12 -04:00
name : S ,
2023-09-11 13:13:28 -04:00
) -> Result < ( ) , Box < dyn std ::error ::Error > > {
2023-09-23 21:03:12 -04:00
let name = name . as_ref ( ) ;
2023-04-19 15:19:21 -04:00
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! (
2023-09-23 21:03:12 -04:00
" AST node name mismatch. Expected a (rust) {expected} cell, but found a (emacs) {found} cell. " ,
2023-04-19 15:19:21 -04:00
expected = name ,
found = first_child
) ) ? ;
}
Ok ( ( ) )
}
2023-04-19 15:29:46 -04:00
2023-09-23 17:59:13 -04:00
/// 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.
2023-10-31 21:59:58 -04:00
fn assert_bounds < ' b , ' s , S : StandardProperties < ' s > + ? Sized > (
2023-09-23 17:59:13 -04:00
original_document : & ' s str ,
2023-10-02 12:36:09 -04:00
emacs : & ' b Token < ' s > ,
rust : & ' b S ,
2023-04-19 15:29:46 -04:00
) -> Result < ( ) , Box < dyn std ::error ::Error > > {
2023-09-23 17:59:13 -04:00
let standard_properties = get_emacs_standard_properties ( emacs ) ? ; // 1-based
2023-10-31 22:10:19 -04:00
// 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 . 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
{
2023-10-31 22:48:09 -04:00
if let Some ( rust_contents ) = rust . get_contents ( ) {
let ( begin , end ) = (
standard_properties
. contents_begin
. ok_or ( " Token should have a contents-begin. " ) ? ,
standard_properties
. contents_end
. ok_or ( " Token should have an contents-end. " ) ? ,
) ;
let ( rust_begin , rust_end ) = get_rust_byte_offsets ( original_document , rust_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 ) ) ? ;
}
2023-10-31 22:57:11 -04:00
} else if standard_properties . contents_begin . is_some ( )
| | standard_properties . contents_end . is_some ( )
{
Err ( format! ( " Rust contents is None but emacs contents bounds are ( {emacs_begin:?} , {emacs_end:?} ) " , emacs_begin = standard_properties . contents_begin , emacs_end = standard_properties . contents_end ) ) ? ;
2023-10-31 22:10:19 -04:00
}
2023-08-25 02:55:01 -04:00
}
Ok ( ( ) )
}
2023-10-31 22:18:28 -04:00
/// 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
2023-10-31 22:32:01 -04:00
let rust_post_blank = rust . get_post_blank ( ) ;
2023-10-31 22:18:28 -04:00
let emacs_post_blank = standard_properties
. post_blank
. ok_or ( " Token should have a post-blank. " ) ? ;
2023-10-31 22:32:01 -04:00
if rust_post_blank as usize ! = emacs_post_blank {
2023-12-15 12:29:46 -05:00
Err ( format! ( " Rust post-blank {rust_post_blank} does not match emacs post-blank ( {emacs_post_blank} ) " , rust_post_blank = rust_post_blank , emacs_post_blank = emacs_post_blank ) ) ? ;
2023-10-31 22:18:28 -04:00
}
Ok ( ( ) )
}
2023-12-27 17:07:42 -05:00
pub ( crate ) struct EmacsStandardProperties {
pub ( crate ) begin : Option < usize > ,
2023-08-25 02:55:01 -04:00
#[ allow(dead_code) ]
2023-12-27 17:07:42 -05:00
pub ( crate ) post_affiliated : Option < usize > ,
2023-08-25 02:55:01 -04:00
#[ allow(dead_code) ]
2023-12-27 17:07:42 -05:00
pub ( crate ) contents_begin : Option < usize > ,
2023-08-25 02:55:01 -04:00
#[ allow(dead_code) ]
2023-12-27 17:07:42 -05:00
pub ( crate ) contents_end : Option < usize > ,
pub ( crate ) end : Option < usize > ,
2023-08-25 02:55:01 -04:00
#[ allow(dead_code) ]
2023-12-27 17:07:42 -05:00
pub ( crate ) post_blank : Option < usize > ,
2023-08-25 02:55:01 -04:00
}
2023-12-27 17:07:42 -05:00
pub ( crate ) fn get_emacs_standard_properties (
2023-10-16 18:54:41 -04:00
emacs : & Token < '_ > ,
2023-09-23 17:44:54 -04:00
) -> Result < EmacsStandardProperties , Box < dyn std ::error ::Error > > {
2023-04-19 15:29:46 -04:00
let children = emacs . as_list ( ) ? ;
2023-10-16 18:29:21 -04:00
let attributes_child = children . get ( 1 ) . ok_or ( " Should have an attributes child. " ) ? ;
2023-04-19 15:29:46 -04:00
let attributes_map = attributes_child . as_map ( ) ? ;
2023-08-13 01:06:55 -04:00
let standard_properties = attributes_map . get ( " :standard-properties " ) ;
2023-08-25 02:55:01 -04:00
Ok ( if standard_properties . is_some ( ) {
let mut std_props = standard_properties
2023-08-13 01:06:55 -04:00
. expect ( " if statement proves its Some " )
2023-08-25 02:55:01 -04:00
. as_vector ( ) ?
2023-10-16 18:54:41 -04:00
. iter ( ) ;
2023-08-25 02:55:01 -04:00
let begin = maybe_token_to_usize ( std_props . next ( ) ) ? ;
let post_affiliated = maybe_token_to_usize ( std_props . next ( ) ) ? ;
let contents_begin = maybe_token_to_usize ( std_props . next ( ) ) ? ;
let contents_end = maybe_token_to_usize ( std_props . next ( ) ) ? ;
let end = maybe_token_to_usize ( std_props . next ( ) ) ? ;
let post_blank = maybe_token_to_usize ( std_props . next ( ) ) ? ;
2023-09-23 17:44:54 -04:00
EmacsStandardProperties {
2023-08-25 02:55:01 -04:00
begin ,
post_affiliated ,
contents_begin ,
contents_end ,
end ,
post_blank ,
}
2023-08-13 01:06:55 -04:00
} else {
2023-10-16 18:54:41 -04:00
let begin = maybe_token_to_usize ( attributes_map . get ( " :begin " ) . copied ( ) ) ? ;
let end = maybe_token_to_usize ( attributes_map . get ( " :end " ) . copied ( ) ) ? ;
let contents_begin = maybe_token_to_usize ( attributes_map . get ( " :contents-begin " ) . copied ( ) ) ? ;
let contents_end = maybe_token_to_usize ( attributes_map . get ( " :contents-end " ) . copied ( ) ) ? ;
let post_blank = maybe_token_to_usize ( attributes_map . get ( " :post-blank " ) . copied ( ) ) ? ;
2023-08-25 02:55:01 -04:00
let post_affiliated =
2023-10-16 18:54:41 -04:00
maybe_token_to_usize ( attributes_map . get ( " :post-affiliated " ) . copied ( ) ) ? ;
2023-09-23 17:44:54 -04:00
EmacsStandardProperties {
2023-08-25 02:55:01 -04:00
begin ,
post_affiliated ,
contents_begin ,
contents_end ,
end ,
post_blank ,
}
} )
}
2023-04-19 15:29:46 -04:00
2023-12-29 15:38:18 -05:00
pub ( crate ) fn maybe_token_to_usize (
2023-08-25 02:55:01 -04:00
token : Option < & Token < '_ > > ,
) -> Result < Option < usize > , Box < dyn std ::error ::Error > > {
Ok ( token
. map ( | token | token . as_atom ( ) )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ?
2023-10-16 18:54:41 -04:00
. and_then ( | val | {
2023-08-25 02:55:01 -04:00
if val = = " nil " {
None
} else {
Some ( val . parse ::< usize > ( ) )
}
} )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ? )
2023-04-19 15:29:46 -04:00
}
2023-08-29 22:07:23 -04:00
2023-09-08 15:57:24 -04:00
/// Get a named property from the emacs token.
///
2023-10-02 19:07:12 -04:00
/// Returns Ok(None) if value is nil or absent.
2023-10-16 18:54:41 -04:00
pub ( crate ) fn get_property < ' b , ' s > (
2023-10-02 12:53:23 -04:00
emacs : & ' b Token < ' s > ,
2023-10-16 18:54:41 -04:00
key : & str ,
2023-10-02 12:53:23 -04:00
) -> Result < Option < & ' b Token < ' s > > , Box < dyn std ::error ::Error > > {
2023-08-29 22:07:23 -04:00
let children = emacs . as_list ( ) ? ;
2023-10-16 18:54:41 -04:00
let attributes_child = children . get ( 1 ) . ok_or ( " Should have an attributes child. " ) ? ;
2023-08-29 22:07:23 -04:00
let attributes_map = attributes_child . as_map ( ) ? ;
2023-10-16 18:54:41 -04:00
let prop = attributes_map . get ( key ) . copied ( ) ;
if let Some ( Ok ( " nil " ) ) = prop . map ( Token ::as_atom ) {
return Ok ( None ) ;
}
2023-10-02 19:07:12 -04:00
Ok ( prop )
2023-08-29 22:07:23 -04:00
}
2023-09-29 13:03:01 -04:00
/// Get a named property containing an unquoted atom from the emacs token.
///
/// Returns None if key is not found.
2023-10-16 18:54:41 -04:00
pub ( crate ) fn get_property_unquoted_atom < ' s > (
emacs : & Token < ' s > ,
key : & str ,
2023-09-29 13:03:01 -04:00
) -> Result < Option < & ' s str > , Box < dyn std ::error ::Error > > {
2023-10-16 18:54:41 -04:00
get_property ( emacs , key ) ?
2023-09-29 13:03:01 -04:00
. map ( Token ::as_atom )
2023-10-16 18:54:41 -04:00
. map_or ( Ok ( None ) , | r | r . map ( Some ) )
2023-09-29 13:03:01 -04:00
}
2023-09-29 17:28:50 -04:00
/// Get a named property containing an quoted string from the emacs token.
///
/// Returns None if key is not found.
2023-10-20 12:44:24 -04:00
pub ( crate ) fn get_property_quoted_string < ' s > (
emacs : & Token < ' s > ,
2023-10-16 18:54:41 -04:00
key : & str ,
2023-10-20 12:44:24 -04:00
) -> Result < Option < Cow < ' s , str > > , Box < dyn std ::error ::Error > > {
2023-10-16 18:54:41 -04:00
get_property ( emacs , key ) ?
2023-09-29 17:28:50 -04:00
. map ( Token ::as_atom )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ?
. map ( unquote )
2023-10-16 18:54:41 -04:00
. map_or ( Ok ( None ) , | r | r . map ( Some ) )
2023-09-29 17:28:50 -04:00
}
2023-10-02 10:48:34 -04:00
2023-10-02 15:49:51 -04:00
/// Get a named property containing an unquoted numeric value.
///
/// Returns None if key is not found.
pub ( crate ) fn get_property_numeric < ' b , ' s , ' x , N : FromStr > (
emacs : & ' b Token < ' s > ,
key : & ' x str ,
2023-10-02 15:51:29 -04:00
) -> Result < Option < N > , Box < dyn std ::error ::Error + ' s > >
2023-10-02 15:49:51 -04:00
where
< N as FromStr > ::Err : std ::error ::Error ,
2023-10-02 15:51:29 -04:00
< N as FromStr > ::Err : ' s ,
2023-10-02 15:49:51 -04:00
{
2023-10-02 15:59:06 -04:00
let unparsed_string = get_property ( emacs , key ) ?
2023-10-02 15:49:51 -04:00
. map ( Token ::as_atom )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ? ;
2023-10-02 15:59:06 -04:00
let parsed_number = unparsed_string
2023-10-02 15:49:51 -04:00
. map ( | val | val . parse ::< N > ( ) )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ? ;
2023-10-02 15:59:06 -04:00
Ok ( parsed_number )
2023-10-02 15:49:51 -04:00
}
2023-10-09 13:00:47 -04:00
pub ( crate ) fn compare_children < ' b , ' s , ' x , RC > (
source : & ' s str ,
emacs : & ' b Token < ' s > ,
2023-12-15 19:57:35 -05:00
rust_children : & ' x [ RC ] ,
2023-10-09 13:00:47 -04:00
child_status : & mut Vec < DiffEntry < ' b , ' s > > ,
this_status : & mut DiffStatus ,
message : & mut Option < String > ,
) -> Result < ( ) , Box < dyn std ::error ::Error > >
where
AstNode < ' b , ' s > : From < & ' x RC > ,
{
let emacs_children = emacs . as_list ( ) ? ;
let emacs_children_length = emacs_children . len ( ) - 2 ;
if emacs_children_length ! = rust_children . len ( ) {
* this_status = DiffStatus ::Bad ;
* message = Some ( format! (
" Child length mismatch (emacs != rust) {:?} != {:?} " ,
emacs_children_length ,
rust_children . len ( )
) ) ;
}
for ( emacs_child , rust_child ) in emacs_children . iter ( ) . skip ( 2 ) . zip ( rust_children . iter ( ) ) {
child_status . push ( compare_ast_node ( source , emacs_child , rust_child . into ( ) ) ? ) ;
}
Ok ( ( ) )
}
2023-10-10 15:19:42 -04:00
pub ( crate ) fn compare_children_iter < ' b , ' s , RC , RI : Iterator < Item = RC > + ExactSizeIterator > (
2023-10-10 14:58:18 -04:00
source : & ' s str ,
emacs : & ' b Token < ' s > ,
rust_children : RI ,
child_status : & mut Vec < DiffEntry < ' b , ' s > > ,
this_status : & mut DiffStatus ,
message : & mut Option < String > ,
) -> Result < ( ) , Box < dyn std ::error ::Error > >
where
2023-10-10 15:19:42 -04:00
AstNode < ' b , ' s > : From < RC > ,
2023-10-10 14:58:18 -04:00
{
let emacs_children = emacs . as_list ( ) ? ;
let emacs_children_length = emacs_children . len ( ) - 2 ;
if emacs_children_length ! = rust_children . len ( ) {
* this_status = DiffStatus ::Bad ;
* message = Some ( format! (
" Child length mismatch (emacs != rust) {:?} != {:?} " ,
emacs_children_length ,
rust_children . len ( )
) ) ;
}
for ( emacs_child , rust_child ) in emacs_children . iter ( ) . skip ( 2 ) . zip ( rust_children ) {
child_status . push ( compare_ast_node ( source , emacs_child , rust_child . into ( ) ) ? ) ;
}
Ok ( ( ) )
}
2023-10-16 18:54:41 -04:00
pub ( crate ) fn assert_no_children (
emacs : & Token < '_ > ,
2023-10-09 13:00:47 -04:00
this_status : & mut DiffStatus ,
message : & mut Option < String > ,
) -> Result < ( ) , Box < dyn std ::error ::Error > > {
let emacs_children_length = emacs . as_list ( ) ? . len ( ) ;
// 2, one for the name of the node and one for the properties. Children would come after that.
if emacs_children_length ! = 2 {
* this_status = DiffStatus ::Bad ;
* message = Some ( format! (
" Should have no children but emacs has {:?} children. " ,
emacs_children_length - 2 ,
) ) ;
}
Ok ( ( ) )
}
2023-10-10 17:40:27 -04:00
pub ( crate ) fn compare_additional_properties < ' b , ' s , RK , RV , RI > (
emacs : & ' b Token < ' s > ,
rust_children : RI ,
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > >
where
RK : AsRef < str > ,
RV : AsRef < str > ,
RI : Iterator < Item = ( RK , RV ) > + ExactSizeIterator ,
{
for ( rust_key , rust_value ) in rust_children {
let rust_key = rust_key . as_ref ( ) ;
let rust_value = rust_value . as_ref ( ) ;
let emacs_value = get_property_quoted_string ( emacs , rust_key ) ? ;
2023-10-16 18:54:41 -04:00
if Some ( rust_value ) ! = emacs_value . as_deref ( ) {
2023-10-10 17:40:27 -04:00
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
rust_key , emacs_value , rust_value
) ) ;
return Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) ) ;
}
}
Ok ( ComparePropertiesResult ::NoChange )
}
2023-10-11 12:42:42 -04:00
pub ( crate ) fn compare_affiliated_keywords < ' b , ' s , GAK > (
2023-10-11 13:00:21 -04:00
source : & ' s str ,
2023-10-11 12:42:42 -04:00
emacs : & ' b Token < ' s > ,
rust : & ' b GAK ,
2023-10-11 13:00:21 -04:00
) -> Result < Vec < ComparePropertiesResult < ' b , ' s > > , Box < dyn std ::error ::Error > >
2023-10-11 12:42:42 -04:00
where
GAK : GetAffiliatedKeywords < ' s > ,
{
2023-10-11 13:00:21 -04:00
let mut ret = Vec ::new ( ) ;
2023-10-11 12:42:42 -04:00
let affiliated_keywords = rust . get_affiliated_keywords ( ) ;
for ( rust_name , rust_value ) in affiliated_keywords . keywords . iter ( ) {
2023-10-16 11:45:54 -04:00
let emacs_property_name = format! ( " : {} " , rust_name ) ;
match rust_value {
AffiliatedKeywordValue ::SingleString ( rust_value ) = > {
let diff = compare_property_quoted_string (
source ,
emacs ,
rust ,
emacs_property_name . as_str ( ) ,
| _ | Some ( * rust_value ) ,
) ? ;
ret . push ( diff ) ;
}
AffiliatedKeywordValue ::ListOfStrings ( rust_value ) = > {
let diff = compare_property_list_of_quoted_string (
source ,
emacs ,
rust ,
emacs_property_name . as_str ( ) ,
| _ | Some ( rust_value . iter ( ) ) ,
) ? ;
ret . push ( diff ) ;
}
AffiliatedKeywordValue ::OptionalPair { optval , val } = > {
2023-10-16 12:05:36 -04:00
let diff = compare_property_optional_pair (
source ,
emacs ,
rust ,
emacs_property_name . as_str ( ) ,
| _ | Some ( ( * optval , * val ) ) ,
) ? ;
ret . push ( diff ) ;
2023-10-16 11:45:54 -04:00
}
2023-10-16 12:50:53 -04:00
AffiliatedKeywordValue ::ObjectTree ( rust_value ) = > {
let diff = compare_property_object_tree (
source ,
emacs ,
rust ,
emacs_property_name . as_str ( ) ,
| _ | Some ( rust_value . iter ( ) ) ,
) ? ;
ret . push ( diff ) ;
}
2023-10-16 11:45:54 -04:00
} ;
2023-10-11 12:42:42 -04:00
}
2023-10-11 13:00:21 -04:00
Ok ( ret )
2023-10-11 12:42:42 -04:00
}
pub ( crate ) fn affiliated_keywords_names < ' s , GAK > ( rust : & ' s GAK ) -> impl Iterator < Item = String > + ' s
where
GAK : GetAffiliatedKeywords < ' s > ,
{
rust . get_affiliated_keywords ( )
. keywords
. keys ( )
. map ( | k | format! ( " : {} " , k ) )
}