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"
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -976,6 +982,17 @@ dependencies = [
"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]]
name = "sharded-slab"
version = "0.1.7"
@ -1070,6 +1087,8 @@ dependencies = [
"rcgen",
"rmp",
"rustls 0.23.4",
"serde",
"serde_json",
"tempfile",
"time",
"tokio",

View file

@ -16,6 +16,8 @@ prost = "0.12.4"
rcgen = "0.13.1"
rmp = "0.8.12"
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"
time = "0.3.35"
tokio = { version = "1.37.0", features = ["full"] }

View file

@ -6,7 +6,7 @@ use crate::{
provider::Provider,
DResult,
},
values::Value,
values::{Value, ValueKind},
};
pub struct ExampleProvider {}
@ -61,16 +61,22 @@ impl DataSource for ExampleDataSource {
}
fn read(&self, config: Value) -> DResult<Value> {
Ok(Value::Object(BTreeMap::from([
Ok(Value::Known(ValueKind::Object(BTreeMap::from([
(
"name".to_owned(),
match config {
Value::Object(mut obj) => obj.remove("name").unwrap(),
Value::Known(ValueKind::Object(mut obj)) => obj.remove("name").unwrap(),
_ => 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 crate::values::Value;
use crate::values::{Type, Value};
use super::DResult;
@ -57,3 +57,25 @@ impl Mode {
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>;
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)
.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(),
crate::values::Value::String("mykitten".to_owned()),
)])));
crate::values::Value::Known(crate::values::ValueKind::String(
"mykitten".to_owned(),
)),
)])),
));
let (state, diagnostics) = match state {
Ok(s) => (
Some(tfplugin6::DynamicValue {

View file

@ -5,9 +5,8 @@ use std::collections::HashMap;
use tokio_util::sync::CancellationToken;
use crate::framework::datasource::{self, DataSource};
use crate::framework::datasource::DataSource;
use crate::framework::provider::Provider;
use crate::framework::DResult;
pub use grpc::plugin::grpc_controller_server::GrpcControllerServer;
pub use grpc::tfplugin6::provider_server::ProviderServer;

View file

@ -1,32 +1,102 @@
// check terraform-plugin-go tfprotov6/internal/toproto for convesions
// 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 {
Bool,
Number,
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 {
pub fn to_json(&self) -> String {
match *self {
Self::String => "\"string\"".to_owned(),
Self::Number => "\"number\"".to_owned(),
let value = self.to_json_inner();
serde_json::to_string(&value).unwrap()
}
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 {
String(String),
Object(BTreeMap<String, Value>),
Known(ValueKind),
Unknown,
Null,
}
impl Value {
pub fn ty(&self) -> Type {
todo!()
}
#[derive(Debug)]
pub enum ValueKind {
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
@ -35,22 +105,170 @@ impl Value {
impl Value {
pub fn msg_pack(&self) -> Vec<u8> {
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
}
pub fn msg_pack_inner(&self, v: &mut Vec<u8>) {
match self {
Value::String(s) => {
rmp::encode::write_str(v, s).unwrap();
pub fn msg_pack_inner(&self, wr: &mut Vec<u8>) -> std::io::Result<()> {
use rmp::encode as mp;
let known = match self {
Value::Unknown => {
wr.extend_from_slice(&[0xd4, 0, 0]);
return Ok(());
}
Value::Object(o) => {
rmp::encode::write_map_len(v, o.len().try_into().unwrap()).unwrap();
Value::Null => {
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 {
rmp::encode::write_str(v, key).unwrap();
val.msg_pack_inner(v);
mp::write_str(wr, key)?;
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))
}
}