From c1cdc53187bedf1d14efc39e7e3a65236d10df48 Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Tue, 1 Sep 2026 20:20:50 +0200 Subject: [PATCH] Select fields: inline static options A select requirement can now declare a plain options: [A, B, C] list instead of a resource - each string is both value and label. Restores the ancestor question format's inline enums (used by the klingenberg port) without needing a resource endpoint for a fixed list. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Y42TyF8Zu7NGRR2893vNcZ --- Cargo.lock | 2 +- src/content.rs | 13 +++++++++++-- src/resource.rs | 10 ++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a47e71..ebf003e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2948,7 +2948,7 @@ dependencies = [ [[package]] name = "portal" -version = "0.3.24" +version = "0.3.25" dependencies = [ "anyhow", "arc-swap", diff --git a/src/content.rs b/src/content.rs index abc6dfd..db02c14 100644 --- a/src/content.rs +++ b/src/content.rs @@ -446,6 +446,12 @@ pub struct Requirement { /// `ResourceSpec` mechanism a `Feature.resource` uses. #[serde(default)] pub resource: Option, + /// `type: select` only - a static list of options, as an + /// alternative to a `resource`. Each string is both the option's + /// stored value and its label. Faithful to the ancestor format's + /// inline enums. + #[serde(default)] + pub options: Vec, /// `type: select` only - which field in each item is the option's /// stable id. Defaults to trying `_id` then `id`. #[serde(default)] @@ -1204,9 +1210,12 @@ pub fn validate_questions( .collect(); for feature in &alternative.features { for requirement in &feature.requirements { - if requirement.kind == "select" && requirement.resource.is_none() { + if requirement.kind == "select" + && requirement.resource.is_none() + && requirement.options.is_empty() + { anyhow::bail!( - "question {:?} alternative {:?} feature {:?}: requirement {:?} is type: select but declares no resource to select from", + "question {:?} alternative {:?} feature {:?}: requirement {:?} is type: select but declares neither a resource nor inline options", question.id, alternative.name, feature.name, requirement.name ); } diff --git a/src/resource.rs b/src/resource.rs index 11410b3..7afdb1c 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -68,6 +68,16 @@ pub async fn get_requirement_options( .iter() .find(|r| r.name == requirement_name) .ok_or_else(|| ServerFnError::new("unknown requirement"))?; + // Inline options: return them as {id,label} objects, the shape the + // SelectField renders, without touching a resource. + if !requirement.options.is_empty() { + let items: Vec = requirement + .options + .iter() + .map(|o| serde_json::json!({ "id": o, "label": o })) + .collect(); + return Ok(serde_json::Value::Array(items)); + } let resource = requirement .resource .as_ref()