use easydoc::prelude::*;
use easydoc_core::{
DocumentBlock, DocumentContent, DocumentList, DocumentListItem, DocumentMeta, DocumentTable,
DocumentTableCell, DocumentTableRow, DocumentTextRun,
};
use std::collections::HashMap;
fn tr(text: &str) -> DocumentTextRun {
DocumentTextRun {
text: text.into(),
..Default::default()
}
}
fn create_simple_docx(path: &std::path::Path) {
let content = DocumentContent {
metadata: DocumentMeta::new().title("Test").author("Author"),
blocks: vec![
DocumentBlock::Heading {
level: 1,
runs: vec![tr("Title")],
},
DocumentBlock::Paragraph(vec![tr("Hello world")]),
DocumentBlock::Table(DocumentTable {
rows: vec![
DocumentTableRow {
cells: vec![
DocumentTableCell {
blocks: vec![DocumentBlock::Paragraph(vec![tr("H1")])],
column_span: 1,
row_span: 1,
},
DocumentTableCell {
blocks: vec![DocumentBlock::Paragraph(vec![tr("H2")])],
column_span: 1,
row_span: 1,
},
],
is_header: true,
},
DocumentTableRow {
cells: vec![
DocumentTableCell {
blocks: vec![DocumentBlock::Paragraph(vec![tr("A")])],
column_span: 1,
row_span: 1,
},
DocumentTableCell {
blocks: vec![DocumentBlock::Paragraph(vec![tr("B")])],
column_span: 1,
row_span: 1,
},
],
is_header: false,
},
],
}),
DocumentBlock::List(DocumentList {
ordered: true,
start_number: Some(1),
items: vec![
DocumentListItem {
blocks: vec![DocumentBlock::Paragraph(vec![tr("Item 1")])],
nested: None,
},
DocumentListItem {
blocks: vec![DocumentBlock::Paragraph(vec![tr("Item 2")])],
nested: Some(Box::new(DocumentList {
ordered: false,
start_number: None,
items: vec![DocumentListItem {
blocks: vec![DocumentBlock::Paragraph(vec![tr("Nested")])],
nested: None,
}],
})),
},
],
}),
DocumentBlock::CodeBlock {
language: Some("rust".into()),
code: "fn main() {}".into(),
},
DocumentBlock::ThematicBreak,
DocumentBlock::PageBreak,
DocumentBlock::ColumnBreak,
DocumentBlock::Footnote {
id: 1,
blocks: vec![DocumentBlock::Paragraph(vec![tr("note")])],
},
DocumentBlock::Endnote {
id: 2,
blocks: vec![DocumentBlock::Paragraph(vec![tr("end")])],
},
DocumentBlock::TextBox(vec![DocumentBlock::Paragraph(vec![tr("inside")])]),
DocumentBlock::Section {
blocks: vec![DocumentBlock::Paragraph(vec![tr("section content")])],
section_type: Some("nextPage".into()),
},
],
};
EasyDoc::write_content(&content, path).unwrap();
}
#[test]
fn easydoc_load_and_write_content() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.docx");
create_simple_docx(&path);
let content = EasyDoc::load(&path).unwrap();
assert!(!content.blocks.is_empty());
let out = dir.path().join("out.docx");
EasyDoc::write_content(&content, &out).unwrap();
assert!(out.exists());
}
#[test]
fn easydoc_write_content_to_bytes() {
let content = DocumentContent {
blocks: vec![DocumentBlock::Paragraph(vec![tr("bytes test")])],
..Default::default()
};
let bytes = EasyDoc::write_content_to_bytes(&content).unwrap();
assert!(!bytes.is_empty());
assert!(bytes.len() > 100); }
#[test]
fn easydoc_read_text() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("text.docx");
create_simple_docx(&path);
let text = EasyDoc::read_text(&path).unwrap();
assert!(!text.is_empty());
assert!(text.contains("Title") || text.contains("Hello"));
}
#[test]
fn easydoc_read_tables_as_text() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tables.docx");
create_simple_docx(&path);
let text = EasyDoc::read_text(&path).unwrap();
assert!(!text.is_empty());
}
#[test]
fn easydoc_to_markdown() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("md.docx");
create_simple_docx(&path);
let md = EasyDoc::to_markdown(&path).unwrap();
assert!(!md.is_empty());
}
#[test]
fn easydoc_write_markdown() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("src.docx");
create_simple_docx(&src);
let out = dir.path().join("out.md");
let result = EasyDoc::write_markdown(&src, &out).unwrap();
assert!(!result.markdown.is_empty());
assert!(out.exists());
}
#[test]
fn easydoc_markdown_builder() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("builder.docx");
create_simple_docx(&src);
let result = EasyDoc::markdown(&src)
.image_directory(dir.path().join("img"))
.image_reference_prefix("images")
.include_front_matter(true)
.do_convert()
.unwrap();
assert!(!result.markdown.is_empty());
}
#[test]
fn easydoc_document_builder() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("built.docx");
let builder = EasyDoc::document(&path)
.add_heading("Built Title", HeadingLevel::H1)
.add_paragraph(easydoc_writer::Paragraph::new().add_text("Built paragraph content"));
builder.build().unwrap().save().unwrap();
assert!(path.exists());
let content = EasyDoc::load(&path).unwrap();
assert!(!content.blocks.is_empty());
}
#[test]
fn easydoc_write_table() {
#[derive(Debug, Clone)]
struct Item {
name: String,
value: i32,
}
impl DocxRow for Item {
fn schema() -> &'static [easydoc_core::metadata::TableColumn] {
static SCHEMA: std::sync::LazyLock<Vec<easydoc_core::metadata::TableColumn>> =
std::sync::LazyLock::new(|| {
vec![
easydoc_core::metadata::TableColumn::new("Name", "name", 0),
easydoc_core::metadata::TableColumn::new("Value", "value", 1),
]
});
&SCHEMA
}
fn from_row(_: &easydoc_core::RowData) -> easydoc_core::Result<Self> {
unimplemented!()
}
fn from_row_with_converters(
_: &easydoc_core::RowData,
_: &easydoc_core::ConverterRegistry,
) -> easydoc_core::Result<Self> {
unimplemented!()
}
fn to_row(&self) -> easydoc_core::Result<Vec<easydoc_core::CellData>> {
Ok(vec![
easydoc_core::CellData::new(self.name.clone()),
easydoc_core::CellData::new(i64::from(self.value)),
])
}
fn to_row_with_converters(
&self,
_: &easydoc_core::ConverterRegistry,
) -> easydoc_core::Result<Vec<easydoc_core::CellData>> {
self.to_row()
}
}
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("table.docx");
let items = vec![
Item {
name: "A".into(),
value: 1,
},
Item {
name: "B".into(),
value: 2,
},
];
EasyDoc::write_table(&path, &items)
.title("Test Table")
.do_write()
.unwrap();
assert!(path.exists());
}
#[test]
fn easydoc_document_to_bytes() {
let bytes = EasyDoc::document_to_bytes(|b| {
b.add_heading("Bytes Doc", HeadingLevel::H1)
.add_paragraph(easydoc_writer::Paragraph::new().add_text("Content"))
})
.unwrap();
assert!(!bytes.is_empty());
}
#[test]
fn easydoc_write_table_to_bytes() {
#[derive(Debug, Clone)]
struct Row {
x: String,
}
impl DocxRow for Row {
fn schema() -> &'static [easydoc_core::metadata::TableColumn] {
static SCHEMA: std::sync::LazyLock<Vec<easydoc_core::metadata::TableColumn>> =
std::sync::LazyLock::new(|| {
vec![easydoc_core::metadata::TableColumn::new("X", "x", 0)]
});
&SCHEMA
}
fn from_row(_: &easydoc_core::RowData) -> easydoc_core::Result<Self> {
unimplemented!()
}
fn from_row_with_converters(
_: &easydoc_core::RowData,
_: &easydoc_core::ConverterRegistry,
) -> easydoc_core::Result<Self> {
unimplemented!()
}
fn to_row(&self) -> easydoc_core::Result<Vec<easydoc_core::CellData>> {
Ok(vec![easydoc_core::CellData::new(self.x.clone())])
}
fn to_row_with_converters(
&self,
_: &easydoc_core::ConverterRegistry,
) -> easydoc_core::Result<Vec<easydoc_core::CellData>> {
self.to_row()
}
}
let bytes = EasyDoc::write_table_to_bytes(&[Row { x: "hello".into() }]).unwrap();
assert!(!bytes.is_empty());
}
#[test]
fn easydoc_edit() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("edit.docx");
create_simple_docx(&path);
let editor = EasyDoc::edit(&path);
assert!(editor.is_ok());
}
#[test]
fn easydoc_fill_template_scalar() {
let dir = tempfile::tempdir().unwrap();
let tpl = dir.path().join("tpl.docx");
create_simple_docx(&tpl);
let out = dir.path().join("filled.docx");
let mut data = HashMap::new();
data.insert("name".to_string(), "Alice".to_string());
let result = EasyDoc::fill_template(&tpl, &out, &data);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn easydoc_read() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("read.docx");
create_simple_docx(&path);
let builder = EasyDoc::read(&path);
let _ = builder;
}
#[test]
fn roundtrip_full_pipeline() {
let dir = tempfile::tempdir().unwrap();
let content = DocumentContent {
metadata: DocumentMeta::new().title("Roundtrip"),
blocks: vec![
DocumentBlock::Heading {
level: 1,
runs: vec![tr("Original")],
},
DocumentBlock::Paragraph(vec![tr("Keep this")]),
DocumentBlock::Table(DocumentTable {
rows: vec![DocumentTableRow {
cells: vec![DocumentTableCell {
blocks: vec![DocumentBlock::Paragraph(vec![tr("Cell")])],
column_span: 1,
row_span: 1,
}],
is_header: false,
}],
}),
],
};
let path1 = dir.path().join("step1.docx");
EasyDoc::write_content(&content, &path1).unwrap();
let loaded = EasyDoc::load(&path1).unwrap();
assert!(!loaded.blocks.is_empty());
let mut modified = loaded;
modified
.blocks
.push(DocumentBlock::Paragraph(vec![tr("Added")]));
let path2 = dir.path().join("step2.docx");
EasyDoc::write_content(&modified, &path2).unwrap();
assert!(path2.exists());
let final_content = EasyDoc::load(&path2).unwrap();
assert!(final_content.blocks.len() >= modified.blocks.len());
}
#[test]
fn markdown_full_pipeline() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("full.docx");
create_simple_docx(&src);
let out = dir.path().join("full.md");
let result = EasyDoc::write_markdown(&src, &out).unwrap();
assert!(!result.markdown.is_empty());
assert!(out.exists());
let md_content = std::fs::read_to_string(&out).unwrap();
assert!(!md_content.is_empty());
}
#[test]
fn template_fill_scalar_real_docx() {
let dir = tempfile::tempdir().unwrap();
let tpl = dir.path().join("tpl.docx");
let content = DocumentContent {
blocks: vec![DocumentBlock::Paragraph(vec![tr(
"Hello {name}, welcome to {company}!",
)])],
..Default::default()
};
EasyDoc::write_content(&content, &tpl).unwrap();
let out = dir.path().join("filled.docx");
let mut data = HashMap::new();
data.insert("name".to_string(), "Alice".to_string());
data.insert("company".to_string(), "Acme".to_string());
EasyDoc::fill_template(&tpl, &out, &data).unwrap();
let filled = EasyDoc::load(&out).unwrap();
let text = format!("{:?}", filled.blocks);
assert!(text.contains("Alice") || text.contains("Acme") || !text.contains("{name}"));
}