gflights 0.2.1__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 (96) hide show
  1. gflights-0.2.1/.gitignore +16 -0
  2. gflights-0.2.1/CHANGELOG.md +135 -0
  3. gflights-0.2.1/CLAUDE.md +164 -0
  4. gflights-0.2.1/Cargo.lock +2877 -0
  5. gflights-0.2.1/Cargo.toml +60 -0
  6. gflights-0.2.1/LICENSE +21 -0
  7. gflights-0.2.1/PKG-INFO +13 -0
  8. gflights-0.2.1/README.md +541 -0
  9. gflights-0.2.1/benches/parse.rs +108 -0
  10. gflights-0.2.1/build.rs +23 -0
  11. gflights-0.2.1/examples/cheapest_dates.rs +96 -0
  12. gflights-0.2.1/examples/date_grid.rs +62 -0
  13. gflights-0.2.1/examples/explore.rs +90 -0
  14. gflights-0.2.1/examples/flights.rs +149 -0
  15. gflights-0.2.1/examples/graph.rs +72 -0
  16. gflights-0.2.1/examples/multi_city.rs +57 -0
  17. gflights-0.2.1/examples/offer.rs +128 -0
  18. gflights-0.2.1/gflights/__init__.py +57 -0
  19. gflights-0.2.1/gflights/_gflights.pyi +220 -0
  20. gflights-0.2.1/gflights/_types.py +12 -0
  21. gflights-0.2.1/gflights/py.typed +0 -0
  22. gflights-0.2.1/gflights-py/.gitignore +9 -0
  23. gflights-0.2.1/gflights-py/Cargo.toml +22 -0
  24. gflights-0.2.1/gflights-py/src/lib.rs +1146 -0
  25. gflights-0.2.1/gflights-py/tests/test_errors.py +103 -0
  26. gflights-0.2.1/gflights-py/tests/test_import.py +103 -0
  27. gflights-0.2.1/gflights-py/tests/test_live.py +269 -0
  28. gflights-0.2.1/gflights-py/tests/test_types.py +39 -0
  29. gflights-0.2.1/gflights-py/uv.lock +7 -0
  30. gflights-0.2.1/hooks/pre-commit +26 -0
  31. gflights-0.2.1/pyproject.toml +36 -0
  32. gflights-0.2.1/src/bin/cli/cheap.rs +154 -0
  33. gflights-0.2.1/src/bin/cli/date_grid.rs +105 -0
  34. gflights-0.2.1/src/bin/cli/explore.rs +296 -0
  35. gflights-0.2.1/src/bin/cli/graph.rs +60 -0
  36. gflights-0.2.1/src/bin/cli/mod.rs +611 -0
  37. gflights-0.2.1/src/bin/cli/multi_city.rs +240 -0
  38. gflights-0.2.1/src/bin/cli/offer.rs +91 -0
  39. gflights-0.2.1/src/bin/cli/search.rs +223 -0
  40. gflights-0.2.1/src/bin/main.rs +46 -0
  41. gflights-0.2.1/src/lib.rs +76 -0
  42. gflights-0.2.1/src/parsers/common/airline.rs +193 -0
  43. gflights-0.2.1/src/parsers/common/duration.rs +281 -0
  44. gflights-0.2.1/src/parsers/common/fixed_flights.rs +156 -0
  45. gflights-0.2.1/src/parsers/common/location.rs +63 -0
  46. gflights-0.2.1/src/parsers/common/mod.rs +258 -0
  47. gflights-0.2.1/src/parsers/common/travelers.rs +138 -0
  48. gflights-0.2.1/src/parsers/common/types.rs +240 -0
  49. gflights-0.2.1/src/parsers/constants.rs +12 -0
  50. gflights-0.2.1/src/parsers/mod.rs +37 -0
  51. gflights-0.2.1/src/parsers/request/calendar_graph_request.rs +223 -0
  52. gflights-0.2.1/src/parsers/request/city_request.rs +58 -0
  53. gflights-0.2.1/src/parsers/request/date_grid_request.rs +242 -0
  54. gflights-0.2.1/src/parsers/request/explore_request.rs +323 -0
  55. gflights-0.2.1/src/parsers/request/flight_request.rs +1293 -0
  56. gflights-0.2.1/src/parsers/request/mod.rs +15 -0
  57. gflights-0.2.1/src/parsers/response/calendar_graph_response.rs +121 -0
  58. gflights-0.2.1/src/parsers/response/city_response.rs +179 -0
  59. gflights-0.2.1/src/parsers/response/date_grid_response.rs +397 -0
  60. gflights-0.2.1/src/parsers/response/explore_response.rs +317 -0
  61. gflights-0.2.1/src/parsers/response/flight_response.rs +1383 -0
  62. gflights-0.2.1/src/parsers/response/mod.rs +17 -0
  63. gflights-0.2.1/src/parsers/response/offer_response.rs +386 -0
  64. gflights-0.2.1/src/protos/message.proto +61 -0
  65. gflights-0.2.1/src/protos/mod.rs +220 -0
  66. gflights-0.2.1/src/protos/readme.md +7 -0
  67. gflights-0.2.1/src/requests/api.rs +1213 -0
  68. gflights-0.2.1/src/requests/config/builder.rs +561 -0
  69. gflights-0.2.1/src/requests/config/currency.rs +175 -0
  70. gflights-0.2.1/src/requests/config/explore.rs +337 -0
  71. gflights-0.2.1/src/requests/config/mod.rs +640 -0
  72. gflights-0.2.1/src/requests/config/multi_city.rs +539 -0
  73. gflights-0.2.1/src/requests/mod.rs +2 -0
  74. gflights-0.2.1/test_files/booking_offer_response.txt +10 -0
  75. gflights-0.2.1/test_files/error0.txt +1 -0
  76. gflights-0.2.1/test_files/error1.txt +1 -0
  77. gflights-0.2.1/test_files/flights.txt +10 -0
  78. gflights-0.2.1/test_files/flights_new_test.txt +1 -0
  79. gflights-0.2.1/test_files/graph_response +4 -0
  80. gflights-0.2.1/test_files/inner_object_test.txt +1 -0
  81. gflights-0.2.1/test_files/low_price_in_second_line.txt +10 -0
  82. gflights-0.2.1/test_files/lux_dubai_oneway.txt +1 -0
  83. gflights-0.2.1/test_files/lux_milan_oneway.txt +1 -0
  84. gflights-0.2.1/test_files/lux_tokyo_oneway.txt +1 -0
  85. gflights-0.2.1/test_files/offers_full.txt +10 -0
  86. gflights-0.2.1/test_files/offers_single_line.txt +1 -0
  87. gflights-0.2.1/test_files/raw.response +1 -0
  88. gflights-0.2.1/test_files/raw_gflights.response +1 -0
  89. gflights-0.2.1/test_files/raw_multiline.txt +14 -0
  90. gflights-0.2.1/test_files/raw_offer_response.txt +12 -0
  91. gflights-0.2.1/test_files/response_non_uniform_city_images.txt +1 -0
  92. gflights-0.2.1/test_files/response_with_first_fixed_full.txt +10 -0
  93. gflights-0.2.1/test_files/with_28_elements.txt +1 -0
  94. gflights-0.2.1/tests/live_api.rs +1473 -0
  95. gflights-0.2.1/tests/negative.rs +405 -0
  96. gflights-0.2.1/tests/wire.rs +283 -0
