easydoc
Rust DOC/DOCX document operations library -- read, write, template fill, Markdown conversion, and streaming event processing with O(1) memory.

English | 简体中文
Positioning · Capabilities ·
Which Crate · Quick Start ·
API · Derive · Quality ·
Upstream · License
Current version: 0.1.0-alpha.1
MSRV: Rust 1.88
Edition: 2024
Workspace Resolver: 3
Maturity: Alpha (public API may change)
Last verified: 2026-08-11
1. Project Positioning & Status
1.1 What It Is
easydoc is the facade crate for the easydoc-rust workspace -- a Rust library for DOC/DOCX document operations. It provides a single EasyDoc static factory (18 methods) that delegates to 7 domain-specific sub-crates.
| Dimension |
Value |
| Crate |
easydoc |
| Current version |
0.1.0-alpha.1 |
| MSRV / Edition |
1.88 / 2024 |
| Default features |
none (all sub-crates are direct dependencies) |
unsafe policy |
forbid (workspace-wide) |
| Publish status |
crates.io (v0.1.0-alpha.1) / docs.rs |
| License |
Apache-2.0 |
1.2 What It Is Not
- Not a DOCX parser by itself -- parsing is delegated to
easydoc-reader (via office_oxide).
- Not a DOCX generator by itself -- generation is delegated to
easydoc-writer (via docx-rs).
- Not an Excel library -- for XLSX operations, use
easyexcel-rust.
- Not a PDF or ODT processor -- only DOCX (full) and DOC (read-only) are supported.
1.3 Status Evidence
| Claim |
Value |
Evidence |
| Workspace builds |
Yes |
cargo check --workspace |
| Tests |
Unit + integration + doc tests |
cargo test --workspace |
| MSRV |
1.88 |
rust-version in Cargo.toml |
unsafe_code |
forbid |
[workspace.lints.rust] |
| crates.io |
Published |
v0.1.0-alpha.1 / docs.rs |
2. Capabilities & Maturity
2.1 Capability Matrix
| Capability |
Status |
Delegated Crate |
Notes |
| Document write (headings, paragraphs, tables, images) |
Stable |
easydoc-writer |
Fluent builder API |
| Table write from structs |
Stable |
easydoc-writer + easydoc-derive |
#[derive(DocxRow)] one-liner |
| Document read (text, tables) |
Stable |
easydoc-reader |
DOC/DOCX auto-detection |
| SAX streaming read (O(1) memory) |
Stable |
easydoc-reader |
EventSink trait |
| ViewMode rendering (Plain/Annotated/Outline/Stats) |
Stable |
easydoc-reader |
LLM-friendly modes |
| Semantic model read-modify-write |
Stable |
easydoc-reader + easydoc-writer |
DocumentContent round-trip |
Template fill ({key}, {.field}) |
Stable |
easydoc-template |
Scalar + collection expansion |
| Markdown conversion |
Stable |
easydoc-markdown |
GFM tables, images, front matter |
| Edit existing DOCX |
Stable |
easydoc-writer |
Text replacement |
| In-memory output |
Stable |
easydoc-writer |
*_to_bytes() variants |
| Custom type converters |
Stable |
easydoc-core |
DocConverter<T> trait |
| Write lifecycle hooks |
Stable |
easydoc-core |
DocWriteHandler trait |
2.2 Status Definitions
| Status |
Definition |
| Stable |
Public API, tests, and documentation present; behaviour may still change at alpha stage |
| Preview |
Usable but API or behaviour likely to change |
| Partial |
Only explicitly listed subset works |
| Planned |
No callable implementation yet |
2.3 Format Support Matrix
| Format |
Read |
Write |
Edit |
Template |
Markdown |
Notes |
| DOCX (.docx) |
Full |
Full |
Full |
Full |
Full |
SAX streaming, semantic model, image extraction |
| DOC (.doc) |
Full |
-- |
-- |
-- |
Full |
Read-only via office_oxide; auto-detection |
3. Which Crate to Depend On
The easydoc-rust workspace contains 9 crates. Most users should depend on easydoc (this crate).
| Need |
Recommended Crate |
Features |
Trade-off |
| General document operations |
easydoc |
default |
Simplest entry point; pulls all sub-crates |
| Core types and traits only |
easydoc-core |
serde (optional) |
Minimal dependencies; no I/O |
| MCP server for LLM agents |
easydoc-mcp |
-- |
Separate binary crate |
| Reading only |
easydoc-reader |
-- |
No write/template/markdown |
| Writing only |
easydoc-writer |
-- |
No read/template/markdown |
| Markdown conversion only |
easydoc-markdown |
-- |
No write/template |
| Template fill only |
easydoc-template |
-- |
No read/write/markdown |
| OOXML low-level operations |
easydoc-ooxml |
-- |
Internal; not recommended for direct use |
| Derive macro only |
easydoc-derive |
-- |
#[derive(DocxRow)] |
[dependencies]
easydoc = "0.1.0-alpha.1"
easydoc-core = "0.1.0-alpha.1"
4. Workspace Architecture
Application / downstream crate
│ cargo add easydoc
▼
┌───────────────────────────────────────────────────────┐
│ easydoc-rust Cargo Workspace (9 crates) │
│ │
│ easydoc Facade -- EasyDoc static factory│
│ easydoc-core Traits, data model, errors │
│ easydoc-derive #[derive(DocxRow)] proc-macro │
│ easydoc-ooxml Safe OOXML rewrite, atomic I/O │
│ easydoc-reader DOC/DOCX reading (office_oxide) │
│ easydoc-writer DOCX creation (docx-rs) │
│ easydoc-template Template placeholder fill │
│ easydoc-markdown DOC/DOCX → Markdown │
│ easydoc-mcp MCP server for LLM agents │
└───────────────────────────────────────────────────────┘
│
▼
[DOCX files / DOC files / in-memory bytes]
flowchart TB
USER["Application"] --> FACADE["easydoc (facade)"]
FACADE --> CORE["easydoc-core"]
FACADE --> DERIVE["easydoc-derive"]
FACADE --> READER["easydoc-reader"]
FACADE --> WRITER["easydoc-writer"]
FACADE --> TEMPLATE["easydoc-template"]
FACADE --> MARKDOWN["easydoc-markdown"]
DERIVE --> CORE
READER --> CORE
WRITER --> CORE
WRITER --> OOXML["easydoc-ooxml"]
TEMPLATE --> CORE
TEMPLATE --> OOXML
MARKDOWN --> CORE
MARKDOWN --> READER
MARKDOWN --> OOXML
4.1 Facade Re-export Map
easydoc module |
Source crate |
Key types |
EasyDoc |
easydoc |
Static factory (18 methods) |
DocxRow, DocConverter, DocWriteHandler, EventSink |
easydoc-core |
Extension traits |
DocumentContent, DocumentBlock, DocumentTextRun |
easydoc-core |
Semantic model |
DocValue, CellData, RowData, TableData |
easydoc-core |
Data types |
DocError, Result |
easydoc-core |
Error types |
#[derive(DocxRow)] |
easydoc-derive |
Derive macro |
DocBuilder, Paragraph, Run, Table |
easydoc-writer |
Write builders |
DocReadBuilder, ViewMode, DocxSaxReader |
easydoc-reader |
Read builders |
MarkdownBuilder, MarkdownResult |
easydoc-markdown |
Markdown conversion |
TemplateFillBuilder, Placeholder |
easydoc-template |
Template fill |
AtomicFile, PackageLimits |
easydoc-ooxml |
OOXML internals |
5. Quick Start
5.1 Installation
[dependencies]
easydoc = "0.1.0-alpha.1"
5.2 Write a Table from Struct Data
use easydoc::prelude::*;
#[derive(DocxRow)]
#[docx(banded_rows = true)]
struct User {
#[docx(name = "Name", order = 0, width = "30%")]
name: String,
#[docx(name = "Age", order = 1, width = "15%")]
age: u32,
#[docx(name = "Email", order = 2, width = "55%")]
email: String,
}
let users = vec![
User { name: "Alice".into(), age: 30, email: "alice@example.com".into() },
User { name: "Bob".into(), age: 25, email: "bob@example.com".into() },
];
EasyDoc::write_table("users.docx", &users)
.title("User Report")
.banded_rows(true)
.do_write()?;
# Ok::<(), easydoc::DocError>(())
5.3 Read a Document (Streaming, O(1) Memory)
use easydoc::prelude::*;
let text = EasyDoc::read_text("document.docx")?;
let tables: Vec<Vec<User>> = EasyDoc::read_tables::<User>("document.docx")?;
struct MySink;
impl EventSink for MySink {
fn on_event(&mut self, event: &DocumentEvent) -> easydoc::Result<()> {
match event {
DocumentEvent::Heading { level, runs } => {
let text: String = runs.iter().map(|r| r.text.as_str()).collect();
println!("H{level}: {text}");
}
_ => {}
}
Ok(())
}
}
EasyDoc::read_events("large.docx", &mut MySink)?;
# Ok::<(), easydoc::DocError>(())
5.4 Convert to Markdown
use easydoc::prelude::*;
let markdown = EasyDoc::to_markdown("document.docx")?;
let result = EasyDoc::markdown("document.docx")
.image_directory("output/assets")
.include_front_matter(true)
.write_to("output/document.md")?;
# Ok::<(), easydoc::DocError>(())
5.5 Build a Full Document
use easydoc::prelude::*;
EasyDoc::document("report.docx")
.title("Annual Report")
.author("Zhang San")
.add_heading("Chapter 1", HeadingLevel::H1)
.add_paragraph(
Paragraph::new()
.add_text("Body text with ")
.add_run(Run::new("bold").bold())
.add_text(" content.")
)
.add_page_break()
.build()?
.save()?;
# Ok::<(), easydoc::DocError>(())
5.6 Template Fill
use easydoc::EasyDoc;
use std::collections::HashMap;
let mut data = HashMap::new();
data.insert("name".into(), "Alice".into());
data.insert("date".into(), "2026-08-11".into());
EasyDoc::fill_template("template.docx", "output.docx", &data)?;
# Ok::<(), easydoc::DocError>(())
5.7 Semantic Model Round-Trip
use easydoc::EasyDoc;
let mut content = EasyDoc::load("input.docx")?;
EasyDoc::write_content(&content, "output.docx")?;
let bytes = EasyDoc::write_content_to_bytes(&content)?;
# Ok::<(), easydoc::DocError>(())
6. EasyDoc API Reference
6.1 Write APIs
| Method |
Returns |
Description |
EasyDoc::document(path) |
DocBuilder |
Build a full document (headings, paragraphs, tables, images) |
EasyDoc::write_table(path, &data) |
TableWriteBuilder |
Write Vec<Struct> as a DOCX table (T: DocxRow) |
EasyDoc::document_to_bytes(f) |
Result<Vec<u8>> |
Build document to in-memory bytes |
EasyDoc::write_table_to_bytes(data) |
Result<Vec<u8>> |
Write table to in-memory bytes |
EasyDoc::edit(path) |
Result<DocEditor> |
Open existing DOCX for text replacement |
EasyDoc::fill_template(tpl, out, &data) |
Result<()> |
Fill scalar {key} placeholders |
EasyDoc::fill_template_list(tpl, out, &[T], field) |
Result<()> |
Fill collection {.field} placeholders |
6.2 Read APIs
| Method |
Returns |
Description |
EasyDoc::read(path) |
DocReadBuilder |
Streaming reader builder |
EasyDoc::read_text(path) |
Result<String> |
Quick plain text extraction |
EasyDoc::read_tables::<T>(path) |
Result<Vec<Vec<T>>> |
Typed table extraction (T: DocxRow) |
EasyDoc::read_events(path, &mut sink) |
Result<()> |
SAX event streaming (O(1) memory) |
EasyDoc::view_as(path, &ViewMode) |
Result<String> |
Multi-mode view rendering |
6.3 Markdown APIs
| Method |
Returns |
Description |
EasyDoc::markdown(path) |
MarkdownBuilder |
Markdown conversion builder |
EasyDoc::to_markdown(path) |
Result<String> |
Quick Markdown conversion |
EasyDoc::write_markdown(src, out) |
Result<MarkdownResult> |
Convert and write to file |
6.4 Semantic Model APIs
| Method |
Returns |
Description |
EasyDoc::load(path) |
Result<DocumentContent> |
Read into semantic document model |
EasyDoc::write_content(content, path) |
Result<()> |
Write semantic model to file |
EasyDoc::write_content_to_bytes(content) |
Result<Vec<u8>> |
Write semantic model to memory |
6.5 ViewMode (4 Modes)
| Mode |
Constructor |
Output Example |
| Plain |
ViewMode::Plain |
Hello world\nNext paragraph |
| Annotated |
ViewMode::Annotated |
[Heading1] Title\n[Paragraph 1] Hello\n[Table 1: 3x4] |
| Outline |
ViewMode::Outline { max_level: 3 } |
# H1 Title\n## H2 Subtitle |
| Stats |
ViewMode::Stats |
Paragraphs: 12\nTables: 3\nWords: 1500 |
7. Derive Macro
#[derive(DocxRow)] generates schema(), from_row(), to_row(), and their converter-aware variants automatically.
7.1 Struct-Level Attributes
| Attribute |
Type |
Example |
Effect |
banded_rows |
bool |
#[docx(banded_rows = true)] |
Zebra striping |
table_width / auto_width |
bool |
#[docx(table_width = Auto)] |
Auto-fit table width |
7.2 Field-Level Attributes
| Attribute |
Type |
Example |
Effect |
name |
string |
#[docx(name = "Full Name")] |
Column header text |
index |
usize |
#[docx(index = 0)] |
Zero-based column index |
order |
u32 |
#[docx(order = 1)] |
Column sort order (lower = leftmost) |
width |
string |
#[docx(width = "2cm")] |
Column width: "2cm", "80px", "50%", "auto" |
format |
string |
#[docx(format = "#,##0.00")] |
Number/date format string |
align |
string |
#[docx(align = "right")] |
"left", "center", "right", "both" |
wrap |
bool |
#[docx(wrap = true)] |
Text wrapping in cells |
converter |
type path |
#[docx(converter = MyConverter)] |
Custom DocConverter<T> |
ignore |
flag |
#[docx(ignore)] |
Skip field during read/write |
7.3 Annotation to OOXML Mapping
| Annotation |
OOXML Output |
width="2cm" |
<w:tcW w:w="..." w:type="dxa"/> |
format="#,##0.00" |
<w:numFmt w:val="..."/> |
align="right" |
<w:jc w:val="right"/> |
wrap=false |
<w:noWrap/> |
8. Error Model
All operations return easydoc::Result<T> (alias for Result<T, DocError>).
| Variant |
Scenario |
Retry? |
Source |
DocError::Io |
File or network I/O |
Depends |
std::io::Error |
DocError::Zip |
ZIP archive corruption |
No |
zip::ZipError |
DocError::Format |
Invalid or unsupported format |
No |
-- |
DocError::Template |
Placeholder parse/process error |
No |
-- |
DocError::Conversion |
Cell/field value conversion failure |
No |
-- |
DocError::Unsupported |
Operation not supported by format |
No |
-- |
DocError::Document |
General document-level error |
No |
-- |
9. Security & Resource Limits
| Limit |
Default |
| Max ZIP entries |
10,000 |
| Max single entry size |
256 MB |
| Max total expanded size |
1 GB |
| Max compression ratio |
1,000:1 |
| Output strategy |
atomic (temp file + persist) |
unsafe_code |
forbid (workspace-wide) |
10. Build, Test & Quality Gates
10.1 Basic Gates
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace
cargo doc --workspace --all-features --no-deps
10.2 Test Matrix
| Type |
Purpose |
Command |
| Unit tests |
Core logic |
cargo test |
| Doc tests |
API examples |
cargo test --doc |
| Integration tests |
Cross-crate collaboration |
cargo test --workspace |
| Clippy |
Lint gate |
cargo clippy -- -D warnings |
11. Upstream Compatibility
easydoc-rust is the DOC/DOCX counterpart of easyexcel-rust, sharing the same architecture: fluent builder + derive macro + converter registry.
11.1 Compatibility Target
| Dimension |
Value |
| Upstream project |
Java EasyExcel / Hutool |
| Authoritative version |
EasyExcel 4.0.3 |
| Rust goal |
Idiomatic API mapping (not ABI or bytecode compatibility) |
| Non-goal |
JVM reflection, dynamic proxy, platform GUI |
11.2 Object & Method Mapping
| Java EasyExcel |
Rust easydoc |
Status |
EasyExcel factory |
EasyDoc static factory |
Stable |
ExcelReader / ReadListener<T> |
DocReadBuilder / EventSink / DocReadListener<T> |
Stable |
ExcelWriter / WriteHandler |
DocBuilder / DocWriteHandler |
Stable |
@ExcelProperty annotation |
#[docx(...)] derive attributes |
Stable |
Converter<T> interface |
DocConverter<T> trait + ConverterRegistry |
Stable |
ExcelDataConvertException |
DocError::Conversion |
Stable |
ByteArrayOutputStream |
document_to_bytes() / write_table_to_bytes() |
Stable |
fill() template |
EasyDoc::fill_template() |
Stable |
11.3 Language Semantic Mapping
| Java Mechanism |
Rust Design |
Reason |
| Exceptions |
Result<T, DocError> |
Explicit error propagation |
null |
Option<T> |
Null-safety |
| Annotations |
#[derive(DocxRow)] + attributes |
Compile-time metadata |
| Reflection |
Trait + ConverterRegistry |
No runtime reflection |
| Inheritance |
Trait + composition |
Explicit capability boundaries |
12. Related Projects
13. License
Apache-2.0 -- see LICENSE for details.
Back to top · docs.rs · crates.io · Issues