openarchieven 0.1.0__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.
Files changed (67) hide show
  1. openarchieven-0.1.0/.github/workflows/ci.yml +47 -0
  2. openarchieven-0.1.0/.github/workflows/homebrew.yml +78 -0
  3. openarchieven-0.1.0/.github/workflows/release.yml +161 -0
  4. openarchieven-0.1.0/.gitignore +12 -0
  5. openarchieven-0.1.0/CHANGELOG.md +26 -0
  6. openarchieven-0.1.0/CLAUDE.md +38 -0
  7. openarchieven-0.1.0/Cargo.lock +2827 -0
  8. openarchieven-0.1.0/Cargo.toml +49 -0
  9. openarchieven-0.1.0/LICENSE +21 -0
  10. openarchieven-0.1.0/Makefile +91 -0
  11. openarchieven-0.1.0/PKG-INFO +51 -0
  12. openarchieven-0.1.0/README.md +28 -0
  13. openarchieven-0.1.0/openarchieven/__init__.py +10 -0
  14. openarchieven-0.1.0/prek.toml +22 -0
  15. openarchieven-0.1.0/pyproject.toml +37 -0
  16. openarchieven-0.1.0/scripts/render-homebrew-formula.sh +74 -0
  17. openarchieven-0.1.0/src/cache.rs +725 -0
  18. openarchieven-0.1.0/src/cli.rs +155 -0
  19. openarchieven-0.1.0/src/client.rs +428 -0
  20. openarchieven-0.1.0/src/commands/archives.rs +72 -0
  21. openarchieven-0.1.0/src/commands/births.rs +209 -0
  22. openarchieven-0.1.0/src/commands/cache_cmd.rs +112 -0
  23. openarchieven-0.1.0/src/commands/census.rs +290 -0
  24. openarchieven-0.1.0/src/commands/deaths.rs +205 -0
  25. openarchieven-0.1.0/src/commands/event_records.rs +188 -0
  26. openarchieven-0.1.0/src/commands/marriages.rs +218 -0
  27. openarchieven-0.1.0/src/commands/match_record.rs +189 -0
  28. openarchieven-0.1.0/src/commands/mod.rs +19 -0
  29. openarchieven-0.1.0/src/commands/search.rs +553 -0
  30. openarchieven-0.1.0/src/commands/show.rs +200 -0
  31. openarchieven-0.1.0/src/commands/stats/archive_stat.rs +151 -0
  32. openarchieven-0.1.0/src/commands/stats/comments.rs +64 -0
  33. openarchieven-0.1.0/src/commands/stats/events.rs +64 -0
  34. openarchieven-0.1.0/src/commands/stats/familynames.rs +390 -0
  35. openarchieven-0.1.0/src/commands/stats/firstnames.rs +269 -0
  36. openarchieven-0.1.0/src/commands/stats/mod.rs +8 -0
  37. openarchieven-0.1.0/src/commands/stats/professions.rs +342 -0
  38. openarchieven-0.1.0/src/commands/stats/records.rs +64 -0
  39. openarchieven-0.1.0/src/commands/stats/sources.rs +64 -0
  40. openarchieven-0.1.0/src/commands/weather.rs +294 -0
  41. openarchieven-0.1.0/src/commands/yearsago.rs +200 -0
  42. openarchieven-0.1.0/src/error.rs +229 -0
  43. openarchieven-0.1.0/src/lib.rs +11 -0
  44. openarchieven-0.1.0/src/main.rs +214 -0
  45. openarchieven-0.1.0/src/output.rs +497 -0
  46. openarchieven-0.1.0/src/runtime.rs +256 -0
  47. openarchieven-0.1.0/src/schema_cmd.rs +261 -0
  48. openarchieven-0.1.0/src/tty.rs +116 -0
  49. openarchieven-0.1.0/tests/archives.rs +101 -0
  50. openarchieven-0.1.0/tests/cache_cmd.rs +101 -0
  51. openarchieven-0.1.0/tests/census.rs +195 -0
  52. openarchieven-0.1.0/tests/cli_smoke.rs +119 -0
  53. openarchieven-0.1.0/tests/client_cache.rs +172 -0
  54. openarchieven-0.1.0/tests/client_http.rs +127 -0
  55. openarchieven-0.1.0/tests/client_retry.rs +133 -0
  56. openarchieven-0.1.0/tests/event_records.rs +259 -0
  57. openarchieven-0.1.0/tests/match.rs +107 -0
  58. openarchieven-0.1.0/tests/schema.rs +67 -0
  59. openarchieven-0.1.0/tests/search.rs +156 -0
  60. openarchieven-0.1.0/tests/show.rs +190 -0
  61. openarchieven-0.1.0/tests/snapshots/schema__schema_is_byte_stable.snap +1140 -0
  62. openarchieven-0.1.0/tests/stats_archive.rs +186 -0
  63. openarchieven-0.1.0/tests/stats_familynames.rs +264 -0
  64. openarchieven-0.1.0/tests/stats_firstnames.rs +158 -0
  65. openarchieven-0.1.0/tests/stats_professions.rs +144 -0
  66. openarchieven-0.1.0/tests/weather.rs +186 -0
  67. openarchieven-0.1.0/tests/yearsago.rs +157 -0
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ env:
10
+ CARGO_TERM_COLOR: always
11
+ RUST_BACKTRACE: 1
12
+
13
+ jobs:
14
+ check:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install Rust toolchain
20
+ uses: dtolnay/rust-toolchain@stable
21
+ with:
22
+ components: rustfmt, clippy
23
+
24
+ - uses: Swatinem/rust-cache@v2
25
+
26
+ - name: Install cargo-nextest
27
+ uses: taiki-e/install-action@nextest
28
+
29
+ - name: make check
30
+ run: make check
31
+
32
+ build:
33
+ runs-on: ${{ matrix.os }}
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ os: [ubuntu-latest, macos-latest]
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+
41
+ - name: Install Rust toolchain
42
+ uses: dtolnay/rust-toolchain@stable
43
+
44
+ - uses: Swatinem/rust-cache@v2
45
+
46
+ - name: make build
47
+ run: make build
@@ -0,0 +1,78 @@
1
+ name: Homebrew
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: [Release]
6
+ types: [completed]
7
+ workflow_dispatch:
8
+ inputs:
9
+ tag:
10
+ description: "Release tag (e.g. v0.1.0)"
11
+ required: true
12
+ type: string
13
+
14
+ jobs:
15
+ update-tap:
16
+ name: Update Homebrew tap
17
+ # workflow_run fires on every Release run, including failures — gate
18
+ # on success here. workflow_dispatch always runs.
19
+ if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Resolve release tag
25
+ id: tag
26
+ run: |
27
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
28
+ tag="${{ inputs.tag }}"
29
+ else
30
+ # workflow_run fires for tag-triggered Release runs;
31
+ # head_branch holds the tag name in that case.
32
+ tag="${{ github.event.workflow_run.head_branch }}"
33
+ fi
34
+ if [ -z "$tag" ] || [ "${tag#v}" = "$tag" ]; then
35
+ echo "could not resolve a v* release tag (got: '$tag')" >&2
36
+ exit 1
37
+ fi
38
+ echo "tag=${tag}" >> "$GITHUB_OUTPUT"
39
+ echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
40
+
41
+ - name: Download release sha256 sidecars
42
+ env:
43
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44
+ run: |
45
+ mkdir -p dist
46
+ gh release download "${{ steps.tag.outputs.tag }}" \
47
+ --repo "${GITHUB_REPOSITORY}" \
48
+ --pattern "*.sha256" \
49
+ --dir dist
50
+
51
+ - name: Render formula
52
+ run: |
53
+ make homebrew-formula \
54
+ VERSION=${{ steps.tag.outputs.version }} \
55
+ TAG=${{ steps.tag.outputs.tag }}
56
+
57
+ - name: Push formula to homebrew-tap
58
+ env:
59
+ GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
60
+ run: |
61
+ if [ -z "${GH_TOKEN}" ]; then
62
+ echo "HOMEBREW_TAP_TOKEN secret is not set; cannot push to tap." >&2
63
+ exit 1
64
+ fi
65
+ tag="${{ steps.tag.outputs.tag }}"
66
+ git clone "https://x-access-token:${GH_TOKEN}@github.com/rvben/homebrew-tap.git" /tmp/tap
67
+ mkdir -p /tmp/tap/Formula
68
+ cp dist/openarchieven.rb /tmp/tap/Formula/openarchieven.rb
69
+ cd /tmp/tap
70
+ git config user.name "github-actions[bot]"
71
+ git config user.email "github-actions[bot]@users.noreply.github.com"
72
+ git add Formula/openarchieven.rb
73
+ if git diff --cached --quiet; then
74
+ echo "Formula already up to date for ${tag}; nothing to push."
75
+ else
76
+ git commit -m "Update openarchieven to ${tag}"
77
+ git push
78
+ fi
@@ -0,0 +1,161 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ env:
12
+ CARGO_TERM_COLOR: always
13
+
14
+ jobs:
15
+ build:
16
+ name: Build ${{ matrix.target }}
17
+ runs-on: ${{ matrix.os }}
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ include:
22
+ - target: x86_64-unknown-linux-gnu
23
+ os: ubuntu-latest
24
+ maturin_args: --compatibility manylinux_2_28 --zig
25
+ - target: aarch64-unknown-linux-gnu
26
+ os: ubuntu-latest
27
+ maturin_args: --compatibility manylinux_2_28 --zig
28
+ - target: x86_64-apple-darwin
29
+ os: macos-latest
30
+ maturin_args: ""
31
+ - target: aarch64-apple-darwin
32
+ os: macos-latest
33
+ maturin_args: ""
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Install Rust toolchain
38
+ uses: dtolnay/rust-toolchain@stable
39
+ with:
40
+ targets: ${{ matrix.target }}
41
+
42
+ - uses: Swatinem/rust-cache@v2
43
+
44
+ - uses: astral-sh/setup-uv@v6
45
+
46
+ - name: Install maturin (with zig for cross-compiled Linux wheels)
47
+ shell: bash
48
+ run: |
49
+ uv venv "${RUNNER_TEMP}/build-venv"
50
+ uv pip install --python "${RUNNER_TEMP}/build-venv/bin/python" maturin ziglang
51
+ echo "${RUNNER_TEMP}/build-venv/bin" >> "$GITHUB_PATH"
52
+
53
+ - name: Compute version from tag
54
+ id: version
55
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
56
+
57
+ - name: make build-wheel
58
+ run: make build-wheel TARGET=${{ matrix.target }} MATURIN_ARGS="${{ matrix.maturin_args }}"
59
+
60
+ - name: make release-archive
61
+ run: |
62
+ make release-archive \
63
+ TARGET=${{ matrix.target }} \
64
+ VERSION=${{ steps.version.outputs.version }}
65
+
66
+ - name: Upload binary archive
67
+ uses: actions/upload-artifact@v4
68
+ with:
69
+ name: binary-${{ matrix.target }}
70
+ path: dist/*
71
+
72
+ - name: Upload wheel
73
+ uses: actions/upload-artifact@v4
74
+ with:
75
+ name: wheel-${{ matrix.target }}
76
+ path: target/wheels/*.whl
77
+
78
+ sdist:
79
+ name: Build sdist
80
+ runs-on: ubuntu-latest
81
+ steps:
82
+ - uses: actions/checkout@v4
83
+ - uses: astral-sh/setup-uv@v6
84
+ - name: Install maturin
85
+ shell: bash
86
+ run: |
87
+ uv venv "${RUNNER_TEMP}/build-venv"
88
+ uv pip install --python "${RUNNER_TEMP}/build-venv/bin/python" maturin
89
+ echo "${RUNNER_TEMP}/build-venv/bin" >> "$GITHUB_PATH"
90
+ - name: make build-sdist
91
+ run: make build-sdist
92
+ - uses: actions/upload-artifact@v4
93
+ with:
94
+ name: sdist
95
+ path: target/wheels/*.tar.gz
96
+
97
+ release:
98
+ name: Publish release
99
+ needs: build
100
+ runs-on: ubuntu-latest
101
+ steps:
102
+ - uses: actions/checkout@v4
103
+
104
+ - uses: actions/download-artifact@v4
105
+ with:
106
+ pattern: binary-*
107
+ path: dist
108
+ merge-multiple: true
109
+
110
+ - name: Create GitHub Release
111
+ env:
112
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
113
+ run: |
114
+ version="${GITHUB_REF_NAME#v}"
115
+ notes_file=$(mktemp)
116
+ if [ -f CHANGELOG.md ]; then
117
+ awk -v v="$version" '
118
+ /^## / { if (in_section) exit; if ($2 == v || $2 == "v"v || $2 == "["v"]") in_section = 1; next }
119
+ in_section
120
+ ' CHANGELOG.md > "$notes_file"
121
+ fi
122
+ if [ ! -s "$notes_file" ]; then
123
+ echo "Release $version" > "$notes_file"
124
+ fi
125
+ gh release create "$GITHUB_REF_NAME" \
126
+ --title "$GITHUB_REF_NAME" \
127
+ --notes-file "$notes_file" \
128
+ dist/*
129
+
130
+ publish-crates:
131
+ name: Publish to crates.io
132
+ needs: release
133
+ runs-on: ubuntu-latest
134
+ steps:
135
+ - uses: actions/checkout@v4
136
+ - uses: dtolnay/rust-toolchain@stable
137
+ - name: make publish-crates
138
+ env:
139
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
140
+ run: make publish-crates
141
+
142
+ publish-pypi:
143
+ name: Publish to PyPI
144
+ needs: [build, sdist]
145
+ runs-on: ubuntu-latest
146
+ steps:
147
+ - uses: actions/checkout@v4
148
+ - uses: actions/download-artifact@v4
149
+ with:
150
+ pattern: wheel-*
151
+ path: artifacts/wheels
152
+ - uses: actions/download-artifact@v4
153
+ with:
154
+ name: sdist
155
+ path: artifacts/sdist
156
+ - uses: astral-sh/setup-uv@v6
157
+ - name: make publish-pypi
158
+ env:
159
+ TWINE_USERNAME: __token__
160
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
161
+ run: make publish-pypi WHEEL_DIR=artifacts/wheels SDIST_DIR=artifacts/sdist
@@ -0,0 +1,12 @@
1
+ /target
2
+ /.cache
3
+ /dist
4
+ **/*.rs.bk
5
+ Cargo.lock.bak
6
+ .DS_Store
7
+ *.swp
8
+ .idea/
9
+ .vscode/
10
+ *.egg-info/
11
+ __pycache__/
12
+ *.pyc
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0] - 2026-04-30
8
+
9
+ Initial release.
10
+
11
+ ### Added
12
+ - All 17 endpoints of the openarchieven.nl API v1.1: `archives`, `search`,
13
+ `show`, `match`, `births`, `deaths`, `marriages`, `yearsago`, `census`,
14
+ `weather`, and the seven `stats` subcommands.
15
+ - Three output formats (`json`, `table`, `markdown`) selected automatically by
16
+ TTY/pipe context, overridable with `--output/-o`.
17
+ - `schema` subcommand emits a byte-stable, machine-readable contract for
18
+ scripts and AI agents.
19
+ - On-disk response cache with advisory locking and per-endpoint TTLs;
20
+ `cache info`, `cache prune`, and `cache clear --yes` for management.
21
+ - Built-in rate limiter (4 req/sec default) and retry with jittered backoff
22
+ that respects `Retry-After`.
23
+ - Structured JSON error contract on stderr with stable `kind` enum.
24
+ - `--fields` projection for list and single-flat responses.
25
+ - Distribution: crates.io, PyPI wheel, and Homebrew tap, all published from
26
+ one tag-driven release workflow.
@@ -0,0 +1,38 @@
1
+ # CLAUDE.md
2
+
3
+ Guidance for Claude Code working in this repository.
4
+
5
+ ## Build & Test
6
+
7
+ ```bash
8
+ make check # lint + test (CI runs this)
9
+ make build # cargo build --release
10
+ make test # cargo nextest run (or cargo test fallback)
11
+ make lint # fmt --check + clippy -D warnings
12
+ make fmt # auto-format
13
+ make install # release build → ~/.local/bin/openarchieven
14
+ ```
15
+
16
+ ## Architecture
17
+
18
+ Binary `openarchieven` wraps the openarchieven.nl API per the design spec at
19
+ `docs/superpowers/specs/2026-04-29-openarchieven-cli-design.md`. Core flow:
20
+ `main.rs` parses args → command module → `Client::get()` (cache + rate limit
21
+ + retry) → response shaped into `Renderable` → renderer (json/table/markdown)
22
+ → stdout. Errors always go to stderr as JSON.
23
+
24
+ ## Modules
25
+
26
+ - `client.rs`: HTTP, rate limiter, retry/backoff, `Retry-After` parsing.
27
+ - `cache.rs`: on-disk JSON cache with atomic writes and advisory locks.
28
+ - `error.rs`: `ErrorKind` enum and structured stderr emission.
29
+ - `output.rs`: `Renderable` shapes and three renderers.
30
+ - `schema_cmd.rs`: emits the machine-readable schema contract.
31
+ - `commands/<name>.rs`: one module per API endpoint.
32
+
33
+ ## Conventions
34
+
35
+ - TDD throughout. Test failure paths explicitly.
36
+ - `cargo nextest run` preferred (fast, token-optimized via RTK).
37
+ - No live network in unit/integration tests — `wiremock` stands in.
38
+ - Schema output is byte-stable; `tests/schema.rs` snapshots it.