Skip to main content

oxihuman_export/
markdown_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3
4//! Export mesh/scene info as a Markdown table.
5
6#![allow(dead_code)]
7
8/// A Markdown table row.
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct MdRow {
12    pub cells: Vec<String>,
13}
14
15/// A Markdown table document.
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct MdTable {
19    pub title: String,
20    pub headers: Vec<String>,
21    pub rows: Vec<MdRow>,
22}
23
24/// Create a new Markdown table with the given title and headers.
25#[allow(dead_code)]
26pub fn new_md_table(title: &str, headers: &[&str]) -> MdTable {
27    MdTable {
28        title: title.to_string(),
29        headers: headers.iter().map(|s| s.to_string()).collect(),
30        rows: Vec::new(),
31    }
32}
33
34/// Add a row to the table.
35#[allow(dead_code)]
36pub fn add_md_row(table: &mut MdTable, cells: &[&str]) {
37    table.rows.push(MdRow {
38        cells: cells.iter().map(|s| s.to_string()).collect(),
39    });
40}
41
42/// Return the number of rows.
43#[allow(dead_code)]
44pub fn row_count(table: &MdTable) -> usize {
45    table.rows.len()
46}
47
48/// Return the number of columns (based on headers).
49#[allow(dead_code)]
50pub fn column_count(table: &MdTable) -> usize {
51    table.headers.len()
52}
53
54/// Render the table as a Markdown string.
55#[allow(dead_code)]
56pub fn to_markdown_string(table: &MdTable) -> String {
57    let mut out = format!("# {}\n\n", table.title);
58    // Header row
59    let header_line = table.headers.join(" | ");
60    out.push_str(&format!("| {} |\n", header_line));
61    // Separator
62    let sep: Vec<&str> = table.headers.iter().map(|_| "---").collect();
63    out.push_str(&format!("| {} |\n", sep.join(" | ")));
64    // Data rows
65    for row in &table.rows {
66        out.push_str(&format!("| {} |\n", row.cells.join(" | ")));
67    }
68    out
69}
70
71/// Export mesh stats as a Markdown table.
72#[allow(dead_code)]
73pub fn export_mesh_stats_md(vertex_count: usize, index_count: usize, name: &str) -> String {
74    let mut table = new_md_table("Mesh Stats", &["Property", "Value"]);
75    add_md_row(&mut table, &["Name", name]);
76    add_md_row(&mut table, &["Vertices", &vertex_count.to_string()]);
77    add_md_row(&mut table, &["Indices", &index_count.to_string()]);
78    add_md_row(&mut table, &["Triangles", &(index_count / 3).to_string()]);
79    to_markdown_string(&table)
80}
81
82/// Export a list of mesh summaries as a Markdown table.
83#[allow(dead_code)]
84pub fn export_mesh_list_md(entries: &[(&str, usize, usize)]) -> String {
85    let mut table = new_md_table("Mesh List", &["Name", "Vertices", "Triangles"]);
86    for &(name, v, i) in entries {
87        add_md_row(&mut table, &[name, &v.to_string(), &(i / 3).to_string()]);
88    }
89    to_markdown_string(&table)
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_new_table_empty() {
98        let t = new_md_table("Test", &["A", "B"]);
99        assert_eq!(row_count(&t), 0);
100        assert_eq!(column_count(&t), 2);
101    }
102
103    #[test]
104    fn test_add_row() {
105        let mut t = new_md_table("T", &["X", "Y"]);
106        add_md_row(&mut t, &["1", "2"]);
107        assert_eq!(row_count(&t), 1);
108    }
109
110    #[test]
111    fn test_to_markdown_contains_header() {
112        let t = new_md_table("Report", &["Name", "Value"]);
113        let s = to_markdown_string(&t);
114        assert!(s.contains("Name | Value"));
115    }
116
117    #[test]
118    fn test_to_markdown_contains_separator() {
119        let t = new_md_table("T", &["A"]);
120        let s = to_markdown_string(&t);
121        assert!(s.contains("---"));
122    }
123
124    #[test]
125    fn test_to_markdown_contains_row_data() {
126        let mut t = new_md_table("T", &["K", "V"]);
127        add_md_row(&mut t, &["alpha", "99"]);
128        let s = to_markdown_string(&t);
129        assert!(s.contains("alpha"));
130        assert!(s.contains("99"));
131    }
132
133    #[test]
134    fn test_export_mesh_stats_md() {
135        let s = export_mesh_stats_md(100, 300, "body");
136        assert!(s.contains("Vertices"));
137        assert!(s.contains("body"));
138    }
139
140    #[test]
141    fn test_export_mesh_list_md() {
142        let entries = vec![("head", 500, 900), ("body", 2000, 4000)];
143        let s = export_mesh_list_md(&entries);
144        assert!(s.contains("head"));
145        assert!(s.contains("body"));
146    }
147
148    #[test]
149    fn test_title_in_output() {
150        let t = new_md_table("MyTitle", &["Col"]);
151        let s = to_markdown_string(&t);
152        assert!(s.contains("MyTitle"));
153    }
154
155    #[test]
156    fn test_multiple_rows() {
157        let mut t = new_md_table("T", &["A", "B"]);
158        add_md_row(&mut t, &["1", "2"]);
159        add_md_row(&mut t, &["3", "4"]);
160        assert_eq!(row_count(&t), 2);
161    }
162
163    #[test]
164    fn test_column_count_three() {
165        let t = new_md_table("T", &["A", "B", "C"]);
166        assert_eq!(column_count(&t), 3);
167    }
168}