use crate::{Type, Types};
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_quote, Error, Lit, LitByteStr, Result};
#[derive(Clone)]
pub enum DefaultValue {
Any(Option<Lit>),
Flag,
Str(Option<&'static str>),
String(Option<String>),
ByteStr(Option<&'static [u8]>),
ByteString(Option<Vec<u8>>),
Byte(Option<u8>),
Char(Option<char>),
I32(Option<i32>),
F32(Option<f32>),
Bool(Option<bool>),
Idents,
}
impl DefaultValue {
pub fn from_lit(ty: Type, lit: Option<Lit>) -> Result<DefaultValue> {
match ty.ty {
Types::Any => Ok(DefaultValue::Any(lit)),
Types::Flag => Ok(DefaultValue::Flag),
Types::Str => lit
.map(|lit| {
if let Lit::Str(v) = lit {
Ok(v.value())
} else {
Err(Error::new(lit.span(), "expected string"))
}
})
.transpose()
.map(DefaultValue::String),
Types::ByteStr => lit
.map(|lit| {
if let Lit::ByteStr(v) = lit {
Ok(v.value())
} else {
Err(Error::new(lit.span(), "expected bytes"))
}
})
.transpose()
.map(DefaultValue::ByteString),
Types::Byte => lit
.map(|lit| {
if let Lit::Byte(v) = lit {
Ok(v.value())
} else {
Err(Error::new(lit.span(), "expected byte"))
}
})
.transpose()
.map(DefaultValue::Byte),
Types::Char => lit
.map(|lit| {
if let Lit::Char(v) = lit {
Ok(v.value())
} else {
Err(Error::new(lit.span(), "expected char"))
}
})
.transpose()
.map(DefaultValue::Char),
Types::I32 => lit
.map(|lit| {
if let Lit::Int(v) = lit {
v.base10_parse()
} else {
Err(Error::new(lit.span(), "expected i32"))
}
})
.transpose()
.map(DefaultValue::I32),
Types::F32 => lit
.map(|lit| {
if let Lit::Int(v) = lit {
v.base10_parse()
} else {
Err(Error::new(lit.span(), "expected f32"))
}
})
.transpose()
.map(DefaultValue::F32),
Types::Bool => lit
.map(|lit| {
if let Lit::Bool(v) = lit {
Ok(v.value)
} else {
Err(Error::new(lit.span(), "expected bool"))
}
})
.transpose()
.map(DefaultValue::Bool),
Types::Idents => lit.map_or_else(
|| Ok(DefaultValue::Idents),
|lit| {
Err(Error::new(
lit.span(),
"default values are not supported for idents",
))
},
),
}
}
#[must_use]
pub fn ty(&self, optional: bool) -> Type {
Type {
ty: Types::from(self),
optional,
}
}
#[must_use]
pub fn has_default_data(&self) -> bool {
match self {
DefaultValue::Any(val) => val.is_some(),
DefaultValue::Flag | DefaultValue::Idents => false,
DefaultValue::Str(val) => val.is_some(),
DefaultValue::String(val) => val.is_some(),
DefaultValue::ByteStr(val) => val.is_some(),
DefaultValue::ByteString(val) => val.is_some(),
DefaultValue::Byte(val) => val.is_some(),
DefaultValue::Char(val) => val.is_some(),
DefaultValue::I32(val) => val.is_some(),
DefaultValue::F32(val) => val.is_some(),
DefaultValue::Bool(val) => val.is_some(),
}
}
pub(crate) fn as_lit(&self) -> Option<Lit> {
match self {
DefaultValue::Flag | DefaultValue::Idents => None,
DefaultValue::Any(val) => val.clone(),
DefaultValue::Str(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::String(val) => val.as_ref().map(|v| parse_quote!(#v)),
DefaultValue::ByteStr(val) => val.map(|v| {
let lbs = LitByteStr::new(&v, Span::call_site());
parse_quote!(#lbs)
}),
DefaultValue::ByteString(val) => val.as_ref().map(|v| {
let lbs = LitByteStr::new(&v, Span::call_site());
parse_quote!(#lbs)
}),
DefaultValue::Byte(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::Char(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::I32(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::F32(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::Bool(val) => val.map(|v| parse_quote!(#v)),
}
}
}
impl From<DefaultValue> for Option<Lit> {
fn from(val: DefaultValue) -> Self {
match val {
DefaultValue::Flag | DefaultValue::Idents => None,
DefaultValue::Any(val) => val,
DefaultValue::Str(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::String(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::ByteStr(val) => val.map(|v| {
let lbs = LitByteStr::new(&v, Span::call_site());
parse_quote!(#lbs)
}),
DefaultValue::ByteString(val) => val.map(|v| {
let lbs = LitByteStr::new(&v, Span::call_site());
parse_quote!(#lbs)
}),
DefaultValue::Byte(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::Char(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::I32(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::F32(val) => val.map(|v| parse_quote!(#v)),
DefaultValue::Bool(val) => val.map(|v| parse_quote!(#v)),
}
}
}
impl From<&DefaultValue> for Types {
fn from(value: &DefaultValue) -> Self {
match value {
DefaultValue::Any(_) => Types::Any,
DefaultValue::Flag => Types::Flag,
DefaultValue::Str(_) | DefaultValue::String(_) => Types::Str,
DefaultValue::ByteStr(_) | DefaultValue::ByteString(_) => Types::ByteStr,
DefaultValue::Byte(_) => Types::Byte,
DefaultValue::Char(_) => Types::Char,
DefaultValue::I32(_) => Types::I32,
DefaultValue::F32(_) => Types::F32,
DefaultValue::Bool(_) => Types::Bool,
DefaultValue::Idents => Types::Idents,
}
}
}
impl ToTokens for DefaultValue {
fn to_tokens(&self, tokens: &mut TokenStream) {
fn map_literal<V: ToTokens>(v: &Option<V>) -> TokenStream {
if let Some(data) = v {
quote!(::core::option::Option::Some(#data))
} else {
quote!(::core::option::Option::None)
}
}
let tts = match self {
DefaultValue::Any(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::Any(#data))
}
DefaultValue::Flag => quote!(::macro_input::DefaultValue::Flag),
DefaultValue::Str(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::Str(#data))
}
DefaultValue::String(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::Str(#data))
}
DefaultValue::ByteStr(v) => {
let data = map_literal(&v.as_ref().map(|v| LitByteStr::new(v, Span::call_site())));
quote!(::macro_input::DefaultValue::ByteStr(#data))
}
DefaultValue::ByteString(v) => {
let data = map_literal(&v.as_ref().map(|v| LitByteStr::new(v, Span::call_site())));
quote!(::macro_input::DefaultValue::ByteStr(#data))
}
DefaultValue::Byte(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::Byte(#data))
}
DefaultValue::Char(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::Char(#data))
}
DefaultValue::I32(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::I32(#data))
}
DefaultValue::F32(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::F32(#data))
}
DefaultValue::Bool(v) => {
let data = map_literal(v);
quote!(::macro_input::DefaultValue::Bool(#data))
}
DefaultValue::Idents => {
quote!(::macro_input::DefaultValue::Idents)
}
};
tokens.extend(tts);
}
}