use rstest::rstest; use super::*; #[rstest] #[case::noo_param("", None, Type::Format, None)] #[case::one_param_type("=u8", None, Type::U8, None)] #[case::one_param_hint(":a", None, Type::Format, Some(DisplayHint::Ascii))] #[case::one_param_index("1", Some(1), Type::Format, None)] #[case::two_param_type_hint("=u8:x", None, Type::U8, Some(DisplayHint::Hexadecimal {alternate: false, uppercase: false, zero_pad: 0}))] #[case::two_param_index_type("0=u8", Some(0), Type::U8, None)] #[case::two_param_index_hint("0:a", Some(0), Type::Format, Some(DisplayHint::Ascii))] #[case::two_param_type_hint("=[u8]:#04x", None, Type::U8Slice, Some(DisplayHint::Hexadecimal {alternate: true, uppercase: false, zero_pad: 4}))] #[case::all_param("1=u8:b", Some(1), Type::U8, Some(DisplayHint::Binary { alternate: false, zero_pad: 0}))] fn all_parse_param_cases( #[case] input: &str, #[case] index: Option, #[case] ty: Type, #[case] hint: Option, ) { assert_eq!( parse_param(input, ParserMode::Strict), Ok(Param { index, ty, hint }) ); } #[rstest] #[case(":a", DisplayHint::Ascii)] #[case(":b", DisplayHint::Binary { alternate: false, zero_pad: 0 })] #[case(":#b", DisplayHint::Binary { alternate: true, zero_pad: 0 })] #[case(":o", DisplayHint::Octal { alternate: false, zero_pad: 0 })] #[case(":#o", DisplayHint::Octal { alternate: true, zero_pad: 0 })] #[case(":x", DisplayHint::Hexadecimal { alternate: false, uppercase: false, zero_pad: 0 })] #[case(":02x", DisplayHint::Hexadecimal { alternate: false, uppercase: false, zero_pad: 2 })] #[case(":#x", DisplayHint::Hexadecimal { alternate: true, uppercase: false, zero_pad: 0 })] #[case(":#04x", DisplayHint::Hexadecimal { alternate: true, uppercase: false, zero_pad: 4 })] #[case(":X", DisplayHint::Hexadecimal { alternate: false, uppercase: true, zero_pad: 0 })] #[case(":#X", DisplayHint::Hexadecimal { alternate: true, uppercase: true, zero_pad: 0 })] #[case(":ms", DisplayHint::Seconds(TimePrecision::Millis))] #[case(":us", DisplayHint::Seconds(TimePrecision::Micros))] #[case(":ts", DisplayHint::Time(TimePrecision::Seconds))] #[case(":tms", DisplayHint::Time(TimePrecision::Millis))] #[case(":tus", DisplayHint::Time(TimePrecision::Micros))] #[case(":iso8601ms", DisplayHint::ISO8601(TimePrecision::Millis))] #[case(":iso8601s", DisplayHint::ISO8601(TimePrecision::Seconds))] #[case(":?", DisplayHint::Debug)] #[case(":02", DisplayHint::NoHint { zero_pad: 2 })] fn all_display_hints(#[case] input: &str, #[case] hint: DisplayHint) { assert_eq!( parse_param(input, ParserMode::Strict), Ok(Param { index: None, ty: Type::Format, hint: Some(hint), }) ); } #[test] // separate test, because of `ParserMode::ForwardsCompatible` fn display_hint_unknown() { assert_eq!( parse_param(":unknown", ParserMode::ForwardsCompatible), Ok(Param { index: None, ty: Type::Format, hint: Some(DisplayHint::Unknown("unknown".to_string())), }) ); } #[rstest] #[case("=i8", Type::I8)] #[case("=i16", Type::I16)] #[case("=i32", Type::I32)] #[case("=i64", Type::I64)] #[case("=i128", Type::I128)] #[case("=isize", Type::Isize)] #[case("=u8", Type::U8)] #[case("=u16", Type::U16)] #[case("=u32", Type::U32)] #[case("=u64", Type::U64)] #[case("=u128", Type::U128)] #[case("=usize", Type::Usize)] #[case("=f32", Type::F32)] #[case("=f64", Type::F64)] #[case("=bool", Type::Bool)] #[case("=?", Type::Format)] #[case("=str", Type::Str)] #[case("=[u8]", Type::U8Slice)] fn all_types(#[case] input: &str, #[case] ty: Type) { assert_eq!( parse_param(input, ParserMode::Strict), Ok(Param { index: None, ty, hint: None, }) ); } #[rstest] #[case::implicit("{=u8}{=u16}", [(0, Type::U8), (1, Type::U16)])] #[case::single_parameter_formatted_twice("{=u8}{0=u8}", [(0, Type::U8), (0, Type::U8)])] #[case::explicit_index("{=u8}{1=u16}", [(0, Type::U8), (1, Type::U16)])] #[case::reversed_order("{1=u8}{0=u16}", [(1, Type::U8), (0, Type::U16)])] fn index(#[case] input: &str, #[case] params: [(usize, Type); 2]) { assert_eq!( parse(input, ParserMode::Strict), Ok(vec![ Fragment::Parameter(Parameter { index: params[0].0, ty: params[0].1.clone(), hint: None, }), Fragment::Parameter(Parameter { index: params[1].0, ty: params[1].1.clone(), hint: None, }), ]) ); } #[rstest] #[case("{=0..4}", 0..4)] #[case::just_inside_128bit_range_1("{=0..128}", 0..128)] #[case::just_inside_128bit_range_2("{=127..128}", 127..128)] fn range(#[case] input: &str, #[case] bit_field: Range) { assert_eq!( parse(input, ParserMode::Strict), Ok(vec![Fragment::Parameter(Parameter { index: 0, ty: Type::BitField(bit_field), hint: None, })]) ); } #[test] fn multiple_ranges() { assert_eq!( parse("{0=30..31}{1=0..4}{1=2..6}", ParserMode::Strict), Ok(vec![ Fragment::Parameter(Parameter { index: 0, ty: Type::BitField(30..31), hint: None, }), Fragment::Parameter(Parameter { index: 1, ty: Type::BitField(0..4), hint: None, }), Fragment::Parameter(Parameter { index: 1, ty: Type::BitField(2..6), hint: None, }), ]) ); } #[rstest] #[case("{=[u8; 0]}", 0)] #[case::space_is_optional("{=[u8;42]}", 42)] #[case::multiple_spaces_are_ok("{=[u8; 257]}", 257)] fn arrays(#[case] input: &str, #[case] length: usize) { assert_eq!( parse(input, ParserMode::Strict), Ok(vec![Fragment::Parameter(Parameter { index: 0, ty: Type::U8Array(length), hint: None, })]) ); } #[rstest] #[case::no_tabs("{=[u8; \t 3]}")] #[case::no_linebreaks("{=[u8; \n 3]}")] #[case::too_large("{=[u8; 9999999999999999999999999]}")] fn arrays_err(#[case] input: &str) { assert!(parse(input, ParserMode::Strict).is_err()); } #[rstest] #[case("{=dunno}", Error::InvalidTypeSpecifier("dunno".to_string()))] #[case("{dunno}", Error::UnexpectedContentInFormatString("dunno".to_string()))] #[case("{=u8;x}", Error::InvalidTypeSpecifier("u8;x".to_string()))] #[case("{dunno=u8:x}", Error::UnexpectedContentInFormatString("dunno=u8:x".to_string()))] #[case("{0dunno}", Error::UnexpectedContentInFormatString("dunno".to_string()))] #[case("{:}", Error::MalformedFormatString)] #[case::stray_braces_1("}string", Error::UnmatchedCloseBracket)] #[case::stray_braces_2("{string", Error::UnmatchedOpenBracket)] #[case::stray_braces_3("}", Error::UnmatchedCloseBracket)] #[case::stray_braces_4("{", Error::UnmatchedOpenBracket)] #[case::range_empty("{=0..0}", Error::InvalidTypeSpecifier("0..0".to_string()))] #[case::range_start_gt_end("{=1..0}", Error::InvalidTypeSpecifier("1..0".to_string()))] #[case::range_out_of_128bit_1("{=0..129}", Error::InvalidTypeSpecifier("0..129".to_string()))] #[case::range_out_of_128bit_2("{=128..128}", Error::InvalidTypeSpecifier("128..128".to_string()))] #[case::range_missing_parts_1("{=0..4", Error::UnmatchedOpenBracket)] #[case::range_missing_parts_2("{=0..}", Error::InvalidTypeSpecifier("0..".to_string()))] #[case::range_missing_parts_3("{=..4}", Error::InvalidTypeSpecifier("..4".to_string()))] #[case::range_missing_parts_4("{=0.4}", Error::InvalidTypeSpecifier("0.4".to_string()))] #[case::range_missing_parts_5("{=0...4}", Error::InvalidTypeSpecifier("0...4".to_string()))] #[case::index_with_different_types( "{0=u8}{0=u16}", Error::ConflictingTypes(0, Type::U8, Type::U16) )] #[case::index_with_different_types_bool_is_autoassigned_index_0( "Hello {1=u16} {0=u8} {=bool}", Error::ConflictingTypes(0, Type::U8, Type::Bool) )] #[case::index_0_is_omitted("{1=u8}", Error::UnusedArgument(0))] #[case::index_1_is_missing("{2=u8}{=u16}", Error::UnusedArgument(1))] #[case::index_0_is_missing("{2=u8}{1=u16}", Error::UnusedArgument(0))] fn error_msg(#[case] input: &str, #[case] err: Error) { assert_eq!(parse(input, ParserMode::Strict), Err(err)); } #[rstest] #[case("}}", "}")] #[case("{{", "{")] #[case("literal{{literal", "literal{literal")] #[case("literal}}literal", "literal}literal")] #[case("{{}}", "{}")] #[case("}}{{", "}{")] fn escaped_braces(#[case] input: &str, #[case] literal: &str) { assert_eq!( parse(input, ParserMode::Strict), Ok(vec![Fragment::Literal(literal.into())]) ); }