use std::collections::HashSet;
use getset::CloneGetters;
use quote::quote;
use syn::parse::ParseStream;
use syn::spanned::Spanned;
use syn::{Ident, Token};
use crate::parsing::common::compiler_error::create_user_parsing_compiler_error;
use crate::parsing::common::spanned_token::SpannedToken;
#[derive(Debug, CloneGetters)]
pub struct AttributeArgument {
#[getset(get_clone = "pub")]
key: SpannedToken,
#[getset(get_clone = "pub")]
value: SpannedToken,
}
impl AttributeArgument {
pub const fn new(key: SpannedToken, value: SpannedToken) -> Self {
Self {
key,
value,
}
}
}
pub fn parse_attribute_arguments(
input: ParseStream,
valid_keys: HashSet<String>,
internal_keys: HashSet<String>,
) -> syn::Result<Vec<AttributeArgument>> {
let mut arguments = Vec::new();
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
while !input.is_empty() {
if !arguments.is_empty() {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
}
let key: Ident = match input.parse::<Ident>() {
Ok(ident) => ident,
Err(err) => {
return Err(create_user_parsing_compiler_error(
err.span(),
"Invalid argument, expected an identifier",
));
},
};
if !valid_keys.contains(&key.to_string()) && !internal_keys.contains(&key.to_string()) {
let keys = get_keys_list(valid_keys);
return Err(create_user_parsing_compiler_error(
key.span(),
format!("Unknown argument '{key}'. Valid arguments are: {keys}."),
));
}
let Ok(eq_token) = input.parse::<Token![=]>() else {
return Err(create_user_parsing_compiler_error(
key.span(),
"Expected '=' after argument",
));
};
if input.is_empty() {
return Err(create_user_parsing_compiler_error(
eq_token.span(),
"Expected value after '='",
));
}
let expr: syn::Expr = match input.parse() {
Ok(expr) => expr,
Err(_) => {
return Err(create_user_parsing_compiler_error(
eq_token.span(),
"Expected argument value",
));
},
};
let value_span = expr.span();
let value = quote!(#expr).to_string();
arguments.push(AttributeArgument::new(
SpannedToken::new(key.to_string(), key.span()),
SpannedToken::new(value, value_span),
));
}
Ok(arguments)
}
fn get_keys_list(keys: HashSet<String>) -> String {
let mut keys = keys.iter().map(|k| format!("'{k}'")).collect::<Vec<_>>();
keys.sort();
keys.join(", ")
}
pub fn parse_boolean_attribute_argument(argument: AttributeArgument) -> syn::Result<bool> {
argument.value().token().parse::<bool>().map_err(|_| {
create_user_parsing_compiler_error(
argument.value().span(),
format!(
"Invalid value for boolean argument '{}'. Valid values are 'true' or 'false'",
argument.key().token(),
),
)
})
}