Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
648 changes: 641 additions & 7 deletions crates/workshop-rs/src/catalog/data/catalog.json

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions crates/workshop-rs/src/catalog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,32 @@ impl Kind {
}
}

/// Literal substitutions accepted at one parameter position and normalized
/// into the parameter's declared semantic type before it enters WIR.
///
/// These are deliberately per-parameter facts. They do not establish a
/// global relationship between Workshop booleans, numbers, arrays, strings,
/// vectors, or null.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ParamCoercions {
/// Accept `False` as numeric zero.
#[serde(default)]
pub false_as_number: bool,
/// Accept `True` as numeric one.
#[serde(default)]
pub true_as_number: bool,
/// Accept numeric zero as `Null`.
#[serde(default)]
pub zero_as_null: bool,
/// Accept `Vector(0, 0, 0)` as `Null`.
#[serde(default)]
pub null_vector_as_null: bool,
/// Accept `Empty Array` as an empty string.
#[serde(default)]
pub empty_array_as_string: bool,
}

/// One catalog builtin.
#[derive(Debug, Clone)]
pub struct CatalogEntry {
Expand All @@ -117,6 +143,8 @@ pub struct CatalogEntry {
/// Evidence-backed semantic type per parameter position. `None` means
/// the available sources do not prove a narrower type.
pub param_types: Vec<Option<String>>,
/// Contextual literal substitutions per parameter position.
pub param_coercions: Vec<Option<ParamCoercions>>,
/// Evidence-backed return type for Value entries. Actions must leave this
/// unset; an absent value is intentionally evidence-insufficient.
pub return_type: Option<String>,
Expand Down Expand Up @@ -204,6 +232,14 @@ impl CatalogEntry {
.and_then(Option::as_deref)
}

/// The contextual literal substitutions for an argument position.
pub fn param_coercions(&self, index: usize) -> Option<&ParamCoercions> {
self.param_coercions
.get(index)
.or_else(|| self.variadic.then(|| self.param_coercions.last()).flatten())
.and_then(Option::as_ref)
}

/// The evidence-backed return type of a Value, when available.
pub fn return_type(&self) -> Option<&str> {
self.return_type.as_deref()
Expand Down Expand Up @@ -387,6 +423,8 @@ struct EntryFile {
#[serde(default)]
param_types: Vec<Option<String>>,
#[serde(default)]
param_coercions: Vec<Option<ParamCoercions>>,
#[serde(default)]
return_type: Option<String>,
#[serde(default)]
variadic: bool,
Expand Down Expand Up @@ -791,6 +829,7 @@ impl Catalog {
param_domains: item.param_domains,
param_defaults: item.param_defaults,
param_types: item.param_types,
param_coercions: item.param_coercions,
return_type: item.return_type,
variadic: item.variadic,
aliases,
Expand Down Expand Up @@ -866,6 +905,13 @@ impl Catalog {
entry.id
)));
}
if entry.param_coercions.len() > entry.params.len() {
return Err(CatalogError::validation(format!(
"{} '{}' declares more param coercions than params",
entry.kind.as_str(),
entry.id
)));
}
if entry.kind != Kind::Value && entry.return_type.is_some() {
return Err(CatalogError::validation(format!(
"{} '{}' declares a return type but is not a value",
Expand Down
Loading
Loading