Bindery i dokumenty bez kodu spajającego
The file was not the problem
The first mistake is thinking the document is the file. It arrives as a file, yes. It has a name, usually with an extension that looks reassuring. The procurement spreadsheet says .xlsx, the report says .docx, the archive dump says .pdf, and everyone pretends the world has become simple because the last four characters look familiar. Lovely. Then the extension lies, the workbook has hidden sheets, the slide deck contains embedded objects, the PDF is mostly text but not quite, and the old Word file still smells faintly of 2003.
Most teams do not build a document pipeline. They build a small museum of format exceptions. One library for Word, another for Excel, something else for PDFs, a heroic shell script for the archive folder, a Python package that was last updated when everyone still thought QR codes were exciting, and a few regular expressions that should be taken outside and given a quiet retirement. Six months later the glue layer is larger than the product. This is not a rare failure mode. This is the normal shape of document work when every format gets its own kingdom.
Bindery exists because the document layer should not become the main project. At the implementation level it isa Rust crate named dweve-bindery. The public page describes one crate with 17 formats, DocQL, Python bindings, and a common engine. The important part is that high-level APIs like Document, Presentation, and Workbook sit over format-specific modules for OLE2, OOXML, ODF, iWork, RTF, PDF, Markdown, EPUB, LaTeX, images, formulas, and DocQL. That list is not there to impress anyone. It is there because real corpora are rude.
The useful claim is simple: open the document through one engine, normalize what can be normalized, and keep the format-specific pain below a shared surface. That does not make every format identical. It makes the differences explicit enough that a pipeline can survive them.
Detection is not a decoration
Format detection sounds like a small utility until it fails. Then it becomes the whole incident. Extensions are metadata supplied by the person, tool, mail gateway, export job, migration script, or tired intern that last touched the file. Sometimes they are right. Sometimes they are a polite suggestion. A serious document engine should inspect magic bytes, container structure, package parts, streams, and internal cues before deciding which reader owns the file.
Bindery treats that as the front door. The README and page both describe automatic format detection. The library documentation shows Document::open and Presentation::open as the normal path, not a choose-your-parser ceremony. That matters because users do not want a training course in document archaeology before they can extract a table. They want the engine to pick the path and then give them a stable API.
There is a dry little lesson here. The less glamour a component has, the more damage it can do when people wave it away. Detection is notglamorous. Neither is encoding conversion, ZIP handling, OLE directory walking, relationship resolution, Snappy decompression, or XML namespace handling. Fine. The boring work is exactly where production pipelines either become reliable or begin collecting lucky charms.
One model does not mean one lie
A unified API can become dangerous when it pretends differences have vanished. Bindery should not claim that a PDF, a spreadsheet, an iWork archive, and an OOXML package are the same animal wearing different hats. They are not. The useful architecture is not to flatten truth into mush. It is to expose common operations where they are common and keep capability boundaries visible where they are not.
The source layout shows that split. There is a unified Word document API, a unified presentation API, spreadsheet traits, formula evaluation behind features, DocQL connectors, and lower-level modules for the formats themselves. The public format matrix says OOXML and ODF are first-class read, write, and query surfaces. PDF and RTF are read-heavy and more careful on writing. EPUB, LaTeX, and Markdown are output formats. Legacy Office and iWork have their own internal machinery. That is the right posture. One engine, yes. One fantasy, no.
This distinction matters in audits and data products. If a compliance workflow extracts clauses from contracts, it must know whether a value came from a paragraph, a table cell, a slide note, a formula, or a PDF text run. If an ingestion job feeds retrieval, it must know whether images, comments,metadata, and relationships were preserved, ignored, or marked as unsupported. The answer cannot be buried inside a parser-specific footnote, because that footnote will not show up when someone asks why the result changed.
Documents are structured data that forgot to admit it
The worst thing a document pipeline can do is reduce everything to text too early. Text is useful. Text is not the whole document. A spreadsheet has formulas, references, sheets, rows, cells, number formats, comments, and workbook structure. A presentation has slides, shapes, images, notes, ordering, and sometimes a corporate template that has survived three mergers and one rebrand through sheer malice. A Word document has paragraphs, runs, tables, headers, footers, styles, relationships, and embedded objects. A PDF has streams and layout decisions that may or may not correspond to reading order. Turning all of that into one flat string is fast, comforting, and often wrong.
Bindery’s high-level APIs are useful because they keep document shape alive long enough to ask better questions. The document API exposes paragraphs, runs, tables, rows, and cells. The spreadsheet module exposes workbook and worksheet traits. The formula engine covers a large Excel-compatible function surface. DocQL adds a SQL-like query language over the document model, with lexer, parser, validator, planner, executor, connectors, values, and functions in the source tree. That is more than a convenience wrapper. It is a way to stop rewriting the same extraction logic for every format.
Imagine asking one corpus question: which cells reference this assumption, which tables contain a risk category, which slides mention a policy, which documents have a custom property, and which formulas depend on a given input. In the per-format kingdom, that becomes four scripts and a spreadsheet of apologies. In a shared model, it becomes a query surface. Still work, obviously. Software rarely gifts you a holiday. But it is the right work.
Dlaczego Rust to rozsądne miejsce na ten bałagan
Formaty dokumentów to wspaniałe połączenie struktur binarnych, skompresowanych pakietów, XML, starszych kodowań, danych obrazowych, systemów dat, semantyki formuł i kwestii bezpieczeństwa. Innymi słowy, rodzaj pracy, w której niejasne zarządzanie pamięcią to styl życia z dołączonymi fakturami. Rust to rozsądna baza, ponieważ Bindery musi dokładnie parsować, zarządzać buforami, głośno obsługiwać błędy i udostępniać API, które nie zmuszają reszty stosu do zgadywania, co poszło nie tak.
Funkcje crate'a opowiadają tę samą historię. Domyślne funkcje obejmują OLE, OOXML, szyfrowanie OOXML i silnik eval. Pełne wsparcie włącza iWork, ODF, RTF, formuły, konwersję obrazów, czcionki i więcej. DocQL to funkcja z własnym plikiem binarnym. Opcjonalne zależności odpowiadają formatom i powierzchniom, które obsługują: obsługa ZIP, szybkie parsowanie XML, konwersja kodowań, Snappy, protobuf, dekodowanie obrazów, statystyka i liczby zespolone do pracy z formułami i tak dalej. To nie jest jeden wielki blok udający, że każda zależność należy wszędzie. Flagi funkcji utrzymują widoczny kształt silnika dokumentów.
To ma znaczenie dla osadzania. System wiedzy może chcieć pełnej powierzchni biurowej i zapytań. Mała usługa może chcieć tylko OOXML i ekstrakcji tekstu. Przepływ pracy w Pythonie może chcieć powiązań na tym samym silniku. Ścieżka inspekcji z linii poleceń może być przydatna do jednorazowych zapytań i testów. Strona mówi o Rust, PyO3 i powierzchni CLI; źródło pokazuje plik binarny DocQL i pakiet PyO3. Ważnym wyborem projektowym jest to, że te punkty wejścia siedzą na jednym silniku. W przeciwnym razie każda integracja staje się własną, nieco inną prawdą, a potem raporty o błędach zaczynają nosić różne kapelusze.
Historia utrzymania to historia produktu
Bindery łatwo opisać jako parser, ale historia utrzymania to prawdziwa historia produktu. Każda nowa biblioteka formatów dodana do potoku ma własne tempo wydań, słownictwo błędów, typy błędów, dziwactwa, ryzyko zależności, testowe pliki i tryby awarii. W małej skali to wygląda na zarządzalne. W skali korpusu staje się operacyjnym segregatorem, który gryzie.
Wspólny silnik nie usuwa złożoności formatów. To byłoby podejrzane. Przenosi złożoność w miejsce, gdzie testy, etykiety możliwości, funkcje i API mogą być zarządzane razem. README zawiera testy end-to-end dla dokumentów, prezentacji, arkuszy kalkulacyjnych, iWork i innych formatów. Drzewo źródłowe ma moduły, które czynią granice formatów oczywistymi. To właśnie ta struktura pozwala zespołowi ulepszać warstwę parsera bez zmuszania każdego zespołu produktowego do ponownego uczenia się różnicy między częścią relacji a strumieniem pliku złożonego.
To dlatego Bindery pasuje do reszty stosu Dweve. Reed zajmuje się parsowaniem z potwierdzeniami. BitWeave zajmuje się deterministycznym wyszukiwaniem. Fabric i Spindle zajmują się zarządzaną wiedzą i zastosowaniem operacyjnym. Bindery znajduje się przed tymi warstwami. Zamienia dokumenty biurowe w ustrukturyzowany materiał, na którym reszta stosu może się oprzeć. Jeśli warstwa wejściowa to klej i nadzieja, system poniżej dziedziczy klej i nadzieję. Bardzo wydajne, jeśli twoim strategicznym celem jest przyszłe cierpienie.
O co zapytać przed wdrożeniem
Pierwsze pytanie nie brzmi, czy Bindery obsługuje twój ulubiony format. To pytanie z listy kontrolnej, a listy kontrolne są dobre, ale to nie wystarczy. Lepiej zapytać, jakie obietnice dotyczące dokumentów są ci potrzebne. Czy potrzebujesz ekstrakcji tylko do odczytu, obsługi zapisu, zachowania wierności przy konwersji, obliczania formuł, zapytań między formatami, metadanych, osadzonych obrazów, zaszyfrowanego OOXML, starszych wersji Office, iWork, ODF, czy eksportu do EPUB, LaTeX i Markdown? To różne zadania. Udawanie, że to jedno zadanie, to sposób, w jaki plany rozwoju zamieniają się w zupę.
Drugie pytanie dotyczy sposobu ujawniania błędów. Jeśli parser nie może zachować struktury, czy to komunikuje? Jeśli zapis jest najlepszego wysiłku, czy to widać? Jeśli formuła nie może zostać obliczona, czy wywołujący może zdecydować, czy zablokować, ostrzec, czy kontynuować? Silnik dokumentów nie jest niezawodny, bo nigdy nie mówi nie. Jest niezawodny, bo jego odmowa jest typowana, konkretna i bliska problemowi.
Trzecie pytanie dotyczy tego, jak testujesz własny zbiór dokumentów. Publiczne przykłady są przydatne, ale twoje archiwum jest prawdopodobnie dziwniejsze niż folder z przykładami. Zawiera zepsute eksporty, starożytne szablony, dziwne formaty liczb, ukryte arkusze, skopiowane i wklejone tabele, uszkodzone pliki PDF oraz pliki o nazwach final_final_really_final. Bindery zapewnia wspólny silnik i testowalne interfejsy. Nadal potrzebujesz testów na swoim zbiorze dokumentów. Niestety, dokumenty nie usamodzielnią się same.
Lekcja
Lekcja Bindery jest taka, że przetwarzanie dokumentów to nie ekstrakcja tekstu z dodatkowymi krokami. To wykrywanie formatu, parsowanie struktury, granice możliwości, powierzchnie zapytań, ścieżki zapisu i dyscyplina utrzymania. Użytkownik widzi plik. System widzi kontener, strumienie, relacje, rekordy, style, formuły, metadane, kodowania i obietnice wyjścia. Dobry silnik utrzymuje tę złożoność pod powierzchnią produktu, nie udając, że nie istnieje.
Zadaniem Bindery jest uczynienie warstwy dokumentów nudną w dobrym sensie. Jeden silnik w Rust. Interfejsy wysokiego poziomu dla dokumentów, prezentacji i arkuszy kalkulacyjnych. Moduły formatów dla skomplikowanych części. DocQL dla wspólnych pytań. Integracja z Pythonem i stosem tam, gdzie to przydatne. Etykiety możliwości zamiast mitologii formatów.
Plik nigdy nie był problemem. Problemem był klej. Bindery to próba zaprzestania płacenia czynszu za tę warstwę kleju.