tac2iwxxm 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 (73) hide show
  1. tac2iwxxm-0.1.0/.gitignore +188 -0
  2. tac2iwxxm-0.1.0/PKG-INFO +61 -0
  3. tac2iwxxm-0.1.0/README.md +49 -0
  4. tac2iwxxm-0.1.0/pyproject.toml +53 -0
  5. tac2iwxxm-0.1.0/rust/Cargo.lock +180 -0
  6. tac2iwxxm-0.1.0/rust/Cargo.toml +18 -0
  7. tac2iwxxm-0.1.0/rust/src/lib.rs +39 -0
  8. tac2iwxxm-0.1.0/src/tac2iwxxm/__init__.py +30 -0
  9. tac2iwxxm-0.1.0/src/tac2iwxxm/bulletin.py +131 -0
  10. tac2iwxxm-0.1.0/src/tac2iwxxm/codec.py +15 -0
  11. tac2iwxxm-0.1.0/src/tac2iwxxm/convert.py +250 -0
  12. tac2iwxxm-0.1.0/src/tac2iwxxm/decode.py +502 -0
  13. tac2iwxxm-0.1.0/src/tac2iwxxm/models.py +121 -0
  14. tac2iwxxm-0.1.0/src/tac2iwxxm/native.py +62 -0
  15. tac2iwxxm-0.1.0/src/tac2iwxxm/products/__init__.py +17 -0
  16. tac2iwxxm-0.1.0/src/tac2iwxxm/products/metar_speci.py +239 -0
  17. tac2iwxxm-0.1.0/src/tac2iwxxm/products/sigmet_airmet.py +145 -0
  18. tac2iwxxm-0.1.0/src/tac2iwxxm/products/taf.py +103 -0
  19. tac2iwxxm-0.1.0/src/tac2iwxxm/products/vaa_tca.py +155 -0
  20. tac2iwxxm-0.1.0/src/tac2iwxxm/profiles/__init__.py +8 -0
  21. tac2iwxxm-0.1.0/src/tac2iwxxm/profiles/annex3.py +143 -0
  22. tac2iwxxm-0.1.0/src/tac2iwxxm/profiles/annex3_products.py +445 -0
  23. tac2iwxxm-0.1.0/src/tac2iwxxm/profiles/iwxxm_us.py +255 -0
  24. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/manifest.json +24 -0
  25. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/metar_basic.golden.xml +62 -0
  26. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/metar_basic.tac +1 -0
  27. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/metar_nil.golden.xml +34 -0
  28. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/metar_nil.tac +1 -0
  29. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/speci_basic.golden.xml +62 -0
  30. tac2iwxxm-0.1.0/tests/fixtures/annex3_golden/speci_basic.tac +1 -0
  31. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_field_annotations.json +29 -0
  32. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/airmet_us_basic.golden.xml +73 -0
  33. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/airmet_us_basic.tac +2 -0
  34. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/manifest.json +43 -0
  35. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/metar_us_ao2_slp.golden.xml +69 -0
  36. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/metar_us_ao2_slp.tac +1 -0
  37. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/metar_us_pk_wnd.golden.xml +80 -0
  38. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/metar_us_pk_wnd.tac +1 -0
  39. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/sigmet_us_basic.golden.xml +77 -0
  40. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/sigmet_us_basic.tac +2 -0
  41. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/speci_us_ao2.golden.xml +68 -0
  42. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/speci_us_ao2.tac +1 -0
  43. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/taf_us_altimeter.golden.xml +61 -0
  44. tac2iwxxm-0.1.0/tests/fixtures/iwxxm_us_golden/taf_us_altimeter.tac +1 -0
  45. tac2iwxxm-0.1.0/tests/fixtures/metar_ahl_with_bbb.txt +2 -0
  46. tac2iwxxm-0.1.0/tests/fixtures/metar_multi_ahl.txt +3 -0
  47. tac2iwxxm-0.1.0/tests/fixtures/metar_single_ahl.txt +2 -0
  48. tac2iwxxm-0.1.0/tests/fixtures/metar_speci_field_annotations.json +34 -0
  49. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/airmet_basic.tac +2 -0
  50. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/manifest.json +50 -0
  51. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/metar_basic.tac +1 -0
  52. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/sigmet_basic.tac +2 -0
  53. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/speci_basic.tac +1 -0
  54. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/taf_basic.tac +1 -0
  55. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/tca_basic.tac +15 -0
  56. tac2iwxxm-0.1.0/tests/fixtures/product_matrix/vaa_basic.tac +15 -0
  57. tac2iwxxm-0.1.0/tests/test_convert_issue_offsets.py +48 -0
  58. tac2iwxxm-0.1.0/tests/test_convert_unit.py +185 -0
  59. tac2iwxxm-0.1.0/tests/test_coverage_gaps.py +322 -0
  60. tac2iwxxm-0.1.0/tests/test_decode_summary.py +93 -0
  61. tac2iwxxm-0.1.0/tests/test_decode_tac.py +118 -0
  62. tac2iwxxm-0.1.0/tests/test_decode_value_aware.py +185 -0
  63. tac2iwxxm-0.1.0/tests/test_native_scaffold.py +34 -0
  64. tac2iwxxm-0.1.0/tests/test_pyo3_hotspots.py +43 -0
  65. tac2iwxxm-0.1.0/tests/test_tac2iwxxm_codec.py +13 -0
  66. tac2iwxxm-0.1.0/tests/test_tac2iwxxm_import.py +9 -0
  67. tac2iwxxm-0.1.0/tests/test_tc_f14_002_validate_extra.py +145 -0
  68. tac2iwxxm-0.1.0/tests/test_tc_f6_001_002_product_matrix.py +79 -0
  69. tac2iwxxm-0.1.0/tests/test_tc_f6_003_metar_speci_iwxxm_us.py +143 -0
  70. tac2iwxxm-0.1.0/tests/test_tc_f6_003_taf_sigmet_airmet_iwxxm_us.py +69 -0
  71. tac2iwxxm-0.1.0/tests/test_tc_f6_010_011_012_edge.py +58 -0
  72. tac2iwxxm-0.1.0/tests/test_tc_f6_020_021_metar_speci_annex3.py +161 -0
  73. tac2iwxxm-0.1.0/tests/test_tc_f6_030_bulletin_split.py +135 -0
