start parsing values incorrectly

This commit is contained in:
nora 2024-04-14 21:37:29 +02:00
parent c4e62f9d2d
commit a7822229f3
8 changed files with 337 additions and 35 deletions

19
Cargo.lock generated
View file

@ -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",

View file

@ -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"] }

View file

@ -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())),
),
]))))
} }
} }

View file

@ -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![],
}
}
}

View file

@ -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))
}
}

View file

@ -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 {

View file

@ -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;

View file

@ -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))
} }
} }