2023-07-21 17:52:18 -04:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
use nom ::bytes ::complete ::tag_no_case ;
use nom ::character ::complete ::anychar ;
use nom ::combinator ::opt ;
use nom ::combinator ::recognize ;
use nom ::combinator ::verify ;
use nom ::multi ::many0 ;
use nom ::multi ::many1 ;
use nom ::multi ::many_till ;
use nom ::sequence ::tuple ;
2023-07-20 00:38:16 -04:00
use super ::Context ;
2023-07-21 17:52:18 -04:00
use crate ::error ::CustomError ;
2023-07-20 00:38:16 -04:00
use crate ::error ::Res ;
2023-07-21 17:52:18 -04:00
use crate ::parser ::citation_reference ::citation_reference ;
use crate ::parser ::citation_reference ::citation_reference_key ;
use crate ::parser ::citation_reference ::get_bracket_depth ;
use crate ::parser ::exiting ::ExitClass ;
2023-07-20 00:38:16 -04:00
use crate ::parser ::object ::Citation ;
2023-07-21 17:52:18 -04:00
use crate ::parser ::object_parser ::standard_set_object ;
use crate ::parser ::parser_context ::CitationBracket ;
use crate ::parser ::parser_context ::ContextElement ;
use crate ::parser ::parser_context ::ExitMatcherNode ;
use crate ::parser ::parser_with_context ::parser_with_context ;
use crate ::parser ::util ::exit_matcher_parser ;
use crate ::parser ::util ::get_consumed ;
use crate ::parser ::Object ;
2023-07-20 00:38:16 -04:00
#[ tracing::instrument(ret, level = " debug " ) ]
pub fn citation < ' r , ' s > ( context : Context < ' r , ' s > , input : & ' s str ) -> Res < & ' s str , Citation < ' s > > {
2023-07-21 17:52:18 -04:00
let ( remaining , _ ) = tag_no_case ( " [cite " ) ( input ) ? ;
let ( remaining , _ ) = opt ( citestyle ) ( remaining ) ? ;
let ( remaining , _ ) = tag ( " : " ) ( remaining ) ? ;
let ( remaining , _prefix ) = parser_with_context! ( global_prefix ) ( context ) ( remaining ) ? ;
let ( remaining , _references ) =
many0 ( parser_with_context! ( citation_reference ) ( context ) ) ( remaining ) ? ;
let ( remaining , _suffix ) = parser_with_context! ( global_suffix ) ( context ) ( remaining ) ? ;
let ( remaining , _ ) = tag ( " ] " ) ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( ( remaining , Citation { source } ) )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn citestyle < ' r , ' s > ( input : & ' s str ) -> Res < & ' s str , & ' s str > {
let ( remaining , _ ) = tuple ( ( tag ( " / " ) , style ) ) ( input ) ? ;
let ( remaining , _ ) = opt ( tuple ( ( tag ( " / " ) , variant ) ) ) ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( ( remaining , source ) )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn style < ' r , ' s > ( input : & ' s str ) -> Res < & ' s str , & ' s str > {
recognize ( many1 ( verify ( anychar , | c | {
c . is_alphanumeric ( ) | | " _- " . contains ( * c )
} ) ) ) ( input )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn variant < ' r , ' s > ( input : & ' s str ) -> Res < & ' s str , & ' s str > {
recognize ( many1 ( verify ( anychar , | c | {
c . is_alphanumeric ( ) | | " _-/ " . contains ( * c )
} ) ) ) ( input )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn global_prefix < ' r , ' s > (
context : Context < ' r , ' s > ,
input : & ' s str ,
) -> Res < & ' s str , Vec < Object < ' s > > > {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context
. with_additional_node ( ContextElement ::CitationBracket ( CitationBracket {
position : input ,
depth : 0 ,
} ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Beta ,
exit_matcher : & global_prefix_end ,
} ) ) ;
let ( remaining , ( children , _exit_contents ) ) = verify (
many_till (
parser_with_context! ( standard_set_object ) ( & parser_context ) ,
parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ,
) ,
| ( children , _exit_contents ) | ! children . is_empty ( ) ,
) ( input ) ? ;
Ok ( ( remaining , children ) )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn global_prefix_end < ' r , ' s > ( context : Context < ' r , ' s > , input : & ' s str ) -> Res < & ' s str , & ' s str > {
let context_depth = get_bracket_depth ( context )
. expect ( " This function should only be called from inside a citation. " ) ;
let text_since_context_entry = get_consumed ( context_depth . position , input ) ;
let mut current_depth = context_depth . depth ;
for c in text_since_context_entry . chars ( ) {
match c {
'[' = > {
current_depth + = 1 ;
}
']' if current_depth = = 0 = > {
panic! ( " Exceeded citation global prefix bracket depth. " )
}
']' if current_depth > 0 = > {
current_depth - = 1 ;
}
_ = > { }
}
}
if current_depth = = 0 {
let close_bracket = tag ::< & str , & str , CustomError < & str > > ( " ] " ) ( input ) ;
if close_bracket . is_ok ( ) {
return close_bracket ;
}
}
alt ( (
tag ( " ; " ) ,
recognize ( parser_with_context! ( citation_reference_key ) ( context ) ) ,
) ) ( input )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn global_suffix < ' r , ' s > (
context : Context < ' r , ' s > ,
input : & ' s str ,
) -> Res < & ' s str , Vec < Object < ' s > > > {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context
. with_additional_node ( ContextElement ::CitationBracket ( CitationBracket {
position : input ,
depth : 0 ,
} ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Beta ,
exit_matcher : & global_suffix_end ,
} ) ) ;
let ( remaining , ( children , _exit_contents ) ) = verify (
many_till (
parser_with_context! ( standard_set_object ) ( & parser_context ) ,
parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ,
) ,
| ( children , _exit_contents ) | ! children . is_empty ( ) ,
) ( input ) ? ;
Ok ( ( remaining , children ) )
}
#[ tracing::instrument(ret, level = " debug " ) ]
fn global_suffix_end < ' r , ' s > ( context : Context < ' r , ' s > , input : & ' s str ) -> Res < & ' s str , & ' s str > {
let context_depth = get_bracket_depth ( context )
. expect ( " This function should only be called from inside a citation. " ) ;
let text_since_context_entry = get_consumed ( context_depth . position , input ) ;
let mut current_depth = context_depth . depth ;
for c in text_since_context_entry . chars ( ) {
match c {
'[' = > {
current_depth + = 1 ;
}
']' if current_depth = = 0 = > {
panic! ( " Exceeded citation global suffix bracket depth. " )
}
']' if current_depth > 0 = > {
current_depth - = 1 ;
}
_ = > { }
}
}
if current_depth = = 0 {
let close_bracket = tag ::< & str , & str , CustomError < & str > > ( " ] " ) ( input ) ;
if close_bracket . is_ok ( ) {
return close_bracket ;
}
}
alt ( (
tag ( " ; " ) ,
recognize ( parser_with_context! ( citation_reference_key ) ( context ) ) ,
) ) ( input )
2023-07-20 00:38:16 -04:00
}