//! Proc macros for the ICU4X data provider. //! //! These macros are re-exported from `icu_provider`.
externcrate proc_macro; use proc_macro::TokenStream; use proc_macro2::Span; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::parenthesized; use syn::parse::{self, Parse, ParseStream}; use syn::parse_macro_input; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::DeriveInput; use syn::{Ident, LitStr, Path, Token}; #[cfg(test)] mod tests;
#[proc_macro_attribute]
/// The `#[data_struct]` attribute should be applied to all types intended /// for use in a `DataStruct`. /// /// It does the following things: /// /// - `Apply #[derive(Yokeable, ZeroFrom)]`. The `ZeroFrom` derive can /// be customized with `#[zerofrom(clone)]` on non-ZeroFrom fields. /// /// In addition, the attribute can be used to implement `DataMarker` and/or `KeyedDataMarker` /// by adding symbols with optional key strings: /// /// ``` /// # // We DO NOT want to pull in the `icu` crate as a dev-dependency, /// # // because that will rebuild the whole tree in proc macro mode /// # // when using cargo test --all-features --all-targets. /// # pub mod icu { /// # pub mod locid_transform { /// # pub mod fallback { /// # pub use icu_provider::_internal::LocaleFallbackPriority; /// # } /// # } /// # pub use icu_provider::_internal::locid; /// # } /// use icu::locid::extensions::unicode::key; /// use icu::locid_transform::fallback::*; /// use icu_provider::yoke; /// use icu_provider::zerofrom; /// use icu_provider::KeyedDataMarker; /// use std::borrow::Cow; /// /// #[icu_provider::data_struct( /// FooV1Marker, /// BarV1Marker = "demo/bar@1", /// marker( /// BazV1Marker, /// "demo/baz@1", /// fallback_by = "region", /// extension_key = "ca" /// ) /// )] /// pub struct FooV1<'data> { /// message: Cow<'data, str>, /// }; /// /// // Note: FooV1Marker implements `DataMarker` but not `KeyedDataMarker`. /// // The other two implement `KeyedDataMarker`. /// /// assert_eq!(&*BarV1Marker::KEY.path(), "demo/bar@1"); /// assert_eq!( /// BarV1Marker::KEY.metadata().fallback_priority, /// LocaleFallbackPriority::Language /// ); /// assert_eq!(BarV1Marker::KEY.metadata().extension_key, None); /// /// assert_eq!(&*BazV1Marker::KEY.path(), "demo/baz@1"); /// assert_eq!( /// BazV1Marker::KEY.metadata().fallback_priority, /// LocaleFallbackPriority::Region /// ); /// assert_eq!(BazV1Marker::KEY.metadata().extension_key, Some(key!("ca"))); /// ``` /// /// If the `#[databake(path = ...)]` attribute is present on the data struct, this will also /// implement it on the markers. pubfn data_struct(attr: TokenStream, item: TokenStream) -> TokenStream {
TokenStream::from(data_struct_impl(
parse_macro_input!(attr as DataStructArgs),
parse_macro_input!(item as DeriveInput),
))
}
if path.is_ident("marker") { let content; let paren = parenthesized!(content in input); letmut marker_name: Option<Path> = None; letmut key_lit: Option<LitStr> = None; letmut fallback_by: Option<LitStr> = None; letmut extension_key: Option<LitStr> = None; letmut fallback_supplement: Option<LitStr> = None; letmut singleton = false; let punct = content.parse_terminated(DataStructMarkerArg::parse, Token![,])?;
for entry in punct { match entry {
DataStructMarkerArg::Path(path) => {
at_most_one_option(&mut marker_name, path, "marker", input.span())?;
}
DataStructMarkerArg::NameValue(name, value) => { if name == "fallback_by" {
at_most_one_option(
&mut fallback_by,
value, "fallback_by",
paren.span.join(),
)?;
} elseif name == "extension_key" {
at_most_one_option(
&mut extension_key,
value, "extension_key",
paren.span.join(),
)?;
} elseif name == "fallback_supplement" {
at_most_one_option(
&mut fallback_supplement,
value, "fallback_supplement",
paren.span.join(),
)?;
} else { return Err(parse::Error::new(
name.span(),
format!("unknown option {name} in marker()"),
));
}
}
DataStructMarkerArg::Lit(lit) => {
at_most_one_option(&mut key_lit, lit, "literal key", input.span())?;
}
DataStructMarkerArg::Singleton => {
singleton = true;
}
}
} let marker_name = iflet Some(marker_name) = marker_name {
marker_name
} else { return Err(parse::Error::new(
input.span(), "marker() must contain a marker!",
));
};
Ok(Self {
marker_name,
key_lit,
fallback_by,
extension_key,
fallback_supplement,
singleton,
})
} else { letmut this = DataStructArg::new(path); let lookahead = input.lookahead1(); if lookahead.peek(Token![=]) { let _t: Token![=] = input.parse()?; let lit: LitStr = input.parse()?;
this.key_lit = Some(lit);
Ok(this)
} else {
Ok(this)
}
}
}
}
/// A single argument to `marker()` in `#[data_struct(..., marker(...), ...)] enum DataStructMarkerArg {
Path(Path),
NameValue(Ident, LitStr),
Lit(LitStr),
Singleton,
} impl Parse for DataStructMarkerArg { fn parse(input: ParseStream<'_>) -> parse::Result<Self> { let lookahead = input.lookahead1(); if lookahead.peek(LitStr) {
Ok(DataStructMarkerArg::Lit(input.parse()?))
} else { let path: Path = input.parse()?; let lookahead = input.lookahead1(); if lookahead.peek(Token![=]) { let _tok: Token![=] = input.parse()?; let ident = path.get_ident().ok_or_else(|| {
parse::Error::new(path.span(), "Expected identifier before `=`, found path")
})?;
Ok(DataStructMarkerArg::NameValue(
ident.clone(),
input.parse()?,
))
} elseif path.is_ident("singleton") {
Ok(DataStructMarkerArg::Singleton)
} else {
Ok(DataStructMarkerArg::Path(path))
}
}
}
}
fn data_struct_impl(attr: DataStructArgs, input: DeriveInput) -> TokenStream2 { if input.generics.type_params().count() > 0 { return syn::Error::new(
input.generics.span(), "#[data_struct] does not support type parameters",
)
.to_compile_error();
} let lifetimes = input.generics.lifetimes().collect::<Vec<_>>();
let name = &input.ident;
let name_with_lt = if !lifetimes.is_empty() {
quote!(#name<'static>)
} else {
quote!(#name)
};
if lifetimes.len() > 1 { return syn::Error::new(
input.generics.span(), "#[data_struct] does not support more than one lifetime parameter",
)
.to_compile_error();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.