j-law-ruby 0.0.3

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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/README.md +109 -0
  4. data/Rakefile +87 -0
  5. data/ext/j_law_ruby/extconf.rb +34 -0
  6. data/lib/j_law_ruby/build_support.rb +129 -0
  7. data/lib/j_law_ruby/c_ffi.rb +662 -0
  8. data/lib/j_law_ruby.rb +532 -0
  9. data/rake_support/vendor_rust.rb +51 -0
  10. data/test/test_c_ffi_adapter.rb +46 -0
  11. data/test/test_consumption_tax.rb +66 -0
  12. data/test/test_gemspec.rb +82 -0
  13. data/test/test_income_tax.rb +77 -0
  14. data/test/test_income_tax_deductions.rb +82 -0
  15. data/test/test_real_estate.rb +98 -0
  16. data/test/test_stamp_tax.rb +68 -0
  17. data/test/test_withholding_tax.rb +65 -0
  18. data/vendor/rust/Cargo.lock +235 -0
  19. data/vendor/rust/Cargo.toml +12 -0
  20. data/vendor/rust/crates/j-law-c-ffi/Cargo.toml +20 -0
  21. data/vendor/rust/crates/j-law-c-ffi/j_law_c_ffi.h +493 -0
  22. data/vendor/rust/crates/j-law-c-ffi/src/lib.rs +1553 -0
  23. data/vendor/rust/crates/j-law-core/Cargo.toml +18 -0
  24. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/calculator.rs +216 -0
  25. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/context.rs +29 -0
  26. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/mod.rs +9 -0
  27. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/params.rs +24 -0
  28. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/policy.rs +34 -0
  29. data/vendor/rust/crates/j-law-core/src/domains/income_tax/assessment.rs +76 -0
  30. data/vendor/rust/crates/j-law-core/src/domains/income_tax/calculator.rs +222 -0
  31. data/vendor/rust/crates/j-law-core/src/domains/income_tax/context.rs +79 -0
  32. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/calculator.rs +167 -0
  33. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/context.rs +172 -0
  34. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/expense.rs +465 -0
  35. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/mod.rs +20 -0
  36. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/params.rs +205 -0
  37. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/personal.rs +324 -0
  38. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/types.rs +61 -0
  39. data/vendor/rust/crates/j-law-core/src/domains/income_tax/mod.rs +24 -0
  40. data/vendor/rust/crates/j-law-core/src/domains/income_tax/params.rs +109 -0
  41. data/vendor/rust/crates/j-law-core/src/domains/income_tax/policy.rs +103 -0
  42. data/vendor/rust/crates/j-law-core/src/domains/mod.rs +5 -0
  43. data/vendor/rust/crates/j-law-core/src/domains/real_estate/calculator.rs +197 -0
  44. data/vendor/rust/crates/j-law-core/src/domains/real_estate/context.rs +48 -0
  45. data/vendor/rust/crates/j-law-core/src/domains/real_estate/mod.rs +9 -0
  46. data/vendor/rust/crates/j-law-core/src/domains/real_estate/params.rs +43 -0
  47. data/vendor/rust/crates/j-law-core/src/domains/real_estate/policy.rs +40 -0
  48. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/calculator.rs +321 -0
  49. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/context.rs +408 -0
  50. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/mod.rs +12 -0
  51. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/params.rs +190 -0
  52. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/policy.rs +105 -0
  53. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/calculator.rs +247 -0
  54. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/context.rs +167 -0
  55. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/mod.rs +9 -0
  56. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/params.rs +80 -0
  57. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/policy.rs +49 -0
  58. data/vendor/rust/crates/j-law-core/src/error.rs +171 -0
  59. data/vendor/rust/crates/j-law-core/src/lib.rs +9 -0
  60. data/vendor/rust/crates/j-law-core/src/types/amount.rs +232 -0
  61. data/vendor/rust/crates/j-law-core/src/types/citation.rs +82 -0
  62. data/vendor/rust/crates/j-law-core/src/types/date.rs +280 -0
  63. data/vendor/rust/crates/j-law-core/src/types/mod.rs +11 -0
  64. data/vendor/rust/crates/j-law-core/src/types/rate.rs +219 -0
  65. data/vendor/rust/crates/j-law-core/src/types/rounding.rs +81 -0
  66. data/vendor/rust/crates/j-law-registry/Cargo.toml +15 -0
  67. data/vendor/rust/crates/j-law-registry/data/consumption_tax/consumption_tax.json +70 -0
  68. data/vendor/rust/crates/j-law-registry/data/income_tax/deductions.json +327 -0
  69. data/vendor/rust/crates/j-law-registry/data/income_tax/income_tax.json +352 -0
  70. data/vendor/rust/crates/j-law-registry/data/real_estate/brokerage_fee.json +125 -0
  71. data/vendor/rust/crates/j-law-registry/data/stamp_tax/stamp_tax.json +674 -0
  72. data/vendor/rust/crates/j-law-registry/data/withholding_tax/withholding_tax.json +70 -0
  73. data/vendor/rust/crates/j-law-registry/src/consumption_tax_loader.rs +325 -0
  74. data/vendor/rust/crates/j-law-registry/src/consumption_tax_schema.rs +49 -0
  75. data/vendor/rust/crates/j-law-registry/src/income_tax_deduction_loader.rs +636 -0
  76. data/vendor/rust/crates/j-law-registry/src/income_tax_deduction_schema.rs +111 -0
  77. data/vendor/rust/crates/j-law-registry/src/income_tax_loader.rs +445 -0
  78. data/vendor/rust/crates/j-law-registry/src/income_tax_schema.rs +44 -0
  79. data/vendor/rust/crates/j-law-registry/src/lib.rs +20 -0
  80. data/vendor/rust/crates/j-law-registry/src/loader.rs +221 -0
  81. data/vendor/rust/crates/j-law-registry/src/schema.rs +73 -0
  82. data/vendor/rust/crates/j-law-registry/src/stamp_tax_loader.rs +374 -0
  83. data/vendor/rust/crates/j-law-registry/src/stamp_tax_schema.rs +72 -0
  84. data/vendor/rust/crates/j-law-registry/src/validator.rs +204 -0
  85. data/vendor/rust/crates/j-law-registry/src/withholding_tax_loader.rs +310 -0
  86. data/vendor/rust/crates/j-law-registry/src/withholding_tax_schema.rs +61 -0
  87. metadata +148 -0
