cbpr-usage-rules 0.4.0__tar.gz → 0.5.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 (98) hide show
  1. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/CHANGELOG.md +12 -0
  2. {cbpr_usage_rules-0.4.0/src/cbpr_usage_rules.egg-info → cbpr_usage_rules-0.5.0}/PKG-INFO +25 -2
  3. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/README.md +24 -1
  4. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/__init__.py +19 -1
  5. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/cli.py +44 -0
  6. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/camt.052.max.xml +79 -79
  7. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/camt.052.min.xml +1 -1
  8. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/camt.054.max.xml +78 -78
  9. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/camt.054.min.xml +1 -1
  10. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2026 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2025}/pacs.002.max.xml +17 -17
  11. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.002.min.xml +3 -3
  12. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.004.max.xml +49 -49
  13. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.004.min.xml +3 -3
  14. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.008.max.xml +41 -41
  15. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.008.min.xml +3 -3
  16. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.008_stp.max.xml +41 -41
  17. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.008_stp.min.xml +6 -6
  18. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2026 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2025}/pacs.009.max.xml +37 -37
  19. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2026 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2025}/pacs.009.min.xml +3 -3
  20. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.009_adv.max.xml +41 -41
  21. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2026 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2025}/pacs.009_adv.min.xml +3 -3
  22. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.009_cov.max.xml +53 -53
  23. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pacs.009_cov.min.xml +3 -3
  24. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2025/pain.001.max.xml +28 -28
  25. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2026 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2025}/pain.001.min.xml +2 -2
  26. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/camt.052.max.xml +79 -79
  27. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/camt.052.min.xml +1 -1
  28. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/camt.054.max.xml +78 -78
  29. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/camt.054.min.xml +1 -1
  30. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2025 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2026}/pacs.002.max.xml +17 -17
  31. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.002.min.xml +3 -3
  32. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.004.max.xml +45 -45
  33. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.004.min.xml +3 -3
  34. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.008.max.xml +41 -41
  35. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.008.min.xml +3 -3
  36. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.008_stp.max.xml +41 -41
  37. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.008_stp.min.xml +6 -6
  38. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2025 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2026}/pacs.009.max.xml +37 -37
  39. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2025 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2026}/pacs.009.min.xml +3 -3
  40. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.009_adv.max.xml +41 -41
  41. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2025 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2026}/pacs.009_adv.min.xml +3 -3
  42. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.009_cov.max.xml +53 -53
  43. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pacs.009_cov.min.xml +3 -3
  44. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/examples/y2026/pain.001.max.xml +27 -27
  45. {cbpr_usage_rules-0.4.0/src/cbpr_rules/examples/y2025 → cbpr_usage_rules-0.5.0/src/cbpr_rules/examples/y2026}/pain.001.min.xml +2 -2
  46. cbpr_usage_rules-0.5.0/src/cbpr_rules/idgen.py +243 -0
  47. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0/src/cbpr_usage_rules.egg-info}/PKG-INFO +25 -2
  48. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_usage_rules.egg-info/SOURCES.txt +3 -1
  49. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/tests/test_examples.py +24 -8
  50. cbpr_usage_rules-0.5.0/tests/test_idgen.py +79 -0
  51. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/LICENSE +0 -0
  52. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/MANIFEST.in +0 -0
  53. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/pyproject.toml +0 -0
  54. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/setup.cfg +0 -0
  55. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/engine.py +0 -0
  56. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/helpers.py +0 -0
  57. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/loader.py +0 -0
  58. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/message.py +0 -0
  59. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/models.py +0 -0
  60. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/py.typed +0 -0
  61. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/reference/__init__.py +0 -0
  62. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/reference/countries.py +0 -0
  63. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/reference/currencies.py +0 -0
  64. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/registry.py +0 -0
  65. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/__init__.py +0 -0
  66. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/__init__.py +0 -0
  67. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/camt_052.py +0 -0
  68. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/camt_054.py +0 -0
  69. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_002.py +0 -0
  70. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_004.py +0 -0
  71. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_008.py +0 -0
  72. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_008_stp.py +0 -0
  73. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_009.py +0 -0
  74. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_009_adv.py +0 -0
  75. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pacs_009_cov.py +0 -0
  76. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2025/pain_001.py +0 -0
  77. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/__init__.py +0 -0
  78. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/camt_052.py +0 -0
  79. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/camt_054.py +0 -0
  80. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_002.py +0 -0
  81. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_004.py +0 -0
  82. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_008.py +0 -0
  83. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_008_stp.py +0 -0
  84. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_009.py +0 -0
  85. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_009_adv.py +0 -0
  86. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pacs_009_cov.py +0 -0
  87. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/rules/y2026/pain_001.py +0 -0
  88. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/schema.py +0 -0
  89. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/validators/__init__.py +0 -0
  90. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/validators/bic.py +0 -0
  91. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/validators/country.py +0 -0
  92. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/validators/currency.py +0 -0
  93. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/validators/iban.py +0 -0
  94. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_rules/validators/lei.py +0 -0
  95. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_usage_rules.egg-info/dependency_links.txt +0 -0
  96. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_usage_rules.egg-info/entry_points.txt +0 -0
  97. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_usage_rules.egg-info/requires.txt +0 -0
  98. {cbpr_usage_rules-0.4.0 → cbpr_usage_rules-0.5.0}/src/cbpr_usage_rules.egg-info/top_level.txt +0 -0
