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")
}