2020-04-10 20:27:27 -04:00
use crate ::parser ::template ;
2020-04-11 23:03:07 -04:00
use crate ::parser ::Body ;
2020-04-11 18:40:36 -04:00
use crate ::parser ::DustTag ;
2020-05-10 14:22:59 -04:00
use crate ::parser ::KVPair ;
2020-05-17 21:11:55 -04:00
use crate ::parser ::PartialNameElement ;
2020-05-25 15:40:42 -04:00
use crate ::parser ::Path ;
2020-05-10 14:22:59 -04:00
use crate ::parser ::RValue ;
2020-05-03 14:52:08 -04:00
use crate ::parser ::Special ;
2020-04-10 20:27:27 -04:00
use crate ::parser ::Template ;
2020-05-23 17:57:19 -04:00
use crate ::parser ::{ Filter , TemplateElement } ;
2020-04-28 19:09:02 -04:00
use crate ::renderer ::context_element ::ContextElement ;
2020-04-11 18:25:48 -04:00
use crate ::renderer ::errors ::CompileError ;
use crate ::renderer ::errors ::RenderError ;
2020-05-09 14:00:19 -04:00
use crate ::renderer ::errors ::WalkError ;
2020-05-09 22:05:43 -04:00
use crate ::renderer ::inline_partial_tree ::extract_inline_partials ;
use crate ::renderer ::inline_partial_tree ::InlinePartialTreeElement ;
2020-05-24 16:57:24 -04:00
use crate ::renderer ::iteration_context ::IterationContext ;
2020-05-10 22:21:18 -04:00
use crate ::renderer ::parameters_context ::ParametersContext ;
2020-05-24 15:21:30 -04:00
use crate ::renderer ::walking ::walk_path ;
2020-05-30 12:24:50 -04:00
use std ::borrow ::Borrow ;
2020-05-23 17:57:19 -04:00
use std ::collections ::HashMap ;
2020-04-10 20:27:27 -04:00
#[ derive(Clone, Debug) ]
pub struct CompiledTemplate < ' a > {
template : Template < ' a > ,
2020-04-11 18:25:48 -04:00
pub name : String ,
2020-04-10 20:27:27 -04:00
}
2020-04-10 20:55:44 -04:00
#[ derive(Clone, Debug) ]
2020-04-10 20:58:55 -04:00
pub struct DustRenderer < ' a > {
2020-04-11 18:25:48 -04:00
templates : HashMap < String , & ' a Template < ' a > > ,
2020-04-10 20:55:44 -04:00
}
2020-04-11 18:25:48 -04:00
pub fn compile_template < ' a > (
source : & ' a str ,
name : String ,
) -> Result < CompiledTemplate < ' a > , CompileError > {
// TODO: This could use better error management
2020-04-10 20:27:27 -04:00
let ( _remaining , parsed_template ) = template ( source ) . expect ( " Failed to compile template " ) ;
2020-04-11 18:25:48 -04:00
Ok ( CompiledTemplate {
2020-04-10 20:27:27 -04:00
template : parsed_template ,
name : name ,
2020-04-11 18:25:48 -04:00
} )
2020-04-10 20:27:27 -04:00
}
2020-04-10 20:55:44 -04:00
2020-04-10 20:58:55 -04:00
impl < ' a > DustRenderer < ' a > {
pub fn new ( ) -> DustRenderer < ' a > {
DustRenderer {
2020-04-11 18:25:48 -04:00
templates : HashMap ::new ( ) ,
2020-04-10 20:55:44 -04:00
}
}
pub fn load_source ( & mut self , template : & ' a CompiledTemplate ) {
2020-04-11 18:25:48 -04:00
self . templates
. insert ( template . name . clone ( ) , & template . template ) ;
2020-04-10 20:55:44 -04:00
}
2020-04-10 20:58:55 -04:00
2020-05-05 20:46:31 -04:00
pub fn render (
2020-05-05 19:51:07 -04:00
& ' a self ,
name : & str ,
breadcrumbs : & Vec < & ' a dyn ContextElement > ,
2020-05-09 22:05:43 -04:00
) -> Result < String , RenderError > {
self . render_template ( name , breadcrumbs , None )
}
fn render_template (
& ' a self ,
name : & str ,
breadcrumbs : & Vec < & ' a dyn ContextElement > ,
blocks : Option < & ' a InlinePartialTreeElement < ' a > > ,
2020-05-09 14:27:42 -04:00
) -> Result < String , RenderError > {
2020-05-05 19:51:07 -04:00
let main_template = match self . templates . get ( name ) {
Some ( tmpl ) = > tmpl ,
None = > {
2020-05-09 14:27:42 -04:00
return Err ( RenderError ::TemplateNotFound ( name . to_owned ( ) ) ) ;
2020-05-05 19:51:07 -04:00
}
} ;
2020-05-09 22:05:43 -04:00
let extracted_inline_partials = extract_inline_partials ( main_template ) ;
let new_blocks = InlinePartialTreeElement ::new ( blocks , extracted_inline_partials ) ;
2020-05-25 19:11:14 -04:00
let new_block_context = BlockContext {
breadcrumbs : breadcrumbs ,
blocks : & new_blocks ,
} ;
self . render_body ( & main_template . contents , breadcrumbs , & new_block_context )
2020-05-05 19:51:07 -04:00
}
2020-05-16 17:17:43 -04:00
fn render_maybe_body (
& ' a self ,
body : & ' a Option < Body > ,
breadcrumbs : & Vec < & ' a dyn ContextElement > ,
2020-05-25 19:11:14 -04:00
blocks : & ' a BlockContext < ' a > ,
2020-05-16 17:17:43 -04:00
) -> Result < String , RenderError > {
match body {
None = > Ok ( " " . to_owned ( ) ) ,
Some ( body ) = > Ok ( self . render_body ( body , breadcrumbs , blocks ) ? ) ,
}
}
2020-05-05 20:46:31 -04:00
fn render_body (
2020-05-05 19:51:07 -04:00
& ' a self ,
body : & ' a Body ,
breadcrumbs : & Vec < & ' a dyn ContextElement > ,
2020-05-25 19:11:14 -04:00
blocks : & ' a BlockContext < ' a > ,
2020-05-09 14:27:42 -04:00
) -> Result < String , RenderError > {
2020-05-05 19:51:07 -04:00
let mut output = String ::new ( ) ;
for elem in & body . elements {
match elem {
TemplateElement ::TEIgnoredWhitespace ( _ ) = > { }
TemplateElement ::TESpan ( span ) = > output . push_str ( span . contents ) ,
TemplateElement ::TETag ( dt ) = > {
2020-05-09 22:29:58 -04:00
output . push_str ( & self . render_tag ( dt , breadcrumbs , blocks ) ? ) ;
2020-05-05 19:51:07 -04:00
}
}
}
Ok ( output )
}
2020-05-25 18:08:29 -04:00
/// For rendering a dynamic partial's name
2020-05-17 21:11:55 -04:00
fn render_partial_name (
& ' a self ,
body : & ' a Vec < PartialNameElement > ,
breadcrumbs : & Vec < & ' a dyn ContextElement > ,
2020-05-25 19:11:14 -04:00
blocks : & ' a BlockContext < ' a > ,
2020-05-17 21:11:55 -04:00
) -> Result < String , RenderError > {
let converted_to_template_elements : Vec < TemplateElement < ' a > > =
body . into_iter ( ) . map ( | e | e . into ( ) ) . collect ( ) ;
self . render_body (
& Body {
elements : converted_to_template_elements ,
} ,
breadcrumbs ,
blocks ,
)
}
2020-05-05 20:46:31 -04:00
fn render_tag (
2020-05-05 19:51:07 -04:00
& ' a self ,
tag : & ' a DustTag ,
breadcrumbs : & Vec < & ' a dyn ContextElement > ,
2020-05-25 19:11:14 -04:00
blocks : & ' a BlockContext < ' a > ,
2020-05-09 14:27:42 -04:00
) -> Result < String , RenderError > {
2020-05-05 19:51:07 -04:00
match tag {
DustTag ::DTComment ( _comment ) = > ( ) ,
DustTag ::DTSpecial ( special ) = > {
return Ok ( match special {
Special ::Space = > " " ,
Special ::NewLine = > " \n " ,
Special ::CarriageReturn = > " \r " ,
Special ::LeftCurlyBrace = > " { " ,
Special ::RightCurlyBrace = > " } " ,
}
. to_owned ( ) )
}
2020-05-23 23:41:05 -04:00
DustTag ::DTLiteralStringBlock ( literal ) = > return Ok ( ( * literal ) . to_owned ( ) ) ,
2020-05-05 19:51:07 -04:00
DustTag ::DTReference ( reference ) = > {
2020-05-24 15:21:30 -04:00
let val = walk_path ( breadcrumbs , & reference . path . keys ) ;
2020-05-06 20:30:03 -04:00
match val {
2020-05-09 14:10:38 -04:00
Err ( WalkError ::CantWalk ) = > return Ok ( " " . to_owned ( ) ) ,
2020-05-06 20:30:03 -04:00
Ok ( final_val ) = > {
2020-05-24 16:57:24 -04:00
return if final_val . is_truthy ( ) {
final_val . render ( & Self ::preprocess_filters ( & reference . filters ) )
2020-05-06 20:30:03 -04:00
} else {
2020-05-24 16:57:24 -04:00
Ok ( " " . to_owned ( ) )
} ;
2020-05-06 20:30:03 -04:00
}
2020-05-05 19:51:07 -04:00
}
}
DustTag ::DTSection ( container ) = > {
2020-05-25 23:28:49 -04:00
let injected_context = ParametersContext ::new ( breadcrumbs , & container . params ) ;
2020-05-24 15:21:30 -04:00
let val = walk_path ( breadcrumbs , & container . path . keys ) ;
2020-05-24 16:57:24 -04:00
match val {
Err ( WalkError ::CantWalk ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_section (
2020-05-25 15:49:30 -04:00
breadcrumbs ,
None ,
2020-05-25 23:28:49 -04:00
Some ( & injected_context ) ,
2020-05-25 15:49:30 -04:00
& container . explicit_context ,
None ,
) ;
2020-05-24 16:57:24 -04:00
return self . render_maybe_body (
& container . else_contents ,
2020-05-25 15:49:30 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-24 16:57:24 -04:00
blocks ,
) ;
}
Ok ( final_val ) = > {
return if final_val . is_truthy ( ) {
match & container . contents {
// If the body is empty, just shortcut
// to an empty string now rather than
// generating intermediate contexts
// and iterating for nothing.
None = > Ok ( " " . to_owned ( ) ) ,
Some ( body ) = > {
let loop_elements : Vec < & dyn ContextElement > =
final_val . get_loop_elements ( ) ;
if loop_elements . is_empty ( ) {
// Scalar value
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_section (
2020-05-25 15:55:52 -04:00
breadcrumbs ,
None ,
2020-05-25 23:28:49 -04:00
Some ( & injected_context ) ,
2020-05-25 15:55:52 -04:00
& container . explicit_context ,
Some ( final_val ) ,
) ;
self . render_body (
body ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
)
2020-05-24 16:57:24 -04:00
} else {
// Array-like value
let total_length = loop_elements . len ( ) ;
let rendered_results : Result < Vec < String > , RenderError > =
loop_elements
. into_iter ( )
. enumerate ( )
. map ( | ( i , array_elem ) | {
2020-05-25 23:28:49 -04:00
let index_context =
2020-05-24 16:57:24 -04:00
IterationContext ::new ( i , total_length ) ;
2020-05-25 23:28:49 -04:00
let new_breadcrumbs =
Self ::new_breadcrumbs_section (
breadcrumbs ,
Some ( & index_context ) ,
Some ( & injected_context ) ,
& container . explicit_context ,
Some ( array_elem ) ,
) ;
2020-05-24 16:57:24 -04:00
self . render_body (
& body ,
2020-05-25 15:55:52 -04:00
new_breadcrumbs
. as_ref ( )
. unwrap_or ( breadcrumbs ) ,
2020-05-24 16:57:24 -04:00
blocks ,
)
} )
. collect ( ) ;
let rendered_slice : & [ String ] = & rendered_results ? ;
return Ok ( rendered_slice . join ( " " ) ) ;
}
}
}
} else {
// Oddly enough if the value is falsey (like
// an empty array or null), Dust uses the
// original context before walking the path as
// the context for rendering the else block
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_section (
2020-05-25 15:55:52 -04:00
breadcrumbs ,
None ,
2020-05-25 23:28:49 -04:00
Some ( & injected_context ) ,
2020-05-25 15:55:52 -04:00
& container . explicit_context ,
None ,
) ;
2020-05-24 16:57:24 -04:00
return self . render_maybe_body (
& container . else_contents ,
2020-05-25 15:55:52 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-24 16:57:24 -04:00
blocks ,
) ;
} ;
2020-05-05 19:51:07 -04:00
}
}
}
2020-05-06 19:10:09 -04:00
DustTag ::DTExists ( container ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
breadcrumbs ,
None ,
& container . explicit_context ,
) ;
2020-05-24 15:21:30 -04:00
let val = walk_path ( breadcrumbs , & container . path . keys ) ;
2020-05-24 16:57:24 -04:00
return if val . map ( | v | v . is_truthy ( ) ) . unwrap_or ( false ) {
2020-05-25 16:03:00 -04:00
self . render_maybe_body (
& container . contents ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
)
2020-05-24 16:57:24 -04:00
} else {
2020-05-25 16:03:00 -04:00
self . render_maybe_body (
& container . else_contents ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
)
2020-05-16 17:17:43 -04:00
} ;
2020-05-06 19:10:09 -04:00
}
2020-05-06 20:13:33 -04:00
DustTag ::DTNotExists ( container ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
breadcrumbs ,
None ,
& container . explicit_context ,
) ;
2020-05-24 15:21:30 -04:00
let val = walk_path ( breadcrumbs , & container . path . keys ) ;
2020-05-24 16:57:24 -04:00
return if ! val . map ( | v | v . is_truthy ( ) ) . unwrap_or ( false ) {
2020-05-25 16:03:00 -04:00
self . render_maybe_body (
& container . contents ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
)
2020-05-24 16:57:24 -04:00
} else {
2020-05-25 16:03:00 -04:00
self . render_maybe_body (
& container . else_contents ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
)
2020-05-16 17:17:43 -04:00
} ;
2020-05-06 20:13:33 -04:00
}
2020-05-17 21:17:34 -04:00
DustTag ::DTPartial ( partial ) = > {
2020-05-17 21:11:55 -04:00
let partial_name = self . render_partial_name ( & partial . name , breadcrumbs , blocks ) ? ;
if partial . params . is_empty ( ) {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
breadcrumbs ,
None ,
& partial . explicit_context ,
) ;
2020-05-25 16:03:00 -04:00
let rendered_content = self . render_template (
& partial_name ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-25 19:11:14 -04:00
Some ( blocks . blocks ) ,
2020-05-25 16:03:00 -04:00
) ? ;
2020-05-17 21:11:55 -04:00
return Ok ( rendered_content ) ;
} else {
let injected_context = ParametersContext ::new ( breadcrumbs , & partial . params ) ;
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 16:30:15 -04:00
breadcrumbs ,
Some ( & injected_context ) ,
& partial . explicit_context ,
) ;
let rendered_content = self . render_template (
& partial_name ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-25 19:11:14 -04:00
Some ( blocks . blocks ) ,
2020-05-25 16:30:15 -04:00
) ? ;
2020-05-17 21:11:55 -04:00
return Ok ( rendered_content ) ;
}
}
2020-05-16 17:17:43 -04:00
DustTag ::DTInlinePartial ( _named_block ) = > {
2020-05-09 21:30:54 -04:00
// Inline partials are blank during rendering (they get injected into blocks)
return Ok ( " " . to_owned ( ) ) ;
}
DustTag ::DTBlock ( named_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:22:20 -04:00
breadcrumbs ,
blocks . breadcrumbs ,
None ,
& named_block . explicit_context ,
) ;
2020-05-30 14:39:31 -04:00
return match blocks . blocks . get_block ( named_block . path . keys [ 0 ] ) {
2020-05-25 19:11:14 -04:00
None = > self . render_maybe_body (
& named_block . contents ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
) ,
2020-05-25 19:22:20 -04:00
Some ( inline_partial ) = > self . render_maybe_body (
inline_partial ,
2020-05-25 19:11:14 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
) ,
2020-05-09 22:48:30 -04:00
} ;
2020-05-09 21:30:54 -04:00
}
2020-05-10 14:22:59 -04:00
DustTag ::DTHelperEquals ( parameterized_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 18:08:29 -04:00
breadcrumbs ,
None ,
& parameterized_block . explicit_context ,
) ;
2020-05-16 17:17:43 -04:00
let param_map : HashMap < & str , & RValue < ' a > > =
Self ::get_rval_map ( & parameterized_block . params ) ;
2020-05-16 15:22:48 -04:00
// Special case: when comparing two RVPaths, if the
// path is equal then dust assumes the values are
// equal (otherwise, non-scalar values are
// automatically not equal)
if Self ::are_paths_identical ( & param_map ) {
return match & parameterized_block . contents {
None = > Ok ( " " . to_owned ( ) ) ,
Some ( body ) = > {
2020-05-25 18:08:29 -04:00
let rendered_content = self . render_body (
body ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
) ? ;
2020-05-16 15:22:48 -04:00
Ok ( rendered_content )
}
} ;
}
2020-05-16 17:17:43 -04:00
let left_side : Result < & dyn ContextElement , WalkError > =
match Self ::get_rval ( breadcrumbs , & param_map , " key " ) {
None = > return Ok ( " " . to_owned ( ) ) ,
Some ( res ) = > res ,
2020-05-10 14:22:59 -04:00
} ;
2020-05-16 17:17:43 -04:00
let right_side : Result < & dyn ContextElement , WalkError > =
Self ::get_rval ( breadcrumbs , & param_map , " value " )
. unwrap_or ( Err ( WalkError ::CantWalk ) ) ;
2020-05-25 18:08:29 -04:00
2020-05-10 21:00:52 -04:00
if left_side = = right_side {
2020-05-16 17:17:43 -04:00
return self . render_maybe_body (
& parameterized_block . contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 17:17:43 -04:00
blocks ,
) ;
2020-05-10 21:14:51 -04:00
} else {
2020-05-16 17:17:43 -04:00
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 17:17:43 -04:00
blocks ,
) ;
2020-05-16 12:26:28 -04:00
}
}
DustTag ::DTHelperNotEquals ( parameterized_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 18:08:29 -04:00
breadcrumbs ,
None ,
& parameterized_block . explicit_context ,
) ;
2020-05-16 17:17:43 -04:00
let param_map : HashMap < & str , & RValue < ' a > > =
Self ::get_rval_map ( & parameterized_block . params ) ;
2020-05-16 15:30:17 -04:00
// Special case: when comparing two RVPaths, if the
// path is equal then dust assumes the values are
// equal (otherwise, non-scalar values are
// automatically not equal)
if Self ::are_paths_identical ( & param_map ) {
return match & parameterized_block . else_contents {
None = > Ok ( " " . to_owned ( ) ) ,
Some ( body ) = > {
2020-05-25 18:08:29 -04:00
let rendered_content = self . render_body (
body ,
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
blocks ,
) ? ;
2020-05-16 15:30:17 -04:00
Ok ( rendered_content )
}
} ;
}
2020-05-16 17:17:43 -04:00
let left_side : Result < & dyn ContextElement , WalkError > =
match Self ::get_rval ( breadcrumbs , & param_map , " key " ) {
None = > return Ok ( " " . to_owned ( ) ) ,
Some ( res ) = > res ,
2020-05-16 12:26:28 -04:00
} ;
2020-05-16 17:17:43 -04:00
let right_side : Result < & dyn ContextElement , WalkError > =
Self ::get_rval ( breadcrumbs , & param_map , " value " )
. unwrap_or ( Err ( WalkError ::CantWalk ) ) ;
2020-05-16 12:26:28 -04:00
if left_side ! = right_side {
2020-05-16 17:17:43 -04:00
return self . render_maybe_body (
& parameterized_block . contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 17:17:43 -04:00
blocks ,
) ;
2020-05-16 12:26:28 -04:00
} else {
2020-05-16 17:17:43 -04:00
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 17:17:43 -04:00
blocks ,
) ;
2020-05-10 19:16:55 -04:00
}
2020-05-10 14:22:59 -04:00
}
2020-05-16 15:45:57 -04:00
DustTag ::DTHelperGreaterThan ( parameterized_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 18:08:29 -04:00
breadcrumbs ,
None ,
& parameterized_block . explicit_context ,
) ;
2020-05-16 17:17:43 -04:00
let param_map : HashMap < & str , & RValue < ' a > > =
Self ::get_rval_map ( & parameterized_block . params ) ;
let left_side : Result < & dyn ContextElement , WalkError > =
match Self ::get_rval ( breadcrumbs , & param_map , " key " ) {
None = > return Ok ( " " . to_owned ( ) ) ,
Some ( res ) = > res ,
2020-05-16 16:24:36 -04:00
} ;
2020-05-16 17:17:43 -04:00
let right_side : Result < & dyn ContextElement , WalkError > =
Self ::get_rval ( breadcrumbs , & param_map , " value " )
. unwrap_or ( Err ( WalkError ::CantWalk ) ) ;
2020-05-16 16:24:36 -04:00
match ( left_side , right_side ) {
2020-05-16 18:28:23 -04:00
( Err ( _ ) , _ ) | ( _ , Err ( _ ) ) = > {
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 18:28:23 -04:00
blocks ,
)
}
2020-05-16 16:24:36 -04:00
( Ok ( left_side_unwrapped ) , Ok ( right_side_unwrapped ) ) = > {
if left_side_unwrapped > right_side_unwrapped {
2020-05-16 17:17:43 -04:00
return self . render_maybe_body (
& parameterized_block . contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 17:17:43 -04:00
blocks ,
) ;
2020-05-16 16:24:36 -04:00
} else {
2020-05-16 17:17:43 -04:00
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 17:17:43 -04:00
blocks ,
) ;
2020-05-16 16:24:36 -04:00
}
}
}
2020-05-16 15:45:57 -04:00
}
2020-05-16 19:05:03 -04:00
DustTag ::DTHelperGreaterThanOrEquals ( parameterized_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 18:08:29 -04:00
breadcrumbs ,
None ,
& parameterized_block . explicit_context ,
) ;
2020-05-16 18:28:23 -04:00
let param_map : HashMap < & str , & RValue < ' a > > =
Self ::get_rval_map ( & parameterized_block . params ) ;
let left_side : Result < & dyn ContextElement , WalkError > =
match Self ::get_rval ( breadcrumbs , & param_map , " key " ) {
None = > return Ok ( " " . to_owned ( ) ) ,
Some ( res ) = > res ,
} ;
let right_side : Result < & dyn ContextElement , WalkError > =
Self ::get_rval ( breadcrumbs , & param_map , " value " )
. unwrap_or ( Err ( WalkError ::CantWalk ) ) ;
match ( left_side , right_side ) {
( Err ( _ ) , _ ) | ( _ , Err ( _ ) ) = > {
return self . render_maybe_body (
2020-05-16 18:36:47 -04:00
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 18:28:23 -04:00
blocks ,
)
}
( Ok ( left_side_unwrapped ) , Ok ( right_side_unwrapped ) ) = > {
2020-05-16 23:10:04 -04:00
if left_side_unwrapped > = right_side_unwrapped {
return self . render_maybe_body (
& parameterized_block . contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 23:10:04 -04:00
blocks ,
) ;
} else {
return self . render_maybe_body (
2020-05-16 18:28:23 -04:00
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 18:28:23 -04:00
blocks ,
2020-05-16 23:10:04 -04:00
) ;
}
2020-05-16 18:28:23 -04:00
}
}
}
2020-05-16 19:05:03 -04:00
DustTag ::DTHelperLessThan ( parameterized_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 18:08:29 -04:00
breadcrumbs ,
None ,
& parameterized_block . explicit_context ,
) ;
2020-05-16 19:05:03 -04:00
let param_map : HashMap < & str , & RValue < ' a > > =
Self ::get_rval_map ( & parameterized_block . params ) ;
let left_side : Result < & dyn ContextElement , WalkError > =
match Self ::get_rval ( breadcrumbs , & param_map , " key " ) {
None = > return Ok ( " " . to_owned ( ) ) ,
Some ( res ) = > res ,
} ;
let right_side : Result < & dyn ContextElement , WalkError > =
Self ::get_rval ( breadcrumbs , & param_map , " value " )
. unwrap_or ( Err ( WalkError ::CantWalk ) ) ;
match ( left_side , right_side ) {
( Err ( _ ) , _ ) | ( _ , Err ( _ ) ) = > {
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 19:05:03 -04:00
blocks ,
)
}
( Ok ( left_side_unwrapped ) , Ok ( right_side_unwrapped ) ) = > {
if left_side_unwrapped < right_side_unwrapped {
return self . render_maybe_body (
& parameterized_block . contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 19:05:03 -04:00
blocks ,
) ;
} else {
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 19:05:03 -04:00
blocks ,
) ;
}
}
}
}
2020-05-16 19:06:40 -04:00
DustTag ::DTHelperLessThanOrEquals ( parameterized_block ) = > {
2020-05-25 23:28:49 -04:00
let new_breadcrumbs = Self ::new_breadcrumbs_partial (
2020-05-25 19:11:14 -04:00
breadcrumbs ,
2020-05-25 18:08:29 -04:00
breadcrumbs ,
None ,
& parameterized_block . explicit_context ,
) ;
2020-05-16 19:06:40 -04:00
let param_map : HashMap < & str , & RValue < ' a > > =
Self ::get_rval_map ( & parameterized_block . params ) ;
let left_side : Result < & dyn ContextElement , WalkError > =
match Self ::get_rval ( breadcrumbs , & param_map , " key " ) {
None = > return Ok ( " " . to_owned ( ) ) ,
Some ( res ) = > res ,
} ;
let right_side : Result < & dyn ContextElement , WalkError > =
Self ::get_rval ( breadcrumbs , & param_map , " value " )
. unwrap_or ( Err ( WalkError ::CantWalk ) ) ;
match ( left_side , right_side ) {
( Err ( _ ) , _ ) | ( _ , Err ( _ ) ) = > {
return self . render_maybe_body (
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 19:06:40 -04:00
blocks ,
)
}
( Ok ( left_side_unwrapped ) , Ok ( right_side_unwrapped ) ) = > {
2020-05-16 23:10:04 -04:00
if left_side_unwrapped < = right_side_unwrapped {
return self . render_maybe_body (
& parameterized_block . contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 23:10:04 -04:00
blocks ,
) ;
} else {
return self . render_maybe_body (
2020-05-16 19:06:40 -04:00
& parameterized_block . else_contents ,
2020-05-25 18:08:29 -04:00
new_breadcrumbs . as_ref ( ) . unwrap_or ( breadcrumbs ) ,
2020-05-16 19:06:40 -04:00
blocks ,
2020-05-16 23:10:04 -04:00
) ;
}
2020-05-16 19:06:40 -04:00
}
}
}
2020-05-05 19:51:07 -04:00
}
Ok ( " " . to_owned ( ) )
}
2020-05-03 15:29:02 -04:00
/// Gets the elements to loop over for a section.
///
/// If the value is falsey, and therefore should render the else
/// block, this will return an empty vector.
fn get_loop_elements < ' b > (
2020-05-09 14:14:22 -04:00
walk_result : Result < & ' b dyn ContextElement , WalkError > ,
) -> Vec < & ' b dyn ContextElement > {
match walk_result {
Err ( WalkError ::CantWalk ) = > Vec ::new ( ) ,
Ok ( walk_target ) = > walk_target . get_loop_elements ( ) ,
2020-05-03 15:29:02 -04:00
}
}
2020-05-16 15:22:48 -04:00
fn are_paths_identical < ' b > ( param_map : & HashMap < & str , & RValue < ' b > > ) -> bool {
match ( param_map . get ( " key " ) , param_map . get ( " value " ) ) {
( None , _ ) = > false ,
( _ , None ) = > false ,
( Some ( key_rval ) , Some ( value_rval ) ) = > match ( key_rval , value_rval ) {
( RValue ::RVPath ( key_path ) , RValue ::RVPath ( value_path ) ) = > {
key_path . keys = = value_path . keys
}
_ = > false ,
} ,
}
}
2020-05-16 17:17:43 -04:00
fn get_rval_map < ' b > ( params : & ' b Vec < KVPair < ' b > > ) -> HashMap < & ' b str , & ' b RValue < ' b > > {
params
. iter ( )
. map ( | pair : & KVPair < ' b > | ( pair . key , & pair . value ) )
. collect ( )
}
fn get_rval < ' b > (
2020-05-24 15:01:41 -04:00
breadcrumbs : & ' b Vec < & ' b dyn ContextElement > ,
2020-05-16 17:17:43 -04:00
param_map : & HashMap < & str , & ' b RValue < ' b > > ,
key : & str ,
) -> Option < Result < & ' b dyn ContextElement , WalkError > > {
match param_map . get ( key ) {
None = > None ,
Some ( rval ) = > match rval {
2020-05-16 22:30:04 -04:00
RValue ::RVLiteral ( literal ) = > Some ( Ok ( literal ) ) ,
2020-05-24 15:21:30 -04:00
RValue ::RVPath ( path ) = > Some ( walk_path ( breadcrumbs , & path . keys ) ) ,
2020-05-16 17:17:43 -04:00
} ,
}
}
2020-05-23 17:57:19 -04:00
fn preprocess_filters ( filters : & Vec < Filter > ) -> Vec < Filter > {
let mut final_filters : Vec < Filter > = filters
. into_iter ( )
. filter ( | f | f ! = & & Filter ::DisableHtmlEncode )
. map ( | f | f . clone ( ) )
. collect ( ) ;
// If the user has not specified any escaping filter (|s or
// |h), automatically add an html escape filter
2020-05-23 18:18:59 -04:00
if ! filters . iter ( ) . any ( | f | f = = & Filter ::DisableHtmlEncode ) {
2020-05-23 17:57:19 -04:00
final_filters . push ( Filter ::HtmlEncode ) ;
}
final_filters
}
2020-05-25 15:40:42 -04:00
/// Generate a new breadcrumbs object
///
/// This function generates a new breadcrumbs object based on the
/// new context information provided.
///
2020-05-25 19:11:14 -04:00
/// breadcrumbs are the breadcrumbs that will be used in the final
/// breadcrumbs (unless omitted due to an explicit context)
///
/// explicit_context_breadcrumbs are the breadcrumbs used to
/// evaluate an explicit context path. Most of the time the two
/// breadcrumbs parameters will be identical, but for
/// blocks/inline partials the explicit_context_breadcrumbs will
/// be the breadcrumbs from the start of the partial containing
/// the block.
///
2020-05-25 15:40:42 -04:00
/// explicit_context is for contexts specified with a `:path`
/// inside a dust tag.
///
/// injected_context is for any generated context. This includes
/// both parameters on a tag and also the handling of $idx and
/// $len.
///
/// New context element is the element is an element to append to
/// the end, generally for use in section tags which walk to a new
/// context.
///
/// If explicit_context is not None, then the final breadcrumb stack will be:
///
2020-05-25 18:08:29 -04:00
/// ```text
2020-05-25 15:40:42 -04:00
/// breadcrumbs
/// injected_context
/// new_context_element
/// ```
///
/// However, if explicit_context is not None, then the old
/// breadcrumbs are omitted, leading to the new breadcrumb stack
/// as:
///
2020-05-25 18:08:29 -04:00
/// ```text
2020-05-25 15:40:42 -04:00
/// injected_context
/// explicit_context
/// new_context_element
/// ```
2020-05-25 23:28:49 -04:00
fn new_breadcrumbs_deprecated < ' b > (
2020-05-25 15:40:42 -04:00
breadcrumbs : & ' b Vec < & ' b dyn ContextElement > ,
2020-05-25 19:11:14 -04:00
explicit_context_breadcrumbs : & ' b Vec < & ' b dyn ContextElement > ,
2020-05-25 15:40:42 -04:00
injected_context : Option < & ' b dyn ContextElement > ,
explicit_context : & Option < Path < ' b > > ,
new_context_element : Option < & ' b dyn ContextElement > ,
2020-05-25 15:49:30 -04:00
) -> Option < Vec < & ' b dyn ContextElement > > {
// If none of the additional contexts are present, return None
// to signal that the original breadcrumbs should be used
// rather than incurring a copy here.
match ( injected_context , explicit_context , new_context_element ) {
( None , None , None ) = > return None ,
_ = > ( ) ,
} ;
2020-05-25 15:40:42 -04:00
let mut new_stack = match explicit_context {
Some ( _ ) = > Vec ::with_capacity ( 3 ) ,
None = > breadcrumbs . clone ( ) ,
} ;
2020-05-25 17:53:04 -04:00
// TODO: Can sections have parameters, and if so, what happens then? Currently when there is an injected context or an explicit context it gets inserted behind the current context, so 1->2->3 becomes 1->2->injected->3 or explicit->3. When there is a new context(4) with injected we're doing 1->2->3->injected->4. When there is an explicit context and a new context we're doing explicit->4. But what happens if there is a section with parameters and an explicit context, hitting all the categories? Would it be parameters->explicit->4? I would definitely have to change the parameters to this function since right now iteration variables and parameters are both sharing injected_context.
2020-05-25 16:30:15 -04:00
injected_context . map ( | ctx | {
// Special case: when there is no explicit context or new
// context element, the injected context gets inserted 1
// spot behind the current context. Otherwise, the
// injected context gets added after the current context
// but before the explicit context and new context
// element.
match ( explicit_context , new_context_element ) {
( None , None ) = > new_stack . insert ( std ::cmp ::max ( new_stack . len ( ) - 1 , 0 ) , ctx ) ,
_ = > new_stack . push ( ctx ) ,
}
} ) ;
2020-05-25 15:40:42 -04:00
explicit_context . as_ref ( ) . map ( | path | {
2020-05-25 19:11:14 -04:00
walk_path ( explicit_context_breadcrumbs , & path . keys ) . map ( | val | {
2020-05-25 15:40:42 -04:00
if val . is_truthy ( ) {
new_stack . push ( val )
}
} ) ;
} ) ;
new_context_element . map ( | ctx | new_stack . push ( ctx ) ) ;
2020-05-25 15:49:30 -04:00
Some ( new_stack )
2020-05-25 15:40:42 -04:00
}
2020-05-25 22:50:10 -04:00
fn new_breadcrumbs_section < ' b > (
breadcrumbs : & ' b Vec < & ' b dyn ContextElement > ,
index_context : Option < & ' b dyn ContextElement > ,
injected_context : Option < & ' b dyn ContextElement > ,
explicit_context : & Option < Path < ' b > > ,
new_context_element : Option < & ' b dyn ContextElement > ,
) -> Option < Vec < & ' b dyn ContextElement > > {
// If none of the additional contexts are present, return None
// to signal that the original breadcrumbs should be used
// rather than incurring a copy here.
match (
index_context ,
injected_context ,
explicit_context ,
new_context_element ,
) {
( None , None , None , None ) = > return None ,
_ = > ( ) ,
}
let mut new_stack = match explicit_context {
Some ( _ ) = > Vec ::with_capacity ( 4 ) ,
None = > breadcrumbs . clone ( ) ,
} ;
explicit_context . as_ref ( ) . map ( | path | {
walk_path ( breadcrumbs , & path . keys ) . map ( | val | {
if val . is_truthy ( ) {
new_stack . push ( val )
}
} ) ;
} ) ;
injected_context . map ( | ctx | new_stack . push ( ctx ) ) ;
new_context_element . map ( | ctx | new_stack . push ( ctx ) ) ;
index_context . map ( | ctx | new_stack . push ( ctx ) ) ;
Some ( new_stack )
}
2020-05-30 12:24:50 -04:00
fn get_index_of_first_non_pseudo_element < ' b , B > ( breadcrumbs : & ' b Vec < B > ) -> Option < usize >
where
B : Borrow < dyn ContextElement + ' a > ,
{
breadcrumbs
. iter ( )
. rposition ( | b | ! ( * b ) . borrow ( ) . is_pseudo_element ( ) )
}
2020-05-25 22:50:10 -04:00
fn new_breadcrumbs_partial < ' b > (
breadcrumbs : & ' b Vec < & ' b dyn ContextElement > ,
explicit_context_breadcrumbs : & ' b Vec < & ' b dyn ContextElement > ,
injected_context : Option < & ' b dyn ContextElement > ,
explicit_context : & Option < Path < ' b > > ,
) -> Option < Vec < & ' b dyn ContextElement > > {
// If none of the additional contexts are present, return None
// to signal that the original breadcrumbs should be used
// rather than incurring a copy here.
match ( injected_context , explicit_context ) {
( None , None ) = > return None ,
_ = > ( ) ,
} ;
let mut new_stack = match explicit_context {
Some ( _ ) = > Vec ::with_capacity ( 3 ) ,
None = > breadcrumbs . clone ( ) ,
} ;
injected_context . map ( | ctx | {
// Special case: when there is no explicit context, the
// injected context gets inserted 1 spot behind the
// current context. Otherwise, the injected context gets
// added after the current context but before the explicit
// context.
match explicit_context {
2020-05-30 12:24:50 -04:00
None = > new_stack . insert (
Self ::get_index_of_first_non_pseudo_element ( & new_stack ) . unwrap_or ( 0 ) ,
ctx ,
) ,
2020-05-25 22:50:10 -04:00
_ = > new_stack . push ( ctx ) ,
}
} ) ;
explicit_context . as_ref ( ) . map ( | path | {
walk_path ( explicit_context_breadcrumbs , & path . keys ) . map ( | val | {
if val . is_truthy ( ) {
new_stack . push ( val )
}
} ) ;
} ) ;
Some ( new_stack )
}
2020-04-10 20:55:44 -04:00
}
2020-04-11 20:31:44 -04:00
2020-05-25 19:11:14 -04:00
struct BlockContext < ' a > {
/// The breadcrumbs at the time of entering the current partial
breadcrumbs : & ' a Vec < & ' a dyn ContextElement > ,
blocks : & ' a InlinePartialTreeElement < ' a > ,
}
2020-04-11 20:31:44 -04:00
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-04-12 21:54:15 -04:00
use crate ::parser ::Filter ;
2020-04-28 19:34:52 -04:00
use crate ::renderer ::context_element ::Loopable ;
2020-04-28 19:09:02 -04:00
use crate ::renderer ::context_element ::Renderable ;
2020-05-24 16:16:43 -04:00
use crate ::renderer ::context_element ::Truthiness ;
2020-04-28 19:09:02 -04:00
use crate ::renderer ::context_element ::Walkable ;
2020-05-10 23:26:15 -04:00
use crate ::renderer ::CompareContextElement ;
2020-05-23 19:34:38 -04:00
use std ::cmp ::Ordering ;
2020-04-11 20:31:44 -04:00
2020-05-16 22:39:29 -04:00
impl ContextElement for String { }
2020-05-24 16:16:43 -04:00
impl Truthiness for String {
fn is_truthy ( & self ) -> bool {
! self . is_empty ( )
}
}
2020-05-16 22:39:29 -04:00
impl Renderable for String {
fn render ( & self , _filters : & Vec < Filter > ) -> Result < String , RenderError > {
Ok ( self . clone ( ) )
}
}
impl Loopable for String {
fn get_loop_elements ( & self ) -> Vec < & dyn ContextElement > {
2020-05-24 16:27:13 -04:00
Vec ::new ( )
2020-05-16 22:39:29 -04:00
}
}
impl Walkable for String {
fn walk ( & self , segment : & str ) -> Result < & dyn ContextElement , WalkError > {
Err ( WalkError ::CantWalk )
}
}
impl CompareContextElement for String {
fn equals ( & self , other : & dyn ContextElement ) -> bool {
match other . to_any ( ) . downcast_ref ::< Self > ( ) {
None = > false ,
Some ( other_string ) = > self = = other_string ,
}
}
fn partial_compare ( & self , other : & dyn ContextElement ) -> Option < Ordering > {
match other . to_any ( ) . downcast_ref ::< Self > ( ) {
None = > None ,
Some ( other_string ) = > self . partial_cmp ( other_string ) ,
}
}
}
impl ContextElement for u64 { }
2020-05-24 16:16:43 -04:00
impl Truthiness for u64 {
fn is_truthy ( & self ) -> bool {
true
}
}
2020-05-16 22:39:29 -04:00
impl Renderable for u64 {
fn render ( & self , _filters : & Vec < Filter > ) -> Result < String , RenderError > {
Ok ( self . to_string ( ) )
}
}
impl Loopable for u64 {
fn get_loop_elements ( & self ) -> Vec < & dyn ContextElement > {
2020-05-24 16:27:13 -04:00
Vec ::new ( )
2020-05-16 22:39:29 -04:00
}
}
impl Walkable for u64 {
fn walk ( & self , segment : & str ) -> Result < & dyn ContextElement , WalkError > {
Err ( WalkError ::CantWalk )
}
}
impl CompareContextElement for u64 {
fn equals ( & self , other : & dyn ContextElement ) -> bool {
match other . to_any ( ) . downcast_ref ::< Self > ( ) {
None = > false ,
Some ( other_num ) = > self = = other_num ,
}
}
fn partial_compare ( & self , other : & dyn ContextElement ) -> Option < Ordering > {
match other . to_any ( ) . downcast_ref ::< Self > ( ) {
None = > None ,
Some ( other_num ) = > self . partial_cmp ( other_num ) ,
}
}
}
2020-05-10 23:26:15 -04:00
impl < I : 'static + ContextElement + Clone > ContextElement for HashMap < String , I > { }
2020-04-11 22:19:54 -04:00
2020-05-24 16:16:43 -04:00
impl < I : ContextElement > Truthiness for HashMap < String , I > {
fn is_truthy ( & self ) -> bool {
true
}
}
2020-05-10 23:26:15 -04:00
impl < I : ContextElement > Renderable for HashMap < String , I > {
2020-05-05 20:22:25 -04:00
fn render ( & self , _filters : & Vec < Filter > ) -> Result < String , RenderError > {
// TODO: handle the filters
2020-05-09 13:46:12 -04:00
Ok ( " [object Object] " . to_owned ( ) )
2020-04-11 20:31:44 -04:00
}
2020-05-05 20:22:25 -04:00
}
2020-04-11 20:31:44 -04:00
2020-05-10 23:26:15 -04:00
impl < I : ContextElement > Walkable for HashMap < String , I > {
2020-05-09 14:22:36 -04:00
fn walk ( & self , segment : & str ) -> Result < & dyn ContextElement , WalkError > {
let child = self . get ( segment ) . ok_or ( WalkError ::CantWalk ) ? ;
2020-05-05 20:22:25 -04:00
Ok ( child )
2020-04-11 21:07:12 -04:00
}
2020-05-05 20:22:25 -04:00
}
2020-04-11 21:07:12 -04:00
2020-05-10 23:26:15 -04:00
impl < I : 'static + ContextElement + Clone > Loopable for HashMap < String , I > {
2020-05-09 14:22:36 -04:00
fn get_loop_elements ( & self ) -> Vec < & dyn ContextElement > {
2020-05-24 16:27:13 -04:00
Vec ::new ( )
2020-04-28 19:34:52 -04:00
}
2020-05-05 20:22:25 -04:00
}
2020-05-10 23:26:15 -04:00
impl < I : 'static + ContextElement + Clone > CompareContextElement for HashMap < String , I > {
fn equals ( & self , other : & dyn ContextElement ) -> bool {
false
2020-05-05 20:22:25 -04:00
}
2020-05-16 21:30:51 -04:00
fn partial_compare ( & self , other : & dyn ContextElement ) -> Option < Ordering > {
// TODO: Implement
None
}
2020-05-05 20:22:25 -04:00
}
#[ test ]
2020-05-05 20:43:53 -04:00
fn test_walk_path ( ) {
2020-05-10 23:26:15 -04:00
let context : HashMap < String , String > = [
( " cat " . to_string ( ) , " kitty " . to_string ( ) ) ,
( " dog " . to_string ( ) , " doggy " . to_string ( ) ) ,
( " tiger " . to_string ( ) , " murderkitty " . to_string ( ) ) ,
]
. iter ( )
. cloned ( )
. collect ( ) ;
let number_context : HashMap < String , u64 > = [
( " cat " . to_string ( ) , 1 ) ,
( " dog " . to_string ( ) , 2 ) ,
( " tiger " . to_string ( ) , 3 ) ,
]
. iter ( )
. cloned ( )
. collect ( ) ;
let deep_context : HashMap < String , HashMap < String , String > > = [
(
" cat " . to_string ( ) ,
[ ( " food " . to_string ( ) , " meat " . to_string ( ) ) ]
. iter ( )
. cloned ( )
. collect ( ) ,
) ,
(
" dog " . to_string ( ) ,
[ ( " food " . to_string ( ) , " meat " . to_string ( ) ) ]
. iter ( )
. cloned ( )
. collect ( ) ,
) ,
(
" tiger " . to_string ( ) ,
[ ( " food " . to_string ( ) , " people " . to_string ( ) ) ]
. iter ( )
. cloned ( )
. collect ( ) ,
) ,
2020-05-05 20:22:25 -04:00
]
. iter ( )
. cloned ( )
. collect ( ) ;
assert_eq! (
2020-05-24 15:21:30 -04:00
walk_path ( & vec! [ & context as & dyn ContextElement ] , & vec! [ " cat " ] )
2020-05-05 20:22:25 -04:00
. unwrap ( )
. render ( & Vec ::new ( ) )
. unwrap ( ) ,
" kitty " . to_owned ( )
) ;
2020-05-05 20:38:42 -04:00
assert_eq! (
2020-05-24 15:21:30 -04:00
walk_path (
2020-05-05 20:38:42 -04:00
& vec! [ & number_context as & dyn ContextElement ] ,
& vec! [ " tiger " ]
)
. unwrap ( )
. render ( & Vec ::new ( ) )
. unwrap ( ) ,
" 3 " . to_owned ( )
) ;
assert_eq! (
2020-05-24 15:21:30 -04:00
walk_path (
2020-05-05 20:38:42 -04:00
& vec! [ & deep_context as & dyn ContextElement ] ,
& vec! [ " tiger " , " food " ]
)
. unwrap ( )
. render ( & Vec ::new ( ) )
. unwrap ( ) ,
" people " . to_owned ( )
) ;
2020-05-05 20:22:25 -04:00
}
2020-04-11 20:31:44 -04:00
}