Improvements

This commit is contained in:
nora 2024-04-29 20:20:19 +02:00
parent edf7b7dac3
commit bf7bd330b3
10 changed files with 189 additions and 72 deletions

34
Cargo.lock generated
View file

@ -650,6 +650,15 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 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]] [[package]]
name = "matchit" name = "matchit"
version = "0.7.3" version = "0.7.3"
@ -990,8 +999,17 @@ checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata", "regex-automata 0.4.6",
"regex-syntax", "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]] [[package]]
@ -1002,9 +1020,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "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]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.3" version = "0.8.3"
@ -1590,10 +1614,14 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [ dependencies = [
"matchers",
"nu-ansi-term", "nu-ansi-term",
"once_cell",
"regex",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
"tracing",
"tracing-core", "tracing-core",
"tracing-log", "tracing-log",
] ]

View file

@ -4,7 +4,9 @@ mod resources;
use std::collections::HashMap; use std::collections::HashMap;
use eyre::Context; 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] #[tokio::main]
async fn main() -> eyre::Result<()> { async fn main() -> eyre::Result<()> {
@ -49,6 +51,6 @@ impl Provider for ExampleProvider {
} }
fn resources(&self) -> terustform::provider::Resources<Self> { fn resources(&self) -> terustform::provider::Resources<Self> {
vec![] vec![resources::class_resource::ClassResource::erase()]
} }
} }

View file

@ -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; use crate::client::CorsClient;
pub struct ClassResource { pub struct ClassResource {
client: CorsClient, client: CorsClient,
} }
impl Resource for ClassResource {
type ProviderData = CorsClient;
async fn read(&self, config: Value) -> DResult<Value> {
todo!()
}
async fn create(&self, config: Value) -> DResult<Value> {
todo!()
}
async fn update(&self, config: Value) -> DResult<Value> {
todo!()
}
async fn delete(&self, state: Value) -> DResult<Value> {
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<Self> {
Ok(Self { client })
}
}

View file

@ -19,3 +19,7 @@ data "corsschool_class" "test" {
output "class" { output "class" {
value = data.corsschool_class.test value = data.corsschool_class.test
} }
resource "corsschool_class" "myclass" {
name = "meow"
}

View file

@ -21,7 +21,7 @@ tokio-stream = { version = "0.1.15", features = ["net"] }
tokio-util = "0.7.10" tokio-util = "0.7.10"
tonic = { version = "0.11.0", features = ["tls"] } tonic = { version = "0.11.0", features = ["tls"] }
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
async-trait = "0.1.80" async-trait = "0.1.80"
[build-dependencies] [build-dependencies]

View file

@ -1,33 +1,34 @@
use std::future::Future; use std::future::Future;
use crate::{ use crate::{
provider::{MkDataSource, ProviderData}, provider::{BoxFut, MkDataSource, ProviderData},
values::Value, values::Value,
Schema, Schema,
}; };
use super::DResult; use super::DResult;
pub trait DataSource: Send + Sync + 'static { pub trait DataSource: Sized + Send + Sync + 'static {
type ProviderData: ProviderData; type ProviderData: ProviderData;
// todo: probably want some kind of Value+Schema thing like tfsdk? whatever. // todo: probably want some kind of Value+Schema thing like tfsdk? whatever.
fn read(&self, config: Value) -> impl Future<Output = DResult<Value>> + Send + Sync; fn read(&self, config: Value) -> impl Future<Output = DResult<Value>> + Send + Sync;
fn name(provider_name: &str) -> String fn name(provider_name: &str) -> String;
where fn schema() -> Schema;
Self: Sized; fn new(data: Self::ProviderData) -> DResult<Self>;
fn schema() -> Schema
where
Self: Sized;
fn new(data: Self::ProviderData) -> DResult<Self>
where
Self: Sized;
fn erase() -> MkDataSource<Self::ProviderData> fn erase() -> MkDataSource<Self::ProviderData> {
where
Self: Sized,
{
MkDataSource::create::<Self>() MkDataSource::create::<Self>()
} }
} }
pub(crate) trait DynDataSource: Send + Sync + 'static {
fn read(&self, config: Value) -> BoxFut<'_, DResult<Value>>;
}
impl<Ds: DataSource> DynDataSource for Ds {
fn read(&self, config: Value) -> BoxFut<'_, DResult<Value>> {
Box::pin(DataSource::read(self, config))
}
}

