Skip to main content

hmd_profile_progress/
lib.rs

1use hmd_core::{BlockDescriptor, MetadataField, MetadataFieldKind, ProfileDescriptor};
2use schemars::{schema_for, JsonSchema};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
6#[serde(rename_all = "snake_case")]
7pub enum EntryKind {
8    Research,
9    Plan,
10    Change,
11    Test,
12    Review,
13    Decision,
14    Blocker,
15    Handoff,
16}
17
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
19pub struct ProgressMeta {
20    pub id: String,
21    pub project: String,
22    pub status: String,
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
26pub struct EntryMeta {
27    pub id: String,
28    pub at: String,
29    pub actor: String,
30    pub kind: EntryKind,
31    pub summary: String,
32    pub corrects: Option<String>,
33    pub supersedes: Option<String>,
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
37pub struct ChangeMeta {
38    pub files: Vec<String>,
39    pub scope: Option<String>,
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
43pub struct TestMeta {
44    pub command: String,
45    pub result: Option<String>,
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
49pub struct BlockerMeta {
50    pub id: String,
51    pub severity: Option<String>,
52    pub owner: Option<String>,
53    pub status: Option<String>,
54}
55
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
57pub struct NextMeta {
58    pub task: String,
59    pub priority: Option<String>,
60}
61
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
63#[serde(rename_all = "camelCase")]
64pub struct ProgressProfileSchema {
65    pub progress: Option<ProgressMeta>,
66    pub entry: Option<EntryMeta>,
67    pub change: Option<ChangeMeta>,
68    pub test: Option<TestMeta>,
69    pub blocker: Option<BlockerMeta>,
70    pub next: Option<NextMeta>,
71}
72
73const ENTRY_KINDS: &[&str] = &[
74    "research", "plan", "change", "test", "review", "decision", "blocker", "handoff",
75];
76
77const PROGRESS_FIELDS: &[MetadataField] = &[
78    MetadataField::required("id", MetadataFieldKind::String),
79    MetadataField::required("project", MetadataFieldKind::String),
80    MetadataField::required("status", MetadataFieldKind::String),
81];
82const ENTRY_FIELDS: &[MetadataField] = &[
83    MetadataField::required("id", MetadataFieldKind::String),
84    MetadataField::required("at", MetadataFieldKind::String),
85    MetadataField::required("actor", MetadataFieldKind::String),
86    MetadataField::required_enum("kind", ENTRY_KINDS),
87    MetadataField::required("summary", MetadataFieldKind::String),
88    MetadataField::optional("corrects", MetadataFieldKind::String),
89    MetadataField::optional("supersedes", MetadataFieldKind::String),
90];
91const CHANGE_FIELDS: &[MetadataField] = &[
92    MetadataField::required("files", MetadataFieldKind::StringArray),
93    MetadataField::optional("scope", MetadataFieldKind::String),
94];
95const TEST_FIELDS: &[MetadataField] = &[
96    MetadataField::required("command", MetadataFieldKind::String),
97    MetadataField::optional("result", MetadataFieldKind::String),
98];
99const BLOCKER_FIELDS: &[MetadataField] = &[
100    MetadataField::required("id", MetadataFieldKind::String),
101    MetadataField::optional("severity", MetadataFieldKind::String),
102    MetadataField::optional("owner", MetadataFieldKind::String),
103    MetadataField::optional("status", MetadataFieldKind::String),
104];
105const NEXT_FIELDS: &[MetadataField] = &[
106    MetadataField::required("task", MetadataFieldKind::String),
107    MetadataField::optional("priority", MetadataFieldKind::String),
108];
109
110const PROGRESS_CHILDREN: &[&str] = &["entry"];
111const ENTRY_CHILDREN: &[&str] = &["change", "test", "blocker", "next"];
112
113const BLOCKS: &[BlockDescriptor] = &[
114    BlockDescriptor {
115        block_type: "progress",
116        schema_id: "https://hmd.dev/schemas/progress@0.1/progress.schema.json",
117        fields: PROGRESS_FIELDS,
118        allowed_child_block_types: Some(PROGRESS_CHILDREN),
119    },
120    BlockDescriptor {
121        block_type: "entry",
122        schema_id: "https://hmd.dev/schemas/progress@0.1/entry.schema.json",
123        fields: ENTRY_FIELDS,
124        allowed_child_block_types: Some(ENTRY_CHILDREN),
125    },
126    BlockDescriptor {
127        block_type: "change",
128        schema_id: "https://hmd.dev/schemas/progress@0.1/change.schema.json",
129        fields: CHANGE_FIELDS,
130        allowed_child_block_types: None,
131    },
132    BlockDescriptor {
133        block_type: "test",
134        schema_id: "https://hmd.dev/schemas/progress@0.1/test.schema.json",
135        fields: TEST_FIELDS,
136        allowed_child_block_types: None,
137    },
138    BlockDescriptor {
139        block_type: "blocker",
140        schema_id: "https://hmd.dev/schemas/progress@0.1/blocker.schema.json",
141        fields: BLOCKER_FIELDS,
142        allowed_child_block_types: None,
143    },
144    BlockDescriptor {
145        block_type: "next",
146        schema_id: "https://hmd.dev/schemas/progress@0.1/next.schema.json",
147        fields: NEXT_FIELDS,
148        allowed_child_block_types: None,
149    },
150];
151
152pub const PROFILE: ProfileDescriptor = ProfileDescriptor {
153    id: "progress@0.1",
154    name: "progress",
155    version: "0.1",
156    source: "official",
157    blocks: BLOCKS,
158    reference_rules: &[],
159};
160
161pub fn descriptor() -> ProfileDescriptor {
162    PROFILE
163}
164
165pub fn register_profile() -> ProfileDescriptor {
166    descriptor()
167}
168
169pub fn json_schema() -> serde_json::Value {
170    serde_json::to_value(schema_for!(ProgressProfileSchema)).expect("serializes schema")
171}