2023-07-13 18:30:18 -04:00
use nom ::branch ::alt ;
2023-07-13 19:09:44 -04:00
use nom ::bytes ::complete ::tag ;
use nom ::bytes ::complete ::tag_no_case ;
2023-07-13 22:54:59 -04:00
use nom ::character ::complete ::anychar ;
2023-07-13 18:30:18 -04:00
use nom ::character ::complete ::none_of ;
2023-07-13 22:54:59 -04:00
use nom ::character ::complete ::one_of ;
2023-07-13 18:30:18 -04:00
use nom ::combinator ::eof ;
2023-07-13 19:09:44 -04:00
use nom ::combinator ::peek ;
2023-07-13 18:30:18 -04:00
use nom ::combinator ::recognize ;
2023-08-29 15:44:04 -04:00
use nom ::combinator ::verify ;
2023-07-13 22:54:59 -04:00
use nom ::multi ::many_till ;
2023-07-13 18:30:18 -04:00
2023-08-23 00:30:26 -04:00
use super ::org_source ::OrgSource ;
2023-09-03 11:05:34 -04:00
use crate ::context ::parser_with_context ;
use crate ::context ::ContextElement ;
use crate ::context ::ExitClass ;
use crate ::context ::ExitMatcherNode ;
use crate ::context ::RefContext ;
2023-07-13 18:30:18 -04:00
use crate ::error ::CustomError ;
use crate ::error ::MyError ;
2023-07-13 18:18:07 -04:00
use crate ::error ::Res ;
2023-07-13 22:54:59 -04:00
use crate ::parser ::util ::exit_matcher_parser ;
2023-07-13 19:09:44 -04:00
use crate ::parser ::util ::get_consumed ;
2023-07-13 18:30:18 -04:00
use crate ::parser ::util ::WORD_CONSTITUENT_CHARACTERS ;
2023-09-03 11:05:34 -04:00
use crate ::types ::PlainLink ;
2023-07-13 18:18:07 -04:00
2023-08-29 15:44:04 -04:00
// TODO: Make this a user-provided variable corresponding to elisp's org-link-parameters
const ORG_LINK_PARAMETERS : [ & 'static str ; 23 ] = [
" id " ,
" eww " ,
" rmail " ,
" mhe " ,
" irc " ,
" info " ,
" gnus " ,
" docview " ,
" bibtex " ,
" bbdb " ,
" w3m " ,
" doi " ,
" file+sys " ,
" file+emacs " ,
" shell " ,
" news " ,
" mailto " ,
" https " ,
" http " ,
" ftp " ,
" help " ,
" file " ,
" elisp " ,
] ;
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 15:44:18 -04:00
pub fn plain_link < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , PlainLink < ' s > > {
2023-07-13 19:09:44 -04:00
let ( remaining , _ ) = pre ( context , input ) ? ;
let ( remaining , proto ) = protocol ( context , remaining ) ? ;
let ( remaining , _separator ) = tag ( " : " ) ( remaining ) ? ;
let ( remaining , path ) = path_plain ( context , remaining ) ? ;
peek ( parser_with_context! ( post ) ( context ) ) ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
2023-07-13 23:22:10 +00:00
Ok ( (
remaining ,
PlainLink {
2023-08-23 00:30:26 -04:00
source : source . into ( ) ,
link_type : proto . into ( ) ,
path : path . into ( ) ,
2023-07-13 23:22:10 +00:00
} ,
) )
2023-07-13 18:18:07 -04:00
}
2023-07-13 18:30:18 -04:00
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 15:44:18 -04:00
fn pre < ' b , ' g , ' r , ' s > (
_context : RefContext < ' b , ' g , ' r , ' s > ,
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , ( ) > {
2023-08-24 16:55:56 -04:00
let preceding_character = input . get_preceding_character ( ) ;
2023-07-13 18:30:18 -04:00
match preceding_character {
// If None, we are at the start of the file which is fine
None = > { }
Some ( x ) if ! WORD_CONSTITUENT_CHARACTERS . contains ( x ) = > { }
Some ( _ ) = > {
// Not at start of line, cannot be a heading
return Err ( nom ::Err ::Error ( CustomError ::MyError ( MyError (
2023-08-23 00:30:26 -04:00
" Not a valid pre character for plain link. " . into ( ) ,
2023-07-13 18:30:18 -04:00
) ) ) ) ;
}
} ;
Ok ( ( input , ( ) ) )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 15:44:18 -04:00
fn post < ' b , ' g , ' r , ' s > (
_context : RefContext < ' b , ' g , ' r , ' s > ,
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , ( ) > {
2023-07-13 18:30:18 -04:00
let ( remaining , _ ) = alt ( ( eof , recognize ( none_of ( WORD_CONSTITUENT_CHARACTERS ) ) ) ) ( input ) ? ;
Ok ( ( remaining , ( ) ) )
}
2023-07-13 19:09:44 -04:00
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 15:44:18 -04:00
pub fn protocol < ' b , ' g , ' r , ' s > (
_context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-13 19:33:38 -04:00
// TODO: This should be defined by org-link-parameters
2023-08-29 15:44:04 -04:00
for link_parameter in ORG_LINK_PARAMETERS {
let result = tag_no_case ::< _ , _ , CustomError < _ > > ( link_parameter ) ( input ) ;
match result {
Ok ( ( remaining , ent ) ) = > {
return Ok ( ( remaining , ent ) ) ;
}
Err ( _ ) = > { }
}
}
Err ( nom ::Err ::Error ( CustomError ::MyError ( MyError (
" NoLinkProtocol " . into ( ) ,
) ) ) )
2023-07-13 19:09:44 -04:00
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 15:44:18 -04:00
fn path_plain < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-13 19:09:44 -04:00
// TODO: "optionally containing parenthesis-wrapped non-whitespace non-bracket substrings up to a depth of two. The string must end with either a non-punctation non-whitespace character, a forwards slash, or a parenthesis-wrapped substring"
2023-09-03 11:05:34 -04:00
let parser_context = ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Gamma ,
exit_matcher : & path_plain_end ,
} ) ;
let parser_context = context . with_additional_node ( & parser_context ) ;
2023-07-13 22:54:59 -04:00
let exit_matcher = parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ;
2023-08-29 15:44:04 -04:00
let ( remaining , path ) = recognize ( verify (
many_till ( anychar , peek ( exit_matcher ) ) ,
| ( children , _exit_contents ) | ! children . is_empty ( ) ,
) ) ( input ) ? ;
2023-08-29 17:35:04 -04:00
2023-07-13 22:54:59 -04:00
Ok ( ( remaining , path ) )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 15:44:18 -04:00
fn path_plain_end < ' b , ' g , ' r , ' s > (
_context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-08-29 17:35:04 -04:00
recognize ( many_till (
verify ( anychar , | c | {
* c ! = '/' & & ( c . is_ascii_punctuation ( ) | | c . is_whitespace ( ) )
} ) ,
one_of ( " \t \r \n ()[]<> " ) ,
) ) ( input )
2023-07-13 19:09:44 -04:00
}