From bf7bd330b398f1464e31cd5cd5635c8733713b75 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:20:19 +0200 Subject: [PATCH] Improvements --- Cargo.lock | 34 ++++++++- terraform-provider-corsschool/src/main.rs | 6 +- .../src/resources/class_resource.rs | 73 ++++++++++++++++++- terraform-provider-corsschool/test/main.tf | 4 + terustform/Cargo.toml | 2 +- terustform/src/datasource.rs | 31 ++++---- terustform/src/lib.rs | 5 ++ terustform/src/provider.rs | 44 +++++------ terustform/src/resource.rs | 54 +++++++++----- terustform/src/server/handler.rs | 8 +- 10 files changed, 189 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aed4add..c6a49a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,6 +650,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -990,8 +999,17 @@ checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1002,9 +1020,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.3", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -1590,10 +1614,14 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] diff --git a/terraform-provider-corsschool/src/main.rs b/terraform-provider-corsschool/src/main.rs index 08cf2e7..aef20eb 100644 --- a/terraform-provider-corsschool/src/main.rs +++ b/terraform-provider-corsschool/src/main.rs @@ -4,7 +4,9 @@ mod resources; use std::collections::HashMap; use eyre::Context; -use terustform::{datasource::DataSource, provider::Provider, DResult, EyreExt, Schema, Value}; +use terustform::{ + datasource::DataSource, provider::Provider, resource::Resource, DResult, EyreExt, Schema, Value, +}; #[tokio::main] async fn main() -> eyre::Result<()> { @@ -49,6 +51,6 @@ impl Provider for ExampleProvider { } fn resources(&self) -> terustform::provider::Resources { - vec![] + vec![resources::class_resource::ClassResource::erase()] } } diff --git a/terraform-provider-corsschool/src/resources/class_resource.rs b/terraform-provider-corsschool/src/resources/class_resource.rs index 50b646b..902c7a3 100644 --- a/terraform-provider-corsschool/src/resources/class_resource.rs +++ b/terraform-provider-corsschool/src/resources/class_resource.rs @@ -1,7 +1,78 @@ -use terustform::{datasource::DataSource, DResult, Value}; +use std::collections::HashMap; + +use terustform::{resource::Resource, Attribute, DResult, Mode, Schema, Value}; use crate::client::CorsClient; pub struct ClassResource { client: CorsClient, } + +impl Resource for ClassResource { + type ProviderData = CorsClient; + + async fn read(&self, config: Value) -> DResult { + todo!() + } + + async fn create(&self, config: Value) -> DResult { + todo!() + } + + async fn update(&self, config: Value) -> DResult { + todo!() + } + + async fn delete(&self, state: Value) -> DResult { + todo!() + } + + fn name(provider_name: &str) -> String { + format!("{provider_name}_class") + } + + fn schema() -> terustform::Schema { + Schema { + description: "A class".into(), + attributes: HashMap::from([ + ( + "id".to_owned(), + // TODO: UUID validation :3 + Attribute::String { + description: "The UUID".to_owned(), + mode: Mode::Computed, + sensitive: false, + }, + ), + ( + "name".to_owned(), + Attribute::String { + description: "The description".to_owned(), + mode: Mode::Required, + sensitive: false, + }, + ), + ( + "description".to_owned(), + Attribute::String { + description: "The description".to_owned(), + mode: Mode::Optional, + sensitive: false, + }, + ), + ( + "discord_id".to_owned(), + Attribute::String { + description: "The discord ID of the class".to_owned(), + mode: Mode::Optional, + sensitive: false, + }, + ), + ]), + } + } + + fn new(client: Self::ProviderData) -> DResult { + Ok(Self { client }) + } +} diff --git a/terraform-provider-corsschool/test/main.tf b/terraform-provider-corsschool/test/main.tf index 1377296..c837778 100644 --- a/terraform-provider-corsschool/test/main.tf +++ b/terraform-provider-corsschool/test/main.tf @@ -18,4 +18,8 @@ data "corsschool_class" "test" { } output "class" { value = data.corsschool_class.test +} + +resource "corsschool_class" "myclass" { + name = "meow" } \ No newline at end of file diff --git a/terustform/Cargo.toml b/terustform/Cargo.toml index 427263e..a3cdd18 100644 --- a/terustform/Cargo.toml +++ b/terustform/Cargo.toml @@ -21,7 +21,7 @@ tokio-stream = { version = "0.1.15", features = ["net"] } tokio-util = "0.7.10" tonic = { version = "0.11.0", features = ["tls"] } tracing = "0.1.40" -tracing-subscriber = "0.3.18" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } async-trait = "0.1.80" [build-dependencies] diff --git a/terustform/src/datasource.rs b/terustform/src/datasource.rs index c217a37..af3ca55 100644 --- a/terustform/src/datasource.rs +++ b/terustform/src/datasource.rs @@ -1,33 +1,34 @@ use std::future::Future; use crate::{ - provider::{MkDataSource, ProviderData}, + provider::{BoxFut, MkDataSource, ProviderData}, values::Value, Schema, }; use super::DResult; -pub trait DataSource: Send + Sync + 'static { +pub trait DataSource: Sized + Send + Sync + 'static { type ProviderData: ProviderData; // todo: probably want some kind of Value+Schema thing like tfsdk? whatever. fn read(&self, config: Value) -> impl Future> + Send + Sync; - fn name(provider_name: &str) -> String - where - Self: Sized; - fn schema() -> Schema - where - Self: Sized; - fn new(data: Self::ProviderData) -> DResult - where - Self: Sized; + fn name(provider_name: &str) -> String; + fn schema() -> Schema; + fn new(data: Self::ProviderData) -> DResult; - fn erase() -> MkDataSource - where - Self: Sized, - { + fn erase() -> MkDataSource { MkDataSource::create::() } } + +pub(crate) trait DynDataSource: Send + Sync + 'static { + fn read(&self, config: Value) -> BoxFut<'_, DResult>; +} + +impl DynDataSource for Ds { + fn read(&self, config: Value) -> BoxFut<'_, DResult> { + Box::pin(DataSource::read(self, config)) + } +} diff --git a/terustform/src/lib.rs b/terustform/src/lib.rs index 116d476..fba78b1 100644 --- a/terustform/src/lib.rs +++ b/terustform/src/lib.rs @@ -18,7 +18,9 @@ pub use values::*; pub use terustform_macros::Model; +pub use async_trait::async_trait; pub use eyre; +use tracing_subscriber::EnvFilter; // -------- // Rest of the file. @@ -29,6 +31,9 @@ use tracing::Level; pub async fn start(provider: P) -> eyre::Result<()> { tracing_subscriber::fmt() .with_max_level(Level::DEBUG) + .with_env_filter(EnvFilter::builder().parse_lossy( + std::env::var("RUST_LOG").unwrap_or_else(|_| "h2=info,rustls=info,debug".into()), + )) .with_writer(std::io::stderr) .without_time() .init(); diff --git a/terustform/src/provider.rs b/terustform/src/provider.rs index 1253305..8d9d09e 100644 --- a/terustform/src/provider.rs +++ b/terustform/src/provider.rs @@ -1,6 +1,10 @@ -use std::{any::Any, future::Future, marker::PhantomData, pin::Pin, sync::Arc}; +use std::{future::Future, pin::Pin, sync::Arc}; -use crate::{datasource::DataSource, resource::Resource, DResult, Schema, Value}; +use crate::{ + datasource::{DataSource, DynDataSource}, + resource::{DynResource, Resource}, + DResult, Schema, Value, +}; // This setup is a bit complicated. // In this explanation, substitute "`Resource`" for "`Resource` or `DataSource`". @@ -14,29 +18,24 @@ use crate::{datasource::DataSource, resource::Resource, DResult, Schema, Value}; pub trait ProviderData: Clone + Send + Sync + 'static {} impl ProviderData for D {} +pub(super) type BoxFut<'a, O> = Pin + Send + Sync + 'a>>; + pub struct MkDataSource { pub(crate) name: fn(&str) -> String, pub(crate) schema: Schema, - pub(crate) mk: fn(D) -> DResult>, + pub(crate) mk: fn(D) -> DResult, } -pub(crate) struct StoredDataSource { - pub(crate) object: Arc, - pub(crate) read: fn( - s: &(dyn Any + Send + Sync), - config: Value, - ) -> Pin> + Send + Sync + '_>>, +pub(crate) struct StoredDataSource { + pub(crate) ds: Arc, pub(crate) schema: Schema, - p: PhantomData, } -impl Clone for StoredDataSource { +impl Clone for StoredDataSource { fn clone(&self) -> Self { Self { - object: self.object.clone(), - read: self.read, + ds: self.ds.clone(), schema: self.schema.clone(), - p: PhantomData, } } } @@ -48,15 +47,8 @@ impl MkDataSource { schema: Ds::schema(), mk: |data| { Ok(StoredDataSource { - object: Arc::new(Ds::new(data)?), - read: |s, config| { - Box::pin(async { - let ds = s.downcast_ref::().unwrap(); - ds.read(config).await - }) - }, + ds: Arc::new(Ds::new(data)?), schema: Ds::schema(), - p: PhantomData, }) }, } @@ -66,15 +58,15 @@ impl MkDataSource { pub struct MkResource { pub(crate) name: fn(&str) -> String, pub(crate) schema: Schema, - pub(crate) mk: fn(D) -> DResult>, + pub(crate) mk: fn(D) -> DResult, } -pub(crate) struct StoredResource { - pub(crate) ds: Arc>, +pub(crate) struct StoredResource { + pub(crate) ds: Arc, pub(crate) schema: Schema, } -impl Clone for StoredResource { +impl Clone for StoredResource { fn clone(&self) -> Self { Self { ds: self.ds.clone(), diff --git a/terustform/src/resource.rs b/terustform/src/resource.rs index 84ff154..6439a3e 100644 --- a/terustform/src/resource.rs +++ b/terustform/src/resource.rs @@ -1,35 +1,49 @@ +use std::future::Future; + use crate::{ - provider::{MkResource, ProviderData}, + provider::{BoxFut, MkResource, ProviderData}, values::Value, Schema, }; use super::DResult; -#[crate::async_trait] -pub trait Resource: Send + Sync + 'static { +pub trait Resource: Sized + Send + Sync + 'static { type ProviderData: ProviderData; // todo: probably want some kind of Value+Schema thing like tfsdk? whatever. - async fn read(&self, config: Value) -> DResult; - async fn create(&self, config: Value) -> DResult; - async fn update(&self, config: Value) -> DResult; - async fn delete(&self, config: Value) -> DResult; + fn read(&self, config: Value) -> impl Future> + Send + Sync; + fn create(&self, config: Value) -> impl Future> + Send + Sync; + fn update(&self, config: Value) -> impl Future> + Send + Sync; + fn delete(&self, state: Value) -> impl Future> + Send + Sync; - fn name(provider_name: &str) -> String - where - Self: Sized; - fn schema() -> Schema - where - Self: Sized; - fn new(data: Self::ProviderData) -> DResult - where - Self: Sized; + fn name(provider_name: &str) -> String; + fn schema() -> Schema; + fn new(data: Self::ProviderData) -> DResult; - fn erase() -> MkResource - where - Self: Sized, - { + fn erase() -> MkResource { MkResource::create::() } } + +pub(crate) trait DynResource: Send + Sync + 'static { + fn read(&self, config: Value) -> BoxFut<'_, DResult>; + fn create(&self, config: Value) -> BoxFut<'_, DResult>; + fn update(&self, config: Value) -> BoxFut<'_, DResult>; + fn delete(&self, config: Value) -> BoxFut<'_, DResult>; +} + +impl DynResource for R { + fn read(&self, config: Value) -> BoxFut<'_, DResult> { + Box::pin(Resource::read(self, config)) + } + fn create(&self, config: Value) -> BoxFut<'_, DResult> { + Box::pin(Resource::create(self, config)) + } + fn update(&self, config: Value) -> BoxFut<'_, DResult> { + Box::pin(Resource::update(self, config)) + } + fn delete(&self, state: Value) -> BoxFut<'_, DResult> { + Box::pin(Resource::create(self, state)) + } +} diff --git a/terustform/src/server/handler.rs b/terustform/src/server/handler.rs index 2893adb..78fb316 100644 --- a/terustform/src/server/handler.rs +++ b/terustform/src/server/handler.rs @@ -26,8 +26,8 @@ enum ProviderState { diags: Diagnostics, }, Configured { - data_sources: HashMap>, - resources: HashMap>, + data_sources: HashMap, + resources: HashMap, }, } @@ -180,7 +180,7 @@ impl ProviderHandler

{ type_name: &str, config: &Option, ) -> (Option, Vec) { - let ds: StoredDataSource = { + let ds: StoredDataSource = { let state = self.state.lock().await; match &*state { ProviderState::Setup { .. } => { @@ -205,7 +205,7 @@ impl ProviderHandler

{ } }; - let state = (ds.read)(&*ds.object, config).await; + let state = ds.ds.read(config).await; let (state, diagnostics) = match state { Ok(s) => ( Some(tfplugin6::DynamicValue {