use std::collections::HashSet;
use proc_macro2::Span;
use syn::{
spanned::Spanned, Error, Expr, Ident, Lit, LitBool, LitFloat, LitInt, LitStr, Path, Type,
TypePath,
};
pub trait FromMetaExpr: Sized {
fn try_from_expr(expr: Expr) -> syn::Result<Self>;
}
impl FromMetaExpr for Expr {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
Ok(expr)
}
}
impl FromMetaExpr for Lit {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Lit(lit) = expr {
return Ok(lit.lit);
}
Err(Error::new(expr.span(), "expected literal"))
}
}
impl FromMetaExpr for LitStr {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Lit(lit) = &expr {
let lit = lit.lit.clone();
if let Lit::Str(lit) = lit {
return Ok(lit);
}
}
Err(Error::new(expr.span(), "expected litteral str"))
}
}
impl FromMetaExpr for LitInt {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Lit(lit) = &expr {
let lit = lit.lit.clone();
if let Lit::Int(lit) = lit {
return Ok(lit);
}
}
Err(Error::new(expr.span(), "expected litteral int"))
}
}
impl FromMetaExpr for LitBool {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Lit(lit) = &expr {
let lit = lit.lit.clone();
if let Lit::Bool(lit) = lit {
return Ok(lit);
}
}
Err(Error::new(expr.span(), "expected litteral bool"))
}
}
impl FromMetaExpr for LitFloat {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Lit(lit) = &expr {
let lit = lit.lit.clone();
if let Lit::Float(lit) = lit {
return Ok(lit);
}
}
Err(Error::new(expr.span(), "expected litteral float"))
}
}
impl FromMetaExpr for bool {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let lit = LitBool::try_from_expr(expr)?;
Ok(lit.value)
}
}
impl FromMetaExpr for f64 {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let lit = LitFloat::try_from_expr(expr)?;
lit.base10_parse::<f64>()
}
}
impl FromMetaExpr for i64 {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let lit = LitInt::try_from_expr(expr)?;
lit.base10_parse::<i64>()
}
}
impl FromMetaExpr for u64 {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let lit = LitInt::try_from_expr(expr)?;
lit.base10_parse::<u64>()
}
}
impl FromMetaExpr for String {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let lit = LitStr::try_from_expr(expr)?;
Ok(lit.value())
}
}
impl<T: FromMetaExpr> FromMetaExpr for Option<T> {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let result = T::try_from_expr(expr)?;
Ok(Some(result))
}
}
impl FromMetaExpr for Type {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Path(ty) = expr {
return Ok(Type::Path(TypePath {
qself: ty.qself,
path: ty.path,
}));
}
Err(Error::new(expr.span(), "expected type"))
}
}
impl FromMetaExpr for Ident {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Path(path) = expr {
let ident = path.path.require_ident()?;
return Ok(ident.clone());
}
Err(Error::new(expr.span(), "expected ident"))
}
}
impl FromMetaExpr for Path {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
if let Expr::Path(path) = expr {
return Ok(path.path);
}
Err(Error::new(expr.span(), "expected path"))
}
}
impl<T: FromMetaExpr> FromMetaExpr for Vec<T> {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let mut result = vec![];
match expr {
Expr::Array(expr) => {
for expr in expr.elems {
let item: T = T::try_from_expr(expr)?;
result.push(item);
}
}
_ => {
return Err(Error::new(
Span::call_site(),
"标签输入格式错误,实例: tags = [\"abc\"]",
))
}
}
Ok(result)
}
}
impl<T: FromMetaExpr + Eq + std::hash::Hash> FromMetaExpr for HashSet<T> {
fn try_from_expr(expr: Expr) -> syn::Result<Self> {
let mut result = HashSet::new();
match expr {
Expr::Array(expr) => {
for expr in expr.elems {
let item: T = T::try_from_expr(expr)?;
result.insert(item);
}
}
_ => {
return Err(Error::new(
Span::call_site(),
"标签输入格式错误,实例: tags = [\"abc\"]",
))
}
}
Ok(result)
}
}