@@ -0,0 +1,188 @@
1
+ # =============================================================================
2
+ # METAR → IWXXM monorepo — apps/, packages/, vendor/
3
+ # =============================================================================
4
+
5
+ # --- Secrets & local operator env (never commit) ---
6
+ .env
7
+ .env.*
8
+ !.env.example
9
+ prod.env
10
+ .envrc
11
+
12
+ # Per-workspace env overrides (apps/* and packages/*)
13
+ apps/**/.env
14
+ apps/**/.env.*
15
+ !apps/**/.env.example
16
+ packages/**/.env
17
+ packages/**/.env.*
18
+ !packages/**/.env.example
19
+
20
+ # Legacy env paths (pre-migration; remove after cutover)
21
+ backend/.env
22
+ auth/.env
23
+ frontend/.env
24
+
25
+ # --- Python (uv workspace: apps/backend, packages/*) ---
26
+ __pycache__/
27
+ *.py[codz]
28
+ *$py.class
29
+ *.so
30
+ .Python
31
+ build/
32
+ develop-eggs/
33
+ dist/
34
+ downloads/
35
+ eggs/
36
+ .eggs/
37
+ lib/
38
+ lib64/
39
+ parts/
40
+ sdist/
41
+ var/
42
+ wheels/
43
+ share/python-wheels/
44
+ *.egg-info/
45
+ .installed.cfg
46
+ *.egg
47
+ MANIFEST
48
+ *.manifest
49
+ *.spec
50
+ pip-log.txt
51
+ pip-delete-this-directory.txt
52
+ __pypackages__/
53
+ *.sage.py
54
+ .venv/
55
+ venv/
56
+ env/
57
+ ENV/
58
+ env.bak/
59
+ venv.bak/
60
+ .pdm-python
61
+ .pdm-build/
62
+ .pixi
63
+
64
+ # --- Node / pnpm workspace (apps/*, packages/*) ---
65
+ **/node_modules/
66
+ .pnp
67
+ .pnp.js
68
+ .pnpm-store/
69
+ npm-debug.log*
70
+ yarn-debug.log*
71
+ yarn-error.log*
72
+ pnpm-debug.log*
73
+ .eslintcache
74
+ *.tsbuildinfo
75
+
76
+ # --- Build & bundle outputs ---
77
+ **/dist/
78
+ **/dist-ssr/
79
+ **/build/
80
+ **/.vite/
81
+ **/.turbo/
82
+ **/.next/
83
+ **/.nuxt/
84
+ **/.output/
85
+ **/.svelte-kit/
86
+
87
+ # --- Test & coverage ---
88
+ .tox/
89
+ .nox/
90
+ .coverage
91
+ .coverage.*
92
+ .coverage-baseline.json
93
+ .cache
94
+ nosetests.xml
95
+ coverage.xml
96
+ *.cover
97
+ *.py.cover
98
+ htmlcov/
99
+ htmlcov_ann/
100
+ cover/
101
+ .hypothesis/
102
+ .pytest_cache/
103
+ .nyc_output
104
+ **/coverage/
105
+
106
+ # Playwright (apps/e2e and legacy frontend/)
107
+ **/test-results/
108
+ **/playwright-report/
109
+ **/blob-report/
110
+ **/.playwright/
111
+ **/playwright/.cache/
112
+
113
+ # --- Typecheck & lint caches ---
114
+ .mypy_cache/
115
+ .dmypy.json
116
+ dmypy.json
117
+ .pyre/
118
+ .pytype/
119
+ .ruff_cache/
120
+ cython_debug/
121
+
122
+ # --- IDE & OS ---
123
+ .vscode/
124
+ .idea/
125
+ *.swp
126
+ *.swo
127
+ *~
128
+ .DS_Store
129
+ Thumbs.db
130
+
131
+ # --- Docs site builds ---
132
+ docs/_build/
133
+ /site
134
+
135
+ # --- Local tooling binaries / artifacts ---
136
+ .tools/
137
+ .local/
138
+
139
+ # --- Cursor (local indexing prefs only; .cursorignore is tracked) ---
140
+ .cursorindexingignore
141
+
142
+ # --- Misc generated ---
143
+ *.log
144
+ *.db
145
+ *.local
146
+ local_settings.py
147
+ db.sqlite3
148
+ db.sqlite3-journal
149
+ instance/
150
+ .webassets-cache
151
+ .scrapy
152
+ .ipynb_checkpoints
153
+ profile_default/
154
+ ipython_config.py
155
+ celerybeat-schedule
156
+ celerybeat.pid
157
+ .abstra/
158
+ .pypirc
159
+ marimo/_static/
160
+ marimo/_lsp/
161
+ __marimo__/
162
+
163
+ # --- Legacy submodule / pre-migration paths (remove after M7 cutover) ---
164
+ # Vendor snapshots live under vendor/schemas/ once migrated (tracked, not ignored).
165
+ GIFTs/.venv/
166
+ GIFTs/.gifts/
167
+ GIFTs/build/
168
+ GIFTs/dist/
169
+ GIFTs/data/
170
+ GIFTs/.coverage
171
+ GIFTs/.pytest_cache/
172
+ GIFTs/.mypy_cache/
173
+ GIFTs/sfstns.db
174
+ GIFTs/driver.py
175
+ GIFTs/wiki/
176
+
177
+ # --- Rust / maturin (tac2iwxxm / iwxxm-validate PyO3) ---
178
+ packages/tac2iwxxm/rust/target/
179
+ packages/iwxxm-validate/rust/target/
180
+
181
+ # E10-34 — synced runtime schema subset (generated; MANIFEST.json is tracked)
182
+ packages/iwxxm-validate/src/iwxxm_validate/schemas/iwxxm/
183
+ packages/iwxxm-validate/src/iwxxm_validate/schemas/iwxxm-us/
184
+ packages/iwxxm-validate/src/iwxxm_validate/schemas/LAST_SYNC.json
185
+
186
+ # ADR-027 — xsdata pydantic trees are committed (T3.7); ignore only bytecode
187
+ packages/shared/src/metar_shared/iwxxm_xsd/**/__pycache__/
188
+
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.4
2
+ Name: tac2iwxxm
3
+ Version: 0.1.0
4
+ Summary: General TAC → IWXXM converter (F6)
5
+ License: MIT
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: msgspec>=0.19
8
+ Provides-Extra: validate
9
+ Requires-Dist: iwxxm-validate>=0.1.0; extra == 'validate'
10
+ Requires-Dist: tac-validate>=0.1.0; extra == 'validate'
11
+ Description-Content-Type: text/markdown
12
+
13
+ # tac2iwxxm
14
+
15
+ General TAC → IWXXM converter package (F6 / F14). MIT licensed.
16
+
17
+ See ADR-013 / ADR-014 / ADR-016 / ADR-017.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ # Convert only (no validators)
23
+ pip install tac2iwxxm
24
+
25
+ # Convert + TAC lint + IWXXM XSD/Schematron (F14)
26
+ pip install 'tac2iwxxm[validate]'
27
+ ```
28
+
29
+ The `[validate]` extra depends on **`tac-validate`** and **`iwxxm-validate`** (E10-20).
30
+ Convert works without the extra.
31
+
32
+ ```python
33
+ from tac2iwxxm import convert
34
+
35
+ result = convert(
36
+ "METAR KJFK 231751Z 18012KT 10SM FEW040 15/07 A3005=",
37
+ product="METAR",
38
+ )
39
+ assert result.ok and result.xml
40
+ ```
41
+
42
+ With `[validate]` installed:
43
+
44
+ ```python
45
+ from tac_validate import lint
46
+ from iwxxm_validate import validate_iwxxm
47
+
48
+ assert lint(tac, product="METAR").ok
49
+ assert validate_iwxxm(result.xml, iwxxm_version="2025-2").ok
50
+ ```
51
+
52
+ ## Native extension (PyO3)
53
+
54
+ Optional Rust hotspots live under `rust/` and import as `tac2iwxxm._rust`
55
+ (ADR-017). Pure Python remains the default `uv sync` path.
56
+
57
+ ```bash
58
+ # requires rustc + maturin
59
+ make build-tac2iwxxm-native
60
+ make test-tac2iwxxm-native
61
+ ```
@@ -0,0 +1,49 @@
1
+ # tac2iwxxm
2
+
3
+ General TAC → IWXXM converter package (F6 / F14). MIT licensed.
4
+
5
+ See ADR-013 / ADR-014 / ADR-016 / ADR-017.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ # Convert only (no validators)
11
+ pip install tac2iwxxm
12
+
13
+ # Convert + TAC lint + IWXXM XSD/Schematron (F14)
14
+ pip install 'tac2iwxxm[validate]'
15
+ ```
16
+
17
+ The `[validate]` extra depends on **`tac-validate`** and **`iwxxm-validate`** (E10-20).
18
+ Convert works without the extra.
19
+
20
+ ```python
21
+ from tac2iwxxm import convert
22
+
23
+ result = convert(
24
+ "METAR KJFK 231751Z 18012KT 10SM FEW040 15/07 A3005=",
25
+ product="METAR",
26
+ )
27
+ assert result.ok and result.xml
28
+ ```
29
+
30
+ With `[validate]` installed:
31
+
32
+ ```python
33
+ from tac_validate import lint
34
+ from iwxxm_validate import validate_iwxxm
35
+
36
+ assert lint(tac, product="METAR").ok
37
+ assert validate_iwxxm(result.xml, iwxxm_version="2025-2").ok
38
+ ```
39
+
40
+ ## Native extension (PyO3)
41
+
42
+ Optional Rust hotspots live under `rust/` and import as `tac2iwxxm._rust`
43
+ (ADR-017). Pure Python remains the default `uv sync` path.
44
+
45
+ ```bash
46
+ # requires rustc + maturin
47
+ make build-tac2iwxxm-native
48
+ make test-tac2iwxxm-native
49
+ ```
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "tac2iwxxm"
3
+ version = "0.1.0"
4
+ description = "General TAC → IWXXM converter (F6)"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ requires-python = ">=3.12"
8
+ dependencies = [
9
+ "msgspec>=0.19",
10
+ ]
11
+
12
+ [project.optional-dependencies]
13
+ # F14 / E10-20: optional TAC lint + IWXXM XSD/Schematron validators.
14
+ validate = [
15
+ "tac-validate>=0.1.0",
16
+ "iwxxm-validate>=0.1.0",
17
+ ]
18
+
19
+ [build-system]
20
+ requires = ["hatchling>=1.25"]
21
+ build-backend = "hatchling.build"
22
+
23
+ [tool.hatch.build.targets.wheel]
24
+ packages = ["src/tac2iwxxm"]
25
+
26
+ # Maturin builds the optional PyO3 cdylib (ADR-017 / T4.3). Pure-Python hatch wheel
27
+ # remains the default ``uv sync`` path; CI rust job runs ``maturin develop``.
28
+ [tool.maturin]
29
+ manifest-path = "rust/Cargo.toml"
30
+ python-source = "src"
31
+ module-name = "tac2iwxxm._rust"
32
+
33
+ [tool.pytest.ini_options]
34
+ testpaths = ["tests"]
35
+ pythonpath = ["src"]
36
+ markers = [
37
+ "slow: longer local/CI smokes (clean-venv wheel installs)",
38
+ ]
39
+
40
+ [tool.coverage.run]
41
+ branch = true
42
+ source = ["src/tac2iwxxm"]
43
+
44
+ [tool.coverage.report]
45
+ fail_under = 95
46
+ show_missing = true
47
+
48
+ [tool.ruff]
49
+ line-length = 120
50
+
51
+ [tool.ruff.lint]
52
+ select = ["E", "W", "F", "I"]
53
+ ignore = ["E501"]
@@ -0,0 +1,180 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.4"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
16
+
17
+ [[package]]
18
+ name = "heck"
19
+ version = "0.5.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
22
+
23
+ [[package]]
24
+ name = "indoc"
25
+ version = "2.0.7"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
28
+ dependencies = [
29
+ "rustversion",
30
+ ]
31
+
32
+ [[package]]
33
+ name = "libc"
34
+ version = "0.2.186"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
37
+
38
+ [[package]]
39
+ name = "memoffset"
40
+ version = "0.9.1"
41
+ source = "registry+https://github.com/rust-lang/crates.io-index"
42
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
43
+ dependencies = [
44
+ "autocfg",
45
+ ]
46
+
47
+ [[package]]
48
+ name = "once_cell"
49
+ version = "1.21.4"
50
+ source = "registry+https://github.com/rust-lang/crates.io-index"
51
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
52
+
53
+ [[package]]
54
+ name = "portable-atomic"
55
+ version = "1.13.1"
56
+ source = "registry+https://github.com/rust-lang/crates.io-index"
57
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
58
+
59
+ [[package]]
60
+ name = "proc-macro2"
61
+ version = "1.0.106"
62
+ source = "registry+https://github.com/rust-lang/crates.io-index"
63
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
64
+ dependencies = [
65
+ "unicode-ident",
66
+ ]
67
+
68
+ [[package]]
69
+ name = "pyo3"
70
+ version = "0.23.5"
71
+ source = "registry+https://github.com/rust-lang/crates.io-index"
72
+ checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
73
+ dependencies = [
74
+ "cfg-if",
75
+ "indoc",
76
+ "libc",
77
+ "memoffset",
78
+ "once_cell",
79
+ "portable-atomic",
80
+ "pyo3-build-config",
81
+ "pyo3-ffi",
82
+ "pyo3-macros",
83
+ "unindent",
84
+ ]
85
+
86
+ [[package]]
87
+ name = "pyo3-build-config"
88
+ version = "0.23.5"
89
+ source = "registry+https://github.com/rust-lang/crates.io-index"
90
+ checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
91
+ dependencies = [
92
+ "once_cell",
93
+ "target-lexicon",
94
+ ]
95
+
96
+ [[package]]
97
+ name = "pyo3-ffi"
98
+ version = "0.23.5"
99
+ source = "registry+https://github.com/rust-lang/crates.io-index"
100
+ checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
101
+ dependencies = [
102
+ "libc",
103
+ "pyo3-build-config",
104
+ ]
105
+
106
+ [[package]]
107
+ name = "pyo3-macros"
108
+ version = "0.23.5"
109
+ source = "registry+https://github.com/rust-lang/crates.io-index"
110
+ checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
111
+ dependencies = [
112
+ "proc-macro2",
113
+ "pyo3-macros-backend",
114
+ "quote",
115
+ "syn",
116
+ ]
117
+
118
+ [[package]]
119
+ name = "pyo3-macros-backend"
120
+ version = "0.23.5"
121
+ source = "registry+https://github.com/rust-lang/crates.io-index"
122
+ checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
123
+ dependencies = [
124
+ "heck",
125
+ "proc-macro2",
126
+ "pyo3-build-config",
127
+ "quote",
128
+ "syn",
129
+ ]
130
+
131
+ [[package]]
132
+ name = "quote"
133
+ version = "1.0.46"
134
+ source = "registry+https://github.com/rust-lang/crates.io-index"
135
+ checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
136
+ dependencies = [
137
+ "proc-macro2",
138
+ ]
139
+
140
+ [[package]]
141
+ name = "rustversion"
142
+ version = "1.0.23"
143
+ source = "registry+https://github.com/rust-lang/crates.io-index"
144
+ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
145
+
146
+ [[package]]
147
+ name = "syn"
148
+ version = "2.0.118"
149
+ source = "registry+https://github.com/rust-lang/crates.io-index"
150
+ checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
151
+ dependencies = [
152
+ "proc-macro2",
153
+ "quote",
154
+ "unicode-ident",
155
+ ]
156
+
157
+ [[package]]
158
+ name = "tac2iwxxm_rust"
159
+ version = "0.1.0"
160
+ dependencies = [
161
+ "pyo3",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "target-lexicon"
166
+ version = "0.12.16"
167
+ source = "registry+https://github.com/rust-lang/crates.io-index"
168
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
169
+
170
+ [[package]]
171
+ name = "unicode-ident"
172
+ version = "1.0.24"
173
+ source = "registry+https://github.com/rust-lang/crates.io-index"
174
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
175
+
176
+ [[package]]
177
+ name = "unindent"
178
+ version = "0.2.4"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
@@ -0,0 +1,18 @@
1
+ [package]
2
+ name = "tac2iwxxm_rust"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "PyO3 native hotspots for tac2iwxxm (ADR-017)"
6
+ license = "MIT"
7
+ rust-version = "1.74"
8
+
9
+ [lib]
10
+ name = "_rust"
11
+ crate-type = ["cdylib"]
12
+
13
+ [dependencies]
14
+ pyo3 = { version = "0.23", features = ["extension-module"] }
15
+
16
+ [package.metadata.maturin]
17
+ python-source = "../src"
18
+ module-name = "tac2iwxxm._rust"
@@ -0,0 +1,39 @@
1
+ //! PyO3 native extension for `tac2iwxxm` (ADR-017).
2
+ //!
3
+ //! T4.3 scaffold + T4.5 `scan_metar_tokens` hotspot (TAC lexer).
4
+
5
+ use pyo3::prelude::*;
6
+
7
+ /// Extension package version (mirrors Cargo.toml).
8
+ #[pyfunction]
9
+ fn extension_version() -> &'static str {
10
+ env!("CARGO_PKG_VERSION")
11
+ }
12
+
13
+ /// Health check used by CI / import smoke tests.
14
+ #[pyfunction]
15
+ fn ping() -> &'static str {
16
+ "pong"
17
+ }
18
+
19
+ /// Split METAR/SPECI TAC into whitespace tokens (hotspot scaffold for T4.5+).
20
+ ///
21
+ /// Strips a trailing ``=`` terminator when present. Does not yet implement a full
22
+ /// remark-aware lexer — that lands with further PyO3 work.
23
+ #[pyfunction]
24
+ fn scan_metar_tokens(tac: &str) -> Vec<String> {
25
+ let trimmed = tac.trim().trim_end_matches('=').trim();
26
+ trimmed
27
+ .split_whitespace()
28
+ .map(|part| part.to_string())
29
+ .collect()
30
+ }
31
+
32
+ #[pymodule]
33
+ fn _rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
34
+ m.add("__version__", env!("CARGO_PKG_VERSION"))?;
35
+ m.add_function(wrap_pyfunction!(extension_version, m)?)?;
36
+ m.add_function(wrap_pyfunction!(ping, m)?)?;
37
+ m.add_function(wrap_pyfunction!(scan_metar_tokens, m)?)?;
38
+ Ok(())
39
+ }
@@ -0,0 +1,30 @@
1
+ """General TAC → IWXXM converter (F6)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from tac2iwxxm.bulletin import BulletinSplitError, split_bulletin
6
+ from tac2iwxxm.convert import ConvertError, convert
7
+ from tac2iwxxm.decode import DecodeResidual, DecodeResult, DecodeSegment, decode_tac
8
+ from tac2iwxxm.models import BulletinMeta, BulletinSplit, ConvertIssue, ConvertResult
9
+ from tac2iwxxm.native import rust_available, rust_module, scan_metar_tokens
10
+
11
+ __version__ = "0.1.0"
12
+
13
+ __all__ = [
14
+ "BulletinMeta",
15
+ "BulletinSplit",
16
+ "BulletinSplitError",
17
+ "ConvertError",
18
+ "ConvertIssue",
19
+ "ConvertResult",
20
+ "DecodeResidual",
21
+ "DecodeResult",
22
+ "DecodeSegment",
23
+ "__version__",
24
+ "convert",
25
+ "decode_tac",
26
+ "rust_available",
27
+ "rust_module",
28
+ "scan_metar_tokens",
29
+ "split_bulletin",
30
+ ]