View file

@ -18,7 +18,9 @@ pub use values::*;
pub use terustform_macros::Model; pub use terustform_macros::Model;
pub use async_trait::async_trait;
pub use eyre; pub use eyre;
use tracing_subscriber::EnvFilter;
// -------- // --------
// Rest of the file. // Rest of the file.
@ -29,6 +31,9 @@ use tracing::Level;
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_max_level(Level::DEBUG) .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) .with_writer(std::io::stderr)
.without_time() .without_time()
.init(); .init();

View file

@ -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. // This setup is a bit complicated.
// In this explanation, substitute "`Resource`" for "`Resource` or `DataSource`". // 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 {} pub trait ProviderData: Clone + Send + Sync + 'static {}
impl<D: Clone + Send + Sync + 'static> ProviderData for D {} impl<D: Clone + Send + Sync + 'static> ProviderData for D {}
pub(super) type BoxFut<'a, O> = Pin<Box<dyn Future<Output = O> + Send + Sync + 'a>>;
pub struct MkDataSource<D: ProviderData> { pub struct MkDataSource<D: ProviderData> {
pub(crate) name: fn(&str) -> String, pub(crate) name: fn(&str) -> String,
pub(crate) schema: Schema, pub(crate) schema: Schema,
pub(crate) mk: fn(D) -> DResult<StoredDataSource<D>>, pub(crate) mk: fn(D) -> DResult<StoredDataSource>,
} }
pub(crate) struct StoredDataSource<D: ProviderData> { pub(crate) struct StoredDataSource {
pub(crate) object: Arc<dyn Any + Send + Sync>, pub(crate) ds: Arc<dyn DynDataSource>,
pub(crate) read: fn(
s: &(dyn Any + Send + Sync),
config: Value,
) -> Pin<Box<dyn Future<Output = DResult<Value>> + Send + Sync + '_>>,
pub(crate) schema: Schema, pub(crate) schema: Schema,
p: PhantomData<D>,
} }
impl<D: ProviderData> Clone for StoredDataSource<D> { impl Clone for StoredDataSource {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
object: self.object.clone(), ds: self.ds.clone(),
read: self.read,
schema: self.schema.clone(), schema: self.schema.clone(),
p: PhantomData,
} }
} }
} }
@ -48,15 +47,8 @@ impl<D: ProviderData> MkDataSource<D> {
schema: Ds::schema(), schema: Ds::schema(),
mk: |data| { mk: |data| {
Ok(StoredDataSource { Ok(StoredDataSource {
object: Arc::new(Ds::new(data)?), ds: Arc::new(Ds::new(data)?),
read: |s, config| {
Box::pin(async {
let ds = s.downcast_ref::<Ds>().unwrap();
ds.read(config).await
})
},
schema: Ds::schema(), schema: Ds::schema(),
p: PhantomData,
}) })
}, },
} }
@ -66,15 +58,15 @@ impl<D: ProviderData> MkDataSource<D> {
pub struct MkResource<D: ProviderData> { pub struct MkResource<D: ProviderData> {
pub(crate) name: fn(&str) -> String, pub(crate) name: fn(&str) -> String,
pub(crate) schema: Schema, pub(crate) schema: Schema,
pub(crate) mk: fn(D) -> DResult<StoredResource<D>>, pub(crate) mk: fn(D) -> DResult<StoredResource>,
} }
pub(crate) struct StoredResource<D: ProviderData> { pub(crate) struct StoredResource {
pub(crate) ds: Arc<dyn Resource<ProviderData = D>>, pub(crate) ds: Arc<dyn DynResource>,
pub(crate) schema: Schema, pub(crate) schema: Schema,
} }
impl<D: ProviderData> Clone for StoredResource<D> { impl Clone for StoredResource {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
ds: self.ds.clone(), ds: self.ds.clone(),

View file

@ -1,35 +1,49 @@
use std::future::Future;
use crate::{ use crate::{
provider::{MkResource, ProviderData}, provider::{BoxFut, MkResource, ProviderData},
values::Value, values::Value,
Schema, Schema,
}; };
use super::DResult; use super::DResult;
#[crate::async_trait] pub trait Resource: Sized + Send + Sync + 'static {
pub trait Resource: Send + Sync + 'static {
type ProviderData: ProviderData; type ProviderData: ProviderData;
// todo: probably want some kind of Value+Schema thing like tfsdk? whatever. // todo: probably want some kind of Value+Schema thing like tfsdk? whatever.
async fn read(&self, config: Value) -> DResult<Value>; fn read(&self, config: Value) -> impl Future<Output = DResult<Value>> + Send + Sync;
async fn create(&self, config: Value) -> DResult<Value>; fn create(&self, config: Value) -> impl Future<Output = DResult<Value>> + Send + Sync;
async fn update(&self, config: Value) -> DResult<Value>; fn update(&self, config: Value) -> impl Future<Output = DResult<Value>> + Send + Sync;
async fn delete(&self, config: Value) -> DResult<Value>; fn delete(&self, state: Value) -> impl Future<Output = DResult<Value>> + Send + Sync;
fn name(provider_name: &str) -> String fn name(provider_name: &str) -> String;
where fn schema() -> Schema;
Self: Sized; fn new(data: Self::ProviderData) -> DResult<Self>;
fn schema() -> Schema
where
Self: Sized;
fn new(data: Self::ProviderData) -> DResult<Self>
where
Self: Sized;
fn erase() -> MkResource<Self::ProviderData> fn erase() -> MkResource<Self::ProviderData> {
where
Self: Sized,
{
MkResource::create::<Self>() MkResource::create::<Self>()
} }
} }
pub(crate) trait DynResource: Send + Sync + 'static {
fn read(&self, config: Value) -> BoxFut<'_, DResult<Value>>;
fn create(&self, config: Value) -> BoxFut<'_, DResult<Value>>;
fn update(&self, config: Value) -> BoxFut<'_, DResult<Value>>;
fn delete(&self, config: Value) -> BoxFut<'_, DResult<Value>>;
}
impl<R: Resource> DynResource for R {
fn read(&self, config: Value) -> BoxFut<'_, DResult<Value>> {
Box::pin(Resource::read(self, config))
}
fn create(&self, config: Value) -> BoxFut<'_, DResult<Value>> {
Box::pin(Resource::create(self, config))
}
fn update(&self, config: Value) -> BoxFut<'_, DResult<Value>> {
Box::pin(Resource::update(self, config))
}
fn delete(&self, state: Value) -> BoxFut<'_, DResult<Value>> {
Box::pin(Resource::create(self, state))
}
}

