@ -138,3 +138,197 @@ impl StyleComponents {
self . 0. clear ( ) ;
}
}
#[ derive(Debug, PartialEq) ]
enum ComponentAction {
Override ,
Add ,
Remove ,
}
impl ComponentAction {
fn extract_from_str ( string : & str ) -> ( ComponentAction , & str ) {
match string . chars ( ) . next ( ) {
Some ( '-' ) = > ( ComponentAction ::Remove , string . strip_prefix ( '-' ) . unwrap ( ) ) ,
Some ( '+' ) = > ( ComponentAction ::Add , string . strip_prefix ( '+' ) . unwrap ( ) ) ,
_ = > ( ComponentAction ::Override , string ) ,
}
}
}
/// A list of [StyleComponent] that can be parsed from a string.
pub struct StyleComponentList ( Vec < ( ComponentAction , StyleComponent ) > ) ;
impl StyleComponentList {
fn expand_into ( & self , components : & mut HashSet < StyleComponent > , interactive_terminal : bool ) {
for ( action , component ) in self . 0. iter ( ) {
let subcomponents = component . components ( interactive_terminal ) ;
use ComponentAction ::* ;
match action {
Override | Add = > components . extend ( subcomponents ) ,
Remove = > components . retain ( | c | ! subcomponents . contains ( c ) ) ,
}
}
}
/// Returns `true` if any component in the list was not prefixed with `+` or `-`.
fn contains_override ( & self ) -> bool {
self . 0. iter ( ) . any ( | ( a , _ ) | * a = = ComponentAction ::Override )
}
/// Combines multiple [StyleComponentList]s into a single [StyleComponents] set.
///
/// ## Precedence
/// The most recent list will take precedence and override all previous lists
/// unless it only contains components prefixed with `-` or `+`. When this
/// happens, the list's components will be merged into the previous list.
///
/// ## Example
/// ```text
/// [numbers,grid] + [header,changes] -> [header,changes]
/// [numbers,grid] + [+header,-grid] -> [numbers,header]
/// ```
pub fn to_components (
lists : impl IntoIterator < Item = StyleComponentList > ,
interactive_terminal : bool ,
) -> StyleComponents {
StyleComponents (
lists
. into_iter ( )
. fold ( HashSet ::new ( ) , | mut components , list | {
if list . contains_override ( ) {
components . clear ( ) ;
}
list . expand_into ( & mut components , interactive_terminal ) ;
components
} ) ,
)
}
}
impl Default for StyleComponentList {
fn default ( ) -> Self {
StyleComponentList ( vec! [ ( ComponentAction ::Override , StyleComponent ::Default ) ] )
}
}
impl FromStr for StyleComponentList {
type Err = Error ;
fn from_str ( s : & str ) -> Result < Self > {
Ok ( StyleComponentList (
s . split ( "," )
. map ( | s | ComponentAction ::extract_from_str ( s ) ) // If the component starts with "-", it's meant to be removed
. map ( | ( a , s ) | Ok ( ( a , StyleComponent ::from_str ( s ) ? ) ) )
. collect ::< Result < Vec < ( ComponentAction , StyleComponent ) > > > ( ) ? ,
) )
}
}
#[ cfg(test) ]
mod test {
use std ::collections ::HashSet ;
use std ::str ::FromStr ;
use super ::ComponentAction ::* ;
use super ::StyleComponent ::* ;
use super ::StyleComponentList ;
#[ test ]
pub fn style_component_list_parse ( ) {
assert_eq! (
StyleComponentList ::from_str ( "grid,+numbers,snip,-snip,header" )
. expect ( "no error" )
. 0 ,
vec! [
( Override , Grid ) ,
( Add , LineNumbers ) ,
( Override , Snip ) ,
( Remove , Snip ) ,
( Override , Header ) ,
]
) ;
assert! ( StyleComponentList ::from_str ( "not-a-component" ) . is_err ( ) ) ;
assert! ( StyleComponentList ::from_str ( "grid,not-a-component" ) . is_err ( ) ) ;
assert! ( StyleComponentList ::from_str ( "numbers,-not-a-component" ) . is_err ( ) ) ;
}
#[ test ]
pub fn style_component_list_to_components ( ) {
assert_eq! (
StyleComponentList ::to_components (
vec! [ StyleComponentList ::from_str ( "grid,numbers" ) . expect ( "no error" ) ] ,
false
)
. 0 ,
HashSet ::from ( [ Grid , LineNumbers ] )
) ;
}
#[ test ]
pub fn style_component_list_to_components_removes_negated ( ) {
assert_eq! (
StyleComponentList ::to_components (
vec! [ StyleComponentList ::from_str ( "grid,numbers,-grid" ) . expect ( "no error" ) ] ,
false
)
. 0 ,
HashSet ::from ( [ LineNumbers ] )
) ;
}
#[ test ]
pub fn style_component_list_to_components_expands_subcomponents ( ) {
assert_eq! (
StyleComponentList ::to_components (
vec! [ StyleComponentList ::from_str ( "full" ) . expect ( "no error" ) ] ,
false
)
. 0 ,
HashSet ::from_iter ( Full . components ( true ) . to_owned ( ) )
) ;
}
#[ test ]
pub fn style_component_list_expand_negates_subcomponents ( ) {
assert! ( ! StyleComponentList ::to_components (
vec! [ StyleComponentList ::from_str ( "full,-numbers" ) . expect ( "no error" ) ] ,
true
)
. numbers ( ) ) ;
}
#[ test ]
pub fn style_component_list_to_components_precedence_overrides_previous_lists ( ) {
assert_eq! (
StyleComponentList ::to_components (
vec! [
StyleComponentList ::from_str ( "grid" ) . expect ( "no error" ) ,
StyleComponentList ::from_str ( "numbers" ) . expect ( "no error" ) ,
] ,
false
)
. 0 ,
HashSet ::from ( [ LineNumbers ] )
) ;
}
#[ test ]
pub fn style_component_list_to_components_precedence_merges_previous_lists ( ) {
assert_eq! (
StyleComponentList ::to_components (
vec! [
StyleComponentList ::from_str ( "grid,header" ) . expect ( "no error" ) ,
StyleComponentList ::from_str ( "-grid" ) . expect ( "no error" ) ,
StyleComponentList ::from_str ( "+numbers" ) . expect ( "no error" ) ,
] ,
false
)
. 0 ,
HashSet ::from ( [ HeaderFilename , LineNumbers ] )
) ;
}
}