implemented derive FromValue for enum
still needs fixing tho
This commit is contained in:
parent
1deba599e2
commit
16f85e49ff
|
@ -31,7 +31,7 @@ pub fn derive_from_value(input: TokenStream2) -> Result<TokenStream2, impl Into<
|
||||||
data_struct,
|
data_struct,
|
||||||
input.generics,
|
input.generics,
|
||||||
)),
|
)),
|
||||||
Data::Enum(data_enum) => Ok(enum_from_value(input.ident, data_enum, input.generics)),
|
Data::Enum(data_enum) => Ok(derive_enum_from_value(input.ident, data_enum, input.generics)),
|
||||||
Data::Union(_) => Err(DeriveError::Unsupported),
|
Data::Union(_) => Err(DeriveError::Unsupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,67 +50,7 @@ fn derive_struct_from_value(ident: Ident, data: DataStruct, generics: Generics)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn struct_from_value(data: &DataStruct) -> TokenStream2 {
|
fn struct_from_value(data: &DataStruct) -> TokenStream2 {
|
||||||
let body = match &data.fields {
|
let body = fields_from_record(&data.fields, quote!(Self));
|
||||||
Fields::Named(fields) => {
|
|
||||||
let fields = fields.named.iter().map(|field| {
|
|
||||||
let ident = field.ident.as_ref().expect("named has idents");
|
|
||||||
let ident_s = ident.to_string();
|
|
||||||
let ty = &field.ty;
|
|
||||||
quote! {
|
|
||||||
#ident: <#ty as nu_protocol::FromValue>::from_value(
|
|
||||||
record
|
|
||||||
.remove(#ident_s)
|
|
||||||
.ok_or_else(|| nu_protocol::ShellError::CantFindColumn {
|
|
||||||
col_name: std::string::ToString::to_string(#ident_s),
|
|
||||||
span: call_span,
|
|
||||||
src_span: span
|
|
||||||
})?,
|
|
||||||
call_span,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
});
|
|
||||||
quote! {
|
|
||||||
let span = v.span();
|
|
||||||
let mut record = v.into_record()?;
|
|
||||||
std::result::Result::Ok(Self {#(#fields),*})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fields::Unnamed(fields) => {
|
|
||||||
let fields = fields.unnamed.iter().enumerate().map(|(i, field)| {
|
|
||||||
let ty = &field.ty;
|
|
||||||
quote! {{
|
|
||||||
<#ty as nu_protocol::FromValue>::from_value(
|
|
||||||
deque
|
|
||||||
.pop_front()
|
|
||||||
.ok_or_else(|| nu_protocol::ShellError::CantFindColumn {
|
|
||||||
col_name: std::string::ToString::to_string(&#i),
|
|
||||||
span: call_span,
|
|
||||||
src_span: span
|
|
||||||
})?,
|
|
||||||
call_span,
|
|
||||||
)?
|
|
||||||
}}
|
|
||||||
});
|
|
||||||
quote! {
|
|
||||||
let span = v.span();
|
|
||||||
let list = v.into_list()?;
|
|
||||||
let mut deque: std::collections::VecDeque<_> = std::convert::From::from(list);
|
|
||||||
std::result::Result::Ok(Self(#(#fields),*))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fields::Unit => quote! {
|
|
||||||
match v {
|
|
||||||
nu_protocol::Value::Nothing {..} => Ok(Self),
|
|
||||||
v => std::result::Result::Err(nu_protocol::ShellError::CantConvert {
|
|
||||||
to_type: <Self as nu_protocol::FromValue>::expected_type().to_string(),
|
|
||||||
from_type: v.get_type().to_string(),
|
|
||||||
span: v.span(),
|
|
||||||
help: std::option::Option::None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
fn from_value(
|
fn from_value(
|
||||||
v: nu_protocol::Value,
|
v: nu_protocol::Value,
|
||||||
|
@ -168,6 +108,128 @@ fn struct_expected_type(fields: &Fields) -> TokenStream2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enum_from_value(ident: Ident, data: DataEnum, generics: Generics) -> TokenStream2 {
|
fn derive_enum_from_value(ident: Ident, data: DataEnum, generics: Generics) -> TokenStream2 {
|
||||||
todo!()
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
let from_value_impl = enum_from_value(&data);
|
||||||
|
// As variants are hard to type with the current type system, we use the
|
||||||
|
// default impl for `expected_type`.
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics nu_protocol::FromValue for #ident #ty_generics #where_clause {
|
||||||
|
#from_value_impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enum_from_value(data: &DataEnum) -> TokenStream2 {
|
||||||
|
let arms = data.variants.iter().map(|variant| {
|
||||||
|
let ident = &variant.ident;
|
||||||
|
let ident_s = format!("{ident}").as_str().to_case(Case::Snake);
|
||||||
|
let fields = fields_from_record(&variant.fields, quote!(Self::#ident));
|
||||||
|
quote!(#ident_s => {#fields})
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn from_value(
|
||||||
|
v: nu_protocol::Value,
|
||||||
|
call_span: nu_protocol::Span
|
||||||
|
) -> std::result::Result<Self, nu_protocol::ShellError> {
|
||||||
|
let span = v.span();
|
||||||
|
let mut record = v.into_record()?;
|
||||||
|
|
||||||
|
let ty = record.remove("type").ok_or_else(|| nu_protocol::ShellError::CantFindColumn {
|
||||||
|
col_name: std::string::ToString::to_string("type"),
|
||||||
|
span: call_span,
|
||||||
|
src_span: span
|
||||||
|
})?;
|
||||||
|
let ty = ty.into_string()?;
|
||||||
|
|
||||||
|
// TODO: make content optional if type refers to a unit variant
|
||||||
|
|
||||||
|
let v = record.remove("content").ok_or_else(|| nu_protocol::ShellError::CantFindColumn {
|
||||||
|
col_name: std::string::ToString::to_string("content"),
|
||||||
|
span: call_span,
|
||||||
|
src_span: span
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match ty.as_str() {
|
||||||
|
#(#arms),*
|
||||||
|
_ => std::result::Result::Err(nu_protocol::ShellError::CantConvert {
|
||||||
|
to_type: std::string::ToString::to_string(
|
||||||
|
&<Self as nu_protocol::FromValue>::expected_type()
|
||||||
|
),
|
||||||
|
from_type: ty,
|
||||||
|
span: span,
|
||||||
|
help: std::option::Option::None
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fields_from_record(
|
||||||
|
fields: &Fields,
|
||||||
|
self_ident: impl ToTokens
|
||||||
|
) -> TokenStream2 {
|
||||||
|
match fields {
|
||||||
|
Fields::Named(fields) => {
|
||||||
|
let fields = fields.named.iter().map(|field| {
|
||||||
|
// TODO: handle missing fields for Options as None
|
||||||
|
let ident = field.ident.as_ref().expect("named has idents");
|
||||||
|
let ident_s = ident.to_string();
|
||||||
|
let ty = &field.ty;
|
||||||
|
quote! {
|
||||||
|
#ident: <#ty as nu_protocol::FromValue>::from_value(
|
||||||
|
record
|
||||||
|
.remove(#ident_s)
|
||||||
|
.ok_or_else(|| nu_protocol::ShellError::CantFindColumn {
|
||||||
|
col_name: std::string::ToString::to_string(#ident_s),
|
||||||
|
span: call_span,
|
||||||
|
src_span: span
|
||||||
|
})?,
|
||||||
|
call_span,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
let span = v.span();
|
||||||
|
let mut record = v.into_record()?;
|
||||||
|
std::result::Result::Ok(#self_ident {#(#fields),*})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Fields::Unnamed(fields) => {
|
||||||
|
let fields = fields.unnamed.iter().enumerate().map(|(i, field)| {
|
||||||
|
let ty = &field.ty;
|
||||||
|
quote! {{
|
||||||
|
<#ty as nu_protocol::FromValue>::from_value(
|
||||||
|
deque
|
||||||
|
.pop_front()
|
||||||
|
.ok_or_else(|| nu_protocol::ShellError::CantFindColumn {
|
||||||
|
col_name: std::string::ToString::to_string(&#i),
|
||||||
|
span: call_span,
|
||||||
|
src_span: span
|
||||||
|
})?,
|
||||||
|
call_span,
|
||||||
|
)?
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
let span = v.span();
|
||||||
|
let list = v.into_list()?;
|
||||||
|
let mut deque: std::collections::VecDeque<_> = std::convert::From::from(list);
|
||||||
|
std::result::Result::Ok(#self_ident(#(#fields),*))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Fields::Unit => quote! {
|
||||||
|
match v {
|
||||||
|
nu_protocol::Value::Nothing {..} => Ok(#self_ident),
|
||||||
|
v => std::result::Result::Err(nu_protocol::ShellError::CantConvert {
|
||||||
|
to_type: std::string::ToString::to_string(&<Self as nu_protocol::FromValue>::expected_type()),
|
||||||
|
from_type: std::string::ToString::to_string(&v.get_type()),
|
||||||
|
span: v.span(),
|
||||||
|
help: std::option::Option::None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,7 +265,7 @@ fn unit_struct_roundtrip() {
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoValue)]
|
#[derive(IntoValue, FromValue, Debug, PartialEq)]
|
||||||
enum Enum {
|
enum Enum {
|
||||||
Unit,
|
Unit,
|
||||||
Tuple(u32, String),
|
Tuple(u32, String),
|
||||||
|
@ -283,29 +283,40 @@ impl Enum {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value() -> Value {
|
||||||
|
Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"type" => Value::test_string("unit")
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"type" => Value::test_string("tuple"),
|
||||||
|
"content" => Value::test_list(vec![
|
||||||
|
Value::test_int(12),
|
||||||
|
Value::test_string("Tuple variant")
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"type" => Value::test_string("struct"),
|
||||||
|
"content" => Value::test_record(record! {
|
||||||
|
"a" => Value::test_int(34),
|
||||||
|
"b" => Value::test_string("Struct variant")
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enum_into_value() {
|
fn enum_into_value() {
|
||||||
let enums = Enum::make().into_value_unknown();
|
let expected = Enum::value();
|
||||||
let expected = Value::test_list(vec![
|
let actual = Enum::make().into_value_unknown();
|
||||||
Value::test_record(record! {
|
assert_eq!(expected, actual);
|
||||||
"type" => Value::test_string("unit")
|
}
|
||||||
}),
|
|
||||||
Value::test_record(record! {
|
#[test]
|
||||||
"type" => Value::test_string("tuple"),
|
fn enum_from_value() {
|
||||||
"content" => Value::test_list(vec![
|
let expected = Enum::make();
|
||||||
Value::test_int(12),
|
let actual = <[Enum; 3]>::from_value(Enum::value(), Span::test_data()).unwrap();
|
||||||
Value::test_string("Tuple variant")
|
assert_eq!(expected, actual);
|
||||||
])
|
|
||||||
}),
|
|
||||||
Value::test_record(record! {
|
|
||||||
"type" => Value::test_string("struct"),
|
|
||||||
"content" => Value::test_record(record! {
|
|
||||||
"a" => Value::test_int(34),
|
|
||||||
"b" => Value::test_string("Struct variant")
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
assert_eq!(enums, expected);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user