2023-10-06 14:07:25 -04:00
use std ::fmt ::Debug ;
2023-10-09 15:13:05 -04:00
use super ::diff ::artificial_diff_scope ;
use super ::diff ::compare_ast_node ;
use super ::diff ::DiffEntry ;
2023-10-06 13:40:11 -04:00
use super ::diff ::DiffStatus ;
2023-10-08 14:40:01 -04:00
use super ::sexp ::unquote ;
2023-10-06 13:40:11 -04:00
use super ::sexp ::Token ;
2023-10-06 18:30:08 -04:00
use super ::util ::get_property ;
2023-10-06 13:40:11 -04:00
use super ::util ::get_property_quoted_string ;
2023-10-06 18:30:08 -04:00
use super ::util ::get_property_unquoted_atom ;
2023-10-09 15:13:05 -04:00
use crate ::types ::AstNode ;
2023-10-06 13:40:11 -04:00
#[ derive(Debug) ]
pub ( crate ) enum EmacsField < ' s > {
Required ( & ' s str ) ,
2023-10-06 14:07:25 -04:00
#[ allow(dead_code) ]
2023-10-06 13:40:11 -04:00
Optional ( & ' s str ) ,
}
2023-10-09 15:13:05 -04:00
#[ derive(Debug) ]
pub ( crate ) enum ComparePropertiesResult < ' b , ' s > {
NoChange ,
/// Return when you want the status for "this" node to change (as opposed to collecting child status).
SelfChange ( DiffStatus , Option < String > ) ,
DiffEntry ( DiffEntry < ' b , ' s > ) ,
}
2023-10-06 14:07:25 -04:00
/// Do no comparison.
///
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
2023-10-06 16:32:49 -04:00
pub ( crate ) fn compare_noop < ' b , ' s , ' x , R , RG > (
2023-10-09 15:13:05 -04:00
_source : & ' s str ,
2023-10-06 14:07:25 -04:00
_emacs : & ' b Token < ' s > ,
2023-10-06 16:19:43 -04:00
_rust_node : R ,
2023-10-06 14:07:25 -04:00
_emacs_field : & ' x str ,
2023-10-06 16:19:43 -04:00
_rust_value_getter : RG ,
2023-10-09 15:13:05 -04:00
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > > {
Ok ( ComparePropertiesResult ::NoChange )
2023-10-06 14:07:25 -04:00
}
2023-10-06 16:19:43 -04:00
/// Do no comparison.
///
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
pub ( crate ) fn compare_identity ( ) -> ( ) {
( )
}
2023-10-06 18:30:08 -04:00
/// Assert that the emacs value is always nil or absent.
///
/// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property.
pub ( crate ) fn compare_property_always_nil < ' b , ' s , ' x , R , RG > (
2023-10-09 15:13:05 -04:00
_source : & ' s str ,
2023-10-06 18:30:08 -04:00
emacs : & ' b Token < ' s > ,
_rust_node : R ,
emacs_field : & ' x str ,
_rust_value_getter : RG ,
2023-10-09 15:13:05 -04:00
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > > {
2023-10-06 18:30:08 -04:00
let value = get_property ( emacs , emacs_field ) ? ;
if value . is_some ( ) {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} was expected to always be nil: {:?} " ,
emacs_field , value
) ) ;
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) )
2023-10-06 18:30:08 -04:00
} else {
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::NoChange )
2023-10-06 18:30:08 -04:00
}
}
2023-10-06 19:49:06 -04:00
pub ( crate ) fn compare_property_quoted_string <
' b ,
' s ,
' x ,
R ,
RV : AsRef < str > + std ::fmt ::Debug ,
RG : Fn ( R ) -> Option < RV > ,
> (
2023-10-09 15:13:05 -04:00
_source : & ' s str ,
2023-10-06 13:40:11 -04:00
emacs : & ' b Token < ' s > ,
2023-10-06 14:07:25 -04:00
rust_node : R ,
2023-10-06 16:03:41 -04:00
emacs_field : & ' x str ,
2023-10-06 14:07:25 -04:00
rust_value_getter : RG ,
2023-10-09 15:13:05 -04:00
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > > {
2023-10-06 14:07:25 -04:00
let value = get_property_quoted_string ( emacs , emacs_field ) ? ;
let rust_value = rust_value_getter ( rust_node ) ;
2023-10-06 19:49:06 -04:00
if rust_value . as_ref ( ) . map ( | s | s . as_ref ( ) ) ! = value . as_ref ( ) . map ( String ::as_str ) {
2023-10-06 18:30:08 -04:00
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , value , rust_value
) ) ;
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) )
2023-10-06 18:30:08 -04:00
} else {
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::NoChange )
2023-10-06 18:30:08 -04:00
}
}
pub ( crate ) fn compare_property_unquoted_atom < ' b , ' s , ' x , R , RG : Fn ( R ) -> Option < & ' s str > > (
2023-10-09 15:13:05 -04:00
_source : & ' s str ,
2023-10-06 18:30:08 -04:00
emacs : & ' b Token < ' s > ,
rust_node : R ,
emacs_field : & ' x str ,
rust_value_getter : RG ,
2023-10-09 15:13:05 -04:00
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > > {
2023-10-06 18:30:08 -04:00
let value = get_property_unquoted_atom ( emacs , emacs_field ) ? ;
let rust_value = rust_value_getter ( rust_node ) ;
if rust_value ! = value {
2023-10-06 14:07:25 -04:00
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , value , rust_value
) ) ;
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) )
2023-10-06 14:07:25 -04:00
} else {
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::NoChange )
2023-10-06 14:07:25 -04:00
}
}
2023-10-08 14:40:01 -04:00
pub ( crate ) fn compare_property_list_of_quoted_string <
' b ,
' s ,
' x ,
R ,
2023-10-08 15:08:21 -04:00
RV : AsRef < str > + std ::fmt ::Debug ,
RI : Iterator < Item = RV > ,
RG : Fn ( R ) -> Option < RI > ,
2023-10-08 14:40:01 -04:00
> (
2023-10-09 15:13:05 -04:00
_source : & ' s str ,
2023-10-08 14:40:01 -04:00
emacs : & ' b Token < ' s > ,
rust_node : R ,
emacs_field : & ' x str ,
rust_value_getter : RG ,
2023-10-09 15:13:05 -04:00
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > > {
2023-10-08 14:40:01 -04:00
let value = get_property ( emacs , emacs_field ) ?
. map ( Token ::as_list )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ? ;
let rust_value = rust_value_getter ( rust_node ) ;
2023-10-08 15:08:21 -04:00
// TODO: Seems we are needlessly coverting to a vec here.
let rust_value : Option < Vec < RV > > = rust_value . map ( | it | it . collect ( ) ) ;
match ( value , & rust_value ) {
2023-10-08 14:40:01 -04:00
( None , None ) = > { }
( None , Some ( _ ) ) | ( Some ( _ ) , None ) = > {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , value , rust_value
) ) ;
2023-10-09 15:13:05 -04:00
return Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) ) ;
2023-10-08 14:40:01 -04:00
}
( Some ( el ) , Some ( rl ) ) if el . len ( ) ! = rl . len ( ) = > {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , value , rust_value
) ) ;
2023-10-09 15:13:05 -04:00
return Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) ) ;
2023-10-08 14:40:01 -04:00
}
( Some ( el ) , Some ( rl ) ) = > {
for ( e , r ) in el . iter ( ) . zip ( rl ) {
let e = unquote ( e . as_atom ( ) ? ) ? ;
let r = r . as_ref ( ) ;
if e ! = r {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
2023-10-08 15:54:56 -04:00
" {} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?} " ,
emacs_field , e , r , value , rust_value
2023-10-08 14:40:01 -04:00
) ) ;
2023-10-09 15:13:05 -04:00
return Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) ) ;
2023-10-08 14:40:01 -04:00
}
}
}
}
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::NoChange )
2023-10-08 14:40:01 -04:00
}
2023-10-08 18:14:48 -04:00
pub ( crate ) fn compare_property_boolean < ' b , ' s , ' x , R , RG : Fn ( R ) -> bool > (
2023-10-09 15:13:05 -04:00
_source : & ' s str ,
2023-10-08 18:14:48 -04:00
emacs : & ' b Token < ' s > ,
rust_node : R ,
emacs_field : & ' x str ,
rust_value_getter : RG ,
2023-10-09 15:13:05 -04:00
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > > {
2023-10-08 18:14:48 -04:00
// get_property already converts nil to None.
let value = get_property ( emacs , emacs_field ) ? . is_some ( ) ;
let rust_value = rust_value_getter ( rust_node ) ;
if rust_value ! = value {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , value , rust_value
) ) ;
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) )
2023-10-08 18:14:48 -04:00
} else {
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::NoChange )
}
}
pub ( crate ) fn compare_property_list_of_ast_nodes <
' b ,
' s ,
' x : ' b + ' s ,
R ,
RV : std ::fmt ::Debug ,
RI : Iterator < Item = RV > ,
RG : Fn ( R ) -> Option < RI > ,
> (
source : & ' s str ,
emacs : & ' b Token < ' s > ,
rust_node : R ,
emacs_field : & ' x str ,
rust_value_getter : RG ,
) -> Result < ComparePropertiesResult < ' b , ' s > , Box < dyn std ::error ::Error > >
where
AstNode < ' b , ' s > : From < RV > ,
{
let value = get_property ( emacs , emacs_field ) ?
. map ( Token ::as_list )
. map_or ( Ok ( None ) , | r | r . map ( Some ) ) ? ;
let rust_value = rust_value_getter ( rust_node ) ;
// TODO: Seems we are needlessly coverting to a vec here.
let rust_value : Option < Vec < RV > > = rust_value . map ( | it | it . collect ( ) ) ;
match ( value , rust_value ) {
( None , None ) = > { }
( None , rv @ Some ( _ ) ) | ( Some ( _ ) , rv @ None ) = > {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , value , rv
) ) ;
return Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) ) ;
}
( Some ( el ) , Some ( rl ) ) if el . len ( ) ! = rl . len ( ) = > {
let this_status = DiffStatus ::Bad ;
let message = Some ( format! (
" {} mismatch (emacs != rust) {:?} != {:?} " ,
emacs_field , el , rl
) ) ;
return Ok ( ComparePropertiesResult ::SelfChange ( this_status , message ) ) ;
}
( Some ( el ) , Some ( rl ) ) = > {
let mut child_status : Vec < DiffEntry < ' b , ' s > > = Vec ::with_capacity ( rl . len ( ) ) ;
for ( e , r ) in el . iter ( ) . zip ( rl ) {
child_status . push ( compare_ast_node ( source , e , r . into ( ) ) ? ) ;
}
let diff_scope = artificial_diff_scope ( emacs_field , child_status ) ? ;
return Ok ( ComparePropertiesResult ::DiffEntry ( diff_scope ) ) ;
}
2023-10-08 18:14:48 -04:00
}
2023-10-09 15:13:05 -04:00
Ok ( ComparePropertiesResult ::NoChange )
2023-10-08 18:14:48 -04:00
}