hmd-profile-progress 0.1.0-alpha.8

Official progress profile validation and schema support for Human Markdown
Documentation
use hmd_core::{BlockDescriptor, MetadataField, MetadataFieldKind, ProfileDescriptor};
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum EntryKind {
    Research,
    Plan,
    Change,
    Test,
    Review,
    Decision,
    Blocker,
    Handoff,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ProgressMeta {
    pub id: String,
    pub project: String,
    pub status: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct EntryMeta {
    pub id: String,
    pub at: String,
    pub actor: String,
    pub kind: EntryKind,
    pub summary: String,
    pub corrects: Option<String>,
    pub supersedes: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ChangeMeta {
    pub files: Vec<String>,
    pub scope: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct TestMeta {
    pub command: String,
    pub result: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct BlockerMeta {
    pub id: String,
    pub severity: Option<String>,
    pub owner: Option<String>,
    pub status: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct NextMeta {
    pub task: String,
    pub priority: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ProgressProfileSchema {
    pub progress: Option<ProgressMeta>,
    pub entry: Option<EntryMeta>,
    pub change: Option<ChangeMeta>,
    pub test: Option<TestMeta>,
    pub blocker: Option<BlockerMeta>,
    pub next: Option<NextMeta>,
}

const ENTRY_KINDS: &[&str] = &[
    "research", "plan", "change", "test", "review", "decision", "blocker", "handoff",
];

const PROGRESS_FIELDS: &[MetadataField] = &[
    MetadataField::required("id", MetadataFieldKind::String),
    MetadataField::required("project", MetadataFieldKind::String),
    MetadataField::required("status", MetadataFieldKind::String),
];
const ENTRY_FIELDS: &[MetadataField] = &[
    MetadataField::required("id", MetadataFieldKind::String),
    MetadataField::required("at", MetadataFieldKind::String),
    MetadataField::required("actor", MetadataFieldKind::String),
    MetadataField::required_enum("kind", ENTRY_KINDS),
    MetadataField::required("summary", MetadataFieldKind::String),
    MetadataField::optional("corrects", MetadataFieldKind::String),
    MetadataField::optional("supersedes", MetadataFieldKind::String),
];
const CHANGE_FIELDS: &[MetadataField] = &[
    MetadataField::required("files", MetadataFieldKind::StringArray),
    MetadataField::optional("scope", MetadataFieldKind::String),
];
const TEST_FIELDS: &[MetadataField] = &[
    MetadataField::required("command", MetadataFieldKind::String),
    MetadataField::optional("result", MetadataFieldKind::String),
];
const BLOCKER_FIELDS: &[MetadataField] = &[
    MetadataField::required("id", MetadataFieldKind::String),
    MetadataField::optional("severity", MetadataFieldKind::String),
    MetadataField::optional("owner", MetadataFieldKind::String),
    MetadataField::optional("status", MetadataFieldKind::String),
];
const NEXT_FIELDS: &[MetadataField] = &[
    MetadataField::required("task", MetadataFieldKind::String),
    MetadataField::optional("priority", MetadataFieldKind::String),
];

const PROGRESS_CHILDREN: &[&str] = &["entry"];
const ENTRY_CHILDREN: &[&str] = &["change", "test", "blocker", "next"];

const BLOCKS: &[BlockDescriptor] = &[
    BlockDescriptor {
        block_type: "progress",
        schema_id: "https://hmd.dev/schemas/progress@0.1/progress.schema.json",
        fields: PROGRESS_FIELDS,
        allowed_child_block_types: Some(PROGRESS_CHILDREN),
    },
    BlockDescriptor {
        block_type: "entry",
        schema_id: "https://hmd.dev/schemas/progress@0.1/entry.schema.json",
        fields: ENTRY_FIELDS,
        allowed_child_block_types: Some(ENTRY_CHILDREN),
    },
    BlockDescriptor {
        block_type: "change",
        schema_id: "https://hmd.dev/schemas/progress@0.1/change.schema.json",
        fields: CHANGE_FIELDS,
        allowed_child_block_types: None,
    },
    BlockDescriptor {
        block_type: "test",
        schema_id: "https://hmd.dev/schemas/progress@0.1/test.schema.json",
        fields: TEST_FIELDS,
        allowed_child_block_types: None,
    },
    BlockDescriptor {
        block_type: "blocker",
        schema_id: "https://hmd.dev/schemas/progress@0.1/blocker.schema.json",
        fields: BLOCKER_FIELDS,
        allowed_child_block_types: None,
    },
    BlockDescriptor {
        block_type: "next",
        schema_id: "https://hmd.dev/schemas/progress@0.1/next.schema.json",
        fields: NEXT_FIELDS,
        allowed_child_block_types: None,
    },
];

pub const PROFILE: ProfileDescriptor = ProfileDescriptor {
    id: "progress@0.1",
    name: "progress",
    version: "0.1",
    source: "official",
    blocks: BLOCKS,
    reference_rules: &[],
};

pub fn descriptor() -> ProfileDescriptor {
    PROFILE
}

pub fn register_profile() -> ProfileDescriptor {
    descriptor()
}

pub fn json_schema() -> serde_json::Value {
    serde_json::to_value(schema_for!(ProgressProfileSchema)).expect("serializes schema")
}