Assign Plastic Rule: the schema's bodies is a name or a non-empty list, not null

The field is `Option<NameList>`, so its published schema allowed `null`
(from the `Option`) and an empty list (from `NameList`), both of which
the handler refuses ("`bodies` must list bodies"). It now has its own
schema: a name, or a list of 1 to 10 000 names. The reader is unchanged,
so strict still gives the handler's message for those calls. The schema
test adds both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-11-05 13:39:19 -06:00
parent 072f32a38d
commit 9dfa1ade23
2 changed files with 17 additions and 3 deletions
+14 -2
View File
@@ -117,15 +117,27 @@ fn rule_name_schema(_: &mut SchemaGenerator) -> Schema {
#[schemars(deny_unknown_fields, transform = bodies_required)]
pub struct AssignRuleParams {
/// The bodies, by name: 1 to 10 000.
// Optional in the type, required in the schema: left out or empty, the handler refuses the
// call with its own message.
// Optional in the type, required in the schema: left out, `null` or empty, the handler
// refuses the call with its own message, so the schema allows none of those.
#[serde(default)]
#[schemars(schema_with = "bodies_schema")]
pub bodies: Option<NameList>,
/// The rule, by name, as `plastic.rules` lists it. Empty or left out: the bodies have no
/// rule of their own, so the active rule applies.
pub rule: Option<String>,
}
/// `bodies` as `plastic.assign_rule` takes it: a name, or a list of 1 to 10 000 names. Not
/// `NameList`'s own schema, which allows the empty list the handler refuses.
fn bodies_schema(_: &mut SchemaGenerator) -> Schema {
json_schema!({
"anyOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}, "minItems": 1, "maxItems": crate::params::MAX_STRING_LIST}
]
})
}
/// List the plastic rules (the library's and the design's), the active rule and the rules
/// assigned to bodies. No parameters.
#[derive(Debug, Deserialize, JsonSchema)]
+3 -1
View File
@@ -93,7 +93,7 @@ fn the_contracts_refuse_what_the_handlers_ignore() {
/// #12313: the schemas take what the handlers read: a rule's name as text, its values as
/// numbers or expressions, `active` and `delete` as true or false; bodies as a name or a list of
/// names, and the rule as text or `null`.
/// one or more names (not `null`), and the rule as text or `null`.
#[test]
fn the_schemas_take_what_the_handlers_read() {
let manage = input_schema_for::<ManageRulesParams>();
@@ -119,6 +119,8 @@ fn the_schemas_take_what_the_handlers_read() {
(json!({"bodies": ["Body1"], "rule": null}), true),
(json!({}), false),
(json!({"bodies": [5]}), false),
(json!({"bodies": null}), false),
(json!({"bodies": []}), false),
(json!({"bodies": ["Body1"], "rule": 5}), false),
] {
assert_eq!(conforms(assign, assign, &p), takes, "{p}");