Support nested object

This commit is contained in:
nora 2024-05-29 20:20:36 +02:00
parent 55506d3748
commit b50fa51e9c
8 changed files with 143 additions and 96 deletions

View file

@ -1,5 +1,3 @@
use std::collections::HashMap;
use terustform::{ use terustform::{
resource::Resource, AttrPath, Attribute, DResult, EyreExt, Mode, Schema, Value, ValueModel, resource::Resource, AttrPath, Attribute, DResult, EyreExt, Mode, Schema, Value, ValueModel,
}; };
@ -78,41 +76,28 @@ impl Resource for ClassResource {
fn schema() -> terustform::Schema { fn schema() -> terustform::Schema {
Schema { Schema {
description: "A class".into(), description: "A class".into(),
attributes: HashMap::from([ attributes: terustform::attrs! {
( "id" => Attribute::String {
"id".to_owned(),
// TODO: UUID validation :3
Attribute::String {
description: "The UUID".to_owned(), description: "The UUID".to_owned(),
mode: Mode::Computed, mode: Mode::Computed,
sensitive: false, sensitive: false,
}, },
), "name" => Attribute::String {
(
"name".to_owned(),
Attribute::String {
description: "The description".to_owned(), description: "The description".to_owned(),
mode: Mode::Required, mode: Mode::Required,
sensitive: false, sensitive: false,
}, },
), "description" => Attribute::String {
(
"description".to_owned(),
Attribute::String {
description: "The description".to_owned(), description: "The description".to_owned(),
mode: Mode::Required, mode: Mode::Required,
sensitive: false, sensitive: false,
}, },
), "discord_id" => Attribute::String {
(
"discord_id".to_owned(),
Attribute::String {
description: "The discord ID of the class".to_owned(), description: "The discord ID of the class".to_owned(),
mode: Mode::Optional, mode: Mode::Optional,
sensitive: false, sensitive: false,
}, },
), },
]),
} }
} }

View file

@ -1,5 +1,3 @@
use std::collections::HashMap;
use eyre::Context; use eyre::Context;
use terustform::{ use terustform::{
datasource::DataSource, Attribute, DResult, EyreExt, Mode, Schema, StringValue, Value, datasource::DataSource, Attribute, DResult, EyreExt, Mode, Schema, StringValue, Value,
@ -41,14 +39,13 @@ impl DataSource for HugoDataSource {
fn schema() -> Schema { fn schema() -> Schema {
Schema { Schema {
description: "Get Hugo Boss".to_owned(), description: "Get Hugo Boss".to_owned(),
attributes: HashMap::from([( attributes: terustform::attrs! {
"hugo".to_owned(), "hugo" => Attribute::String {
Attribute::String {
description: "Hugo Boss".to_owned(), description: "Hugo Boss".to_owned(),
mode: Mode::Computed, mode: Mode::Computed,
sensitive: false, sensitive: false,
}, },
)]), },
} }
} }

View file

@ -1,5 +1,3 @@
use std::collections::HashMap;
use terustform::{ use terustform::{
datasource::DataSource, AttrPath, Attribute, DResult, Mode, Schema, StringValue, Value, datasource::DataSource, AttrPath, Attribute, DResult, Mode, Schema, StringValue, Value,
ValueModel, ValueModel,
@ -13,7 +11,13 @@ pub struct ExampleDataSource {}
struct ExampleDataSourceModel { struct ExampleDataSourceModel {
name: StringValue, name: StringValue,
meow: StringValue, meow: StringValue,
id: StringValue, paws: ExampleDataSourceModelPaws,
}
#[derive(terustform::Model)]
struct ExampleDataSourceModelPaws {
left: StringValue,
right: StringValue,
} }
impl DataSource for ExampleDataSource { impl DataSource for ExampleDataSource {
@ -26,32 +30,35 @@ impl DataSource for ExampleDataSource {
fn schema() -> Schema { fn schema() -> Schema {
Schema { Schema {
description: "an example".to_owned(), description: "an example".to_owned(),
attributes: HashMap::from([ attributes: terustform::attrs! {
( "name" => Attribute::String {
"name".to_owned(),
Attribute::String {
description: "a cool name".to_owned(), description: "a cool name".to_owned(),
mode: Mode::Required, mode: Mode::Required,
sensitive: false, sensitive: false,
}, },
), "meow" => Attribute::String {
(
"meow".to_owned(),
Attribute::String {
description: "the meow of the cat".to_owned(), description: "the meow of the cat".to_owned(),
mode: Mode::Computed, mode: Mode::Computed,
sensitive: false, sensitive: false,
}, },
), "paws" => Attribute::Object {
(
"id".to_owned(),
Attribute::String {
description: "the ID of the meowy cat".to_owned(), description: "the ID of the meowy cat".to_owned(),
mode: Mode::Computed, mode: Mode::Required,
sensitive: false,
attrs: terustform::attrs! {
"left" => Attribute::String {
description: "meow".to_owned(),
mode: Mode::Required,
sensitive: false, sensitive: false,
}, },
), "right" => Attribute::String {
]), description: "meow".to_owned(),
mode: Mode::Required,
sensitive: false,
},
},
},
},
} }
} }
@ -67,7 +74,7 @@ impl DataSource for ExampleDataSource {
let meow = format!("mrrrrr i am {name_str}"); let meow = format!("mrrrrr i am {name_str}");
model.meow = StringValue::Known(meow); model.meow = StringValue::Known(meow);
model.id = StringValue::Known("0".to_owned()); model.paws.right = StringValue::Known("O".to_owned());
Ok(model.to_value()) Ok(model.to_value())
} }

View file

@ -19,8 +19,18 @@ data "corsschool_class" "test" {
output "class" { output "class" {
value = data.corsschool_class.test value = data.corsschool_class.test
} }
/*
resource "corsschool_class" "myclass" { resource "corsschool_class" "myclass" {
name = "meow" name = "meow"
description = "???" description = "???"
}*/
data "corsschool_kitty" "name" {
name = "a"
paws = {
left = "x"
right = "y"
}
}
output "kitty_paw" {
value = data.corsschool_kitty.name.paws.right
} }

View file

@ -50,7 +50,7 @@ fn data_source_model_inner(
let #tf::Some(#name) = obj.remove(#name_str) else { let #tf::Some(#name) = obj.remove(#name_str) else {
return #tf::Err( return #tf::Err(
#tf::Diagnostics::from(#tf::Diagnostic::error_string( #tf::Diagnostics::from(#tf::Diagnostic::error_string(
format!("Expected property '{}', which was not present", #name_str), format!("Expected property '{}' when deserializing value, which was not present in the value", #name_str),
).with_path(path.clone())) ).with_path(path.clone()))
); );
}; };

View file

@ -29,9 +29,12 @@ use provider::Provider;
pub async fn start<P: Provider>(provider: P) -> eyre::Result<()> { pub async fn start<P: Provider>(provider: P) -> eyre::Result<()> {
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_env_filter(EnvFilter::builder().parse_lossy( .with_env_filter(
std::env::var("RUST_LOG").unwrap_or_else(|_| "h2=info,rustls=info,hyper_util=info,debug".into()), EnvFilter::builder().parse_lossy(
)) std::env::var("RUST_LOG")
.unwrap_or_else(|_| "h2=info,rustls=info,hyper_util=info,debug".into()),
),
)
.with_writer(std::io::stderr) .with_writer(std::io::stderr)
.without_time() .without_time()
.init(); .init();
@ -39,6 +42,28 @@ pub async fn start<P: Provider>(provider: P) -> eyre::Result<()> {
server::serve(provider).await server::serve(provider).await
} }
/// ```rust
/// # use std::collections::HashMap;
/// let x: HashMap<String, u8> = terustform::attrs! {
/// "hello" => 0,
/// };
/// ```
#[macro_export]
macro_rules! attrs {
(
$( $name:literal => $rhs:expr ,)*
) => {
<$crate::__derive_private::HashMap<_, _> as $crate::__derive_private::FromIterator<(_, _)>>::from_iter([
$(
(
$name.into(),
$rhs,
),
)*
])
};
}
/// Private, only for use for with the derive macro. /// Private, only for use for with the derive macro.
#[doc(hidden)] #[doc(hidden)]
pub mod __derive_private { pub mod __derive_private {
@ -46,7 +71,7 @@ pub mod __derive_private {
AttrPath, AttrPathSegment, BaseValue, DResult, Diagnostic, Diagnostics, Value, ValueKind, AttrPath, AttrPathSegment, BaseValue, DResult, Diagnostic, Diagnostics, Value, ValueKind,
ValueModel, ValueModel,
}; };
pub use {Clone, Option::Some, Result::Err, ToOwned}; pub use {std::collections::HashMap, Clone, FromIterator, Option::Some, Result::Err, ToOwned};
pub fn new_object<const N: usize>(elems: [(&str, Value); N]) -> Value { pub fn new_object<const N: usize>(elems: [(&str, Value); N]) -> Value {
Value::Known(ValueKind::Object(std::collections::BTreeMap::from_iter( Value::Known(ValueKind::Object(std::collections::BTreeMap::from_iter(

View file

@ -20,6 +20,12 @@ pub enum Attribute {
mode: Mode, mode: Mode,
sensitive: bool, sensitive: bool,
}, },
Object {
description: String,
mode: Mode,
sensitive: bool,
attrs: HashMap<String, Attribute>,
},
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -35,6 +41,7 @@ impl Attribute {
match *self { match *self {
Self::Int64 { mode, .. } => mode, Self::Int64 { mode, .. } => mode,
Self::String { mode, .. } => mode, Self::String { mode, .. } => mode,
Self::Object { mode, .. } => mode,
} }
} }
} }
@ -55,22 +62,28 @@ impl Mode {
impl Schema { impl Schema {
pub fn typ(&self) -> Type { pub fn typ(&self) -> Type {
let attrs = self attrs_typ(&self.attributes)
.attributes }
.iter() }
.map(|(name, attr)| {
let attr_type = match attr { impl Attribute {
pub fn typ(&self) -> Type {
match self {
Attribute::Int64 { .. } => Type::Number, Attribute::Int64 { .. } => Type::Number,
Attribute::String { .. } => Type::String, Attribute::String { .. } => Type::String,
}; Attribute::Object { attrs, .. } => attrs_typ(attrs),
}
}
}
(name.clone(), attr_type) fn attrs_typ(attrs: &HashMap<String, Attribute>) -> Type {
}) let attrs = attrs
.iter()
.map(|(name, attr)| (name.clone(), attr.typ()))
.collect(); .collect();
Type::Object { Type::Object {
attrs, attrs,
optionals: vec![], optionals: vec![],
} }
}
} }

View file

@ -1,4 +1,4 @@
use crate::{values::Type, AttrPathSegment, Attribute, Diagnostics, Mode, Schema, Value}; use crate::{AttrPathSegment, Attribute, Diagnostics, Mode, Schema, Value};
use super::grpc::tfplugin6; use super::grpc::tfplugin6;
@ -43,13 +43,14 @@ impl Attribute {
attr.computed = mode.computed(); attr.computed = mode.computed();
}; };
attr.r#type = self.typ().to_json().into_bytes();
match self { match self {
Attribute::String { Attribute::String {
description, description,
mode, mode,
sensitive, sensitive,
} => { } => {
attr.r#type = Type::String.to_json().into_bytes();
attr.description = description; attr.description = description;
set_modes(&mut attr, mode); set_modes(&mut attr, mode);
attr.sensitive = sensitive; attr.sensitive = sensitive;
@ -59,7 +60,16 @@ impl Attribute {
mode, mode,
sensitive, sensitive,
} => { } => {
attr.r#type = Type::Number.to_json().into_bytes(); attr.description = description;
set_modes(&mut attr, mode);
attr.sensitive = sensitive;
}
Attribute::Object {
description,
mode,
sensitive,
attrs: _,
} => {
attr.description = description; attr.description = description;
set_modes(&mut attr, mode); set_modes(&mut attr, mode);
attr.sensitive = sensitive; attr.sensitive = sensitive;