use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use zpl_toolchain_spec_tables::{
Arg, ArgUnion, CommandCategory, CommandScope, Composite, Constraint, Effects, Example, Plane,
Signature, Stability,
};
#[derive(Debug, Clone, Deserialize)]
pub struct SourceSpecFile {
#[serde(default)]
pub version: Option<String>,
#[serde(rename = "schemaVersion")]
pub schema_version: String,
pub commands: Vec<SourceCommand>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SourceCommand {
#[serde(default)]
pub codes: Option<Vec<String>>,
#[serde(default)]
pub code: Option<String>,
#[serde(default)]
pub aliases: Option<Vec<String>>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub category: Option<CommandCategory>,
#[serde(default)]
pub plane: Option<Plane>,
#[serde(default)]
pub scope: Option<CommandScope>,
#[serde(default)]
pub docs: Option<String>,
pub arity: u32,
#[serde(default)]
pub signature: Option<Signature>,
#[serde(default)]
pub signature_overrides: Option<HashMap<String, Signature>>,
#[serde(default)]
pub composites: Option<Vec<Composite>>,
#[serde(default)]
pub args: Option<Vec<ArgUnion>>,
#[serde(default)]
pub defaults: Option<serde_json::Value>,
#[serde(default)]
pub units: Option<String>,
#[serde(default, rename = "raw_payload")]
pub raw_payload: bool,
#[serde(default, rename = "field_data")]
pub field_data: bool,
#[serde(default, rename = "opens_field")]
pub opens_field: bool,
#[serde(default, rename = "closes_field")]
pub closes_field: bool,
#[serde(default, rename = "hex_escape_modifier")]
pub hex_escape_modifier: bool,
#[serde(default, rename = "field_number")]
pub field_number: bool,
#[serde(default)]
pub serialization: bool,
#[serde(default, rename = "requires_field")]
pub requires_field: bool,
#[serde(default)]
pub constraints: Option<Vec<Constraint>>,
#[serde(default)]
pub printer_gates: Option<Vec<String>>,
#[serde(default)]
pub effects: Option<Effects>,
#[serde(default)]
pub field_data_rules: Option<zpl_toolchain_spec_tables::FieldDataRules>,
#[serde(default)]
pub since: Option<String>,
#[serde(default)]
pub deprecated: Option<bool>,
#[serde(default)]
pub deprecated_since: Option<String>,
#[serde(default)]
pub stability: Option<Stability>,
#[serde(default)]
pub examples: Option<Vec<Example>>,
#[serde(default)]
pub extras: Option<serde_json::Value>,
}
impl SourceCommand {
pub fn canonical_code(&self) -> Option<String> {
if let Some(codes) = &self.codes {
codes.first().cloned()
} else {
self.code.clone()
}
}
pub fn all_codes(&self) -> Vec<String> {
if let Some(codes) = &self.codes {
codes.clone()
} else {
let mut v = Vec::new();
if let Some(c) = &self.code {
v.push(c.clone());
}
if let Some(a) = &self.aliases {
v.extend(a.iter().cloned());
}
v
}
}
pub fn is_structural(&self) -> bool {
self.arity == 0 || self.field_data
}
pub fn signature_params(&self) -> Vec<String> {
self.signature
.as_ref()
.map(|s| s.params.clone())
.unwrap_or_default()
}
pub fn arg_keys(&self) -> Vec<String> {
let mut keys = Vec::new();
if let Some(args) = &self.args {
for item in args {
match item {
ArgUnion::Single(arg) => {
if let Some(k) = &arg.key
&& !k.is_empty()
{
keys.push(k.clone());
}
}
ArgUnion::OneOf { one_of } => {
if let Some(k) = one_of
.iter()
.find_map(|a| a.key.as_ref().filter(|k| !k.is_empty()))
{
keys.push(k.clone());
}
}
}
}
}
keys
}
pub fn all_arg_keys(&self) -> Vec<String> {
let mut keys = Vec::new();
if let Some(args) = &self.args {
for item in args {
let args_to_check: Vec<&Arg> = match item {
ArgUnion::OneOf { one_of } => one_of.iter().collect(),
ArgUnion::Single(arg) => vec![arg.as_ref()],
};
for arg in args_to_check {
if let Some(k) = &arg.key
&& !k.is_empty()
{
keys.push(k.clone());
}
}
}
}
keys
}
}