use crate::registry::{ArrayProperty, PropertyDefinition, ScalarProperty, StructProperty};
use crate::registry::{Attr, PropertyDefinitionRaw, RegistryDefinition};
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::parse::{Parse, ParseBuffer, ParseStream};
use syn::{Error, Expr, Lit, LitInt, LitStr, Token};
impl Parse for RegistryDefinition {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut registry_name: Option<Ident> = None;
let mut version: Option<LitInt> = None;
while !input.is_empty() {
let fork = input.fork();
let _ = fork.parse::<Ident>()?;
if fork.parse::<Token![=]>().is_err() {
break;
}
let header_key: Ident = input.parse()?;
let _ = input.parse::<Token![=]>()?;
match header_key.to_string().as_str() {
"name" => {
if registry_name.is_some() {
return Err(Error::new(
header_key.span(),
"Duplicate `name` header in config_registry!",
));
}
registry_name = Some(input.parse()?);
}
"version" => {
if version.is_some() {
return Err(Error::new(
header_key.span(),
"Duplicate `version` header in config_registry!",
));
}
version = Some(input.parse()?);
}
_ => {
return Err(Error::new(
header_key.span(),
"Unknown config_registry! header; supported headers are `name` and `version`",
));
}
}
}
let registry_name = registry_name.ok_or(Error::new(
input.span(),
"Missing required `name = <Ident>` header in config_registry!",
))?;
let version = version.ok_or(Error::new(
input.span(),
"Missing required `version = <integer>` header in config_registry!",
))?;
let mut registry_def = RegistryDefinition::new(registry_name.clone(), version);
while !input.is_empty() {
let ParsedPropertyDefinition(prop_def, raw_def) =
ParsedPropertyDefinition::parse_with_name(input, ®istry_name)?;
registry_def.push(prop_def)?;
registry_def.registry_entries.push(raw_def);
}
Ok(registry_def)
}
}
impl ParsedPropertyDefinition {
fn parse_with_name(input: ParseStream, registry_name: &Ident) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
let span = ident.span();
let content: ParseBuffer;
syn::parenthesized!(content in input);
let key_lit = content.parse::<LitStr>()?;
let key_span = key_lit.span();
let mut raw_definition = PropertyDefinitionRaw::new(ident, key_lit);
let lookahead = content.lookahead1();
if lookahead.peek(Token![,]) {
while !content.is_empty() {
let _ = content.parse::<Token![,]>()?;
let ident = content.parse::<syn::Ident>()?;
let token_eq = content.parse::<Token![=]>();
let value = match token_eq {
Ok(_) => {
content.parse::<Expr>().ok()
}
Err(_) => {
Some(Expr::Lit(syn::ExprLit {
attrs: vec![],
lit: Lit::Bool(syn::LitBool {
value: true,
span: ident.span(),
}),
}))
}
};
raw_definition.add_attr(Attr::new(ident, value));
}
}
let key = raw_definition.key.value();
let mut key_parts: Vec<&str> = key.split(".").collect();
if key_parts.len() == 0 {
return Err(Error::new(input.span(), "Property key cannot be empty"));
}
let key = key_parts.pop().ok_or(Error::new(
input.span(),
"Property key must have at least one part",
))?;
let ty = raw_definition.get_final_type()?;
let scalar = ScalarProperty::new(
Ident::new(sanitize_key(key).as_str(), key_span),
ty,
raw_definition.get_attr("default"),
raw_definition.get_attr("optional").is_some(),
);
let mut prop_def = PropertyDefinition::Scalar(scalar);
if is_array_key(key) {
prop_def = PropertyDefinition::Array(ArrayProperty::new(
Ident::new(sanitize_key(key).as_str(), key_span),
get_array_index(key)?,
prop_def,
));
}
while !key_parts.is_empty() {
let key = key_parts.pop().unwrap();
let ident = Ident::new(sanitize_key(key).as_str(), span);
let mut struct_prop = StructProperty::new(ident);
struct_prop.add_field(prop_def);
prop_def = PropertyDefinition::Struct(struct_prop);
if is_array_key(key) {
prop_def = PropertyDefinition::Array(ArrayProperty::new(
Ident::new(sanitize_key(key).as_str(), key_span),
get_array_index(key)?,
prop_def,
));
}
}
let mut root = StructProperty::new_root(registry_name.clone());
root.add_field(prop_def);
Ok(ParsedPropertyDefinition(
PropertyDefinition::Struct(root),
raw_definition,
))
}
}
fn get_array_index(key: &str) -> syn::Result<Lit> {
if let Some(start) = key.find('[') {
if let Some(end) = key.find(']') {
let index_str = &key[start + 1..end];
let index_lit = syn::parse2(quote!(#index_str))?;
Ok(index_lit)
} else {
Err(Error::new(
Span::call_site(),
"Missing closing bracket for array index",
))
}
} else {
Err(Error::new(Span::call_site(), "No array index found in key"))
}
}
fn is_array_key(key: &str) -> bool {
key.contains('[') && key.contains(']')
}
fn sanitize_key(key: &str) -> String {
let sanitized = if let (Some(li), Some(_)) = (key.find('['), key.find(']')) {
let (key, _) = key.split_at(li);
key
} else {
key
};
stringcase::snake_case(sanitized)
}
struct ParsedPropertyDefinition(PropertyDefinition, PropertyDefinitionRaw);