mirror of
https://github.com/danbulant/convex-macros
synced 2026-05-23 05: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::parse::{Parse, ParseBuffer, ParseStream};
|
||||||
use syn::{Error, Ident, Lit, Result, Token};
|
use syn::{Error, Ident, Lit, Result, Token};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ConvexName {
|
pub struct ConvexName {
|
||||||
pub path: Vec<String>,
|
pub path: Vec<String>,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ConvexField {
|
pub struct ConvexField {
|
||||||
pub name: ConvexName,
|
pub name: ConvexName,
|
||||||
pub t: ConvexType,
|
pub t: ConvexType,
|
||||||
}
|
}
|
||||||
|
|
||||||
// See: https://docs.convex.dev/functions/args-validation
|
// See: https://docs.convex.dev/functions/args-validation
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ConvexType {
|
pub enum ConvexType {
|
||||||
// Core types.
|
// Core types.
|
||||||
Id(String),
|
Id(String),
|
||||||
|
|
@ -33,6 +33,7 @@ pub enum ConvexType {
|
||||||
BoolLiteral(bool),
|
BoolLiteral(bool),
|
||||||
IntLiteral(i64),
|
IntLiteral(i64),
|
||||||
// TODO: Any,
|
// TODO: Any,
|
||||||
|
Array(Box<ConvexField>),
|
||||||
Optional(Box<ConvexField>),
|
Optional(Box<ConvexField>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,6 +59,9 @@ impl ConvexType {
|
||||||
| ConvexType::Optional(child) => {
|
| ConvexType::Optional(child) => {
|
||||||
child.t.print().map(|ts| quote! { Option<#ts> })
|
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.
|
// These depend on field.name to generate a struct name.
|
||||||
| ConvexType::Object(_) => None,
|
| 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::Optional(_) => panic!("Unions may not contain optional branches"),
|
||||||
| ConvexType::Union(_) => panic!("Unions may not directly contain other unions, put other types between them"),
|
| 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!(
|
panic!(
|
||||||
"Internal Error: Should have a complex field like object or union"
|
"Internal Error: Should have a complex field like object or union"
|
||||||
)
|
)
|
||||||
|
|
@ -403,6 +409,14 @@ impl ConvexField {
|
||||||
rendered_fields.push(quote! {
|
rendered_fields.push(quote! {
|
||||||
pub #field_name: #field_type,
|
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 {
|
} else {
|
||||||
let field_type = field.name.to_struct_name();
|
let field_type = field.name.to_struct_name();
|
||||||
let mut child_struct = field.print();
|
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")
|
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" => {
|
| "object" => {
|
||||||
let object_inner;
|
let object_inner;
|
||||||
let _ = syn::braced!(object_inner in 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