undocx 0.5.2

DOCX to Markdown converter written in Rust
Documentation
use undocx::{ConvertOptions, DocxToMarkdown};
use rs_docx::document::{
    AbstractNum, AbstractNumId, BodyContent, Comment, CommentReference, Comments, FootNote,
    FootNotes, FootnoteReference, Level, LevelStart, LevelText, Num, NumFmt, Numbering, Paragraph,
    Run, RunContent,
};
use rs_docx::formatting::{NumberingProperty, ParagraphProperty};
use rs_docx::Docx;
use std::borrow::Cow;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};

fn temp_docx_path(prefix: &str) -> PathBuf {
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system time must be after UNIX_EPOCH")
        .as_nanos();
    std::env::temp_dir().join(format!(
        "undocx_regression_{}_{}_{}.docx",
        prefix,
        std::process::id(),
        nanos
    ))
}

#[test]
fn generated_docx_deep_list_indentation_regression() {
    let mut docx = Docx {
        numbering: Some(Numbering {
            abstract_numberings: vec![AbstractNum {
                abstract_num_id: Some(1),
                levels: vec![Level {
                    i_level: Some(3),
                    start: Some(LevelStart { value: Some(1) }),
                    number_format: Some(NumFmt {
                        value: Cow::Borrowed("decimal"),
                    }),
                    level_text: Some(LevelText {
                        value: Some(Cow::Borrowed("%4.")),
                    }),
                    ..Default::default()
                }],
                ..Default::default()
            }],
            numberings: vec![Num {
                num_id: Some(2),
                abstract_num_id: Some(AbstractNumId { value: Some(1) }),
                ..Default::default()
            }],
        }),
        ..Default::default()
    };

    let para = Paragraph::default()
        .property(ParagraphProperty {
            numbering: Some(NumberingProperty::from((2isize, 3isize))),
            ..Default::default()
        })
        .push_text("Deep Item");
    docx.document.push(para);

    let path = temp_docx_path("deep_list");
    docx.write_file(&path)
        .expect("failed to write generated docx");

    let converter = DocxToMarkdown::new(ConvertOptions::default());
    let markdown = converter
        .convert(&path)
        .expect("failed to convert generated deep-list docx");

    let _ = std::fs::remove_file(&path);

    let first_line = markdown.lines().next().unwrap_or("");
    assert_eq!(first_line, "      1. Deep Item");
}

#[test]
fn generated_docx_duplicate_note_comment_references_regression() {
    let footnote_body = Paragraph::default().push_text("Same footnote text");
    let comment_body = Paragraph::default().push_text("Shared comment");
    let mut docx = Docx {
        footnotes: Some(FootNotes {
            content: vec![FootNote {
                id: Some(5),
                content: vec![BodyContent::Paragraph(footnote_body)],
                ..Default::default()
            }],
        }),
        comments: Some(Comments {
            comments: vec![Comment {
                id: Some(9),
                author: Cow::Borrowed("tester"),
                content: comment_body,
            }],
        }),
        ..Default::default()
    };

    let footnote_refs = Paragraph::default()
        .push(
            Run::default().push(RunContent::FootnoteReference(FootnoteReference {
                id: Some(Cow::Borrowed("5")),
                ..Default::default()
            })),
        )
        .push(
            Run::default().push(RunContent::FootnoteReference(FootnoteReference {
                id: Some(Cow::Borrowed("5")),
                ..Default::default()
            })),
        );
    docx.document.push(footnote_refs);

    let comment_refs = Paragraph::default()
        .push(
            Run::default().push(RunContent::CommentReference(CommentReference {
                id: Some(Cow::Borrowed("9")),
            })),
        )
        .push(
            Run::default().push(RunContent::CommentReference(CommentReference {
                id: Some(Cow::Borrowed("9")),
            })),
        );
    docx.document.push(comment_refs);

    let path = temp_docx_path("notes_comments");
    docx.write_file(&path)
        .expect("failed to write generated docx");

    let converter = DocxToMarkdown::new(ConvertOptions::default());
    let markdown = converter
        .convert(&path)
        .expect("failed to convert generated note/comment docx");

    let _ = std::fs::remove_file(&path);

    assert!(markdown.contains("[^1][^1]"));
    assert!(markdown.contains("[^c9][^c9]"));
    assert_eq!(markdown.matches("[^1]:").count(), 1);
    assert_eq!(markdown.matches("[^c9]:").count(), 1);
    assert!(markdown.contains("[^1]: Same footnote text"));
    assert!(markdown.contains("[^c9]: Shared comment"));
}