Skip to main content

moss_core/contract/
describe.rs

1//! Serializable payload for `moss describe --json`.
2//!
3//! The JSON shape is itself a contract — version it independently of
4//! `moss_html_version`. Bumping `describe_schema_version` is required for
5//! any breaking change to the JSON envelope.
6
7use serde::Serialize;
8use std::collections::BTreeMap;
9
10use crate::contract::components::{COMPONENTS, Status};
11use crate::contract::tokens::Tokens;
12
13pub const DESCRIBE_SCHEMA_VERSION: u32 = 1;
14pub const MOSS_HTML_VERSION: u32 = 1;
15
16#[derive(Serialize)]
17pub struct DescribePayload<'a> {
18    pub describe_schema_version: u32,
19    pub moss_html_version: u32,
20    pub moss_binary_version: &'static str,
21    pub tokens: BTreeMap<&'a str, Vec<TokenJson<'a>>>,
22    pub components: Vec<ComponentJson>,
23}
24
25#[derive(Serialize)]
26pub struct TokenJson<'a> {
27    pub name: &'a str,
28    pub value: &'a str,
29    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
30    pub type_hint: Option<&'a str>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub description: Option<&'a str>,
33}
34
35#[derive(Serialize)]
36pub struct ComponentJson {
37    pub class: &'static str,
38    pub kind: &'static str,
39    pub parent: &'static str,
40    pub data_attrs: Vec<DataAttrJson>,
41    pub example_html: &'static str,
42    pub example_markdown: &'static str,
43    pub status: &'static str,
44    pub since: &'static str,
45    pub description: &'static str,
46}
47
48#[derive(Serialize)]
49pub struct DataAttrJson {
50    pub name: &'static str,
51    pub values: &'static [&'static str],
52    pub default: &'static str,
53    pub description: &'static str,
54}
55
56impl<'a> DescribePayload<'a> {
57    pub fn new(tokens: &'a Tokens) -> Self {
58        let mut tokens_map: BTreeMap<&str, Vec<TokenJson>> = BTreeMap::new();
59        for group in &tokens.groups {
60            let entries: Vec<TokenJson> = group
61                .entries
62                .iter()
63                .map(|t| TokenJson {
64                    name: &t.name,
65                    value: &t.value,
66                    type_hint: t.type_hint.as_deref(),
67                    description: t.description.as_deref(),
68                })
69                .collect();
70            tokens_map.insert(&group.name, entries);
71        }
72
73        let components: Vec<ComponentJson> = COMPONENTS
74            .iter()
75            .map(|c| ComponentJson {
76                class: c.class,
77                kind: c.kind,
78                parent: c.parent,
79                data_attrs: c
80                    .data_attrs
81                    .iter()
82                    .map(|a| DataAttrJson {
83                        name: a.name,
84                        values: a.values,
85                        default: a.default,
86                        description: a.description,
87                    })
88                    .collect(),
89                example_html: c.example_html,
90                example_markdown: c.example_markdown,
91                status: match c.status {
92                    Status::Confirmed => "confirmed",
93                    Status::Emerging => "emerging",
94                    Status::Retired => "retired",
95                },
96                since: c.since,
97                description: c.description,
98            })
99            .collect();
100
101        DescribePayload {
102            describe_schema_version: DESCRIBE_SCHEMA_VERSION,
103            moss_html_version: MOSS_HTML_VERSION,
104            moss_binary_version: env!("CARGO_PKG_VERSION"),
105            tokens: tokens_map,
106            components,
107        }
108    }
109}