@@ -4,6 +4,18 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
5
5
  to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.5.0]
8
+
9
+ ### Added
10
+ - Deterministic **ID generation** library (`cbpr_rules.idgen`, "Counting Strings"
11
+ algorithm — no `random`/`uuid`): `generate_iban`, `generate_lei`, `generate_bic`,
12
+ `generate_uuid`/`generate_uetr`, `generate_mid`, plus `counting_string`/
13
+ `counting_sequence` and seeded helpers. Every id is structurally valid and passes
14
+ the package's own validators; same seed → same output. New CLI
15
+ `cbpr-validate --generate {iban,lei,bic,uuid,uetr,mid,text}` (with
16
+ `--seed/--country/--bank/--branch/--count`). The bundled example messages now
17
+ source their identifiers from this library (seeded per example).
18
+
7
19
  ## [0.4.0]
8
20
 
9
21
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cbpr-usage-rules
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
5
5
  Author: Pete Houghton
6
6
  License: MIT License
@@ -172,11 +172,34 @@ xml = cbpr_rules.example_message(2025, "pacs.008", "max") # variant defaults t
172
172
  cbpr_rules.example_variants(2025, "pacs.008") # ['min', 'max']
173
173
  ```
174
174
 
175
- The data is **fictitious** (synthetic BICs/IBANs/LEIs, `EXAMPLE` names) and does
175
+ The data is **fictitious** (synthetic, generated BICs/IBANs/LEIs) and does
176
176
  not reproduce any real sample — use them as starting templates, not real payments.
177
177
 
178
178
  ---
179
179
 
180
+ ## Generating identifiers
181
+
182
+ The example values above are produced by a small **deterministic** ID generator
183
+ (the "Counting Strings" algorithm — no `random`/`uuid`, same seed → same output).
184
+ Every generated id is structurally valid and passes the tool's own validators.
185
+
186
+ ```bash
187
+ cbpr-validate --generate iban --country AT --seed s1 # ISO 13616 + mod-97
188
+ cbpr-validate --generate lei --seed acme # ISO 17442 + check digits
189
+ cbpr-validate --generate bic --bank JSBP --country GB # -> JSBPGB..XXX
190
+ cbpr-validate --generate uuid --count 3 # RFC 4122 v4 (UETR)
191
+ cbpr-validate --generate mid --country GB # message identifier
192
+ ```
193
+ ```python
194
+ from cbpr_rules import generate_iban, generate_lei, generate_bic, generate_uetr
195
+ generate_iban("DE", seed="acme") # deterministic, valid German IBAN
196
+ generate_bic(country="GB", seed="acme")
197
+ ```
198
+
199
+ IBAN generation supports the 27 EU countries plus GB.
200
+
201
+ ---
202
+
180
203
  ## The result object
181
204
 
182
205
  Both `validate_file` and `validate_string` return a dictionary:
@@ -117,11 +117,34 @@ xml = cbpr_rules.example_message(2025, "pacs.008", "max") # variant defaults t
117
117
  cbpr_rules.example_variants(2025, "pacs.008") # ['min', 'max']
118
118
  ```
119
119
 
120
- The data is **fictitious** (synthetic BICs/IBANs/LEIs, `EXAMPLE` names) and does
120
+ The data is **fictitious** (synthetic, generated BICs/IBANs/LEIs) and does
121
121
  not reproduce any real sample — use them as starting templates, not real payments.
122
122
 
123
123
  ---
124
124
 
125
+ ## Generating identifiers
126
+
127
+ The example values above are produced by a small **deterministic** ID generator
128
+ (the "Counting Strings" algorithm — no `random`/`uuid`, same seed → same output).
129
+ Every generated id is structurally valid and passes the tool's own validators.
130
+
131
+ ```bash
132
+ cbpr-validate --generate iban --country AT --seed s1 # ISO 13616 + mod-97
133
+ cbpr-validate --generate lei --seed acme # ISO 17442 + check digits
134
+ cbpr-validate --generate bic --bank JSBP --country GB # -> JSBPGB..XXX
135
+ cbpr-validate --generate uuid --count 3 # RFC 4122 v4 (UETR)
136
+ cbpr-validate --generate mid --country GB # message identifier
137
+ ```
138
+ ```python
139
+ from cbpr_rules import generate_iban, generate_lei, generate_bic, generate_uetr
140
+ generate_iban("DE", seed="acme") # deterministic, valid German IBAN
141
+ generate_bic(country="GB", seed="acme")
142
+ ```
143
+
144
+ IBAN generation supports the 27 EU countries plus GB.
145
+
146
+ ---
147
+
125
148
  ## The result object
