Skip to content

refactor(api)!: one way to open a file - #836

Merged
andiwand merged 2 commits into
mainfrom
refactor/one-way-to-open
Sep 6, 2026
Merged

refactor(api)!: one way to open a file#836
andiwand merged 2 commits into
mainfrom
refactor/one-way-to-open

Conversation

@andiwand

@andiwand andiwand commented Sep 6, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

PR 6 of the v7 API plan.
Stacked on #835 — dropping DocumentFile::type/::meta there is what
leaves these constructors with no callers. Review #835 first; base retargets to
main when it merges.

odr::open had a twin

odr.cpp:203-230 was six one-line return DecodedFile(...). Not a wrapper with
a purpose — the same call under a second name, and the split had already spread:

  • JNI exposed both (Odr.open(path) and new DecodedFile(path)), with
    the javadoc admitting it: "Obtain via Odr#open or the constructors"
  • Apple went through the constructors
  • Python bound five DecodedFile.__init__ overloads and six open functions

The bodies move into odr::open. DecodedFile keeps only the constructor that
wraps an already-decoded impl.

DocumentFile was never a second kind of open

DocumentFile::from_disk, ::from_memory, DocumentFile(File) and
DocumentFile(path) all reached open_strategy::open_document_file — which
repeats open_file's cascade engine for engine for zip and cfb, differing
only in the fallback, and both ends throw NoDocumentFile. as_document_file()
throws it too (file.cpp:278).

So open(f).as_document_file() is not an approximation of DocumentFile(f)
same engines, same order, same exception. 73 lines of open_strategy.cpp go
with it
(737 → 664).

Bindings

gone replacement
Java new DecodedFile(path), (path, as), (file), new DocumentFile(path) Odr.open(...), .asDocumentFile()
Python DecodedFile(...) ×5, DocumentFile(...) ×2, DocumentFile.from_disk / .from_memory pyodr.open(...), .as_document_file()
ObjC surface unchanged; decode(...) now calls odr::open

Java gains Odr.open(File), which is exactly what the removed
DecodedFile(File) constructor was — otherwise an opened File would have had
no public way to decode.

About the test churn

~85 call sites moved. I rewrote them with a balanced-paren pass rather than a
regex, because a first attempt with a naive regex silently mangled a function
declaration (DecodedFile csv_file(const std::uint32_t rows, ...) became
DecodedFile csv_file = open(const std::uint32_t rows, ...)). The final pass
guards against parameter-declaration shapes and leaves impl-handle
constructions alone — DecodedFile(xml_file(content)) and the rtf/svg cases
pass a shared_ptr to an internal impl, so they keep the impl constructor.

Two internal tests called open_strategy::open_document_file directly and now
go through open_file + as_document_file().

Verified

Full build clean. Full gtest suite: 1456 passed, 6 skipped (the same six
that skip on main). 69 python tests and the JNI junit suite pass.

Migration

DecodedFile(path)                →  odr::open(path)
DecodedFile(file, as)            →  odr::open(file, as)
DocumentFile(path)               →  odr::open(path).as_document_file()
DocumentFile::from_memory(bytes) →  odr::open(File::from_memory(bytes)).as_document_file()
new DecodedFile(path)   →  Odr.open(path)
new DecodedFile(file)   →  Odr.open(file)
new DocumentFile(path)  →  Odr.open(path).asDocumentFile()

@andiwand
andiwand force-pushed the refactor/one-way-to-detect branch from 3ba5f19 to 0e2a00c Compare September 6, 2026 13:36
Base automatically changed from refactor/one-way-to-detect to main September 6, 2026 13:37
andiwand and others added 2 commits September 6, 2026 15:40
odr::open was six one-line forwards to six DecodedFile constructors, and the
split had already reached the bindings: jni exposed both, apple went through
the constructors, python bound each. The bodies move to open and the
constructors go; DecodedFile keeps only the one that wraps a decoded impl.

DocumentFile loses its factories and its decoding constructors with them. It
was never a second kind of open - open_document_file repeated open_file's
cascade engine for engine for zip and cfb, and both ends threw NoDocumentFile,
which as_document_file() throws too. open(...).as_document_file() is the same
call, so 73 lines of open_strategy go as well.

Java gains Odr.open(File), which is what the removed DecodedFile(File)
constructor was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016hxDa2rev11eLUEJZJ5nmz
Its open() and openAs() built the handle directly rather than going through
odr::open. Qualified, because the file has an open() of its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016hxDa2rev11eLUEJZJ5nmz
@andiwand
andiwand force-pushed the refactor/one-way-to-open branch from ca771d0 to 25dd861 Compare September 6, 2026 13:46
@andiwand
andiwand merged commit 130dbb6 into main Sep 6, 2026
23 checks passed
@andiwand
andiwand deleted the refactor/one-way-to-open branch September 6, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant