use crate::format::FormatHint;
use crate::ordering::{CoalesceInfo, NodeRole};
#[derive(Debug)]
pub struct NodeSchema {
pub id: &'static str,
pub label: &'static str,
pub description: &'static str,
pub group: NodeGroup,
pub role: NodeRole,
pub params: &'static [ParamDesc],
pub tags: &'static [&'static str],
pub coalesce: Option<CoalesceInfo>,
pub format: FormatHint,
pub version: u32,
pub compat_version: u32,
pub json_key: &'static str,
pub deny_unknown_fields: bool,
pub inputs: &'static [InputPort],
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct InputPort {
pub name: &'static str,
pub label: &'static str,
pub edge_kind: EdgeKind,
pub required: bool,
pub variadic: bool,
pub from_io_id: bool,
}
impl InputPort {
pub const fn new(
name: &'static str,
label: &'static str,
edge_kind: EdgeKind,
required: bool,
variadic: bool,
from_io_id: bool,
) -> Self {
Self {
name,
label,
edge_kind,
required,
variadic,
from_io_id,
}
}
pub const fn input(name: &'static str, label: &'static str) -> Self {
Self::new(name, label, EdgeKind::Input, true, false, false)
}
pub const fn canvas(name: &'static str, label: &'static str) -> Self {
Self::new(name, label, EdgeKind::Canvas, true, false, false)
}
pub const fn from_io(name: &'static str, label: &'static str) -> Self {
Self::new(name, label, EdgeKind::Input, true, false, true)
}
pub const fn variadic(name: &'static str, label: &'static str) -> Self {
Self::new(name, label, EdgeKind::Input, true, true, false)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum EdgeKind {
Input,
Canvas,
}
impl NodeSchema {
pub fn phase(&self) -> NodeRole {
self.role
}
pub fn effective_json_key(&self) -> &'static str {
if self.json_key.is_empty() {
self.id
} else {
self.json_key
}
}
pub fn to_markdown(&self) -> alloc::string::String {
use alloc::fmt::Write;
let mut md = alloc::string::String::new();
let _ = write!(md, "### `{}`\n\n", self.id);
if !self.description.is_empty() {
let _ = write!(md, "{}\n\n", self.description);
}
let _ = write!(
md,
"**Group:** {:?} | **Role:** {:?}",
self.group, self.role
);
if !self.tags.is_empty() {
let _ = write!(md, " | **Tags:** {}", self.tags.join(", "));
}
md.push_str("\n\n");
if !self.params.is_empty() {
md.push_str("| Parameter | Type | Default | Range | KV Keys | Description |\n");
md.push_str("|-----------|------|---------|-------|---------|-------------|\n");
for p in self.params {
let (ty, default, range) = match &p.kind {
ParamKind::Float {
min, max, default, ..
} => (
"f32",
alloc::format!("{default}"),
alloc::format!("{min}..{max}"),
),
ParamKind::Int { min, max, default } => (
"i32",
alloc::format!("{default}"),
alloc::format!("{min}..{max}"),
),
ParamKind::U32 { min, max, default } => (
"u32",
alloc::format!("{default}"),
alloc::format!("{min}..{max}"),
),
ParamKind::Bool { default } => (
"bool",
alloc::format!("{default}"),
alloc::string::String::new(),
),
ParamKind::Str { default } => (
"string",
alloc::format!("\"{default}\""),
alloc::string::String::new(),
),
ParamKind::Enum { default, variants } => {
let names: alloc::vec::Vec<&str> =
variants.iter().map(|v| v.name).collect();
("enum", alloc::format!("\"{default}\""), names.join(" \\| "))
}
ParamKind::FloatArray {
len,
min,
max,
default,
..
} => (
"f32[]",
alloc::format!("[{default}; {len}]"),
alloc::format!("{min}..{max}"),
),
ParamKind::Color { default } => (
"color",
alloc::format!("{default:?}"),
alloc::string::String::new(),
),
ParamKind::Json { .. } => (
"json",
alloc::string::String::from("(complex)"),
alloc::string::String::new(),
),
ParamKind::Object { params } => (
"object",
alloc::format!("{} fields", params.len()),
alloc::string::String::new(),
),
ParamKind::TaggedUnion { variants } => {
let tags: alloc::vec::Vec<&str> = variants.iter().map(|v| v.tag).collect();
("union", tags.join(" \\| "), alloc::string::String::new())
}
};
let keys = if p.kv_keys.is_empty() {
alloc::string::String::from("—")
} else {
p.kv_keys
.iter()
.map(|k| alloc::format!("`{k}`"))
.collect::<alloc::vec::Vec<_>>()
.join(", ")
};
let _ = writeln!(
md,
"| `{}` | {} | {} | {} | {} | {} |",
p.name,
ty,
default,
range,
keys,
p.description.replace('\n', " "),
);
}
md.push('\n');
}
md
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParamDesc {
pub name: &'static str,
pub label: &'static str,
pub description: &'static str,
pub kind: ParamKind,
pub unit: &'static str,
pub section: &'static str,
pub slider: SliderMapping,
pub kv_keys: &'static [&'static str],
pub since_version: u32,
pub visible_when: &'static str,
pub optional: bool,
pub json_name: &'static str,
pub json_aliases: &'static [&'static str],
}
impl ParamDesc {
pub fn effective_json_name(&self) -> &'static str {
if self.json_name.is_empty() {
self.name
} else {
self.json_name
}
}
pub fn matches_json_key(&self, key: &str) -> bool {
let eff = self.effective_json_name();
if key == eff || key == self.name {
return true;
}
self.json_aliases.contains(&key)
}
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum ParamKind {
Float {
min: f32,
max: f32,
default: f32,
identity: f32,
step: f32,
},
Int { min: i32, max: i32, default: i32 },
U32 { min: u32, max: u32, default: u32 },
Bool { default: bool },
Str { default: &'static str },
Enum {
variants: &'static [EnumVariant],
default: &'static str,
},
FloatArray {
len: usize,
min: f32,
max: f32,
default: f32,
labels: &'static [&'static str],
},
Color { default: [f32; 4] },
Json {
json_schema: &'static str,
default_json: &'static str,
},
Object {
params: &'static [ParamDesc],
},
TaggedUnion {
variants: &'static [TaggedVariant],
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct EnumVariant {
pub name: &'static str,
pub label: &'static str,
pub description: &'static str,
}
pub trait NodeParams {
const PARAM_KIND: ParamKind;
}
pub use NodeParams as JsonParam;
#[derive(Clone, Debug, PartialEq)]
pub struct TaggedVariant {
pub tag: &'static str,
pub label: &'static str,
pub description: &'static str,
pub params: &'static [ParamDesc],
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SliderMapping {
Linear,
SquareFromSlider,
FactorCentered,
Logarithmic,
NotSlider,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NodeGroup {
Decode,
Encode,
Tone,
ToneRange,
ToneMap,
Color,
Detail,
Effects,
Geometry,
Layout,
Canvas,
Composite,
Quantize,
Analysis,
Hdr,
Raw,
Auto,
Other,
}