mirror of
https://github.com/danbulant/convex-macros
synced 2026-05-19 03:58:31 +00:00
array support
This commit is contained in:
parent
404357fad9
commit
e22fa46dc3
2 changed files with 149 additions and 4 deletions
49
src/model.rs
49
src/model.rs
|
|
@ -3,20 +3,20 @@ use quote::quote;
|
|||
use syn::parse::{Parse, ParseBuffer, ParseStream};
|
||||
use syn::{Error, Ident, Lit, Result, Token};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConvexName {
|
||||
pub path: Vec<String>,
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConvexField {
|
||||
pub name: ConvexName,
|
||||
pub t: ConvexType,
|
||||
}
|
||||
|
||||
// See: https://docs.convex.dev/functions/args-validation
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConvexType {
|
||||
// Core types.
|
||||
Id(String),
|
||||
|
|
@ -33,6 +33,7 @@ pub enum ConvexType {
|
|||
BoolLiteral(bool),
|
||||
IntLiteral(i64),
|
||||
// TODO: Any,
|
||||
Array(Box<ConvexField>),
|
||||
Optional(Box<ConvexField>),
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +59,9 @@ impl ConvexType {
|
|||
| ConvexType::Optional(child) => {
|
||||
child.t.print().map(|ts| quote! { Option<#ts> })
|
||||
},
|
||||
| ConvexType::Array(child) => {
|
||||
child.t.print().map(|ts| quote! { Vec<#ts> })
|
||||
}
|
||||
|
||||
// These depend on field.name to generate a struct name.
|
||||
| ConvexType::Object(_) => None,
|
||||
|
|
@ -288,6 +292,7 @@ impl ConvexField {
|
|||
}
|
||||
});
|
||||
},
|
||||
| ConvexType::Array(_) => panic!("Arrays in unions are not supported yet"),
|
||||
|
||||
| ConvexType::Optional(_) => panic!("Unions may not contain optional branches"),
|
||||
| ConvexType::Union(_) => panic!("Unions may not directly contain other unions, put other types between them"),
|
||||
|
|
@ -345,7 +350,8 @@ impl ConvexField {
|
|||
}
|
||||
})
|
||||
},
|
||||
| _ => {
|
||||
| any => {
|
||||
dbg!(any);
|
||||
panic!(
|
||||
"Internal Error: Should have a complex field like object or union"
|
||||
)
|
||||
|
|
@ -403,6 +409,14 @@ impl ConvexField {
|
|||
rendered_fields.push(quote! {
|
||||
pub #field_name: #field_type,
|
||||
});
|
||||
} else if let ConvexType::Array(child) = &field.t {
|
||||
let struct_name = field.name.to_struct_name();
|
||||
let field_type = quote! { Vec<#struct_name> };
|
||||
let mut child_struct = child.print();
|
||||
structs.append(&mut child_struct);
|
||||
rendered_fields.push(quote! {
|
||||
pub #field_name: #field_type,
|
||||
});
|
||||
} else {
|
||||
let field_type = field.name.to_struct_name();
|
||||
let mut child_struct = field.print();
|
||||
|
|
@ -585,6 +599,28 @@ impl ConvexField {
|
|||
};
|
||||
}
|
||||
},
|
||||
|
||||
| ConvexType::Array(next_t) => {
|
||||
let next_target = Ident::new("value", Span::call_site());
|
||||
let child_match = Self::print_extract_field(next_t, Some(next_target));
|
||||
quote! {
|
||||
let #ident = match #match_target {
|
||||
| ::core::option::Option::Some(::convex::Value::Array(values)) => {
|
||||
let mut result = Vec::new();
|
||||
for value in values {
|
||||
let value = ::core::option::Option::Some(value);
|
||||
#child_match
|
||||
result.push(#ident);
|
||||
}
|
||||
result
|
||||
},
|
||||
| _ => {
|
||||
return Err(::anyhow::anyhow!("Expected '{}' to be an array", #error_name));
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
| _ => {
|
||||
panic!("Unimplemented print_extract_type")
|
||||
},
|
||||
|
|
@ -823,6 +859,11 @@ impl ConvexField {
|
|||
})))
|
||||
},
|
||||
|
||||
| "array" => {
|
||||
let t = Self::parse_validator_call(name, &inner)?;
|
||||
Ok(ConvexType::Array(Box::new(ConvexField { name: name.clone(), t })))
|
||||
},
|
||||
|
||||
| "object" => {
|
||||
let object_inner;
|
||||
let _ = syn::braced!(object_inner in inner);
|
||||
|
|
|
|||
104
tests/array.rs
Normal file
104
tests/array.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
use convex::Value;
|
||||
use maplit::btreemap;
|
||||
use ragkit_convex_macros::convex_model;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn basic_string_array() {
|
||||
convex_model!(Model { a: v.array(v.string()) });
|
||||
|
||||
let convex_data = Value::Object(btreemap! {
|
||||
"a".into() => Value::Array(vec![Value::String("apple".into()), Value::String("banana".into())]),
|
||||
});
|
||||
let json_data = json!({
|
||||
"a": ["apple", "banana"],
|
||||
});
|
||||
|
||||
let model = Model::from_convex_value(&convex_data);
|
||||
assert!(model.is_ok());
|
||||
let model = model.unwrap();
|
||||
assert_eq!(vec!["apple".to_string(), "banana".to_string()], model.a);
|
||||
assert_eq!(json_data, json!(model));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_array() {
|
||||
convex_model!(Model { a: v.array(v.union(v.string(), v.number())) });
|
||||
|
||||
let convex_data = Value::Object(btreemap! {
|
||||
"a".into() => Value::Array(vec![Value::String("apple".into()), Value::Float64(42.)]),
|
||||
});
|
||||
let json_data = json!({
|
||||
"a": ["apple", 42.],
|
||||
});
|
||||
|
||||
let model = Model::from_convex_value(&convex_data);
|
||||
assert!(model.is_ok());
|
||||
let model = model.unwrap();
|
||||
// assert_eq!(vec!["apple".to_string(), 42], model.a);
|
||||
assert_eq!(json_data, json!(model));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_array_objects() {
|
||||
convex_model!(Model {
|
||||
a: v.array(
|
||||
v.union(
|
||||
v.object({
|
||||
typ: v.literal("http"),
|
||||
hostname: v.string(),
|
||||
port: v.number()
|
||||
}),
|
||||
v.object({
|
||||
typ: v.literal("tcp"),
|
||||
port: v.number()
|
||||
})
|
||||
)
|
||||
)
|
||||
});
|
||||
|
||||
let convex_data = Value::Object(btreemap! {
|
||||
"a".into() => Value::Array(vec![
|
||||
Value::Object(btreemap! {
|
||||
"typ".into() => Value::String("http".into()),
|
||||
"hostname".into() => Value::String("example.com".into()),
|
||||
"port".into() => Value::Float64(80.0),
|
||||
}),
|
||||
Value::Object(btreemap! {
|
||||
"typ".into() => Value::String("tcp".into()),
|
||||
"port".into() => Value::Float64(8080.0),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
let json_data = json!({
|
||||
"a": [
|
||||
{
|
||||
"typ": "http",
|
||||
"hostname": "example.com",
|
||||
"port": 80.0,
|
||||
},
|
||||
{
|
||||
"typ": "tcp",
|
||||
"port": 8080.0,
|
||||
},
|
||||
],
|
||||
});
|
||||
let model = Model::from_convex_value(&convex_data);
|
||||
assert!(model.is_ok());
|
||||
let model = model.unwrap();
|
||||
assert_eq!(
|
||||
vec![
|
||||
ModelA::Variant1(ModelAVariant1 {
|
||||
typ: "http".to_string(),
|
||||
hostname: "example.com".to_string(),
|
||||
port: 80.0,
|
||||
}),
|
||||
ModelA::Variant2(ModelAVariant2 {
|
||||
typ: "tcp".to_string(),
|
||||
port: 8080.0,
|
||||
}),
|
||||
],
|
||||
model.a
|
||||
);
|
||||
assert_eq!(json_data, json!(model));
|
||||
}
|
||||
Loading…
Reference in a new issue