View file

@ -26,8 +26,8 @@ enum ProviderState<P: Provider> {
diags: Diagnostics, diags: Diagnostics,
}, },
Configured { Configured {
data_sources: HashMap<String, StoredDataSource<P::Data>>, data_sources: HashMap<String, StoredDataSource>,
resources: HashMap<String, StoredResource<P::Data>>, resources: HashMap<String, StoredResource>,
}, },
} }
@ -180,7 +180,7 @@ impl<P: Provider> ProviderHandler<P> {
type_name: &str, type_name: &str,
config: &Option<tfplugin6::DynamicValue>, config: &Option<tfplugin6::DynamicValue>,
) -> (Option<tfplugin6::DynamicValue>, Vec<tfplugin6::Diagnostic>) { ) -> (Option<tfplugin6::DynamicValue>, Vec<tfplugin6::Diagnostic>) {
let ds: StoredDataSource<P::Data> = { let ds: StoredDataSource = {
let state = self.state.lock().await; let state = self.state.lock().await;
match &*state { match &*state {
ProviderState::Setup { .. } => { ProviderState::Setup { .. } => {
@ -205,7 +205,7 @@ impl<P: Provider> ProviderHandler<P> {
} }
}; };
let state = (ds.read)(&*ds.object, config).await; let state = ds.ds.read(config).await;
let (state, diagnostics) = match state { let (state, diagnostics) = match state {
Ok(s) => ( Ok(s) => (
Some(tfplugin6::DynamicValue { Some(tfplugin6::DynamicValue {