diff --git a/terraform-provider-example/src/main.rs b/terraform-provider-example/src/main.rs index 6cc8028..bc3d950 100644 --- a/terraform-provider-example/src/main.rs +++ b/terraform-provider-example/src/main.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use terustform::{ framework::{ @@ -6,7 +6,7 @@ use terustform::{ provider::Provider, AttrPath, DResult, Diagnostics, StringValue, ValueModel, }, - values::{Value, ValueKind}, + values::Value, }; #[tokio::main] @@ -28,6 +28,13 @@ impl Provider for ExampleProvider { struct ExampleDataSource {} +#[derive(terustform::DataSourceModel)] +struct ExampleDataSourceModel { + name: StringValue, + meow: StringValue, + id: StringValue, +} + impl DataSource for ExampleDataSource { fn name(&self, provider_name: &str) -> String { format!("{provider_name}_kitty") @@ -66,7 +73,7 @@ impl DataSource for ExampleDataSource { } fn read(&self, config: Value) -> DResult { - let model = ExampleDataSourceModel::from_value(config, &AttrPath::root())?; + let mut model = ExampleDataSourceModel::from_value(config, &AttrPath::root())?; let StringValue::Known(name_str) = &model.name else { return Err(Diagnostics::error_string( @@ -75,23 +82,9 @@ impl DataSource for ExampleDataSource { }; let meow = format!("mrrrrr i am {name_str}"); - Ok(Value::Known(ValueKind::Object(BTreeMap::from([ - ("name".to_owned(), model.name.to_value()), - ( - "meow".to_owned(), - Value::Known(ValueKind::String(meow)), - ), - ( - "id".to_owned(), - Value::Known(ValueKind::String("0".to_owned())), - ), - ])))) + model.meow = StringValue::Known(meow); + model.id = StringValue::Known("0".to_owned()); + + Ok(model.to_value()) } } - -#[derive(terustform::DataSourceModel)] -struct ExampleDataSourceModel { - name: StringValue, - meow: StringValue, - id: StringValue, -} diff --git a/terustform-macros/src/lib.rs b/terustform-macros/src/lib.rs index ab9f3b8..7aec14c 100644 --- a/terustform-macros/src/lib.rs +++ b/terustform-macros/src/lib.rs @@ -30,7 +30,7 @@ fn data_source_model_inner( )); }; - let terustform = quote!(::terustform::__derive_private); + let tf = quote!(::terustform::__derive_private); let fields = fields .named @@ -44,61 +44,74 @@ fn data_source_model_inner( }) .collect::, _>>()?; - let field_extractions = fields.iter().map(|(name, ty)| { let name_str = proc_macro2::Literal::string(&name.to_string()); quote! { - let #terustform::Some(#name) = obj.remove(#name_str) else { - return #terustform::Err( - #terustform::Diagnostics::error_string( + let #tf::Some(#name) = obj.remove(#name_str) else { + return #tf::Err( + #tf::Diagnostics::error_string( format!("Expected property '{}', which was not present", #name_str), ).with_path(path.clone()) ); }; - let #name = <#ty as #terustform::ValueModel>::from_value( + let #name = <#ty as #tf::ValueModel>::from_value( #name, - &path.append_attribute_name(#terustform::ToOwned::to_owned(#name_str)) + &path.append_attribute_name(#tf::ToOwned::to_owned(#name_str)) )?; } }); let constructor_fields = fields.iter().map(|(name, _)| quote! { #name, }); + let to_value_fields = fields.iter().map(|(name, ty)| { + let name_str = proc_macro2::Literal::string(&name.to_string()); + + quote! { (#name_str, <#ty as #tf::ValueModel>::to_value(self.#name)), } + }); + let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); Ok(quote! { #[automatically_derived] - impl #impl_generics #terustform::ValueModel + impl #impl_generics #tf::ValueModel for #struct_name #type_generics #where_clause { - fn from_value(v: #terustform::Value, path: &#terustform::AttrPath) -> #terustform::DResult { + fn from_value(v: #tf::Value, path: &#tf::AttrPath) -> #tf::DResult { match v { - #terustform::BaseValue::Unknown => { - return #terustform::Err(#terustform::Diagnostics::with_path( - #terustform::Diagnostics::error_string(#terustform::ToOwned::to_owned("Expected object, found unknown value")), - #terustform::Clone::clone(&path), + #tf::BaseValue::Unknown => { + return #tf::Err(#tf::Diagnostics::with_path( + #tf::Diagnostics::error_string(#tf::ToOwned::to_owned("Expected object, found unknown value")), + #tf::Clone::clone(&path), )); }, - #terustform::BaseValue::Null => { - return #terustform::Err(#terustform::Diagnostics::with_path( - #terustform::Diagnostics::error_string(#terustform::ToOwned::to_owned("Expected object, found null value")), - #terustform::Clone::clone(&path), + #tf::BaseValue::Null => { + return #tf::Err(#tf::Diagnostics::with_path( + #tf::Diagnostics::error_string(#tf::ToOwned::to_owned("Expected object, found null value")), + #tf::Clone::clone(&path), )); }, - #terustform::BaseValue::Known(#terustform::ValueKind::Object(mut obj)) => { + #tf::BaseValue::Known(#tf::ValueKind::Object(mut obj)) => { #(#field_extractions)* Ok(#struct_name { #(#constructor_fields)* }) }, - #terustform::BaseValue::Known(v) => { - return #terustform::Err(#terustform::Diagnostics::with_path( - #terustform::Diagnostics::error_string(format!("Expected object, found {} value", v.diagnostic_type_str())), - #terustform::Clone::clone(&path), + #tf::BaseValue::Known(v) => { + return #tf::Err(#tf::Diagnostics::with_path( + #tf::Diagnostics::error_string(format!("Expected object, found {} value", v.diagnostic_type_str())), + #tf::Clone::clone(&path), )); }, } } + + fn to_value(self) -> #tf::Value { + #tf::new_object( + [ + #(#to_value_fields)* + ] + ) + } } }) } diff --git a/terustform/src/framework/mod.rs b/terustform/src/framework/mod.rs index b4e833b..cf0eeaf 100644 --- a/terustform/src/framework/mod.rs +++ b/terustform/src/framework/mod.rs @@ -90,9 +90,7 @@ impl BaseValue { pub trait ValueModel: Sized { fn from_value(v: Value, path: &AttrPath) -> DResult; - fn to_value(self) -> Value { - todo!() - } + fn to_value(self) -> Value; } impl ValueModel for StringValue { diff --git a/terustform/src/lib.rs b/terustform/src/lib.rs index f78e426..877cc62 100644 --- a/terustform/src/lib.rs +++ b/terustform/src/lib.rs @@ -108,5 +108,11 @@ pub mod __derive_private { AttrPath, AttrPathSegment, BaseValue, DResult, Diagnostics, ValueModel, }; pub use crate::values::{Value, ValueKind}; - pub use {Clone, Result::Err, Option::Some, ToOwned}; + pub use {Clone, Option::Some, Result::Err, ToOwned}; + + pub fn new_object(elems: [(&str, Value); N]) -> Value { + Value::Known(ValueKind::Object(std::collections::BTreeMap::from_iter( + elems.into_iter().map(|(k, v)| (k.to_owned(), v)), + ))) + } }