moonods

    Typed OpenDocument spreadsheet (.ods) reader and writer with bounded, deterministic packages

    ods
    opendocument
    spreadsheet
    csv
    Download zip
    Version
    0.5.1
    License
    MIT
    Last updated
    yesterday
    Downloads
    161

    #MoonODS

    MoonBit 数据型 ODS 读写库:类型化工作簿、有界 ZIP/XML 读取、显式损失诊断、CSV 与表头记录。适合报表与离线数据处理,不是通用办公软件。

    #先确认版本与证据

    评审与验收索引按“要求—实现主张—可查证据—剩余限制”提供入口。

    本次文档整理基准为默认分支源码提交 999e1e2(模块版本 0.5.0 候选)。897c906 的复审代码及后续修复已在该提交历史中;2026-10-31 回应里的“不是 main 合并”描述的是当时快照,不是当前源码状态。

    对象本文的证据边界
    默认分支 0.5.0 候选源码已纳入复审修复;不能由此推断 registry 已发布
    Mooncakes 0.5.0尚未取得发布及干净消费者精确安装回执;源码版本号不是发布证明
    历史 170 / 182 阶段 LibreOffice GUI只证明对应阶段及样本;不能替代 0.5.0 候选的重新往返验证
    2026-10-31 保存输出审计绑定 897c906 的历史文件摘要;重读保存文件不是重新执行库、GUI 或完整符合性验证

    #0.5.0 发行候选源码

    当前默认分支包含基于公开提交 6dcb77df291acf8b592224c7ba94ce648f395cc2 的后续修订,模块版本为 0.5.0 发行候选,尚无新 registry 发布回执。内容及迁移边界见 0.5.0 说明。moon add sundaysebasidian-byte/moonods@0.4.0 不包含本轮修改。

    新增 Unicode XML 名称、UTF-8/BOM UTF-16、内部 DTD/实体/默认属性、非致命 xml:id 赋型报告、PI/notation/未读取外部依赖通知,以及安全附加包部件、manifest 与签名 XML 处理。签名不做密码学验证;未知部件与嵌入文档不解释其语义,转换时明确报告丢失。外部 XML 资源不联网获取,必须依赖其内容的导入明确拒绝。接口与限制见 XML处理器说明。

    当前不声明完整 ODF 或 XML 标准认证,也不以有限样本通过替代通用符合性证明。公式仅保存文本和缓存、检查外层前缀,不验证完整 OpenFormula,也不通用重算。

    #从当前默认分支源码运行

    使用 TOOLCHAIN.lock 固定的 MoonBit 0.10.20(moon 0.1.20261104、moonc v0.10.20+65efd5b93);项目内安装脚本 验证 SDK/core 双 SHA,不改全局配置。

    版本要求:MoonBit 0.10.20 及以上。 源码使用该版本的 #derive 语法,0.10.18 无法编译。锁定的上游依赖 moonbit-community/flate@0.9.0(截至 2026-11-11 的最新版 0.10.0 亦然)仍使用旧 derive(...) 写法,在 0.10.20 上能正常编译但会产生 deprecated_syntax 告警;由于它以工作区成员方式参与构建,本仓库的严格命令统一写作 --deny-warn --warn-list=-deprecated_syntax,moon fmt --check 也只针对本模块自己的包。fresh-clone.yml 同时在锁定 SDK 与最新官方 SDK 上运行,并每周定时执行。

    python3 scripts/install-ci-sdk.py --dest "$PWD/.toolchains/ci-sdk" export MOON_REGISTRY_API=1 export MOON_HOME="$PWD/.toolchains/ci-sdk" export PATH="$MOON_HOME/bin:$PATH" python3 scripts/restore_deps.py moon update moon test --target js moon run examples/generate --target js

    生成文件位于 examples/generated/。当前默认分支已包含这些修复;旧注册表包不包含本轮修订,新包可安装性须以官方注册表回执为准。

    #公共 API

    fn make_report() -> Bytes raise @ods.OdsError {
    let workbook = @ods.Workbook::new()
    let sheet = workbook.add_sheet("销售")
    sheet.set(0, 0, @ods.Cell::new(Text("十月销售")).styled(Header))
    sheet.merge(0, 0, 1, 2)
    sheet.set(1, 0, @ods.Cell::new(Date(2026, 10, 1)).styled(DateISO))
    sheet.set(1, 1, @ods.Cell::new(Number(399.88)).styled(Decimal2))
    sheet.set(2, 1, @ods.Cell::formula("of:=[.B2]*2", Number(799.76)))
    sheet.set_column_width(1, 45)
    workbook.to_ods()
    }

    在消费模块的 moon.pkg 导入 "sundaysebasidian-byte/moonods" @ods。上述 API 已包含在 0.4.0 注册表包;0.3.1 不含新增类型与格式 API。公开接口见 pkg.generated.mbti,独立模块测试见 tests/consumer。

    默认一律拒绝导入,要求显式确认接受数据型转换可能损失信息,即使输入只含支持类型也是如此。调用 @ods.read_ods(bytes, allow_formatting_loss=true) 显式接受后,得到 Workbook 及带表名、行列、错误码的损失诊断;默认保留值、公式文本/缓存及合并,丢弃样式。另设 preserve_basic_formatting=true 可保留可识别的五种预设样式、精确物理列宽及固定行高(mm/cm/in/pt/pc,0 < 长度 ≤ 500mm,最多 12 位十进制数字);未知格式仍报告损失。扩展类型另需 allow_extended_types=true。CSV 按文本导入,导出时丢失公式、类型、样式及 Empty/空字符串区别。公式缓存可能过期,编辑不会自动重算。

    #读取、修改和导出

    以下输入由项目合成 FODS 经 LibreOfficeDev 转换而来,来源见 SOURCES.md:

    mkdir -p /tmp/moonods-example-output ODS_INPUT=tests/reader-fixtures/external/libreoffice-dev-types.ods \ ODS_ACCEPT_LOSS=1 ODS_SET=0,1,0,8.5 \ ODS_OUTPUT=/tmp/moonods-example-output/edited.ods \ ODS_CSV=/tmp/moonods-example-output/details.csv \ ODS_CSV_CACHE=1 ODS_CSV_REJECT_FORMULA_TEXT=1 \ moon run examples/ods_tool --target js

    ODS_CSV_REJECT_FORMULA_TEXT=1 拒绝以公式符号开头的文本;默认保留原文本语义,类型化负数不会被当作文本公式拒绝。这个选项不是办公软件沙箱。

    #采购数量修正与预算复核

    先生成完全合成的三表模板,再读取订单数量修正 CSV:

    mkdir -p /tmp/moonods-procurement/input /tmp/moonods-procurement/output REPORT_OUTPUT=/tmp/moonods-procurement/input moon run examples/procurement --target js printf '订单,数量\r\nSYN-202610-001,5\r\nSYN-202610-004,0\r\n' > /tmp/moonods-procurement/changes.csv RECONCILE_INPUT=/tmp/moonods-procurement/input/procurement.ods \ RECONCILE_CHANGES=/tmp/moonods-procurement/changes.csv \ RECONCILE_OUTPUT=/tmp/moonods-procurement/output RECONCILE_ACCEPT_LOSS=1 \ moon run examples/reconcile --target js

    输出 reconciled.ods、details.csv、audit.csv。请使用新的输出目录;复用目录会替换同名输出,先保留旧文件。这个样例的采购额从 80300 分变为 100650 分,研发超预算 18750 分;audit.csv 记录 CSV 请求修改的订单,以及未请求但缓存实际变化的订单。输入可以替换为符合相同三表契约的 ODS,订单和汇总不是程序中写死的数据。

    这是固定模板的业务示例:数量 0..10000,金额以整数分计,总额不超过 2,000,000,000 分;只重算模板内已知的算术公式,遇到未知公式直接拒绝,不是通用采购系统或公式引擎。

    #验证与范围

    此前处理器及包层冻结阶段根包加独立消费者四后端各 176/176;独立包层审查另生成31份API夹具,首批29份执行58次CLI,13份成功导入编辑再导出的包通过CRC、XML值和CSV检查。独立 native 全包542项包含额外夹具/示例,与176统计范围不同。

    真实 LibreOffice 26.8.2.2 GUI 在此前170阶段完成类型、公式缓存和合并单元格闭环:修改 A1 后由 Calc 将 B1=A1*3 的缓存6重算12,库导出后再开仍保留公式和A2:B2合并。库没有计算公式。后续包层候选另读回同一Calc文件;这不是新版本再次GUI操作或所有Office兼容证明。详细版本绑定见技术证据。

    使用锁定 Node/Python 与 requirements-verify.txt,可运行 python scripts/acceptance.py --output /tmp/moonods-acceptance。源码CI配置见 .github/workflows/ci.yml;配置存在不等于本轮远端CI已通过。

    #边界

    • 默认拒绝有损读取,需要显式接受;基础格式与扩展类型分别显式开启。
    • 仅解释根 spreadsheet 数据模型。附加部件可安全解析包结构,但嵌入文档不验证 XML、不保留语义;签名丢弃且不验签。
    • 保留安全路径、CRC、条目数量、4 MiB总解压、XML深度/实体预算等资源限制;超限是实现限制,不称为标准禁止。
    • 支持类型、值、公式缓存、合并及有限基础格式,不承诺图片、宏、图表、全样式、加密、ZIP64或打印保真。
    • 没有 Microsoft Office 互通实测;金额使用binary64,非精确十进制金融计算。
    • 每个输出文件独立rename,多文件不是整体事务;业务模板只重算已知算术关系。
    • ods_tool 必须同时检查退出码与 stderr:输入已接受的 stdout 不等于后续导出成功。验证/用法错误在 stdout 报 ERROR,其他 MoonBit 捕获错误报 FAILED;Node 文件系统异常在 stderr 报错并退出 1。CLI 不是逐行 JSON 协议,不能只查找 stdout 的 ERROR 判断失败。

    #许可与来源

    原创代码 MIT;flate 依赖 Apache-2.0;SDK不打包。LICENSE、SOURCES.md、THIRD_PARTY_NOTICES.md、AI_USAGE.md。历史发行能力和性能数据保留在版本文档及历史技术索引,不当作当前修订的实测。

    #精确物理布局(有边界)

    set_column_width_length(col, "0.889in") / column_width_length(col) 与 set_row_height_length(row, "12.5pt") / row_height_length(row) 保留经验证的原始长度单位与十进制值,避免强制换算成整数毫米造成损失。旧 set_column_width(col, Int) 保持兼容;新长度只有恰好为整数毫米时,旧 column_width() 才返回整数,否则返回 None。同列最后一次 setter 生效。

    导入必须显式启用 preserve_basic_formatting=true,并仍确认 allow_formatting_loss=true。样式默认值和父样式先解析再决定是否可保留:固定行高不能有有效的 use-optimal-row-height=true;缺失行高不推测。输出固定布局显式写 optimal=false。未知属性、最小行高、相对宽度、自动适应和强制分页不假装保留。这里不是完整样式、字体、图片、对象或视觉布局保真。

    长度预算属于产品范围而不是 ODF 有效性规则:例如合法但超范围的 501mm/px 不在此 API 范围。拒绝零、负数、指数、百分比、超过 12 位数字与超过 500mm 的物理长度。失败 setter 不修改工作表。导入超范围布局给出损失诊断;不将它误称为无效 ODF。

    历史182布局阶段根包及消费者 JS / Wasm / WasmGC / native 各 182/182。独立审查另使用 31 份输入包逐一读改写并核对 Fraction 精确尺寸和输出 ODF 1.3 RNG;下游 API JS/native 各 9/9(其中 3 组为本次独立新增)。真实 Calc 产物原 0.889in 列宽可保留,optimal=true 的自动行高不误转为固定。上述结构验收不等于完整视觉保真。

    #新布局182阶段的实际GUI互通

    历史 182 阶段记录;本节不是 0.5.0 候选的再次 GUI 验证。

    官方LibreOffice26.8.2.2实际打开本阶段导出、另存、关闭再开,最后182再次读取Calc另存件并导出,再由GUI打开。A1修改7→8保持;原生另存XML列宽0.889in、固定行高0.2173in、optimal=false保持,独立Fraction尺寸差为0。界面以0.01in显示为0.89/0.22,不能把界面舍入读数当作底层XML精确值。对话框均取消未确认改变尺寸。该正控不证明所有视觉样式保真,仍有12条其他损失诊断。

    本次GUI精确绑定182 CLI SHA a60cad4b05d895b2df43e1b27c22b5b5050f7fd122c0a6cc0f7d7bc7039eef62,并核对161份源码清单零差异;这与此前170阶段公式/合并GUI是两个独立回执。182阶段集成23步骤验收通过,源码hash结束逐项无差异;这是历史182回执,不代表后续189阶段的GUI或远端CI。

    #Subsequent normative regression fixes

    Physical length setters reject leading plus and non-XML surrounding whitespace; accepted tokens are checked against external ODF1.3 RNG cases. Paragraph raw XML whitespace collapses across spans according to Part3 section6.1.2, while explicit text:s/tab/line-break and NBSP remain. Text containing CR is represented with office:string-value numeric references rather than a separate display paragraph; this preserves data, not display fidelity. Historical182/GUI evidence remains historical; validation results apply only to their tested source revision.

    189修订四后端根包及consumer各189/189;独立14段落首读/再读28次CLI与18份真实API导出RNG通过。+1mm拒绝,.5mm、1.mm和合法前导零按外部schema保留;只trim XML空白,不接受NBSP/EM SPACE等包围长度。CR数据属性路径已用独立负控确认:改/删属性或加入冲突段落均被验收拒绝。普通Text显式控制与NBSP保留。旧GUI测试不外推此新CR表示。

    最终189集成严格23步骤全部通过,结束后全部源码/脚本哈希与运行起点一致。此运行使用固定SDK和锁定依赖;新GUI及远端CI未在该验收中运行。原错误oracle和环境依赖失败记录与修后结果分别保存,不覆盖历史。

    #Typed lexical normalization

    The importer applies XML Schema / RNG token whitespace collapse at typed boundaries using only XML SPACE/TAB/CR/LF. It does not use Unicode trim: NBSP and other non-XML whitespace remain significant and invalid in numeric/token values. Numeric values (including percentages and currency), boolean tokens, supported date/time values, and the value-type token are normalized before their existing lexical/range checks. Precision diagnostics compare normalized decimal identities; finite-number, underflow, and monetary exactness safeguards remain in force.

    Repeat/span positiveInteger values accept a single leading + and surrounding XML whitespace. Date components still use the strict digit-only parser after whole-date normalization, so 2026-+10-30 remains invalid. text:s uses a separate nonNegativeInteger parser, allowing zero (including lexical -0), while repeated rows/columns/spans remain strictly positive. Resource limits are unchanged. Physical length strings still reject a leading + because ODF positiveLength uses a different lexical definition.

    Boolean import follows the ODF RNG token values true and false; the former nonstandard 1/0 compatibility acceptance is intentionally removed. office:string-value and ordinary strings are not collapsed by this typed normalization.

    Non-finite numeric tokens remain unsupported; numeric diagnostics distinguishes legal unsupported values from malformed lexical forms.

    For string cells, explicit office:string-value is authoritative under ODF 1.3 Part 3 §19.389, even if validated paragraph display text differs. Without the attribute, the paragraph content defines the value. Distinct present display text is not retained by the data model and is reported as STRING_DISPLAY_TEXT_DROPPED; malformed or unsupported display content is still rejected. This is typed-data preservation, not display fidelity.

    Repeated rows containing horizontal-only merges are supported within the existing grid and cell budgets (ODF 1.3 §19.681). Repeated merged cells across columns and row repetition containing vertical merges remain rejected (§19.679.3/§19.681). Covered-cell positions must still be explicit and consistent; this does not add arbitrary layout preservation.

    OdsError

    pub suberror OdsError {
    Invalid(String)
    } derive(
    Debug
    )

    A recoverable validation error; no user input is inserted as raw XML.

    OdsError::is_unread_xml_dependency

    fn OdsError::is_unread_xml_dependency(self : OdsError) -> Bool

    Stable classification for a recognized-but-unread XML dependency. This is unsupported input, not evidence that its XML is malformed.

    OdsError::to_repr

    Cell

    type Cell

    Cell::excel_1900_date

    fn Cell::excel_1900_date(year : Int, month : Int, day : Int) -> Cell raise OdsError

    Optional date policy for Excel's 1900 date system, with an ISO date style. Rejects dates before 1900-01-01 and all invalid Gregorian dates, including Excel's fictional 1900-02-29. Does not configure a reader's date system or guarantee rendering. For historical display, callers may explicitly use Text("0001-01-01"), which has no date arithmetic semantics.

    Cell::expression

    fn Cell::expression(self : Cell) -> String?

    Cell::formula

    fn Cell::formula(expression : String, cache : Value) -> Cell raise OdsError

    OpenFormula text must begin of:=. This validates storage, not formula syntax. The cache is supplied by the caller and may be stale. Neither editing other cells nor serialization recalculates it; application open/save may retain it. Formula text is stored verbatim, including possible external references. Only pass trusted expressions and manage cache freshness explicitly. Date caches default to DateISO so readers keep the date presentation when evaluating DATE formulas. Callers may still explicitly override the style.

    Cell::new

    fn Cell::new(value : Value) -> Cell raise OdsError

    Validate ODF values; application-specific date ranges are not inferred.

    Cell::style

    fn Cell::style(self : Cell) -> Style

    Current normalized preset, not an arbitrary source style definition.

    Cell::styled

    fn Cell::styled(self : Cell, style : Style) -> Cell

    Cell::value

    fn Cell::value(self : Cell) -> Value

    Returns the stored literal value or caller/imported formula cache. This does not evaluate the formula or assert that its cache matches current operands.

    ImportDiagnostic

    pub(all) struct ImportDiagnostic {
    sheet : String
    row : Int
    col : Int
    code : String
    message : String
    } derive(Eq,
    Debug
    )

    Coordinates are zero-based. -1 means workbook/sheet scope.

    ImportDiagnostic::equal

    ImportDiagnostic::not_equal

    fn ImportDiagnostic::not_equal(x : ImportDiagnostic, y : ImportDiagnostic) -> Bool

    ImportResult

    type ImportResult

    ImportResult::diagnostics

    fn ImportResult::diagnostics(self : ImportResult) -> Array[ImportDiagnostic]

    A copy: callers cannot mutate the reader's diagnostic record.

    ImportResult::workbook

    fn ImportResult::workbook(self : ImportResult) -> Workbook

    ImportResult::xml_events

    ImportResult::xml_id_diagnostics

    fn ImportResult::xml_id_diagnostics(self : ImportResult) -> Array[XmlIdDiagnostic]

    ImportResult::xml_ids

    Sheet

    type Sheet

    Sheet::column_width

    fn Sheet::column_width(self : Sheet, col : Int) -> Int?

    Explicit integer-mm width. None means no retained/requested fixed width.

    Sheet::column_width_length

    fn Sheet::column_width_length(self : Sheet, col : Int) -> String?

    Exact stored token, or integer-mm token for the legacy width API.

    Sheet::dimensions

    fn Sheet::dimensions(self : Sheet) -> (Int, Int)

    Sheet::get

    fn Sheet::get(self : Sheet, row : Int, col : Int) -> Cell?

    Sheet::is_covered

    fn Sheet::is_covered(self : Sheet, row : Int, col : Int) -> Bool

    Sheet::merge

    fn Sheet::merge(self : Sheet, row : Int, col : Int, rows : Int, cols : Int) -> Unit raise OdsError

    Merge a zero-based rectangle. Covered values are never silently discarded.

    Sheet::merge_span

    fn Sheet::merge_span(self : Sheet, row : Int, col : Int) -> (Int, Int)?

    Sheet::name

    fn Sheet::name(self : Sheet) -> String

    Sheet::records

    fn Sheet::records(self : Sheet, header_row? : Int) -> Array[TableRecord] raise OdsError

    Exact, case-sensitive header names. Every header must be a nonblank Text literal and unique. Rejects merged sheets rather than guessing fill-down. Includes blank data rows; absent cells map to Empty, Text("") stays distinct.

    Sheet::row_height_length

    fn Sheet::row_height_length(self : Sheet, row : Int) -> String?

    Sheet::set

    fn Sheet::set(self : Sheet, row : Int, col : Int, cell : Cell) -> Unit raise OdsError

    Zero-based coordinate. Validation precedes mutation.

    Sheet::set_column_width

    fn Sheet::set_column_width(self : Sheet, col : Int, mm : Int) -> Unit raise OdsError

    Emit the requested ODF fixed column length in integer millimeters (1..500). Viewer rounding, font metrics, zoom and physical print layout are not guaranteed. No app-specific pixel/point compensation is applied.

    Sheet::set_column_width_length

    fn Sheet::set_column_width_length(self : Sheet, col : Int, length : String) -> Unit raise OdsError

    Set an exact physical column length in mm/cm/in/pt/pc (0 < mm <= 500). At most 12 decimal digits; no sign, exponent, percent or pixel lengths. The validated token is retained; no DPI, rounding or visual-fit promise.

    Sheet::set_row

    fn Sheet::set_row(self : Sheet, row : Int, start_col : Int, cells : Array[Cell]) -> Unit raise OdsError

    Write a contiguous row atomically. All cells are validated before mutation, including merge coverage and the net text budget after replacements. An empty array is a no-op at a valid coordinate and does not grow the grid.

    Sheet::set_row_height_length

    fn Sheet::set_row_height_length(self : Sheet, row : Int, length : String) -> Unit raise OdsError

    Set a fixed physical row height. Automatic/content-fit heights are not modeled.

    Sheet::to_csv

    fn Sheet::to_csv(self : Sheet, use_formula_cache? : Bool, reject_formula_like_text? : Bool, extended_types_as_text? : Bool) -> String raise OdsError

    Deterministic comma CSV, quoted fields and CRLF records. Dates use ISO dates, booleans true/false, numbers binary64 text. Empty and Text("") both emit "". Merges are rejected. Formula caches require an explicit opt-in; formulas, types, styles and sheet metadata cannot survive a CSV roundtrip. External spreadsheet applications may infer types/formulas from CSV text. use_formula_cache exports stored, possibly stale caches without recalculation. reject_formula_like_text skips only leading ASCII space, TAB, CR and LF, then rejects Text values starting =, +, - or @. It never changes data or rejects typed negative numbers, and is a bounded policy rather than an application sandbox. The default preserves the existing lossless text semantics.

    Style

    pub(all) enum Style {
    Plain
    Header
    Highlight
    Decimal2
    DateISO
    } derive(Eq,
    Debug
    )

    Small interoperable style vocabulary; arbitrary properties are outside MVP.

    Style::equal

    fn Style::equal(Style, Style) -> Bool

    Style::not_equal

    fn Style::not_equal(x : Style, y : Style) -> Bool

    Style::to_repr

    TableRecord

    pub(all) struct TableRecord {
    row : Int
    values : Map[String, Value]
    }

    A snapshot of literal values or stored formula caches, with zero-based row. Mapping does not evaluate formulas or fill merged cells.

    Value

    pub(all) enum Value {
    Empty
    Text(String)
    Number(Double)
    Percentage(Double)
    Currency(Double, String)
    Duration(String)
    DateTime(String)
    Boolean(Bool)
    Date(Int, Int, Int)
    } derive(Eq,
    Debug
    )

    Formula cache values have the same types as literal values. Date is a Gregorian ODF date in 1..9999, independent of an application's serial date epoch. It does not promise Excel supports historical dates.

    Value::equal

    fn Value::equal(Value, Value) -> Bool

    Value::not_equal

    fn Value::not_equal(x : Value, y : Value) -> Bool

    Value::to_repr

    Workbook

    type Workbook

    Workbook::add_csv_sheet

    fn Workbook::add_csv_sheet(self : Workbook, name : String, text : String) -> Sheet raise OdsError

    RFC4180 quoting with CRLF or LF record separators; bare CR is rejected. Every field becomes Text (including empty fields and leading '='). No type guessing or formula evaluation. Empty input imports one empty text cell. Parsing/validation finishes before the workbook is mutated.

    Workbook::add_sheet

    fn Workbook::add_sheet(self : Workbook, name : String) -> Sheet raise OdsError

    Workbook::content_xml

    fn Workbook::content_xml(self : Workbook) -> String raise OdsError

    Inspectable XML for diagnostics. ODS output is provided by to_ods().

    Workbook::new

    fn Workbook::new() -> Workbook

    Workbook::sheet

    fn Workbook::sheet(self : Workbook, index : Int) -> Sheet?

    Workbook::sheet_count

    fn Workbook::sheet_count(self : Workbook) -> Int

    Workbook::to_ods

    fn Workbook::to_ods(self : Workbook) -> Bytes raise OdsError

    Return a deterministic ODS within the reader's bounded output profile: 4 MiB total uncompressed entry bytes and the existing XML depth/node caps. Grid/text limits alone do not guarantee serializability. Oversized output raises before returning any package; I/O belongs to the caller.

    XmlIdAssignment

    pub(all) struct XmlIdAssignment {
    document_part : String
    element_index : Int
    element_name : String
    attribute_name : String
    value : String
    attribute_type : String
    valid_ncname : Bool
    unique : Bool
    declared_type : String?
    } derive(Eq,
    Debug
    )

    XML element indices are zero-based preorder within document_part. Type assignment is always ID, including values with reported xml:id errors. This is import metadata, not a promise to preserve IDs on data-only export.

    XmlIdAssignment::equal

    XmlIdAssignment::not_equal

    fn XmlIdAssignment::not_equal(x : XmlIdAssignment, y : XmlIdAssignment) -> Bool

    XmlIdDiagnostic

    pub(all) struct XmlIdDiagnostic {
    document_part : String
    element_index : Int
    value : String
    code : String
    message : String
    } derive(Eq,
    Debug
    )

    Non-fatal xml:id constraint diagnostic; XML well-formedness errors remain the responsibility of the enclosing XML parser, not this diagnostic layer.

    XmlIdDiagnostic::equal

    XmlIdDiagnostic::not_equal

    fn XmlIdDiagnostic::not_equal(x : XmlIdDiagnostic, y : XmlIdDiagnostic) -> Bool

    XmlProcessorEvent

    pub(all) struct XmlProcessorEvent {
    document_part : String
    sequence : Int
    location : String
    kind : String
    name : String
    data : String
    public_id : String?
    system_id : String?
    notation : String?
    } derive(Eq,
    Debug
    )

    XML processor observations, scoped and ordered within a document part. PI data excludes its separating whitespace. External identifiers are metadata, never fetched. Event records do not imply data-only export preserves metadata.

    XmlProcessorEvent::equal

    XmlProcessorEvent::not_equal

    fn XmlProcessorEvent::not_equal(x : XmlProcessorEvent, y : XmlProcessorEvent) -> Bool

    read_ods

    fn read_ods(data : Bytes, allow_formatting_loss? : Bool, allow_extended_types? : Bool, preserve_basic_formatting? : Bool) -> ImportResult raise OdsError

    Import a bounded, data-only ODS subset. The explicit opt-in acknowledges formatting, application metadata and view settings are not retained. Unsupported semantic features fail, with sheet/cell context where known. No partial workbook is returned on failure; formulas are never evaluated.