126
149
 
127
150
  Both `validate_file` and `validate_string` return a dictionary:
@@ -4,7 +4,7 @@ This package is AI generated. See the README for details and caveats.
4
4
  """
5
5
  from __future__ import annotations
6
6
 
7
- __version__ = "0.4.0"
7
+ __version__ = "0.5.0"
8
8
 
9
9
  from .models import Rule, Severity, Violation
10
10
  from .engine import (
@@ -15,6 +15,16 @@ from .engine import (
15
15
  example_message,
16
16
  example_variants,
17
17
  )
18
+ from .idgen import (
19
+ generate_iban,
20
+ generate_lei,
21
+ generate_bic,
22
+ generate_uuid,
23
+ generate_uetr,
24
+ generate_mid,
25
+ counting_string,
26
+ counting_sequence,
27
+ )
18
28
 
19
29
  __all__ = [
20
30
  "__version__",
@@ -27,4 +37,12 @@ __all__ = [
27
37
  "available",
28
38
  "example_message",
29
39
  "example_variants",
40
+ "generate_iban",
41
+ "generate_lei",
42
+ "generate_bic",
43
+ "generate_uuid",
44
+ "generate_uetr",
45
+ "generate_mid",
46
+ "counting_string",
47
+ "counting_sequence",
30
48
  ]
@@ -9,6 +9,7 @@ import textwrap
9
9
  from typing import List, Optional
10
10
 
11
11
  from . import __version__
12
+ from . import idgen
12
13
  from .engine import (
13
14
  ValidationError,
14
15
  available,
@@ -19,6 +20,29 @@ from .engine import (
19
20
  )
20
21
 
21
22
 
23
+ def _generate(args) -> List[str]:
24
+ """Produce the ``--generate`` output lines (one id per --count)."""
25
+ kind = args.generate
26
+ out: List[str] = []
27
+ for i in range(max(args.count, 1)):
28
+ seed = f"{args.seed}:{i}" if args.count > 1 else args.seed
29
+ if kind == "iban":
30
+ if not args.country:
31
+ raise ValueError("--generate iban requires --country (e.g. --country AT)")
32
+ out.append(idgen.generate_iban(args.country, seed))
33
+ elif kind == "lei":
34
+ out.append(idgen.generate_lei(seed))
35
+ elif kind == "bic":
36
+ out.append(idgen.generate_bic(args.bank, args.country or "GB", args.branch, seed))
37
+ elif kind in ("uuid", "uetr"):
38
+ out.append(idgen.generate_uuid(idgen.deterministic_int(seed or str(i), 0, 2**63)))
39
+ elif kind == "mid":
40
+ out.append(idgen.generate_mid("CBPR", args.country or "GB", i + 1))
41
+ elif kind == "text":
42
+ out.append(idgen.deterministic_string(seed or "text", 16))
43
+ return out
44
+
45
+
22
46
  def _build_parser() -> argparse.ArgumentParser:
23
47
  p = argparse.ArgumentParser(
24
48
  prog="cbpr-validate",
@@ -72,6 +96,17 @@ def _build_parser() -> argparse.ArgumentParser:
72
96
  help="Root element tag wrapping the AppHdr+Document in --example output "
73
97
  "(default: Envelope).",
74
98
  )
99
+ p.add_argument(
100
+ "--generate",
101
+ choices=("iban", "lei", "bic", "uuid", "uetr", "mid", "text"),
102
+ help="Generate a deterministic, structurally-valid identifier and print it "
103
+ "(use --seed/--country/--bank/--branch/--count to control output).",
104
+ )
105
+ p.add_argument("--seed", default="", help="Seed for --generate (same seed -> same output).")
106
+ p.add_argument("--country", help="Country code for --generate iban/bic/mid (e.g. AT, GB).")
107
+ p.add_argument("--bank", help="Bank code for --generate bic (4 letters, padded with X).")
108
+ p.add_argument("--branch", default="XXX", help="Branch code for --generate bic (default XXX).")
109
+ p.add_argument("--count", type=int, default=1, help="How many ids to generate (default 1).")
75
110
  p.add_argument(
76
111
  "--list",
77
112
  action="store_true",
@@ -211,6 +246,15 @@ def main(argv: Optional[List[str]] = None) -> int:
211
246
  print(json.dumps(types) if args.json else "\n".join(types))
212
247
  return 0
213
248
 
249
+ if args.generate:
250
+ try:
251
+ for line in _generate(args):
252
+ print(line)
253
+ except (ValueError, ValidationError) as exc:
254
+ print(f"error: {exc}", file=sys.stderr)
255
+ return 2
256
+ return 0
257
+
214
258
  if args.example:
215
259
  if args.year is None or args.msgtype is None:
216
260
  print("error: --example requires --year and --type", file=sys.stderr)