Module attr_parse

Source
Available on crate feature attr_parse only.
Expand description

Utilities for parsing syn Attributes.

§Examples

Basic usage
use macroific::attr_parse::prelude::*;

#[derive(AttributeOptions)]
struct MyOptions {
  str_option: String,
  optional: Option<syn::Lifetime>,
  bool_option1: bool,
  bool_option2: bool,
  bool_option3: bool,
  a_list: Punctuated<u32, syn::Token![,]>,
  a_path: Option<syn::Path>,
}

// You'd normally get this from `DeriveInput`
let attributes: Vec<syn::Attribute> = vec![
  syn::parse_quote! { #[my_opts(str_option = "hello", bool_option2, bool_option3 = false)] },
  syn::parse_quote! { #[my_opts(a_list(10, 20, 30), a_path(std::fs::File))] }
];

let opts = MyOptions::from_iter_named("my_opts", Span::call_site(), attributes).unwrap();

// Strings & numbers can be converted straight from syn types
assert_eq!(opts.str_option, "hello");

// Optionals are optional (:
assert!(opts.optional.is_none());

// We didn't provide bool1 so it `Default::default()`ed to false
assert!(!opts.bool_option1);

// Booleans can be provided with just the property name so bool2 is true
assert!(opts.bool_option2);

// Though we can provide an explicit value too
assert!(!opts.bool_option3);

// Punctuated lists are supported as the enclosed type implements ParseOption
assert_eq!(opts.a_list[0], 10);
assert_eq!(opts.a_list[1], 20);
assert_eq!(opts.a_list[2], 30);

// The path is provided with an alternative syntax as an example, but it could've just as
// easily been provided as `a_path = std::fs::File`
assert_eq!(opts.a_path.unwrap().to_token_stream().to_string(), "std :: fs :: File");
Renaming & default values
use macroific::attr_parse::prelude::*;

#[derive(AttributeOptions, Debug)]
struct MyOptions {
  #[attr_opts(
    rename = "A",
    default = false // fail if this attr is not provided
  )]
  num1: u8,

  #[attr_opts(default = some_module::default_num)] // use this function for the default value
  num2: u8,
}

mod some_module {
  pub(super) fn default_num() -> u8 { u8::MAX }
}

let opts = MyOptions::from_attr(parse_quote! { #[foo_attr(A = 10)] }).unwrap();
assert_eq!(opts.num1, 10);
assert_eq!(opts.num2, u8::MAX);

let err = MyOptions::from_attr(parse_quote! { #[foo_attr()] }).unwrap_err();
assert_eq!(err.to_string(), r#"Missing required attribute: "A""#);

Full table on supported syntaxes for providing option values can be found on parse_bool_attr and parse_valued_attr. See the derive macro doc page for options you can pass to it.

Nesting structs

Nesting structs can be achieved by deriving the ParseOption trait which uses the same options as AttributeOptions.

use macroific::attr_parse::prelude::*;

#[derive(ParseOption, Debug, Eq, PartialEq)]
struct Nested {
  required: bool,
  foo: String,
  count: Option<u8>,
}

#[derive(AttributeOptions, Debug, Eq, PartialEq)]
struct Options {
  #[attr_opts(default = false)]
  nest: Nested,

  #[attr_opts(default = false)]
  alt: Nested,

  root: String,
}

let opts = Options::from_attr(parse_quote! { #[some_attr(root = "^", nest(count = 5, required), alt(foo = "Bar"))] })
  .unwrap();

let expect = Options {
 nest: Nested { required: true, count: Some(5), foo: String::new() },
 alt: Nested { required: false, count: None, foo: "Bar".into() },
 root: "^".into(),
};

assert_eq!(opts, expect);
Enum option
use macroific::attr_parse::prelude::*;

// Say we want a subset of `syn::Lit` - we can define an enum like this and
// derive `ParseOption` with `from_parse`.
#[derive(ParseOption, Debug, PartialEq)]
#[attr_opts(from_parse)]
enum BoolOrStr {
  Int(syn::LitInt),
  Str(syn::LitStr)
}

// Then implement the `Parse` trait `from_parse` expects.
impl Parse for BoolOrStr {
  fn parse(input: ParseStream) -> syn::Result<Self> {
    if input.peek(syn::LitInt) {
      input.parse().map(Self::Int)
    } else if input.peek(syn::LitStr) {
      input.parse().map(Self::Str)
    } else {
      Err(input.error("Expected integer or string literal"))
    }
  }
}

// Define an options struct that uses the enum
#[derive(AttributeOptions)]
struct MyOptions {
  #[attr_opts(default = false)]
  will_be_int: BoolOrStr, // Will provide an integer here

  #[attr_opts(default = false)]
  will_be_str: BoolOrStr, // Will provide a string here
}

// Initialise an example attribute
let attr: syn::Attribute = parse_quote! {
  #[foo(will_be_int = 10, will_be_str = "hello")]
};

// Parse it
let opts = MyOptions::from_attr(attr).unwrap();

assert_eq!(opts.will_be_int, BoolOrStr::Int(syn::LitInt::new("10", Span::call_site())));
assert_eq!(opts.will_be_str, BoolOrStr::Str(syn::LitStr::new("hello", Span::call_site())));

§Features

Enable the full feature to implement ParseOption for syn types that require it.

Modules§

ext
Extension traits for syn
prelude

Structs§

DelimitedIter
Punctuated, in iterator form
FieldWithOpts
A Field with options parsed.
ParseWrapper
A wrapper to make any ParseOption into a Parse.

Enums§

FieldsWithOpts
Fields with options parsed.
ValueSyntax
Syntax used for providing a value

Traits§

AttributeOptions
Options derivable from Attributes.
FromExpr
Construct this type from an Expr.
ParseOption
Makes a type usable for AttributeOptions

Derive Macros§

AttributeOptions
Derive the AttributeOptions trait for a struct.
ParseOption
Derive the ParseOption trait for a struct. Uses the same field options as AttributeOptions.