@@ -0,0 +1,16 @@
1
+ # Generated by Cargo
2
+ # will have compiled files and executables
3
+ debug/
4
+ target/
5
+
6
+ # Cargo.lock is committed: this crate ships a binary, so pinning deps matters.
7
+ # https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8
+
9
+ # These are backup files generated by rustfmt
10
+ **/*.rs.bk
11
+
12
+ # MSVC Windows builds of rustc generate these, which store debugging information
13
+ *.pdb
14
+
15
+ .cargo/
16
+ .vscode/
@@ -0,0 +1,135 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ---
11
+
12
+ ## [0.2.1] — 2026-06-04
13
+
14
+ ### Changed
15
+
16
+ - TLS backend switched from native-tls (vendored OpenSSL) to **rustls**. This
17
+ removes the OpenSSL — and its Perl — build dependency, making builds hermetic
18
+ on every platform and fixing portable (manylinux) wheel builds. No API change.
19
+
20
+ ---
21
+
22
+ ## [0.2.0] — 2026-06-04
23
+
24
+ ### Added
25
+
26
+ #### Core library
27
+
28
+ - **`gflights explore`** — `GetExploreDestinations` endpoint + CLI `explore` subcommand.
29
+ Returns `Vec<ExploreResult>` with destination name, country, airport codes, price, dates,
30
+ airline, CO₂, and accommodation price.
31
+ - `ExploreResult::flight_airport` — actual flight destination airport (may differ from the
32
+ geographic `nearest_airport`; e.g. Verdon Gorge shows MRS, not NCE).
33
+ - `--interest` flag accepts names (`beaches`, `climbing`, …), aliases, or raw Knowledge-Graph
34
+ MIDs (`/m/…`).
35
+ - `--to` flag filters to a destination airport or geographic region (Alps, Northern Europe, …).
36
+ - **`request_cheapest_dates`** — scans a range of months for the cheapest one-way or round-trip
37
+ departure dates; returns `Vec<CheapDate>` sorted by price.
38
+ - **`request_date_grid`** — full departure × return price matrix from `GetCalendarGrid`;
39
+ returns `Vec<DateGridEntry>`. Now runs chunks concurrently (`buffer_unordered(8)`)
40
+ with per-chunk body-read retry on EOF, reducing round-trip scan time from ~10 min to ~30 s.
41
+ - **Multi-city (open-jaw) search** — `MultiCityConfig::builder()` with per-leg
42
+ `max_price`, `carry_on`, `checked_bags` filters.
43
+ - **Up to 7 airports per side** — `departure` / `destination` (and each multi-city
44
+ leg) now accept up to 7 origin/destination airports, matching Google's maximum
45
+ (previously capped at 4).
46
+ - **Arrives-next-day detection** — `Itinerary::arrives_next_day()`, `arrival_date()`,
47
+ `departure_date()` derived from raw date fields.
48
+ - **`max_price` filter** on `Config` and `MultiCityConfig`.
49
+ - **Baggage filter** (`carry_on`, `checked_bags`) on `Config`.
50
+ - **`AirlineFilter`** with `Alliance` variants (OneWorld, SkyTeam, StarAlliance) and
51
+ `FromStr` parsing (`"LX"`, `"ONEWORLD"`, `"star_alliance"`).
52
+ - **`stopover_min`** field on `Config` — minimum layover duration.
53
+ - **`language` / `country`** on `Config` and `ExploreConfig` — removes hard-coded `en-GB`.
54
+ - **`sort_order`** field on `Config` (`SortOrder` enum: Best, Price, Duration,
55
+ DepartureTime, ArrivalTime).
56
+ - **Rate-limiter** (governor token bucket, 10 req/s default) with 429 detection flag and
57
+ `reset_rate_limit()`. Shared across all clones of an `ApiClient`.
58
+ - **Retry logic** (`RetryConfig`) — exponential back-off for 5xx/timeout; configurable via
59
+ `ApiClient::with_retry_config()`. `request_date_grid` also retries body-read errors.
60
+ - **`RateLimitedError`** — downcasting sentinel for HTTP 429.
61
+ - **`Travelers::new()`** — validates ≥1 adult, total ≤9; previously unchecked.
62
+ - **`PlaceType::from(i32)` non-panicking** — unknown discriminants return `Unspecified`
63
+ with a `tracing::warn!` instead of panicking.
64
+
65
+ #### CLI (`gflights` binary)
66
+
67
+ - `search` subcommand: `--airline`, `--exclude-airline`, `--via`, `--min-layover`,
68
+ `--max-layover`, `--lower-emissions`, `--sort`, `--format`.
69
+ - `search --show-co2` — CO₂ kg column in table output.
70
+ - `search --detail` — layover airports (`via ZRH (65 min)`) and `+1` next-day marker.
71
+ - `graph` subcommand.
72
+ - `dgrid` subcommand.
73
+ - `offer` subcommand (booking offers + deep links).
74
+ - `cheap` subcommand (cheapest departure dates, `--trip-days N` for round trips).
75
+ - `explore` subcommand with `--interest`, `--to`, `--duration`, `--month`, `--budget`.
76
+ - `mcity` (multi-city) subcommand.
77
+ - Interactive **REPL** with readline history; `--help` works inside the REPL.
78
+
79
+ #### Python bindings (`gflights-py`)
80
+
81
+ - Async Python bindings via pyo3 + maturin.
82
+ - Wheels use the CPython **abi3** stable ABI: a single wheel per platform works
83
+ on CPython 3.10+.
84
+ - `GFlights` class with methods: `search`, `price_graph`, `date_grid`,
85
+ `multi_city_search`, `explore`, `cheapest_dates`.
86
+ - **`GFlightsError`** — typed exception class; all API errors now raise
87
+ `GFlightsError` (a `Exception` subclass) instead of `RuntimeError`.
88
+ - Full `.pyi` type stubs for IDE support and mypy compatibility.
89
+ - `CheapDate`, `ExploreResult`, `EmissionsInfo`, `LayoverInfo`, `LegInfo` data classes.
90
+ - `rate_limited` flag and `reset_rate_limit()` on `GFlights`.
91
+
92
+ #### Tests
93
+
94
+ - **Wire-protocol tests** (`tests/wire.rs`) — 19 tests feeding captured fixtures through
95
+ parsers and asserting field values.
96
+ - **Negative / error-input tests** (`tests/negative.rs`) — 39 tests covering bad input
97
+ validation, malformed response bodies, and error message content.
98
+ - Live integration tests (`tests/live_api.rs`, gated behind `RUN_LIVE_TESTS=1`).
99
+ - Python offline test suite: `test_import.py`, `test_types.py`, `test_errors.py`.
100
+
101
+ ### Fixed
102
+
103
+ - `Travelers::new([0, …])` returned `Ok` with 0 adults; now returns `Err`.
104
+ - `PlaceType::from(i32)` panicked on undocumented values; now logs warning and
105
+ returns `Unspecified`.
106
+ - `explore` CLI ARPT column showed geographic nearest airport (NCE) instead of the
107
+ actual flight destination airport (MRS for Verdon Gorge).
108
+ - REPL `--help` flag triggered `UnknownArgument` error instead of printing help.
109
+ - `_gflights.pyi` had unresolved merge-conflict markers from `feat/multi-city`;
110
+ `explore()`, `cheapest_dates()`, and `multi_city_search(max_price, …)` signatures
111
+ were missing.
112
+ - Date-grid chunking looped without moving the window, producing all-identical requests.
113
+
114
+ ### Changed
115
+
116
+ - `request_date_grid` chunks are now dispatched concurrently with `buffer_unordered(8)`.
117
+ - `do_request()` retries 5xx/timeout errors; `request_date_grid_chunk` additionally
118
+ retries body-read errors (EOF from mid-stream connection closure).
119
+ - CI runs on pull requests only (not on every push to master).
120
+
121
+ ---
122
+
123
+ ## [0.1.0] — 2026-04-01
124
+
125
+ Initial release.
126
+
127
+ - Flight search (one-way and return).
128
+ - Price graph (`GetCalendarGraph`).
129
+ - Booking offer resolution.
130
+ - City / airport lookup.
131
+
132
+ [Unreleased]: https://github.com/nas-/google-flights-rs/compare/v0.2.1...HEAD
133
+ [0.2.1]: https://github.com/nas-/google-flights-rs/compare/v0.2.0...v0.2.1
134
+ [0.2.0]: https://github.com/nas-/google-flights-rs/compare/v0.1.0...v0.2.0
135
+ [0.1.0]: https://github.com/nas-/google-flights-rs/releases/tag/v0.1.0
@@ -0,0 +1,164 @@
1
+ # gflights — Claude instructions
2
+
3
+ ## Workspace layout
4
+
5
+ ```
6
+ google-flights-rs/
7
+ ├── src/ Rust library + CLI
8
+ │ ├── bin/cli/ CLI subcommands
9
+ │ ├── parsers/ Request builders & response parsers
10
+ │ └── requests/ ApiClient, Config, retry logic
11
+ ├── gflights-py/ Python bindings (pyo3 + maturin)
12
+ │ ├── src/lib.rs Rust extension (_gflights)
13
+ │ ├── gflights/ Python package (re-exports, stubs, types)
14
+ │ └── tests/ Python test suite
15
+ ├── benches/ Criterion benchmarks
16
+ └── tests/ Rust integration / live API tests
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Git workflow
22
+
23
+ - **Never commit directly to `master`** — always use a feature or fix branch.
24
+ - Branch naming: `feat/<topic>`, `fix/<topic>`, `chore/<topic>`.
25
+ - After every feature or fix is complete, run `/verify` to confirm the change works end-to-end at the CLI / Python surface before merging. The `--profile dev` build is sufficient for verification.
26
+
27
+ ---
28
+
29
+ ## Before every commit — run locally first
30
+
31
+ Always verify locally before pushing. CI runs the same checks and failing there wastes time.
32
+
33
+ ### Rust crate
34
+
35
+ ```sh
36
+ cargo fmt # format (required — CI blocks on diff)
37
+ cargo clippy --all-targets -- -D warnings # lint (zero warnings policy)
38
+ cargo test --lib # 152 unit tests
39
+ cargo test --bin gflights # 13 CLI tests
40
+ cargo test --doc # doc tests
41
+ cargo build --benches # ensure benchmarks still compile
42
+ ```
43
+
44
+ ### Python bindings (run from `gflights-py/`)
45
+
46
+ ```sh
47
+ cd gflights-py
48
+ python -m maturin develop # rebuild extension after Rust changes
49
+ .venv/Scripts/pytest.exe tests/test_import.py tests/test_types.py tests/test_errors.py -v
50
+ ```
51
+
52
+ All offline tests must pass before pushing.
53
+
54
+ ---
55
+
56
+ ## Live / integration tests
57
+
58
+ These hit the real Google Flights API. They are skipped unless the
59
+ `RUN_LIVE_TESTS` environment variable is set to a non-empty value.
60
+
61
+ ### Rust
62
+ ```sh
63
+ RUN_LIVE_TESTS=1 cargo test --test live_api
64
+ ```
65
+
66
+ ### Python
67
+ ```sh
68
+ cd gflights-py
69
+ RUN_LIVE_TESTS=1 .venv/Scripts/pytest.exe tests/test_live.py -v
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Test coverage
75
+
76
+ Keep line coverage **≥ 80%** for the Rust crate.
77
+
78
+ ```sh
79
+ cargo install cargo-tarpaulin # one-time
80
+ cargo tarpaulin --out Stdout # check coverage
81
+ ```
82
+
83
+ Current baseline: **84%** overall (parsers 84–99%; `api.rs` ~26% — network code, accepted).
84
+ If coverage drops below 80%, add tests before merging.
85
+
86
+ ---
87
+
88
+ ## Examples parity rule
89
+
90
+ Every public-facing feature must have a corresponding example in `examples/`.
91
+
92
+ | Feature | Example file |
93
+ |---|---|
94
+ | Flight search + offers + booking URL | `examples/flights.rs` |
95
+ | Price graph | `examples/graph.rs` |
96
+ | Date grid | `examples/date_grid.rs` |
97
+ | Cheapest dates (one-way + round-trip) | `examples/cheapest_dates.rs` |
98
+ | Booking offers + URL resolution | `examples/offer.rs` |
99
+ | Multi-city search | `examples/multi_city.rs` |
100
+ | Explore destinations | `examples/explore.rs` |
101
+
102
+ When adding a new public API method, add or update the relevant example. All examples guard network calls behind `RUN_LIVE=1` so `cargo test --examples` passes offline.
103
+
104
+ ```sh
105
+ cargo build --examples # must compile clean
106
+ RUN_LIVE=1 cargo run --example <name> # smoke-test with network
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Python bindings parity rule
112
+
113
+ Whenever `src/` (the Rust crate) changes, update `gflights-py/` to match:
114
+
115
+ | Rust change | Bindings update needed |
116
+ |---|---|
117
+ | New public method / field / type | Expose in `gflights-py/src/lib.rs`; add to `gflights/_gflights.pyi` |
118
+ | Renamed / removed API | Mirror in bindings |
119
+ | New `Config` option or filter | Add parameter to the affected Python method(s) |
120
+ | New response field | Expose on the relevant Python data class |
121
+ | Behaviour change | Update affected Python tests |
122
+
123
+ The Rust crate and Python bindings must stay in sync at all times.
124
+
125
+ ---
126
+
127
+ ## Building the Python extension
128
+
129
+ ```sh
130
+ cd gflights-py
131
+ uv venv --python 3.11 .venv # one-time
132
+ uv pip install maturin pytest pytest-asyncio
133
+ python -m maturin develop # build + install into .venv
134
+ ```
135
+
136
+ After any change to `gflights-py/src/lib.rs`, re-run `maturin develop` before running Python tests.
137
+
138
+ ---
139
+
140
+ ## Security & dependency hygiene
141
+
142
+ ```sh
143
+ cargo audit # check for CVEs (runs in CI)
144
+ ```
145
+
146
+ Zero CVE policy — fix or justify any advisory before merging.
147
+
148
+ ---
149
+
150
+ ## Benchmarks
151
+
152
+ ```sh
153
+ cargo bench # must be run from the project root (test_files/ paths)
154
+ ```
155
+
156
+ Benchmarks are in `benches/parse.rs`. They use fixtures from `test_files/`.
157
+ Do not move or rename fixtures without updating the benchmark.
158
+
159
+ ---
160
+
161
+ ## Publishing (Rust crate)
162
+
163
+ `cargo publish --dry-run` must be clean before tagging a release.
164
+ The crate is live at https://crates.io/crates/gflights.