mirror of
https://github.com/Noratrieb/terustform.git
synced 2026-01-14 16:35:11 +01:00
start parsing values incorrectly
This commit is contained in:
parent
c4e62f9d2d
commit
a7822229f3
8 changed files with 337 additions and 35 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
|
@ -950,6 +950,12 @@ version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
|
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|
@ -976,6 +982,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.115"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
@ -1070,6 +1087,8 @@ dependencies = [
|
||||||
"rcgen",
|
"rcgen",
|
||||||
"rmp",
|
"rmp",
|
||||||
"rustls 0.23.4",
|
"rustls 0.23.4",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ prost = "0.12.4"
|
||||||
rcgen = "0.13.1"
|
rcgen = "0.13.1"
|
||||||
rmp = "0.8.12"
|
rmp = "0.8.12"
|
||||||
rustls = { version = "0.23.4", default-features = false, features = ["ring", "logging", "std", "tls12"] }
|
rustls = { version = "0.23.4", default-features = false, features = ["ring", "logging", "std", "tls12"] }
|
||||||
|
serde = "1.0.197"
|
||||||
|
serde_json = "1.0.115"
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
time = "0.3.35"
|
time = "0.3.35"
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
provider::Provider,
|
provider::Provider,
|
||||||
DResult,
|
DResult,
|
||||||
},
|
},
|
||||||
values::Value,
|
values::{Value, ValueKind},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ExampleProvider {}
|
pub struct ExampleProvider {}
|
||||||
|
|
@ -61,16 +61,22 @@ impl DataSource for ExampleDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(&self, config: Value) -> DResult<Value> {
|
fn read(&self, config: Value) -> DResult<Value> {
|
||||||
Ok(Value::Object(BTreeMap::from([
|
Ok(Value::Known(ValueKind::Object(BTreeMap::from([
|
||||||
(
|
(
|
||||||
"name".to_owned(),
|
"name".to_owned(),
|
||||||
match config {
|
match config {
|
||||||
Value::Object(mut obj) => obj.remove("name").unwrap(),
|
Value::Known(ValueKind::Object(mut obj)) => obj.remove("name").unwrap(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("meow".to_owned(), Value::String("mrrrrr".to_owned())),
|
(
|
||||||
("id".to_owned(), Value::String("0".to_owned())),
|
"meow".to_owned(),
|
||||||
])))
|
Value::Known(ValueKind::String("mrrrrr".to_owned())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"id".to_owned(),
|
||||||
|
Value::Known(ValueKind::String("0".to_owned())),
|
||||||
|
),
|
||||||
|
]))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::values::Value;
|
use crate::values::{Type, Value};
|
||||||
|
|
||||||
use super::DResult;
|
use super::DResult;
|
||||||
|
|
||||||
|
|
@ -57,3 +57,25 @@ impl Mode {
|
||||||
matches!(self, Self::OptionalComputed | Self::Computed)
|
matches!(self, Self::OptionalComputed | Self::Computed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Schema {
|
||||||
|
pub fn typ(&self) -> Type {
|
||||||
|
let attrs = self
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(|(name, attr)| {
|
||||||
|
let attr_type = match attr {
|
||||||
|
Attribute::Int64 { .. } => Type::Number,
|
||||||
|
Attribute::String { .. } => Type::String,
|
||||||
|
};
|
||||||
|
|
||||||
|
(name.clone(), attr_type)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Type::Object {
|
||||||
|
attrs,
|
||||||
|
optionals: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,17 @@ pub struct Diagnostics {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type DResult<T> = Result<T, Diagnostics>;
|
pub type DResult<T> = Result<T, Diagnostics>;
|
||||||
|
|
||||||
|
impl Diagnostics {
|
||||||
|
pub fn error_string(msg: String) -> Self {
|
||||||
|
Self {
|
||||||
|
errors: vec![msg],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: std::error::Error + std::fmt::Debug> From<E> for Diagnostics {
|
||||||
|
fn from(value: E) -> Self {
|
||||||
|
Self::error_string(format!("{:?}", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -237,10 +237,32 @@ impl Provider for super::ProviderHandler {
|
||||||
.get(&request.get_ref().type_name)
|
.get(&request.get_ref().type_name)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let state = ds.read(crate::values::Value::Object(BTreeMap::from([(
|
let typ = ds.schema().typ();
|
||||||
|
let config = match &request.get_ref().config {
|
||||||
|
None => crate::values::Value::Null,
|
||||||
|
Some(v) => {
|
||||||
|
let value = crate::values::Value::msg_unpack(&v.msgpack, &typ);
|
||||||
|
match value {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(errs) => {
|
||||||
|
return Ok(Response::new(tfplugin6::read_data_source::Response {
|
||||||
|
deferred: None,
|
||||||
|
state: None,
|
||||||
|
diagnostics: errs.to_tfplugin_diags(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = ds.read(crate::values::Value::Known(
|
||||||
|
crate::values::ValueKind::Object(BTreeMap::from([(
|
||||||
"name".to_owned(),
|
"name".to_owned(),
|
||||||
crate::values::Value::String("mykitten".to_owned()),
|
crate::values::Value::Known(crate::values::ValueKind::String(
|
||||||
)])));
|
"mykitten".to_owned(),
|
||||||
|
)),
|
||||||
|
)])),
|
||||||
|
));
|
||||||
let (state, diagnostics) = match state {
|
let (state, diagnostics) = match state {
|
||||||
Ok(s) => (
|
Ok(s) => (
|
||||||
Some(tfplugin6::DynamicValue {
|
Some(tfplugin6::DynamicValue {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::framework::datasource::{self, DataSource};
|
use crate::framework::datasource::DataSource;
|
||||||
use crate::framework::provider::Provider;
|
use crate::framework::provider::Provider;
|
||||||
use crate::framework::DResult;
|
|
||||||
|
|
||||||
pub use grpc::plugin::grpc_controller_server::GrpcControllerServer;
|
pub use grpc::plugin::grpc_controller_server::GrpcControllerServer;
|
||||||
pub use grpc::tfplugin6::provider_server::ProviderServer;
|
pub use grpc::tfplugin6::provider_server::ProviderServer;
|
||||||
|
|
|
||||||
258
src/values.rs
258
src/values.rs
|
|
@ -1,32 +1,102 @@
|
||||||
// check terraform-plugin-go tfprotov6/internal/toproto for convesions
|
// check terraform-plugin-go tfprotov6/internal/toproto for convesions
|
||||||
// tftypes for types and values
|
// tftypes for types and values
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
|
io::{self, Read},
|
||||||
|
};
|
||||||
|
|
||||||
|
use rmp::encode::write_bool;
|
||||||
|
|
||||||
|
use crate::framework::{DResult, Diagnostics};
|
||||||
|
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
Bool,
|
||||||
Number,
|
Number,
|
||||||
String,
|
String,
|
||||||
|
Dynamic,
|
||||||
|
/// A list of elements of the same type.
|
||||||
|
List {
|
||||||
|
elem: Box<Type>,
|
||||||
|
},
|
||||||
|
/// A bunch of unordered string-value pair of the same type.
|
||||||
|
Map {
|
||||||
|
elem: Box<Type>,
|
||||||
|
},
|
||||||
|
/// A set of unique values of the same type.
|
||||||
|
Set {
|
||||||
|
elem: Box<Type>,
|
||||||
|
},
|
||||||
|
/// A bunch of unordered string-value pairs of different types.
|
||||||
|
/// The attributes are statically known.
|
||||||
|
Object {
|
||||||
|
attrs: HashMap<String, Type>,
|
||||||
|
/// The attributes in `attrs` that are optional.
|
||||||
|
/// Always empty for now because of JSON reasons.
|
||||||
|
optionals: Vec<String>,
|
||||||
|
},
|
||||||
|
/// An ordered list of values of different types.
|
||||||
|
Tuple {
|
||||||
|
elems: Vec<Type>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
pub fn to_json(&self) -> String {
|
pub fn to_json(&self) -> String {
|
||||||
match *self {
|
let value = self.to_json_inner();
|
||||||
Self::String => "\"string\"".to_owned(),
|
serde_json::to_string(&value).unwrap()
|
||||||
Self::Number => "\"number\"".to_owned(),
|
}
|
||||||
|
pub fn to_json_inner(&self) -> serde_json::Value {
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
let compound =
|
||||||
|
|tag: &str, inner: Value| Value::Array(vec![Value::String(tag.to_owned()), inner]);
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Bool => Value::String("bool".to_owned()),
|
||||||
|
Self::String => Value::String("string".to_owned()),
|
||||||
|
Self::Number => Value::String("number".to_owned()),
|
||||||
|
Self::Dynamic => Value::String("dynamic".to_owned()),
|
||||||
|
Self::List { elem } => compound("list", elem.to_json_inner()),
|
||||||
|
Self::Map { elem } => compound("map", elem.to_json_inner()),
|
||||||
|
Self::Set { elem } => compound("set", elem.to_json_inner()),
|
||||||
|
Self::Object {
|
||||||
|
attrs,
|
||||||
|
optionals: _,
|
||||||
|
} => compound(
|
||||||
|
"object",
|
||||||
|
Value::Object(
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (k.clone(), v.to_json_inner()))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Self::Tuple { elems } => compound(
|
||||||
|
"tuple",
|
||||||
|
elems.iter().map(|elem| elem.to_json_inner()).collect(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is very dumb and wrong
|
#[derive(Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
String(String),
|
Known(ValueKind),
|
||||||
Object(BTreeMap<String, Value>),
|
Unknown,
|
||||||
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
#[derive(Debug)]
|
||||||
pub fn ty(&self) -> Type {
|
pub enum ValueKind {
|
||||||
todo!()
|
String(String),
|
||||||
}
|
Number(f64),
|
||||||
|
Bool(bool),
|
||||||
|
List(Vec<Value>),
|
||||||
|
Set(Vec<Value>),
|
||||||
|
Map(BTreeMap<String, Value>),
|
||||||
|
Tuple(Vec<Value>),
|
||||||
|
Object(BTreeMap<String, Value>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshal msg pack
|
// marshal msg pack
|
||||||
|
|
@ -35,22 +105,170 @@ impl Value {
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn msg_pack(&self) -> Vec<u8> {
|
pub fn msg_pack(&self) -> Vec<u8> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
self.msg_pack_inner(&mut buf);
|
self.msg_pack_inner(&mut buf)
|
||||||
|
.expect("writing to Vec<u8> cannot fail");
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn msg_pack_inner(&self, v: &mut Vec<u8>) {
|
pub fn msg_pack_inner(&self, wr: &mut Vec<u8>) -> std::io::Result<()> {
|
||||||
match self {
|
use rmp::encode as mp;
|
||||||
Value::String(s) => {
|
|
||||||
rmp::encode::write_str(v, s).unwrap();
|
let known = match self {
|
||||||
|
Value::Unknown => {
|
||||||
|
wr.extend_from_slice(&[0xd4, 0, 0]);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
Value::Object(o) => {
|
Value::Null => {
|
||||||
rmp::encode::write_map_len(v, o.len().try_into().unwrap()).unwrap();
|
mp::write_nil(wr)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Value::Known(known) => known,
|
||||||
|
};
|
||||||
|
|
||||||
|
match known {
|
||||||
|
ValueKind::String(s) => {
|
||||||
|
mp::write_str(wr, s)?;
|
||||||
|
}
|
||||||
|
&ValueKind::Number(n) => {
|
||||||
|
if n.is_infinite() {
|
||||||
|
if n.signum() == -1.0 {
|
||||||
|
mp::write_f64(wr, f64::NEG_INFINITY)?;
|
||||||
|
} else {
|
||||||
|
mp::write_f64(wr, f64::INFINITY)?;
|
||||||
|
}
|
||||||
|
} else if (n as i64 as f64) == n {
|
||||||
|
// is int
|
||||||
|
mp::write_i64(wr, n as i64)?;
|
||||||
|
} else {
|
||||||
|
mp::write_f64(wr, n)?;
|
||||||
|
}
|
||||||
|
// Terraform handles bigfloats but we do emphatically not care
|
||||||
|
}
|
||||||
|
ValueKind::Bool(b) => {
|
||||||
|
mp::write_bool(wr, *b)?;
|
||||||
|
}
|
||||||
|
ValueKind::List(elems) | ValueKind::Set(elems) | ValueKind::Tuple(elems) => {
|
||||||
|
mp::write_array_len(wr, elems.len().try_into().unwrap())?;
|
||||||
|
for elem in elems {
|
||||||
|
elem.msg_pack_inner(wr)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueKind::Map(o) | ValueKind::Object(o) => {
|
||||||
|
mp::write_map_len(wr, o.len().try_into().unwrap())?;
|
||||||
for (key, val) in o {
|
for (key, val) in o {
|
||||||
rmp::encode::write_str(v, key).unwrap();
|
mp::write_str(wr, key)?;
|
||||||
val.msg_pack_inner(v);
|
val.msg_pack_inner(wr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn msg_unpack(data: &[u8], typ: &Type) -> DResult<Self> {
|
||||||
|
let mut read = io::Cursor::new(data);
|
||||||
|
Self::msg_unpack_inner(&mut read, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msg_unpack_inner(rd: &mut io::Cursor<&[u8]>, typ: &Type) -> DResult<Self> {
|
||||||
|
use rmp::decode as mp;
|
||||||
|
|
||||||
|
if let Ok(()) = mp::read_nil(rd) {
|
||||||
|
return Ok(Value::Null);
|
||||||
|
}
|
||||||
|
rd.set_position(rd.position() - 1); // revert past the nil
|
||||||
|
|
||||||
|
let read_string = |rd: &mut io::Cursor<&[u8]>| -> DResult<String> {
|
||||||
|
let len = std::cmp::min(mp::read_str_len(rd)?, 1024 * 1024); // you're not gonna get more than a 1MB string...
|
||||||
|
let mut buf = Vec::with_capacity(len as usize);
|
||||||
|
rd.read_exact(&mut buf)?;
|
||||||
|
Ok(String::from_utf8(buf)?)
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = match typ {
|
||||||
|
Type::Bool => {
|
||||||
|
let b = mp::read_bool(rd)?;
|
||||||
|
ValueKind::Bool(b)
|
||||||
|
}
|
||||||
|
Type::Number => {
|
||||||
|
let prev = rd.position();
|
||||||
|
if let Ok(int) = mp::read_int::<i64, _>(rd) {
|
||||||
|
ValueKind::Number(int as f64)
|
||||||
|
} else {
|
||||||
|
rd.set_position(prev);
|
||||||
|
if let Ok(f32) = mp::read_f32(rd) {
|
||||||
|
ValueKind::Number(f32 as f64)
|
||||||
|
} else {
|
||||||
|
rd.set_position(prev);
|
||||||
|
let f64 = mp::read_f64(rd)?;
|
||||||
|
ValueKind::Number(f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::String => ValueKind::String(read_string(rd)?),
|
||||||
|
Type::Dynamic => todo!("dynamic"),
|
||||||
|
Type::List { elem } => {
|
||||||
|
let len = mp::read_array_len(rd)?;
|
||||||
|
let elems = (0..len)
|
||||||
|
.map(|_| Value::msg_unpack_inner(rd, &elem))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ValueKind::List(elems)
|
||||||
|
}
|
||||||
|
Type::Map { elem } => {
|
||||||
|
let len = mp::read_array_len(rd)?;
|
||||||
|
let elems = (0..len)
|
||||||
|
.map(|_| -> DResult<_> {
|
||||||
|
let key = read_string(rd)?;
|
||||||
|
let value = Value::msg_unpack_inner(rd, &elem)?;
|
||||||
|
Ok((key, value))
|
||||||
|
})
|
||||||
|
.collect::<DResult<BTreeMap<_, _>>>()?;
|
||||||
|
ValueKind::Map(elems)
|
||||||
|
}
|
||||||
|
Type::Set { elem } => {
|
||||||
|
let len = mp::read_array_len(rd)?;
|
||||||
|
let elems = (0..len)
|
||||||
|
.map(|_| Value::msg_unpack_inner(rd, &elem))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ValueKind::Set(elems)
|
||||||
|
}
|
||||||
|
Type::Object { attrs, optionals } => {
|
||||||
|
assert!(optionals.is_empty());
|
||||||
|
let len = mp::read_array_len(rd)?;
|
||||||
|
if attrs.len() != (len as usize) {
|
||||||
|
return Err(Diagnostics::error_string(format!(
|
||||||
|
"expected {} attrs, found {len} attrs in object",
|
||||||
|
attrs.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let elems = (0..len)
|
||||||
|
.map(|_| -> DResult<_> {
|
||||||
|
let key = read_string(rd)?;
|
||||||
|
let typ = attrs.get(&key).ok_or_else(|| {
|
||||||
|
Diagnostics::error_string(format!("unexpected attribute: {key}"))
|
||||||
|
})?;
|
||||||
|
let value = Value::msg_unpack_inner(rd, &typ)?;
|
||||||
|
Ok((key, value))
|
||||||
|
})
|
||||||
|
.collect::<DResult<BTreeMap<_, _>>>()?;
|
||||||
|
ValueKind::Object(elems)
|
||||||
|
}
|
||||||
|
Type::Tuple { elems } => {
|
||||||
|
let len = mp::read_array_len(rd)?;
|
||||||
|
if elems.len() != (len as usize) {
|
||||||
|
return Err(Diagnostics::error_string(format!(
|
||||||
|
"expected {} elems, found {len} elems in tuple",
|
||||||
|
elems.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let elems = elems
|
||||||
|
.iter()
|
||||||
|
.map(|typ| Value::msg_unpack_inner(rd, &typ))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ValueKind::List(elems)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Value::Known(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue