az-device-contract-codegen 2026.5.18

Generate deterministic Modbus C, Markdown, and Tokio client artifacts from typed device contract definitions.
Documentation
use super::artifact::build_artifact;
use crate::model::{ArtifactGroup, GeneratedArtifact, TransportKind};
use crate::planner::{ContractPlan, PlannedValue, STRING_BYTE_CAPACITY, STRING_REGISTER_WIDTH};

pub(super) fn render_markdown_artifact(
    plan: &ContractPlan,
    transport: &TransportKind,
) -> GeneratedArtifact {
    build_artifact(
        format!(
            "Docs/generated/modbus/protocols/modbus-{}-contract.md",
            transport.file_segment()
        ),
        None,
        ArtifactGroup::MarkdownProtocol,
        transport.clone(),
        render_markdown_contract(plan, transport),
    )
}

fn render_markdown_contract(plan: &ContractPlan, transport: &TransportKind) -> String {
    let mut lines = vec![
        format!("# {} Modbus Contract", transport.as_str()),
        "".to_string(),
        "## 默认编码规则".to_string(),
        "".to_string(),
        "- `BOOLEAN`".to_string(),
        "  映射为单个 bit 或单个寄存器,值为 `0/1`。".to_string(),
        "- `INT`".to_string(),
        "  映射为两个 16 位寄存器,按高字在前组合为 `i32`。".to_string(),
        "- `STRING` / `BYTES`".to_string(),
        format!(
            "  固定占用 `{STRING_REGISTER_WIDTH}` 个寄存器:第一个寄存器是长度,后面最多承载 {STRING_BYTE_CAPACITY} 字节有效载荷。"
        ),
        "".to_string(),
        "## 方法映射".to_string(),
        "".to_string(),
        "| 服务 | 方法 | Api | 地址域 | 功能码 | 起始地址 | 数量 | 说明 |".to_string(),
        "| --- | --- | --- | --- | --- | --- | --- | --- |".to_string(),
    ];
    for method in &plan.methods {
        lines.push(format!(
            "| {} | {} | {} | {} | {} | {} | {} | {} |",
            method.interface_name,
            method.method_name,
            method.api_kind.as_str(),
            method.area_kind.c_label(),
            method.area_kind.function_code_label(),
            method.start_address,
            method.quantity,
            method.method_summary.clone().unwrap_or_default()
        ));
    }
    lines.push(String::new());
    for method in &plan.methods {
        lines.push(format!(
            "### {}.{}",
            method.interface_name, method.method_name
        ));
        lines.push(String::new());
        match &method.planned_value {
            PlannedValue::Dto { type_name, items } => {
                lines.push(format!("- DTO:`{type_name}`"));
                for item in items {
                    lines.push(format!(
                        "- `{}`: {:?}, offset={}, width={}",
                        item.name, item.value_type, item.offset, item.width
                    ));
                }
            }
            PlannedValue::Scalar { item } => {
                lines.push(format!(
                    "- 标量返回:`{:?}`, width={}",
                    item.value_type, item.width
                ));
            }
            PlannedValue::Parameters { items } => {
                lines.push("- 写入参数:".to_string());
                for item in items {
                    lines.push(format!(
                        "- `{}`: {:?}, offset={}, width={}",
                        item.name, item.value_type, item.offset, item.width
                    ));
                }
            }
        }
        lines.push(String::new());
    }
    lines.join("\n")
}