@@ -0,0 +1,70 @@
1
+ {
2
+ "domain": "withholding_tax",
3
+ "history": [
4
+ {
5
+ "effective_from": "2013-01-01",
6
+ "effective_until": "2037-12-31",
7
+ "status": "active",
8
+ "citation": {
9
+ "law_name": "所得税法",
10
+ "article": 204,
11
+ "paragraph": 1
12
+ },
13
+ "params": {
14
+ "categories": [
15
+ {
16
+ "code": "manuscript_and_lecture",
17
+ "label": "原稿料・講演料等",
18
+ "method": {
19
+ "kind": "two_tier",
20
+ "threshold": 1000000,
21
+ "base_rate": {
22
+ "numer": 1021,
23
+ "denom": 10000
24
+ },
25
+ "excess_rate": {
26
+ "numer": 2042,
27
+ "denom": 10000
28
+ }
29
+ },
30
+ "submission_prize_exemption_threshold": 50000
31
+ },
32
+ {
33
+ "code": "professional_fee",
34
+ "label": "税理士等の報酬・料金",
35
+ "method": {
36
+ "kind": "two_tier",
37
+ "threshold": 1000000,
38
+ "base_rate": {
39
+ "numer": 1021,
40
+ "denom": 10000
41
+ },
42
+ "excess_rate": {
43
+ "numer": 2042,
44
+ "denom": 10000
45
+ }
46
+ },
47
+ "submission_prize_exemption_threshold": null
48
+ },
49
+ {
50
+ "code": "exclusive_contract_fee",
51
+ "label": "専属契約金",
52
+ "method": {
53
+ "kind": "two_tier",
54
+ "threshold": 1000000,
55
+ "base_rate": {
56
+ "numer": 1021,
57
+ "denom": 10000
58
+ },
59
+ "excess_rate": {
60
+ "numer": 2042,
61
+ "denom": 10000
62
+ }
63
+ },
64
+ "submission_prize_exemption_threshold": null
65
+ }
66
+ ]
67
+ }
68
+ }
69
+ ]
70
+ }
@@ -0,0 +1,325 @@
1
+ use crate::consumption_tax_schema::{ConsumptionTaxHistoryEntry, ConsumptionTaxRegistry};
2
+ use j_law_core::domains::consumption_tax::params::{ConsumptionTaxParams, ConsumptionTaxRate};
3
+ use j_law_core::types::date::LegalDate;
4
+ use j_law_core::{JLawError, RegistryError};
5
+
6
+ const PATH: &str = "consumption_tax/consumption_tax.json";
7
+
8
+ /// `consumption_tax.json` をロードして `target_date` に対応するパラメータを返す。
9
+ ///
10
+ /// # 法的根拠
11
+ /// 消費税法 第29条
12
+ ///
13
+ /// # 日付の範囲外について
14
+ /// 消費税導入前(1989年4月1日以前)の日付には消費税が存在しないため、
15
+ /// エラーではなく税率0%のパラメータを返す。
16
+ /// 返却される `standard_rate` は `{ numer: 0, denom: 1 }`(0/1 = 0%)。
17
+ /// `denom: 1` はゼロ除算が起きない最小の分母であり、「導入前=非課税」を意味する。
18
+ pub fn load_consumption_tax_params(
19
+ target_date: LegalDate,
20
+ ) -> Result<ConsumptionTaxParams, JLawError> {
21
+ target_date.validate()?;
22
+
23
+ let json_str = include_str!("../data/consumption_tax/consumption_tax.json");
24
+
25
+ let registry: ConsumptionTaxRegistry =
26
+ serde_json::from_str(json_str).map_err(|e| RegistryError::ParseError {
27
+ path: PATH.into(),
28
+ cause: e.to_string(),
29
+ })?;
30
+ validate_registry(&registry)?;
31
+
32
+ let date_str = target_date.to_date_str();
33
+
34
+ match find_entry(&registry, &date_str) {
35
+ Some(entry) => Ok(to_params(entry)),
36
+ // 消費税導入前(1989-04-01以前): エラーではなく0%を返す
37
+ None => Ok(ConsumptionTaxParams {
38
+ standard_rate: ConsumptionTaxRate { numer: 0, denom: 1 },
39
+ reduced_rate: None,
40
+ }),
41
+ }
42
+ }
43
+
44
+ /// `date_str` ("YYYY-MM-DD") に対応する履歴エントリを返す。
45
+ fn find_entry<'a>(
46
+ registry: &'a ConsumptionTaxRegistry,
47
+ date_str: &str,
48
+ ) -> Option<&'a ConsumptionTaxHistoryEntry> {
49
+ registry.history.iter().find(|entry| {
50
+ let from_ok = entry.effective_from.as_str() <= date_str;
51
+ let until_ok = match &entry.effective_until {
52
+ Some(until) => date_str <= until.as_str(),
53
+ None => true,
54
+ };
55
+ from_ok && until_ok
56
+ })
57
+ }
58
+
59
+ /// `ConsumptionTaxRegistry` の整合性を検証する。
60
+ ///
61
+ /// # 検証内容
62
+ /// - 適用期間の重複(Overlap)
63
+ /// - 適用期間の空白(Gap)
64
+ /// - 分母ゼロ(standard_rate.denom / reduced_rate.denom)
65
+ fn validate_registry(registry: &ConsumptionTaxRegistry) -> Result<(), RegistryError> {
66
+ let domain = &registry.domain;
67
+
68
+ // 分母ゼロチェック
69
+ for (i, entry) in registry.history.iter().enumerate() {
70
+ if entry.params.standard_rate.denom == 0 {
71
+ return Err(RegistryError::ZeroDenominator {
72
+ path: format!("{domain}/history[{i}]/standard_rate.denom"),
73
+ });
74
+ }
75
+ if let Some(reduced) = &entry.params.reduced_rate {
76
+ if reduced.denom == 0 {
77
+ return Err(RegistryError::ZeroDenominator {
78
+ path: format!("{domain}/history[{i}]/reduced_rate.denom"),
79
+ });
80
+ }
81
+ }
82
+ }
83
+
84
+ // 期間の重複・ギャップチェック
85
+ let mut sorted = registry.history.clone();
86
+ sorted.sort_by(|a, b| a.effective_from.cmp(&b.effective_from));
87
+
88
+ for [current, next] in sorted.array_windows::<2>() {
89
+ let current_until = match &current.effective_until {
90
+ Some(d) => d.clone(),
91
+ None => {
92
+ return Err(RegistryError::PeriodOverlap {
93
+ domain: domain.clone(),
94
+ from: next.effective_from.clone(),
95
+ until: "open-ended".into(),
96
+ });
97
+ }
98
+ };
99
+
100
+ if current_until >= next.effective_from {
101
+ return Err(RegistryError::PeriodOverlap {
102
+ domain: domain.clone(),
103
+ from: next.effective_from.clone(),
104
+ until: current_until.clone(),
105
+ });
106
+ }
107
+
108
+ let until_date = LegalDate::from_date_str(&current_until).ok_or_else(|| {
109
+ RegistryError::InvalidDateFormat {
110
+ domain: domain.clone(),
111
+ value: current_until.clone(),
112
+ }
113
+ })?;
114
+ let expected_next_from = until_date.next_day().to_date_str();
115
+ if expected_next_from != next.effective_from {
116
+ return Err(RegistryError::PeriodGap {
117
+ domain: domain.clone(),
118
+ end: current_until,
119
+ next_start: next.effective_from.clone(),
120
+ });
121
+ }
122
+ }
123
+
124
+ Ok(())
125
+ }
126
+
127
+ fn to_params(entry: &ConsumptionTaxHistoryEntry) -> ConsumptionTaxParams {
128
+ ConsumptionTaxParams {
129
+ standard_rate: ConsumptionTaxRate {
130
+ numer: entry.params.standard_rate.numer,
131
+ denom: entry.params.standard_rate.denom,
132
+ },
133
+ reduced_rate: entry
134
+ .params
135
+ .reduced_rate
136
+ .as_ref()
137
+ .map(|r| ConsumptionTaxRate {
138
+ numer: r.numer,
139
+ denom: r.denom,
140
+ }),
141
+ }
142
+ }
143
+
144
+ #[cfg(test)]
145
+ #[allow(clippy::disallowed_methods)] // テストコードでは unwrap 使用を許可
146
+ mod tests {
147
+ use super::*;
148
+ use crate::consumption_tax_schema::{
149
+ ConsumptionTaxCitationEntry, ConsumptionTaxFraction, ConsumptionTaxHistoryEntry,
150
+ ConsumptionTaxParamsEntry, ConsumptionTaxRegistry,
151
+ };
152
+
153
+ fn make_registry(entries: Vec<ConsumptionTaxHistoryEntry>) -> ConsumptionTaxRegistry {
154
+ ConsumptionTaxRegistry {
155
+ domain: "consumption_tax".into(),
156
+ history: entries,
157
+ }
158
+ }
159
+
160
+ fn make_entry(from: &str, until: Option<&str>) -> ConsumptionTaxHistoryEntry {
161
+ ConsumptionTaxHistoryEntry {
162
+ effective_from: from.into(),
163
+ effective_until: until.map(|s| s.into()),
164
+ status: "active".into(),
165
+ citation: ConsumptionTaxCitationEntry {
166
+ law_id: "test".into(),
167
+ law_name: "消費税法".into(),
168
+ article: 29,
169
+ paragraph: None,
170
+ ministry: "財務省".into(),
171
+ },
172
+ params: ConsumptionTaxParamsEntry {
173
+ standard_rate: ConsumptionTaxFraction {
174
+ numer: 10,
175
+ denom: 100,
176
+ },
177
+ reduced_rate: None,
178
+ },
179
+ }
180
+ }
181
+
182
+ #[test]
183
+ fn registry_validation_passes_for_current_data() {
184
+ let json_str = include_str!("../data/consumption_tax/consumption_tax.json");
185
+ let registry: ConsumptionTaxRegistry = serde_json::from_str(json_str).unwrap();
186
+ assert!(validate_registry(&registry).is_ok());
187
+ }
188
+
189
+ #[test]
190
+ fn registry_validation_detects_overlap() {
191
+ let reg = make_registry(vec![
192
+ make_entry("1989-04-01", Some("1997-04-15")),
193
+ make_entry("1997-04-01", None),
194
+ ]);
195
+ let err = validate_registry(&reg).unwrap_err();
196
+ assert!(matches!(err, RegistryError::PeriodOverlap { .. }));
197
+ }
198
+
199
+ #[test]
200
+ fn registry_validation_detects_gap() {
201
+ let reg = make_registry(vec![
202
+ make_entry("1989-04-01", Some("1997-03-31")),
203
+ make_entry("1997-04-03", None),
204
+ ]);
205
+ let err = validate_registry(&reg).unwrap_err();
206
+ assert!(matches!(err, RegistryError::PeriodGap { .. }));
207
+ }
208
+
209
+ #[test]
210
+ fn registry_validation_detects_zero_denominator_standard_rate() {
211
+ let mut reg = make_registry(vec![make_entry("1989-04-01", None)]);
212
+ reg.history[0].params.standard_rate.denom = 0;
213
+ let err = validate_registry(&reg).unwrap_err();
214
+ assert!(matches!(err, RegistryError::ZeroDenominator { .. }));
215
+ }
216
+
217
+ #[test]
218
+ fn registry_validation_detects_zero_denominator_reduced_rate() {
219
+ let mut reg = make_registry(vec![make_entry("2019-10-01", None)]);
220
+ reg.history[0].params.reduced_rate = Some(ConsumptionTaxFraction { numer: 8, denom: 0 });
221
+ let err = validate_registry(&reg).unwrap_err();
222
+ assert!(matches!(err, RegistryError::ZeroDenominator { .. }));
223
+ }
224
+
225
+ #[test]
226
+ fn registry_validation_detects_open_ended_before_next() {
227
+ let reg = make_registry(vec![
228
+ make_entry("1989-04-01", None), // effective_until なし(open-ended)
229
+ make_entry("1997-04-01", None),
230
+ ]);
231
+ let err = validate_registry(&reg).unwrap_err();
232
+ assert!(matches!(err, RegistryError::PeriodOverlap { .. }));
233
+ }
234
+
235
+ #[test]
236
+ fn load_3pct_1990() {
237
+ let params = load_consumption_tax_params(LegalDate::new(1990, 1, 1)).unwrap();
238
+ assert_eq!(params.standard_rate.numer, 3);
239
+ assert_eq!(params.standard_rate.denom, 100);
240
+ assert!(params.reduced_rate.is_none());
241
+ }
242
+
243
+ #[test]
244
+ fn load_5pct_2000() {
245
+ let params = load_consumption_tax_params(LegalDate::new(2000, 1, 1)).unwrap();
246
+ assert_eq!(params.standard_rate.numer, 5);
247
+ assert_eq!(params.standard_rate.denom, 100);
248
+ assert!(params.reduced_rate.is_none());
249
+ }
250
+
251
+ #[test]
252
+ fn load_8pct_2016() {
253
+ let params = load_consumption_tax_params(LegalDate::new(2016, 1, 1)).unwrap();
254
+ assert_eq!(params.standard_rate.numer, 8);
255
+ assert_eq!(params.standard_rate.denom, 100);
256
+ assert!(params.reduced_rate.is_none());
257
+ }
258
+
259
+ #[test]
260
+ fn load_10pct_with_reduced_2020() {
261
+ let params = load_consumption_tax_params(LegalDate::new(2020, 1, 1)).unwrap();
262
+ assert_eq!(params.standard_rate.numer, 10);
263
+ assert_eq!(params.standard_rate.denom, 100);
264
+ let reduced = params.reduced_rate.unwrap();
265
+ assert_eq!(reduced.numer, 8);
266
+ assert_eq!(reduced.denom, 100);
267
+ }
268
+
269
+ #[test]
270
+ fn before_introduction_returns_zero_rate() {
271
+ // 消費税導入前はエラーではなく0%を返す
272
+ let params = load_consumption_tax_params(LegalDate::new(1988, 12, 31)).unwrap();
273
+ assert_eq!(params.standard_rate.numer, 0);
274
+ assert!(params.reduced_rate.is_none());
275
+ }
276
+
277
+ #[test]
278
+ fn boundary_1989_04_01_is_3pct() {
279
+ let params = load_consumption_tax_params(LegalDate::new(1989, 4, 1)).unwrap();
280
+ assert_eq!(params.standard_rate.numer, 3);
281
+ }
282
+
283
+ #[test]
284
+ fn boundary_1997_03_31_is_3pct() {
285
+ // 1997-03-31 まで3%
286
+ let params = load_consumption_tax_params(LegalDate::new(1997, 3, 31)).unwrap();
287
+ assert_eq!(params.standard_rate.numer, 3);
288
+ }
289
+
290
+ #[test]
291
+ fn boundary_1997_04_01_is_5pct() {
292
+ // 1997-04-01 から5%
293
+ let params = load_consumption_tax_params(LegalDate::new(1997, 4, 1)).unwrap();
294
+ assert_eq!(params.standard_rate.numer, 5);
295
+ }
296
+
297
+ #[test]
298
+ fn boundary_2014_04_01_is_8pct() {
299
+ let params = load_consumption_tax_params(LegalDate::new(2014, 4, 1)).unwrap();
300
+ assert_eq!(params.standard_rate.numer, 8);
301
+ }
302
+
303
+ #[test]
304
+ fn boundary_2019_09_30_is_8pct() {
305
+ let params = load_consumption_tax_params(LegalDate::new(2019, 9, 30)).unwrap();
306
+ assert_eq!(params.standard_rate.numer, 8);
307
+ assert!(params.reduced_rate.is_none());
308
+ }
309
+
310
+ #[test]
311
+ fn boundary_2019_10_01_is_10pct() {
312
+ let params = load_consumption_tax_params(LegalDate::new(2019, 10, 1)).unwrap();
313
+ assert_eq!(params.standard_rate.numer, 10);
314
+ assert!(params.reduced_rate.is_some());
315
+ }
316
+
317
+ #[test]
318
+ fn invalid_date_returns_input_error() {
319
+ let result = load_consumption_tax_params(LegalDate::new(2024, 13, 1));
320
+ assert!(matches!(
321
+ result,
322
+ Err(JLawError::Input(j_law_core::InputError::InvalidDate { .. }))
323
+ ));
324
+ }
325
+ }
@@ -0,0 +1,49 @@
1
+ use serde::Deserialize;
2
+
3
+ /// JSON の分数表現 `{ "numer": N, "denom": N }`。
4
+ #[derive(Debug, Clone, Deserialize)]
5
+ pub struct ConsumptionTaxFraction {
6
+ pub numer: u64,
7
+ pub denom: u64,
8
+ }
9
+
10
+ /// 1世代の消費税パラメータ群。
11
+ #[derive(Debug, Clone, Deserialize)]
12
+ pub struct ConsumptionTaxParamsEntry {
13
+ pub standard_rate: ConsumptionTaxFraction,
14
+ pub reduced_rate: Option<ConsumptionTaxFraction>,
15
+ }
16
+
17
+ /// 法令引用情報。
18
+ #[derive(Debug, Clone, Deserialize)]
19
+ #[allow(dead_code)]
20
+ pub struct ConsumptionTaxCitationEntry {
21
+ pub law_id: String,
22
+ pub law_name: String,
23
+ pub article: u16,
24
+ pub paragraph: Option<u16>,
25
+ pub ministry: String,
26
+ }
27
+
28
+ /// 1世代の履歴エントリ。
29
+ #[derive(Debug, Clone, Deserialize)]
30
+ pub struct ConsumptionTaxHistoryEntry {
31
+ /// 施行日 `"YYYY-MM-DD"`。
32
+ pub effective_from: String,
33
+ /// 廃止日 `"YYYY-MM-DD"`。現行版は `null`。
34
+ pub effective_until: Option<String>,
35
+ /// `"active"` または `"superseded"`。
36
+ #[allow(dead_code)]
37
+ pub status: String,
38
+ #[allow(dead_code)]
39
+ pub citation: ConsumptionTaxCitationEntry,
40
+ pub params: ConsumptionTaxParamsEntry,
41
+ }
42
+
43
+ /// `consumption_tax.json` のルートスキーマ。
44
+ #[derive(Debug, Clone, Deserialize)]
45
+ pub struct ConsumptionTaxRegistry {
46
+ #[allow(dead_code)]
47
+ pub domain: String,
48
+ pub history: Vec<ConsumptionTaxHistoryEntry>,
49
+ }