Generate native core discovery from typed dispatch contracts

This commit is contained in:
Sausage
2026-11-02 00:10:34 +00:00
parent ee88adf6c6
commit facaf1d9b1
15 changed files with 702 additions and 98 deletions
+3 -2
View File
@@ -48,8 +48,9 @@ pub fn client_rs(name: &str, doc: &Value) -> Result<String, String> {
let mut result = vec![];
for method in methods.as_array().into_iter().flatten() {
let wire = method["name"].as_str().ok_or("method name missing")?;
let ident = syn::parse_str::<syn::Ident>(wire)
.or_else(|_| syn::parse_str::<syn::Ident>(&format!("r#{wire}")))
let rust_name = wire.replace('.', "_");
let ident = syn::parse_str::<syn::Ident>(&rust_name)
.or_else(|_| syn::parse_str::<syn::Ident>(&format!("r#{rust_name}")))
.map_err(|e| e.to_string())?;
let mut rs = method["result"]["schema"].clone();
local_refs(&mut rs);
+46 -1
View File
@@ -204,7 +204,7 @@ fn supported(sender: &Value, document: &Value, seen: &mut HashSet<usize>) -> Res
supported(child, document, seen)?;
}
}
"additionalProperties" if value.is_boolean() => (),
"additionalProperties" => supported(value, document, seen)?,
"enum"
if value
.as_array()
@@ -447,6 +447,15 @@ fn compare_variants(
{
let error = if let Some(other) = receiver["properties"].get(key) {
subset(child, other, sender_doc, receiver_doc, active).err()
} else if receiver["additionalProperties"].is_object() {
subset(
child,
&receiver["additionalProperties"],
sender_doc,
receiver_doc,
active,
)
.err()
} else if receiver["additionalProperties"] == false {
Some((
format!(
@@ -471,6 +480,42 @@ fn compare_variants(
));
}
}
// A typed dictionary can send arbitrary keys, unlike a struct's
// closed set of output fields. Check both receiver named fields
// and its catch-all value contract.
if let Some(extra) = sender.get("additionalProperties").filter(|v| v.is_object()) {
let mut key = "extra".to_string();
while sender["properties"].get(&key).is_some()
|| receiver["properties"].get(&key).is_some()
{
key.push('!');
}
let catch_all = receiver
.get("additionalProperties")
.unwrap_or(&Value::Bool(true));
let targets = receiver["properties"]
.as_object()
.into_iter()
.flatten()
.filter(|(key, _)| sender["properties"].get(*key).is_none())
.chain(std::iter::once((&key, catch_all)));
for (key, target) in targets {
if let Err((message, example)) =
subset(extra, target, sender_doc, receiver_doc, active)
{
let mut object = sample(sender, sender_doc, &mut HashSet::new());
if let (Some(Value::Object(object)), Some(value)) = (&mut object, example) {
object.insert(key.clone(), value);
} else {
object = None;
}
return Err((
format!("the dictionary field {key:?} is incompatible: {message}"),
object,
));
}
}
}
}
"array" => {
if let Err((message, example)) = subset(