use std::collections::HashMap;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use syn::parse::{Parse, ParseStream};
use syn::token::Static;
use syn::{Expr, ExprLit, Ident, Lit, Path, PathArguments, Result as SynResult, Token};
use thiserror::Error;
use translatable_shared::macros::errors::IntoCompileError;
use translatable_shared::misc::language::Language;
use super::input_type::InputType;
#[derive(Error, Debug)]
enum TranslationMacroArgsError {
#[error("The literal '{0}' is an invalid ISO 639-1 string, and cannot be parsed")]
InvalidIsoLiteral(String),
#[error("This translation path contains generic arguments, and cannot be parsed")]
InvalidPathContainsGenerics,
}
pub struct TranslationMacroArgs {
language: InputType<Language>,
path: InputType<Vec<String>>,
replacements: HashMap<Ident, TokenStream2>,
}
impl Parse for TranslationMacroArgs {
fn parse(input: ParseStream) -> SynResult<Self> {
let parsed_language_arg = match input.parse::<Expr>()? {
Expr::Lit(ExprLit { lit: Lit::Str(literal), .. }) => {
match literal
.value()
.parse::<Language>()
{
Ok(language) => InputType::Static(language),
Err(_) => Err(TranslationMacroArgsError::InvalidIsoLiteral(literal.value())
.to_syn_error(literal))?,
}
},
other => InputType::Dynamic(other.into_token_stream()),
};
input.parse::<Token![,]>()?;
let parsed_path_arg = match input.parse::<Static>() {
Ok(_) => {
let language_arg = input
.parse::<Path>()?
.segments
.into_iter()
.map(|segment| match segment.arguments {
PathArguments::None => Ok(segment
.ident
.to_string()),
other => Err(TranslationMacroArgsError::InvalidPathContainsGenerics
.to_syn_error(other)),
})
.collect::<Result<Vec<_>, _>>()?;
InputType::Static(language_arg)
},
Err(_) => InputType::Dynamic(
input
.parse::<Expr>()?
.to_token_stream(),
),
};
let mut replacements = HashMap::new();
if input.peek(Token![,]) {
while !input.is_empty() {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
let key = input.parse::<Ident>()?;
let value = match input.parse::<Token![=]>() {
Ok(_) => input
.parse::<Expr>()?
.to_token_stream(),
Err(_) => key
.clone()
.into_token_stream(),
};
replacements.insert(key, value);
}
}
Ok(Self {
language: parsed_language_arg,
path: parsed_path_arg,
replacements,
})
}
}
impl TranslationMacroArgs {
#[inline]
#[allow(unused)]
pub fn language(&self) -> &InputType<Language> {
&self.language
}
#[inline]
#[allow(unused)]
pub fn path(&self) -> &InputType<Vec<String>> {
&self.path
}
#[inline]
#[allow(unused)]
pub fn replacements(&self) -> &HashMap<Ident, TokenStream2> {
&self.replacements
}
}