events: Add support for type aliases

Allow to use unstable types
This commit is contained in:
Kévin Commaille 2022-05-05 21:41:44 +02:00 committed by Kévin Commaille
parent 6a2950884d
commit 1073530ac6
6 changed files with 309 additions and 88 deletions

View File

@ -6,7 +6,7 @@ error: no event type attribute found, add `#[ruma_event(type = "any.room.event",
|
= note: this error originates in the derive macro `EventContent` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected one of: `type`, `kind`, `skip_redaction`, `custom_redacted`, `type_fragment`, `state_key_type`
error: expected one of: `type`, `kind`, `skip_redaction`, `custom_redacted`, `type_fragment`, `state_key_type`, `alias`
--> tests/events/ui/03-invalid-event-type.rs:11:14
|
11 | #[ruma_event(event = "m.macro.test", kind = State)]

View File

@ -4,7 +4,10 @@ use ruma_macros::event_enum;
event_enum! {
/// Any global account data event.
enum GlobalAccountData {
#[ruma_enum(alias = "io.ruma.direct")]
"m.direct" => events::direct,
#[ruma_enum(alias = "m.identity_server")]
"io.ruma.identity_server" => events::identity_server,
#[cfg(test)]
"m.ignored_user_list" => events::ignored_user_list,
// Doesn't actually have a wildcard, but this should work as a wildcard test
@ -14,7 +17,24 @@ event_enum! {
}
}
fn main() {}
fn main() {
assert_eq!(GlobalAccountDataEventType::from("m.direct"), GlobalAccountDataEventType::Direct);
assert_eq!(
GlobalAccountDataEventType::from("io.ruma.direct"),
GlobalAccountDataEventType::Direct
);
assert_eq!(GlobalAccountDataEventType::Direct.to_cow_str(), "m.direct");
assert_eq!(
GlobalAccountDataEventType::from("m.identity_server"),
GlobalAccountDataEventType::IdentityServer
);
assert_eq!(
GlobalAccountDataEventType::from("io.ruma.identity_server"),
GlobalAccountDataEventType::IdentityServer
);
assert_eq!(GlobalAccountDataEventType::IdentityServer.to_cow_str(), "io.ruma.identity_server");
}
#[doc(hidden)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]

View File

@ -23,6 +23,8 @@ mod kw {
syn::custom_keyword!(type_fragment);
// The type to use for a state events' `state_key` field.
syn::custom_keyword!(state_key_type);
// Another type string accepted for deserialization.
syn::custom_keyword!(alias);
}
/// Parses attributes for `*EventContent` derives.
@ -47,6 +49,9 @@ enum EventMeta {
TypeFragment,
StateKeyType(Box<Type>),
/// Variant that holds alternate event type accepted for deserialization.
Alias(LitStr),
}
impl EventMeta {
@ -70,6 +75,13 @@ impl EventMeta {
_ => None,
}
}
fn get_alias(&self) -> Option<&LitStr> {
match self {
Self::Alias(t) => Some(t),
_ => None,
}
}
}
impl Parse for EventMeta {
@ -96,6 +108,10 @@ impl Parse for EventMeta {
let _: kw::state_key_type = input.parse()?;
let _: Token![=] = input.parse()?;
input.parse().map(EventMeta::StateKeyType)
} else if lookahead.peek(kw::alias) {
let _: kw::alias = input.parse()?;
let _: Token![=] = input.parse()?;
input.parse().map(EventMeta::Alias)
} else {
Err(lookahead.error())
}
@ -120,6 +136,10 @@ impl MetaAttrs {
fn get_state_key_type(&self) -> Option<&Type> {
self.0.iter().find_map(|a| a.get_state_key_type())
}
fn get_aliases(&self) -> impl Iterator<Item = &LitStr> {
self.0.iter().filter_map(|a| a.get_alias())
}
}
impl Parse for MetaAttrs {
@ -227,6 +247,16 @@ pub fn expand_event_content(
));
}
let aliases: Vec<_> = content_attr.iter().flat_map(|attrs| attrs.get_aliases()).collect();
for alias in &aliases {
if alias.value().ends_with(".*") != prefix.is_some() {
return Err(syn::Error::new_spanned(
event_type,
"aliases should have the same `.*` suffix, or lack thereof, as the main event type",
));
}
}
// We only generate redacted content structs for state and message-like events
let redacted_event_content = needs_redacted(&content_attr, event_kind).then(|| {
generate_redacted_event_content(
@ -235,6 +265,7 @@ pub fn expand_event_content(
event_type,
event_kind,
state_key_type.as_ref(),
&aliases,
ruma_common,
)
.unwrap_or_else(syn::Error::into_compile_error)
@ -246,6 +277,7 @@ pub fn expand_event_content(
event_type,
event_kind,
state_key_type.as_ref(),
&aliases,
ruma_common,
)
.unwrap_or_else(syn::Error::into_compile_error);
@ -270,6 +302,7 @@ fn generate_redacted_event_content<'a>(
event_type: &LitStr,
event_kind: Option<EventKind>,
state_key_type: Option<&TokenStream>,
aliases: &[&LitStr],
ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
assert!(
@ -354,6 +387,7 @@ fn generate_redacted_event_content<'a>(
event_type,
event_kind,
state_key_type,
aliases,
ruma_common,
)
.unwrap_or_else(syn::Error::into_compile_error);
@ -362,6 +396,12 @@ fn generate_redacted_event_content<'a>(
generate_static_event_content_impl(&redacted_ident, k, true, event_type, ruma_common)
});
let mut event_types = aliases.to_owned();
event_types.push(event_type);
let event_types = quote! {
[#(#event_types,)*]
};
Ok(quote! {
// this is the non redacted event content's impl
#[automatically_derived]
@ -387,9 +427,9 @@ fn generate_redacted_event_content<'a>(
#[automatically_derived]
impl #ruma_common::events::RedactedEventContent for #redacted_ident {
fn empty(ev_type: &str) -> #serde_json::Result<Self> {
if ev_type != #event_type {
if !#event_types.contains(&ev_type) {
return Err(#serde::de::Error::custom(
format!("expected event type `{}`, found `{}`", #event_type, ev_type)
format!("expected event type as one of `{:?}`, found `{}`", #event_types, ev_type)
));
}
@ -476,6 +516,7 @@ fn generate_event_content_impl<'a>(
event_type: &LitStr,
event_kind: Option<EventKind>,
state_key_type: Option<&TokenStream>,
aliases: &[&'a LitStr],
ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
let serde = quote! { #ruma_common::exports::serde };
@ -565,24 +606,41 @@ fn generate_event_content_impl<'a>(
}
});
let from_parts_fn_impl = if let Some((type_prefix, type_fragment_field)) = &type_suffix_data {
let event_types = aliases.iter().chain([&event_type]);
let from_parts_fn_impl = if let Some((_, type_fragment_field)) = &type_suffix_data {
let type_prefixes = event_types.map(|ev_type| {
ev_type
.value()
.strip_suffix('*')
.expect("aliases have already been checked to have the same suffix")
.to_owned()
});
let type_prefixes = quote! {
[#(#type_prefixes,)*]
};
quote! {
if let Some(type_fragment) = ev_type.strip_prefix(#type_prefix) {
if let Some(type_fragment) = #type_prefixes.iter().find_map(|prefix| ev_type.strip_prefix(prefix)) {
let mut content: Self = #serde_json::from_str(content.get())?;
content.#type_fragment_field = type_fragment.to_owned();
::std::result::Result::Ok(content)
} else {
::std::result::Result::Err(#serde::de::Error::custom(
::std::format!("expected event type starting with `{}`, found `{}`", #type_prefix, ev_type)
::std::format!("expected event type starting with one of `{:?}`, found `{}`", #type_prefixes, ev_type)
))
}
}
} else {
let event_types = quote! {
[#(#event_types,)*]
};
quote! {
if ev_type != #event_type {
if !#event_types.contains(&ev_type) {
return ::std::result::Result::Err(#serde::de::Error::custom(
::std::format!("expected event type `{}`, found `{}`", #event_type, ev_type)
::std::format!("expected event type as one of `{:?}`, found `{}`", #event_types, ev_type)
));
}

View File

@ -1,6 +1,6 @@
//! Implementation of event enum and event content enum macros.
use std::fmt;
use std::{fmt, iter::zip};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, IdentFragment, ToTokens};
@ -49,24 +49,24 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
let kind = input.kind;
let attrs = &input.attrs;
let events: Vec<_> =
input.events.iter().map(|entry| (entry.ev_type.clone(), entry.ev_path.clone())).collect();
let docs: Vec<_> = input.events.iter().map(EventEnumEntry::docs).collect::<syn::Result<_>>()?;
let variants: Vec<_> =
input.events.iter().map(EventEnumEntry::to_variant).collect::<syn::Result<_>>()?;
let events = &events;
let events = &input.events;
let docs = &docs;
let variants = &variants;
let ruma_common = &ruma_common;
res.extend(expand_content_enum(kind, events, attrs, variants, ruma_common));
res.extend(expand_content_enum(kind, events, docs, attrs, variants, ruma_common));
res.extend(
expand_event_enum(kind, V::None, events, attrs, variants, ruma_common)
expand_event_enum(kind, V::None, events, docs, attrs, variants, ruma_common)
.unwrap_or_else(syn::Error::into_compile_error),
);
if matches!(kind, EventKind::MessageLike | EventKind::State) {
res.extend(
expand_event_enum(kind, V::Sync, events, attrs, variants, ruma_common)
expand_event_enum(kind, V::Sync, events, docs, attrs, variants, ruma_common)
.unwrap_or_else(syn::Error::into_compile_error),
);
res.extend(
@ -89,18 +89,18 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
if matches!(kind, EventKind::Ephemeral) {
res.extend(
expand_event_enum(kind, V::Sync, events, attrs, variants, ruma_common)
expand_event_enum(kind, V::Sync, events, docs, attrs, variants, ruma_common)
.unwrap_or_else(syn::Error::into_compile_error),
);
}
if matches!(kind, EventKind::State) {
res.extend(
expand_event_enum(kind, V::Stripped, events, attrs, variants, ruma_common)
expand_event_enum(kind, V::Stripped, events, docs, attrs, variants, ruma_common)
.unwrap_or_else(syn::Error::into_compile_error),
);
res.extend(
expand_event_enum(kind, V::Initial, events, attrs, variants, ruma_common)
expand_event_enum(kind, V::Initial, events, docs, attrs, variants, ruma_common)
.unwrap_or_else(syn::Error::into_compile_error),
);
}
@ -111,7 +111,8 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
fn expand_event_enum(
kind: EventKind,
var: EventEnumVariation,
events: &[(LitStr, Path)],
events: &[EventEnumEntry],
docs: &[TokenStream],
attrs: &[Attribute],
variants: &[EventEnumVariant],
ruma_common: &TokenStream,
@ -120,13 +121,18 @@ fn expand_event_enum(
let ident = kind.to_event_enum_ident(var.into())?;
let variant_decls = variants.iter().map(|v| v.decl());
let content: Vec<_> =
events.iter().map(|(name, path)| to_event_path(name, path, kind, var)).collect();
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
let content: Vec<_> = events
.iter()
.map(|event| {
event
.stable_name()
.map(|stable_name| to_event_path(stable_name, &event.ev_path, kind, var))
})
.collect::<syn::Result<_>>()?;
let custom_ty = format_ident!("Custom{}Content", kind);
let deserialize_impl = expand_deserialize_impl(kind, var, events, variants, ruma_common)?;
let deserialize_impl = expand_deserialize_impl(kind, var, events, ruma_common)?;
let field_accessor_impl = expand_accessor_methods(kind, var, variants, ruma_common)?;
let from_impl = expand_from_impl(&ident, &content, variants);
@ -137,7 +143,7 @@ fn expand_event_enum(
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum #ident {
#(
#[doc = #event_types]
#docs
#variant_decls(#content),
)*
/// An event not defined by the Matrix specification
@ -156,8 +162,7 @@ fn expand_event_enum(
fn expand_deserialize_impl(
kind: EventKind,
var: EventEnumVariation,
events: &[(LitStr, Path)],
variants: &[EventEnumVariant],
events: &[EventEnumEntry],
ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
let serde = quote! { #ruma_common::exports::serde };
@ -165,13 +170,27 @@ fn expand_deserialize_impl(
let ident = kind.to_event_enum_ident(var.into())?;
let variant_attrs = variants.iter().map(|v| {
let attrs = &v.attrs;
quote! { #(#attrs)* }
});
let self_variants = variants.iter().map(|v| v.ctor(quote! { Self }));
let content = events.iter().map(|(name, path)| to_event_path(name, path, kind, var));
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
let match_arms: TokenStream = events
.iter()
.map(|event| {
let variant = event.to_variant()?;
let variant_attrs = {
let attrs = &variant.attrs;
quote! { #(#attrs)* }
};
let self_variant = variant.ctor(quote! { Self });
let content = to_event_path(event.stable_name()?, &event.ev_path, kind, var);
let ev_types = event.aliases.iter().chain([&event.ev_type]);
Ok(quote! {
#variant_attrs #(#ev_types)|* => {
let event = #serde_json::from_str::<#content>(json.get())
.map_err(D::Error::custom)?;
Ok(#self_variant(event))
},
})
})
.collect::<syn::Result<_>>()?;
Ok(quote! {
#[allow(unused_qualifications)]
@ -187,13 +206,7 @@ fn expand_deserialize_impl(
#ruma_common::serde::from_raw_json_value(&json)?;
match &*ev_type {
#(
#variant_attrs #event_types => {
let event = #serde_json::from_str::<#content>(json.get())
.map_err(D::Error::custom)?;
Ok(#self_variants(event))
},
)*
#match_arms
_ => {
let event = #serde_json::from_str(json.get()).map_err(D::Error::custom)?;
Ok(Self::_Custom(event))
@ -293,11 +306,12 @@ fn expand_into_full_event(
/// Create a content enum from `EventEnumInput`.
fn expand_content_enum(
kind: EventKind,
events: &[(LitStr, Path)],
events: &[EventEnumEntry],
docs: &[TokenStream],
attrs: &[Attribute],
variants: &[EventEnumVariant],
ruma_common: &TokenStream,
) -> TokenStream {
) -> syn::Result<TokenStream> {
let serde = quote! { #ruma_common::exports::serde };
let serde_json = quote! { #ruma_common::exports::serde_json };
@ -305,31 +319,53 @@ fn expand_content_enum(
let event_type_enum = kind.to_event_type_enum();
let content: Vec<_> =
events.iter().map(|(name, path)| to_event_content_path(kind, name, path, None)).collect();
let event_type_match_arms = events.iter().map(|(s, _)| {
if let Some(prefix) = s.value().strip_suffix(".*") {
quote! { _s if _s.starts_with(#prefix) }
} else {
quote! { #s }
}
});
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
let content: Vec<_> = events
.iter()
.map(|event| {
let stable_name = event.stable_name()?;
Ok(to_event_content_path(kind, stable_name, &event.ev_path, None))
})
.collect::<syn::Result<_>>()?;
let event_type_match_arms: TokenStream = zip(zip(events, variants), &content)
.map(|((event, variant), ev_content)| {
let variant_attrs = {
let attrs = &variant.attrs;
quote! { #(#attrs)* }
};
let variant_ctor = variant.ctor(quote! { Self });
let ev_types = event.aliases.iter().chain([&event.ev_type]);
let ev_types = if event.ev_type.value().ends_with(".*") {
let ev_types = ev_types.map(|ev_type| {
ev_type
.value()
.strip_suffix(".*")
.expect("aliases have already been checked to have the same suffix")
.to_owned()
});
quote! { _s if #(_s.starts_with(#ev_types))||* }
} else {
quote! { #(#ev_types)|* }
};
Ok(quote! {
#variant_attrs #ev_types => {
let content = #ev_content::from_parts(event_type, input)?;
::std::result::Result::Ok(#variant_ctor(content))
},
})
})
.collect::<syn::Result<_>>()?;
let variant_decls = variants.iter().map(|v| v.decl()).collect::<Vec<_>>();
let variant_attrs = variants.iter().map(|v| {
let attrs = &v.attrs;
quote! { #(#attrs)* }
});
let variant_arms = variants.iter().map(|v| v.match_arm(quote! { Self })).collect::<Vec<_>>();
let variant_ctors = variants.iter().map(|v| v.ctor(quote! { Self }));
let from_impl = expand_from_impl(&ident, &content, variants);
let serialize_custom_event_error_path =
quote! { #ruma_common::events::serialize_custom_event_error }.to_string();
quote! {
Ok(quote! {
#( #attrs )*
#[derive(Clone, Debug, #serde::Serialize)]
#[serde(untagged)]
@ -337,7 +373,7 @@ fn expand_content_enum(
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum #ident {
#(
#[doc = #event_types]
#docs
#variant_decls(#content),
)*
#[doc(hidden)]
@ -363,12 +399,7 @@ fn expand_content_enum(
input: &#serde_json::value::RawValue,
) -> #serde_json::Result<Self> {
match event_type {
#(
#variant_attrs #event_type_match_arms => {
let content = #content::from_parts(event_type, input)?;
::std::result::Result::Ok(#variant_ctors(content))
}
)*
#event_type_match_arms
ty => {
::std::result::Result::Ok(Self::_Custom {
event_type: crate::PrivOwnedStr(ty.into()),
@ -379,7 +410,7 @@ fn expand_content_enum(
}
#from_impl
}
})
}
fn expand_redact(
@ -654,10 +685,75 @@ impl EventEnumEntry {
pub(crate) fn to_variant(&self) -> syn::Result<EventEnumVariant> {
let attrs = self.attrs.clone();
let ident = m_prefix_name_to_type_name(&self.ev_type)?;
let ident = m_prefix_name_to_type_name(self.stable_name()?)?;
Ok(EventEnumVariant { attrs, ident })
}
pub(crate) fn stable_name(&self) -> syn::Result<&LitStr> {
if self.ev_type.value().starts_with("m.") {
Ok(&self.ev_type)
} else {
self.aliases.iter().find(|alias| alias.value().starts_with("m.")).ok_or_else(|| {
syn::Error::new(
Span::call_site(),
format!(
"A matrix event must declare a well-known type that starts with `m.` \
either as the main type or as an alias, found `{}`",
self.ev_type.value()
),
)
})
}
}
pub(crate) fn docs(&self) -> syn::Result<TokenStream> {
let stable_name = self.stable_name()?;
let mut doc = quote! {
#[doc = #stable_name]
};
if self.ev_type != *stable_name {
let unstable_name =
format!("This variant uses the unstable type `{}`.", self.ev_type.value());
doc.extend(quote! {
#[doc = ""]
#[doc = #unstable_name]
});
}
match self.aliases.len() {
0 => {}
1 => {
let alias = format!(
"This variant can also be deserialized from the `{}` type.",
self.aliases[0].value()
);
doc.extend(quote! {
#[doc = ""]
#[doc = #alias]
});
}
_ => {
let aliases = format!(
"This variant can also be deserialized from the following types: {}.",
self.aliases
.iter()
.map(|alias| format!("`{}`", alias.value()))
.collect::<Vec<_>>()
.join(", ")
);
doc.extend(quote! {
#[doc = ""]
#[doc = #aliases]
});
}
}
Ok(doc)
}
}
pub(crate) fn expand_from_impls_derived(input: DeriveInput) -> TokenStream {

View File

@ -7,6 +7,7 @@ use quote::{format_ident, IdentFragment};
use syn::{
braced,
parse::{self, Parse, ParseStream},
punctuated::Punctuated,
Attribute, Ident, LitStr, Path, Token,
};
@ -14,6 +15,7 @@ use syn::{
mod kw {
syn::custom_keyword!(kind);
syn::custom_keyword!(events);
syn::custom_keyword!(alias);
}
// If the variants of this enum change `to_event_path` needs to be updated as well.
@ -230,18 +232,41 @@ pub fn to_kind_variation(ident: &Ident) -> Option<(EventKind, EventKindVariation
pub struct EventEnumEntry {
pub attrs: Vec<Attribute>,
pub aliases: Vec<LitStr>,
pub ev_type: LitStr,
pub ev_path: Path,
}
impl Parse for EventEnumEntry {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let (ruma_enum_attrs, attrs) = input
.call(Attribute::parse_outer)?
.into_iter()
.partition::<Vec<_>, _>(|attr| attr.path.is_ident("ruma_enum"));
let ev_type: LitStr = input.parse()?;
let _: Token![=>] = input.parse()?;
let ev_path = input.call(Path::parse_mod_style)?;
let has_suffix = ev_type.value().ends_with(".*");
Ok(Self { attrs, ev_type, ev_path })
let mut aliases = Vec::with_capacity(ruma_enum_attrs.len());
for attr_list in ruma_enum_attrs {
for alias_attr in attr_list
.parse_args_with(Punctuated::<EventEnumAliasAttr, Token![,]>::parse_terminated)?
{
let alias = alias_attr.into_inner();
if alias.value().ends_with(".*") == has_suffix {
aliases.push(alias)
} else {
return Err(syn::Error::new_spanned(
&attr_list,
"aliases should have the same `.*` suffix, or lack thereof, as the main event type",
));
}
}
}
Ok(Self { attrs, aliases, ev_type, ev_path })
}
}
@ -285,3 +310,20 @@ impl Parse for EventEnumInput {
Ok(EventEnumInput { enums })
}
}
pub struct EventEnumAliasAttr(LitStr);
impl EventEnumAliasAttr {
pub fn into_inner(self) -> LitStr {
self.0
}
}
impl Parse for EventEnumAliasAttr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _: kw::alias = input.parse()?;
let _: Token![=] = input.parse()?;
let s: LitStr = input.parse()?;
Ok(Self(s))
}
}

View File

@ -37,6 +37,7 @@ pub fn expand_event_type_enum(
}
let presence = vec![EventEnumEntry {
attrs: vec![],
aliases: vec![],
ev_type: LitStr::new("m.presence", Span::call_site()),
ev_path: parse_quote! { #ruma_common::events::presence },
}];
@ -139,28 +140,32 @@ fn generate_enum(
})
.collect::<syn::Result<_>>()?;
let from_str_match_arms: Vec<_> = deduped
.iter()
.map(|e| {
let v = e.to_variant()?;
let ctor = v.ctor(quote! { Self });
let mut from_str_match_arms = TokenStream::new();
for event in &deduped {
let v = event.to_variant()?;
let ctor = v.ctor(quote! { Self });
let ev_types = event.aliases.iter().chain([&event.ev_type]);
let attrs = &event.attrs;
let match_arm = if let Some(prefix) = e.ev_type.value().strip_suffix('*') {
quote! {
if event.ev_type.value().ends_with(".*") {
for ev_type in ev_types {
let name = ev_type.value();
let prefix = name
.strip_suffix('*')
.expect("aliases have already been checked to have the same suffix");
from_str_match_arms.extend(quote! {
#(#attrs)*
// Use if-let guard once available
_s if _s.starts_with(#prefix) => {
#ctor(::std::convert::From::from(_s.strip_prefix(#prefix).unwrap()))
}
}
} else {
let t = &e.ev_type;
quote! { #t => #ctor }
};
let attrs = &e.attrs;
Ok(quote! { #(#attrs)* #match_arm })
})
.collect::<syn::Result<_>>()?;
})
}
} else {
from_str_match_arms.extend(quote! { #(#attrs)* #(#ev_types)|* => #ctor, })
}
}
let from_ident_for_room = if ident == "StateEventType" || ident == "MessageLikeEventType" {
let match_arms: Vec<_> = deduped
@ -232,7 +237,7 @@ fn generate_enum(
impl ::std::convert::From<&::std::primitive::str> for #ident {
fn from(s: &::std::primitive::str) -> Self {
match s {
#(#from_str_match_arms,)*
#from_str_match_arms
_ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))),
}
}