use std::collections::HashMap; use rt_format::argument::{ ArgumentSource, NamedArguments, NoNamedArguments, NoPositionalArguments, PositionalArguments }; use rt_format::parser::{parse_specifier}; use rt_format::{Align, ParsedFormat, Format, Pad, Precision, Repr, Sign, Specifier, Width}; mod common; use common::Variant; type ParseResult<'a> = Result, usize> ; fn parse<'a, P, N>(format: &'a str, positional: &'a P, named: &'a N) -> ParseResult<'a> where P: PositionalArguments<'a, Variant>, N: NamedArguments, { ParsedFormat::parse(format, positional, named) } #[test] fn unmatched_brace() { assert_eq!(Err(4), parse("foo {", &NoPositionalArguments, &NoNamedArguments)); assert_eq!(Err(4), parse("bar } baz", &NoPositionalArguments, &NoNamedArguments)); } #[test] fn escaped_braces() { assert_eq!( "{}", parse("{{}}", &NoPositionalArguments, &NoNamedArguments) .unwrap() .to_string() ); } #[test] fn invalid_specifier() { assert_eq!( Err(4), parse("foo {:Z} bar", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn invalid_arg_position() { assert_eq!( Err(4), parse("foo {0bar} baz", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn positional_arg_iter() { assert_eq!( "42 42.042", parse("{} {}", &[Variant::Int(42), Variant::Float(42.042)], &NoNamedArguments) .unwrap() .to_string() ); } #[test] fn positional_arg_lookup() { assert_eq!( "42.042", parse("{1}", &[Variant::Int(42), Variant::Float(42.042)], &NoNamedArguments) .unwrap() .to_string() ); } #[test] fn named_arg_lookup() { let mut map = HashMap::new(); map.insert("arglebargle".to_string(), Variant::Float(-42.042)); assert_eq!( "-42.042", parse("{arglebargle}", &NoPositionalArguments, &map) .unwrap() .to_string() ); } #[test] fn missing_next_arg() { assert_eq!( Err(3), parse("{} {}", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn missing_positional_arg() { assert_eq!(Err(0), parse("{1}", &[Variant::Int(42)], &NoNamedArguments)); } #[test] fn missing_named_arg() { assert_eq!(Err(0), parse("{arglebargle}", &NoPositionalArguments, &NoNamedArguments)); } #[test] fn missing_positional_width() { assert_eq!( Err(0), parse("{:1$}", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn missing_named_width() { assert_eq!( Err(0), parse("{:arglebargle$}", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn missing_positional_precision() { assert_eq!( Err(0), parse("{:.1$}", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn missing_named_precision() { assert_eq!( Err(0), parse("{:.arglebargle$}", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn missing_asterisk_precision() { assert_eq!( Err(3), parse("{} {0:.*}", &[Variant::Int(42)], &NoNamedArguments) ); } #[test] fn named_argument_validity() { let mut map = HashMap::new(); map.insert("ascii_identifier".to_string(), Variant::Int(42)); map.insert("_leading_underscore".to_string(), Variant::Int(4242)); map.insert("уникод".to_string(), Variant::Float(42.042)); map.insert("0leading_digit".to_string(), Variant::Int(-42)); map.insert("invalid/character".to_string(), Variant::Float(-42.042)); assert!(parse("{ascii_identifier}", &NoPositionalArguments, &map).is_ok()); assert!(parse("{_leading_underscore}", &NoPositionalArguments, &map).is_ok()); assert!(parse("{уникод}", &NoPositionalArguments, &map).is_ok()); assert_eq!(Err(0), parse("{0leading_digit}", &NoPositionalArguments, &map)); assert_eq!(Err(0), parse("{invalid/character}", &NoPositionalArguments, &map)); } #[test] fn parse_specifier_smoke_test() { struct NoValues; impl ArgumentSource for NoValues { fn next_argument(&mut self) -> Option<&Variant> { None } fn lookup_argument_by_index(&self, _: usize) -> Option<&Variant> { None } fn lookup_argument_by_name(&self, _: &str) -> Option<&Variant> { None } } assert_eq!( Ok(Specifier { align: Align::Right, sign: Sign::Always, repr: Repr::Alt, pad: Pad::Zero, width: Width::AtLeast { width: 42 }, precision: Precision::Exactly { precision: 17 }, format: Format::UpperExp, }), parse_specifier(">+#042.17E", &mut NoValues {}) ); }