gedcomkit 0.1.4__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gedcomkit-0.1.5/CHANGELOG.md +126 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/Cargo.lock +1 -1
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/Cargo.toml +1 -1
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/PKG-INFO +1 -1
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/README.md +3 -3
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/bindings/gedcomkit-py/Cargo.lock +2 -2
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/bindings/gedcomkit-py/Cargo.toml +1 -1
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/bindings/gedcomkit-py/src/lib.rs +76 -0
- gedcomkit-0.1.5/src/gedzip.rs +2344 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/requirement_corpus.rs +3 -2
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/version_conversion.rs +3 -2
- gedcomkit-0.1.4/CHANGELOG.md +0 -56
- gedcomkit-0.1.4/src/gedzip.rs +0 -965
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/LICENSE +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/bindings/gedcomkit-py/LICENSE +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/bindings/gedcomkit-py/README.md +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/LICENSE +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/README.md +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/cardinalities.tsv +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/enumerations.tsv +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/enumerationsets.tsv +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/grammar.abnf +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/grammar.gedstruct +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/payloads.tsv +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom-spec/substructures.tsv +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/age-invalid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/age-valid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/atsign.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_ascii_1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_ascii_2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf16be-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf16be-2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf16le-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf16le-2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf8-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf8-2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/char_utf8-3.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/date-all.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/date-dual-invalid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/date-dual-valid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/enum-ext.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/filename-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/lang-all.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/notes-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/obje-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/obsolete-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/pedi-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/rela_1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/sour-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/tiny-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/5/xref-case.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/age-invalid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/age-valid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/atsign.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_ascii_1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_ascii_2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf16be-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf16be-2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf16le-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf16le-2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf8-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf8-2.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/char_utf8-3.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/date-all.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/date-dual-invalid.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/enum-ext.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/filename-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/lang-all.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/notes-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/obje-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/obsolete-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/pedi-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/rela_1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/sour-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/tiny-1.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/7/xref-case.ged +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/LICENSE +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/gedcom7code/UPSTREAM-README.md +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/zip-interop/README.md +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/zip-interop/python-zip64.gdz +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/fixtures/vendored/zip-interop/python-zipfile.gdz +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/pyproject.toml +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/convert.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/dates.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/decode.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/family.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/lib.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/names.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/schema.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/synthetic.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/tags.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/validate.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/src/view.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/endurance.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/external_charset.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/properties.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/spec_conformance.rs +0 -0
- {gedcomkit-0.1.4 → gedcomkit-0.1.5}/tests/vendored_corpus.rs +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. The
|
|
4
|
+
format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
|
+
the project uses semantic versioning within the usual pre-1.0 compatibility
|
|
6
|
+
rules.
|
|
7
|
+
|
|
8
|
+
## [0.1.5] - 2026-09-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `gedzip::ArchiveWriter`, a streaming GEDZIP writer: entries are added one
|
|
13
|
+
at a time and written straight to a sink, so peak memory is a small
|
|
14
|
+
multiple of the largest single entry plus the growing central directory,
|
|
15
|
+
never the archive's total. Measured, one `add` runs from roughly 2x the
|
|
16
|
+
entry (already-compressed media, stored) to a little over 3.5x (highly
|
|
17
|
+
compressible text, which briefly holds the source, the deflate output and
|
|
18
|
+
the encoder's state together) — never a function of how many entries came
|
|
19
|
+
before. A hash set of every lowercased path added so far is resident for
|
|
20
|
+
the writer's lifetime too, and at the default 100,000-entry limit the
|
|
21
|
+
central directory is bounded but not small.
|
|
22
|
+
It produces Zip64 structures once the entry count, an entry's size or the
|
|
23
|
+
running offset needs them, so it has no archive-size ceiling; the local
|
|
24
|
+
header's Zip64 extra carries both the original and compressed size
|
|
25
|
+
together whenever either overflows, as APPNOTE 4.5.3 requires for readers
|
|
26
|
+
that resolve local headers while streaming. Entry names carry
|
|
27
|
+
general-purpose bit 11 (UTF-8). A write failure to the sink poisons the
|
|
28
|
+
writer, so no entry is ever written next to bytes a failed write left
|
|
29
|
+
truncated, and `finish` flushes the sink before returning it rather than
|
|
30
|
+
leaving the last bytes to a `Drop` that cannot report failure.
|
|
31
|
+
`ArchiveWriter` implements `Debug` — entry count, bytes written, the
|
|
32
|
+
active limits and whether it is poisoned, never the sink or the directory
|
|
33
|
+
buffer's contents.
|
|
34
|
+
- `gedzip::Archive::total_size`, the sum of an opened archive's entries'
|
|
35
|
+
claimed decompressed sizes, read from the central directory alone — so a
|
|
36
|
+
caller can ask what extracting will cost before decompressing anything.
|
|
37
|
+
- Python and JavaScript/WASM bindings for both: `ArchiveWriter` (`new`,
|
|
38
|
+
`add`, `finish`) and `archive_total_size`. Both bindings write to an
|
|
39
|
+
in-memory `Vec<u8>` sink and copy it again when `finish` hands the bytes
|
|
40
|
+
to the host language, so — unlike the Rust API given a real sink such as
|
|
41
|
+
a file — they do not get the streaming memory benefit yet; their
|
|
42
|
+
docstrings say so.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
Both entries below change `gedzip::write_archive`, which has shipped since
|
|
47
|
+
0.1.0. Everything under **Added** is new in this release and never shipped
|
|
48
|
+
broken; these two are what moves under an existing caller.
|
|
49
|
+
|
|
50
|
+
- **`write_archive` now sets general-purpose bit 11 (UTF-8 names)**, so a
|
|
51
|
+
non-ASCII path round-trips as itself instead of being read back as IBM
|
|
52
|
+
code page 437 mush (confirmed against Python 3.12 `zipfile` and Java 17
|
|
53
|
+
`ZipFile`; .NET's `ZipArchive` happened to decode the unflagged form
|
|
54
|
+
correctly, and 7-Zip guesses). Directory entries also now carry the MS-DOS
|
|
55
|
+
directory external attribute (`0x10`) instead of `0`. **This changes the
|
|
56
|
+
bytes `write_archive` emits**: on the same five-entry input, exactly
|
|
57
|
+
eleven bytes differ from 0.1.4 — the flags field on every local and
|
|
58
|
+
central record, and the external-attribute byte on each directory entry.
|
|
59
|
+
Versions, timestamps, CRCs, sizes, offsets and names are unchanged. No
|
|
60
|
+
fixture or test pinned the previous bytes.
|
|
61
|
+
- **`write_archive` now refuses one value earlier**, at the Zip64 sentinel
|
|
62
|
+
rather than past it. It rejected a count or size only once it *exceeded*
|
|
63
|
+
what a plain ZIP field holds, so it accepted a count of exactly 65,535
|
|
64
|
+
entries, or a size, compressed size, directory offset or directory size of
|
|
65
|
+
exactly 4,294,967,295 — but those values *are* the sentinels a reader
|
|
66
|
+
takes as "see the Zip64 record", which `write_archive` never writes. It
|
|
67
|
+
emitted the bare sentinel, and this crate's own `Archive::open` then
|
|
68
|
+
refused the result ("the archive needs a Zip64 directory and has none")
|
|
69
|
+
while Python, Java, 7-Zip and `bsdtar` accepted the broken archive.
|
|
70
|
+
**An input that produced a malformed archive in 0.1.4 is now an error**,
|
|
71
|
+
so a caller writing exactly 65,535 entries sees a refusal where it
|
|
72
|
+
previously saw a file. The design is unchanged — `write_archive` still
|
|
73
|
+
refuses rather than emit Zip64 — only the boundary moved from `>` the
|
|
74
|
+
sentinel to `>=` it, for the entry count and every 32-bit size and offset
|
|
75
|
+
field alike. Confirmed at 65,535 (refused), 65,534 and 4,294,967,294
|
|
76
|
+
(accepted and read back) against this crate's reader, Python 3.12, Java 17
|
|
77
|
+
`ZipFile` and `ZipInputStream`, 7-Zip 26.02, `bsdtar` and .NET's
|
|
78
|
+
`ZipArchive`.
|
|
79
|
+
|
|
80
|
+
## [0.1.4] - 2026-09-16
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
- Migrated Cargo release publishing to crates.io Trusted Publishing through
|
|
85
|
+
GitLab OIDC, retaining the API token only as a transition fallback.
|
|
86
|
+
|
|
87
|
+
## [0.1.3] - 2026-09-16
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- Constrained PyPI Trusted Publishing to the protected `release` environment.
|
|
92
|
+
- Recorded the completed npm OIDC migration and the legacy token removal.
|
|
93
|
+
|
|
94
|
+
## [0.1.2] - 2026-09-16
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- Switched npm release publishing to GitLab OIDC Trusted Publishing.
|
|
99
|
+
- Documented the synchronized multi-registry release checklist.
|
|
100
|
+
|
|
101
|
+
## [0.1.1] - 2026-09-16
|
|
102
|
+
|
|
103
|
+
### Changed
|
|
104
|
+
|
|
105
|
+
- Synchronized the Rust core, Python binding, and JavaScript/WASM binding at
|
|
106
|
+
version 0.1.1 for the next registry release.
|
|
107
|
+
- Added protected-tag publishing for Cargo, PyPI, and npm, with clean
|
|
108
|
+
registry-install verification documented for all three packages.
|
|
109
|
+
|
|
110
|
+
## [0.1.0] - 2026-09-08
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- Archival GEDCOM 5.5 through 7.x parsing, decoding, views, validation, and
|
|
115
|
+
reported version conversion.
|
|
116
|
+
- Bounded GEDZIP reading and writing.
|
|
117
|
+
- Python and JavaScript/WASM bindings over the same Rust core.
|
|
118
|
+
- Byte-exact vendored corpus, conformance, property, interoperability, and
|
|
119
|
+
adversarial test suites.
|
|
120
|
+
|
|
121
|
+
### Security
|
|
122
|
+
|
|
123
|
+
- Refuse traversal, platform-special, duplicate, and central/local-mismatched
|
|
124
|
+
GEDZIP paths before returning entry bytes.
|
|
125
|
+
- Stage and checksum external fixture downloads before making them visible.
|
|
126
|
+
- Reject line injection through programmatically constructed GEDCOM fields.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# was checked on crates.io, npm, and PyPI before the initial release.
|
|
4
4
|
[package]
|
|
5
5
|
name = "gedcomkit"
|
|
6
|
-
version = "0.1.
|
|
6
|
+
version = "0.1.5"
|
|
7
7
|
description = "A byte-preserving GEDCOM document model: decoding, parsing, readings, version conversion, plausibility checks, and the GEDZIP container, for GEDCOM 5.5 through 7.x."
|
|
8
8
|
edition = "2024"
|
|
9
9
|
# The true minimum, not the workspace's toolchain: a parser library is a leaf
|
|
@@ -203,9 +203,9 @@ than dropped.
|
|
|
203
203
|
|
|
204
204
|
## AI disclosure
|
|
205
205
|
|
|
206
|
-
This project was created with the help of AI — specifically
|
|
207
|
-
[
|
|
208
|
-
tests, CI configuration, and documentation, with design decisions and
|
|
206
|
+
This project was created with the help of AI — specifically Anthropic's
|
|
207
|
+
[Claude](https://claude.com/claude-code). That includes the library code,
|
|
208
|
+
bindings, tests, CI configuration, and documentation, with design decisions and
|
|
209
209
|
validation made by a human. See [`AGENTS.md`](AGENTS.md) for the conventions AI
|
|
210
210
|
contributors are expected to follow.
|
|
211
211
|
|
|
@@ -35,7 +35,7 @@ dependencies = [
|
|
|
35
35
|
|
|
36
36
|
[[package]]
|
|
37
37
|
name = "gedcomkit"
|
|
38
|
-
version = "0.1.
|
|
38
|
+
version = "0.1.5"
|
|
39
39
|
dependencies = [
|
|
40
40
|
"flate2",
|
|
41
41
|
"serde",
|
|
@@ -43,7 +43,7 @@ dependencies = [
|
|
|
43
43
|
|
|
44
44
|
[[package]]
|
|
45
45
|
name = "gedcomkit-py"
|
|
46
|
-
version = "0.1.
|
|
46
|
+
version = "0.1.5"
|
|
47
47
|
dependencies = [
|
|
48
48
|
"gedcomkit",
|
|
49
49
|
"pyo3",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# abi3 keeps it to one wheel per platform across Python versions.
|
|
3
3
|
[package]
|
|
4
4
|
name = "gedcomkit-py"
|
|
5
|
-
version = "0.1.
|
|
5
|
+
version = "0.1.5"
|
|
6
6
|
description = "Python bindings for gedcomkit: an archival-grade GEDCOM library — byte-preserving round-trip, encoding detection, GEDCOM 5.5 through 7.x, GEDZIP."
|
|
7
7
|
edition = "2024"
|
|
8
8
|
rust-version = "1.88"
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
//! record and structure path. Everything else stays in Rust.
|
|
8
8
|
|
|
9
9
|
use gedcomkit::family::FamilyGraph;
|
|
10
|
+
use gedcomkit::gedzip::{self, Archive};
|
|
10
11
|
use gedcomkit::view::{FamilyView, IndividualView, RecordView};
|
|
11
12
|
use gedcomkit::{Document, EncodingReport};
|
|
12
13
|
use pyo3::exceptions::{PyKeyError, PyValueError};
|
|
13
14
|
use pyo3::prelude::*;
|
|
14
15
|
use pyo3::types::{PyBytes, PyModule};
|
|
16
|
+
use pyo3::wrap_pyfunction;
|
|
15
17
|
use serde::Serialize;
|
|
16
18
|
|
|
17
19
|
/// A parsed GEDCOM document and the report of how its bytes were read.
|
|
@@ -219,6 +221,78 @@ impl Gedcom {
|
|
|
219
221
|
}
|
|
220
222
|
}
|
|
221
223
|
|
|
224
|
+
/// Builds a GEDZIP archive one entry at a time. Peak memory per `add` call
|
|
225
|
+
/// is a small multiple of the entry just added, not the archive's total —
|
|
226
|
+
/// roughly 2x for already-compressed media (stored rather than deflated) up
|
|
227
|
+
/// to a little over 3.5x for highly compressible text, plus the growing
|
|
228
|
+
/// central directory; see `gedzip::ArchiveWriter`'s own documentation for
|
|
229
|
+
/// the full accounting. This binding's sink is an in-memory buffer rather
|
|
230
|
+
/// than a real file, though, so `finish` copies that whole buffer again to
|
|
231
|
+
/// hand the bytes to Python — the streaming benefit a real file sink gets
|
|
232
|
+
/// in Rust does not yet extend past this binding's boundary.
|
|
233
|
+
/// `write_archive`-equivalent behavior for a whole list already in memory
|
|
234
|
+
/// is not bound separately — build one of these and call `add` in a loop
|
|
235
|
+
/// instead.
|
|
236
|
+
#[pyclass]
|
|
237
|
+
struct ArchiveWriter {
|
|
238
|
+
/// `None` once `finish` has been called.
|
|
239
|
+
inner: Option<gedzip::ArchiveWriter<Vec<u8>>>,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#[pymethods]
|
|
243
|
+
impl ArchiveWriter {
|
|
244
|
+
/// Starts a new archive under the default bounds
|
|
245
|
+
/// (`gedcomkit.gedzip.ArchiveLimits` has no Python binding yet; this
|
|
246
|
+
/// uses the library's defaults).
|
|
247
|
+
#[staticmethod]
|
|
248
|
+
fn new() -> Self {
|
|
249
|
+
Self {
|
|
250
|
+
inner: Some(gedzip::ArchiveWriter::new(Vec::new())),
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/// Adds one entry. Raises `ValueError` for an unsafe or repeated path,
|
|
255
|
+
/// one that exceeds the writer's per-entry limit, or a call after
|
|
256
|
+
/// `finish`.
|
|
257
|
+
fn add(&mut self, name: &str, data: &[u8]) -> PyResult<()> {
|
|
258
|
+
let writer = self
|
|
259
|
+
.inner
|
|
260
|
+
.as_mut()
|
|
261
|
+
.ok_or_else(|| PyValueError::new_err("finish() was already called"))?;
|
|
262
|
+
writer
|
|
263
|
+
.add(name, data)
|
|
264
|
+
.map_err(|error| PyValueError::new_err(error.to_string()))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// Writes the central directory and returns the finished archive's
|
|
268
|
+
/// bytes. Valid to call once; raises `ValueError` on a second call.
|
|
269
|
+
fn finish<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
|
|
270
|
+
let writer = self
|
|
271
|
+
.inner
|
|
272
|
+
.take()
|
|
273
|
+
.ok_or_else(|| PyValueError::new_err("finish() was already called"))?;
|
|
274
|
+
let bytes = writer
|
|
275
|
+
.finish()
|
|
276
|
+
.map_err(|error| PyValueError::new_err(error.to_string()))?;
|
|
277
|
+
Ok(PyBytes::new(py, &bytes))
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
impl Default for ArchiveWriter {
|
|
282
|
+
fn default() -> Self {
|
|
283
|
+
Self::new()
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// The sum of a GEDZIP archive's entries' claimed decompressed sizes, read
|
|
288
|
+
/// from its central directory alone — nothing is decompressed to answer
|
|
289
|
+
/// this. Raises `ValueError` for bytes that do not open as an archive.
|
|
290
|
+
#[pyfunction]
|
|
291
|
+
fn archive_total_size(data: &[u8]) -> PyResult<u64> {
|
|
292
|
+
let archive = Archive::open(data).map_err(|error| PyValueError::new_err(error.to_string()))?;
|
|
293
|
+
Ok(archive.total_size())
|
|
294
|
+
}
|
|
295
|
+
|
|
222
296
|
/// An archival-grade GEDCOM library: byte-preserving round-trip, encoding
|
|
223
297
|
/// detection and reporting, GEDCOM 5.5 through 7.x.
|
|
224
298
|
///
|
|
@@ -229,6 +303,8 @@ impl Gedcom {
|
|
|
229
303
|
#[pyo3(name = "gedcomkit")]
|
|
230
304
|
fn gedcomkit_py(module: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
231
305
|
module.add_class::<Gedcom>()?;
|
|
306
|
+
module.add_class::<ArchiveWriter>()?;
|
|
307
|
+
module.add_function(wrap_pyfunction!(archive_total_size, module)?)?;
|
|
232
308
|
module.add("__version__", env!("CARGO_PKG_VERSION"))?;
|
|
233
309
|
Ok(())
|
|
234
310
|
}
|