#[derive(FromScanf)]
{
// Attributes available to this derive:
#[sscanf]
}
Expand description
A derive macro for FromScanf
.
For structs
#[derive(sscanf::FromScanf)]
#[sscanf(format = "<format>")] // format string. has to contain placeholders for all
struct MyStruct { // non-default fields: {<field>}, {<field_2>}, {<field_with_conversion>}
<field>: <type>, // requires <type>: FromScanf (implemented for all primitive types
// and several others from std)
<field_2>: <type_2>, // requires <type_2>: FromScanf
// ...
// possible attributes on fields:
#[sscanf(default)]
<field_with_default>: <type>, // requires <type>: Default, but doesn't need FromScanf
#[sscanf(default = <expression>)] // accepts any expression that returns <type>
<field_with_custom_default>: <type>, // no traits required.
#[sscanf(map = |input: <matched_type>| { <conversion from <matched_type> to <actual_type>> })]
<field_with_conversion>: <actual_type>, // requires <matched_type>: FromScanf
}
// tuple structs have the same capabilities, just without field names:
#[derive(sscanf::FromScanf)]
#[sscanf(format = "<format>")] // format string references fields by index: {0}, ...
struct MyTupleStruct(<type>, #[sscanf(default)] <type>, ...);
Attributes
On the struct
format
: The format string to parse the struct from. Similar to the format string forsscanf
, but with field names/indices instead of types for the placeholders. So, if you have a struct with fieldsa
,b
andc
, the format string could be something like"{a} {b:/.*?/} {c}"
. All fields that are not annotated withdefault
must appear exactly once in the format string. Indices can be omitted if the fields are in the same order as the placeholders{}
in the format string. So, the above example could also be written as"{} {:/.*?/} {}"
.format_unescaped
: Same asformat
, but allows use of Regex in the format String. Seesscanf_unescaped
for more information.transparent
: If the struct has exactly one field, the struct will be constructed from the field directly. This is useful for newtype structs, where the struct is just a wrapper around another type. The field has to implementFromScanf
.
Note that only one of the above attributes can be used on a struct. The format =
part can
be omitted, so #[sscanf("<format>")]
is also valid. In this case, the distinction between
format
and format_unescaped
is made by using a regular string literal for format
and a
raw string literal (starting with r#"
or r#"
) for format_unescaped
.
On the fields
default
ordefault = <expression>
: Marks the field to be set from a default value rather than the input string. A simple#[sscanf(default)]
will set the field toDefault::default()
. If the field type doesn’t implementDefault
, the attribute can take an expression that will be evaluated to get the default value. The expression can be any code, including function calls or{ <code> }
blocks, as long as they can be assigned to the field type.map = |<param>: <type>| <conversion>
: Allows matching against a different type than the field type. Themap
attribute takes a closure that takes the matched type as input and returns the field type. The type of the parameter of the closure has to be explicitly specified, since it is needed to generate the matching code.filter_map = |<param>: <type>| <conversion>
: Same asmap
, but the closure returns anOption
instead of the field type. If the closure returnsNone
, the parsing fails.from = <type>
: Allows matching against a different type than the field type. Thefrom
attribute takes a Type as input, which implementsFromScanf
and can be converted to the field type usingFrom
.try_from = <type>
: Same asfrom
, but the conversion can fail. If the conversion fails, the parsing fails.
For enums
#[derive(sscanf::FromScanf)]
enum MyEnum {
#[sscanf(format = "<format>")] // has to contain `{<field>}` and any other fields
Variant1 {
<field>: <type>, // requires <type>: FromScanf
#[sscanf(default)]
<field_with_default>: <type2>, // requires <type2>: Default
// ... (same as for structs)
},
#[sscanf("<format>")] // the `format = ` part can be omitted
Variant2(<type>, #[sscanf(default)] <type2>),
#[sscanf("<format>")] // variant has no fields => no placeholders in format string
Variant3,
Variant4, // this variant won't be constructed by sscanf
}
An enum takes multiple format strings, one for each variant. The value returned from sscanf
is constructed from the variant that matched the input. If multiple variants match, the first
one in the enum definition is used. No variant matching means the entire enum won’t match.
Attributes
On the enum
autogen = "<case>"
orautogenerate = "<case>"
: Automatically create the format strings for all variants based on the variant names. This only works for variants without fields. The format can be overridden by specifying aformat =
attribute on the variant. Thecase
parameter can be one of:"CaseSensitive"
: The variant name is used as-is. Default if nocase
parameter is specified."CaseInsensitive"
: Same as"CaseSensitive"
, but case is ignored."lower case"
: Lower case with spaces between words."UPPER CASE"
: Upper case with spaces between words."lowercase"
: Lower case, but without spaces."UPPERCASE"
: Upper case, but without spaces."PascalCase"
"camelCase"
"snake_case"
"SCREAMING_SNAKE_CASE"
"kebab-case"
"SCREAMING-KEBAB-CASE"
On the variants
Same as for structs. If no format string or transparent
attribute is specified, the variant
won’t be constructed by sscanf
. Unless autogen
is specified, in which case the format string
is generated automatically. To avoid this, add the skip
attribute to the variant. skip
has
no effect without autogen
.
A note on Generics
Any lifetime parameters will be carried over. Any type &'a str
will contain a borrow of the
input string, with an appropriate lifetime.
As for type generics: RegexRepresentation
cannot be implemented
for generic types, since the contained associated const
is only created once by Rust for all
generic instances, meaning that different regexes for different T
are not possible. This
also means that deriving FromScanf
for a struct that wants to match a generic field without
a map
or default
attribute will generally fail. The only possibilities are:
#[derive(sscanf::FromScanf)]
#[sscanf(format = "...{field:/<regex>/}...")]
struct MyGenericStruct<T>
where
T: std::str::FromStr + 'static,
<T as std::str::FromStr>::Err: std::error::Error + 'static,
{
field: T,
}
let input = "...<regex>...";
let res = sscanf::sscanf!(input, "{MyGenericStruct<String>}").unwrap();
assert_eq!(res.field, String::from("<regex>"));
There are two important things in this example:
- Since
RegexRepresentation
cannot be used, every occurrence of generic fields in the format string have to have a regex (`{:/…/}) attached to them. - The type bounds on
T
have to contain all of those exact bounds.
Any T
has to be constructed by FromStr
from what is matched by the
specified regex, making this setup virtually useless for all but a few selected types. Since
the generic parameter has to be specified in the actual sscanf
call, it is usually better
to just use a concrete type in the struct itself.
It is possible to have T
directly require FromScanf
like this: T: for<'a> FromScanf<'a>
.
However, since FromScanf
implementations usually rely on capture groups inside of their regex,
this would require also having the exact same capture groups in the format string, which is
currently not possible. Implementations that don’t rely on capture groups are usually those
that were blanket-implemented based on their FromStr
implementation.