1use crate::cost::{BookedCost, Cost, CostNumber, CostSpec};
33use crate::directive::{
34 Balance, Close, Commodity, Custom, Directive, Document, Event, MetaValue, Note, Open, Pad,
35 Posting, Price, PriceAnnotation, PriceKind, Query, Transaction,
36};
37use crate::identifiers::{Account, Currency, Link, Tag};
38use crate::{Amount, IncompleteAmount, InternedStr, ShiftSpans, Span};
39
40impl<K, V: ShiftSpans, S> ShiftSpans for std::collections::HashMap<K, V, S> {
57 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
58 for value in self.values_mut() {
59 value.shift_spans(shift);
60 }
61 }
62}
63
64crate::impl_shift_spans_noop!(
71 rust_decimal::Decimal,
72 jiff::civil::Date,
73 char,
74 f32,
75 f64,
76 InternedStr,
77 Account,
78 Currency,
79 Tag,
80 Link,
81);
82
83impl ShiftSpans for Amount {
86 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
87 let Self { number, currency } = self;
88 number.shift_spans(shift);
89 currency.shift_spans(shift);
90 }
91}
92
93impl ShiftSpans for IncompleteAmount {
94 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
95 match self {
100 Self::Complete(amount) => amount.shift_spans(shift),
101 Self::NumberOnly(number) => number.shift_spans(shift),
102 Self::CurrencyOnly(currency) => currency.shift_spans(shift),
103 }
104 }
105}
106
107impl ShiftSpans for Cost {
110 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
111 let Self {
112 number,
113 currency,
114 date,
115 label,
116 } = self;
117 number.shift_spans(shift);
118 currency.shift_spans(shift);
119 date.shift_spans(shift);
120 label.shift_spans(shift);
121 }
122}
123
124impl ShiftSpans for CostSpec {
125 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
126 let Self {
127 number,
128 currency,
129 date,
130 label,
131 merge,
132 } = self;
133 number.shift_spans(shift);
134 currency.shift_spans(shift);
135 date.shift_spans(shift);
136 label.shift_spans(shift);
137 merge.shift_spans(shift);
138 }
139}
140
141impl ShiftSpans for CostNumber {
142 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
143 match self {
144 Self::PerUnit { value } | Self::Total { value } => value.shift_spans(shift),
145 Self::PerUnitFromTotal(booked) => booked.shift_spans(shift),
146 Self::Compound { per_unit, total } => {
147 per_unit.shift_spans(shift);
148 total.shift_spans(shift);
149 }
150 }
151 }
152}
153
154impl ShiftSpans for BookedCost {
155 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
156 let Self { per_unit, total } = self;
157 per_unit.shift_spans(shift);
158 total.shift_spans(shift);
159 }
160}
161
162impl ShiftSpans for PriceAnnotation {
165 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
166 let Self { kind, amount } = self;
167 kind.shift_spans(shift);
168 amount.shift_spans(shift);
169 }
170}
171
172impl ShiftSpans for PriceKind {
173 fn shift_spans<F: Fn(&mut Span)>(&mut self, _: &F) {
174 match self {
177 Self::Unit | Self::Total => {}
178 }
179 }
180}
181
182impl ShiftSpans for MetaValue {
185 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
186 match self {
201 Self::String(s) => s.shift_spans(shift),
202 Self::Account(a) => a.shift_spans(shift),
203 Self::Currency(c) => c.shift_spans(shift),
204 Self::Tag(t) => t.shift_spans(shift),
205 Self::Link(l) => l.shift_spans(shift),
206 Self::Date(d) => d.shift_spans(shift),
207 Self::Number(n) => n.shift_spans(shift),
208 Self::Bool(b) => b.shift_spans(shift),
209 Self::Amount(a) => a.shift_spans(shift),
210 Self::None => {}
211 Self::Int(i) => i.shift_spans(shift),
212 }
213 }
214}
215
216impl ShiftSpans for Posting {
219 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
220 let Self {
221 account,
222 units,
223 cost,
224 price,
225 flag,
226 meta,
227 comments,
228 trailing_comments,
229 } = self;
230 account.shift_spans(shift);
231 units.shift_spans(shift);
232 cost.shift_spans(shift);
233 price.shift_spans(shift);
234 flag.shift_spans(shift);
235 meta.shift_spans(shift);
236 comments.shift_spans(shift);
237 trailing_comments.shift_spans(shift);
238 }
239}
240
241impl ShiftSpans for Transaction {
244 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
245 let Self {
246 date,
247 flag,
248 payee,
249 narration,
250 tags,
251 links,
252 meta,
253 postings,
254 trailing_comments,
255 } = self;
256 date.shift_spans(shift);
257 flag.shift_spans(shift);
258 payee.shift_spans(shift);
259 narration.shift_spans(shift);
260 tags.shift_spans(shift);
261 links.shift_spans(shift);
262 meta.shift_spans(shift);
263 postings.shift_spans(shift);
264 trailing_comments.shift_spans(shift);
265 }
266}
267
268impl ShiftSpans for Open {
269 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
270 let Self {
271 date,
272 account,
273 currencies,
274 booking,
275 meta,
276 } = self;
277 date.shift_spans(shift);
278 account.shift_spans(shift);
279 currencies.shift_spans(shift);
280 booking.shift_spans(shift);
281 meta.shift_spans(shift);
282 }
283}
284
285impl ShiftSpans for Close {
286 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
287 let Self {
288 date,
289 account,
290 meta,
291 } = self;
292 date.shift_spans(shift);
293 account.shift_spans(shift);
294 meta.shift_spans(shift);
295 }
296}
297
298impl ShiftSpans for Balance {
299 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
300 let Self {
301 date,
302 account,
303 amount,
304 tolerance,
305 meta,
306 } = self;
307 date.shift_spans(shift);
308 account.shift_spans(shift);
309 amount.shift_spans(shift);
310 tolerance.shift_spans(shift);
311 meta.shift_spans(shift);
312 }
313}
314
315impl ShiftSpans for Pad {
316 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
317 let Self {
318 date,
319 account,
320 source_account,
321 meta,
322 } = self;
323 date.shift_spans(shift);
324 account.shift_spans(shift);
325 source_account.shift_spans(shift);
326 meta.shift_spans(shift);
327 }
328}
329
330impl ShiftSpans for Note {
331 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
332 let Self {
333 date,
334 account,
335 comment,
336 tags,
337 links,
338 meta,
339 } = self;
340 date.shift_spans(shift);
341 account.shift_spans(shift);
342 comment.shift_spans(shift);
343 tags.shift_spans(shift);
344 links.shift_spans(shift);
345 meta.shift_spans(shift);
346 }
347}
348
349impl ShiftSpans for Document {
350 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
351 let Self {
352 date,
353 account,
354 path,
355 tags,
356 links,
357 meta,
358 } = self;
359 date.shift_spans(shift);
360 account.shift_spans(shift);
361 path.shift_spans(shift);
362 tags.shift_spans(shift);
363 links.shift_spans(shift);
364 meta.shift_spans(shift);
365 }
366}
367
368impl ShiftSpans for Price {
369 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
370 let Self {
371 date,
372 currency,
373 amount,
374 meta,
375 } = self;
376 date.shift_spans(shift);
377 currency.shift_spans(shift);
378 amount.shift_spans(shift);
379 meta.shift_spans(shift);
380 }
381}
382
383impl ShiftSpans for Custom {
384 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
385 let Self {
386 date,
387 custom_type,
388 values,
389 meta,
390 } = self;
391 date.shift_spans(shift);
392 custom_type.shift_spans(shift);
393 values.shift_spans(shift);
400 meta.shift_spans(shift);
401 }
402}
403
404impl ShiftSpans for Event {
405 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
406 let Self {
407 date,
408 event_type,
409 value,
410 meta,
411 } = self;
412 date.shift_spans(shift);
413 event_type.shift_spans(shift);
414 value.shift_spans(shift);
415 meta.shift_spans(shift);
416 }
417}
418
419impl ShiftSpans for Query {
420 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
421 let Self {
422 date,
423 name,
424 query,
425 meta,
426 } = self;
427 date.shift_spans(shift);
428 name.shift_spans(shift);
429 query.shift_spans(shift);
430 meta.shift_spans(shift);
431 }
432}
433
434impl ShiftSpans for Commodity {
435 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
436 let Self {
437 date,
438 currency,
439 meta,
440 } = self;
441 date.shift_spans(shift);
442 currency.shift_spans(shift);
443 meta.shift_spans(shift);
444 }
445}
446
447impl ShiftSpans for Directive {
450 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
451 match self {
452 Self::Transaction(t) => t.shift_spans(shift),
453 Self::Balance(b) => b.shift_spans(shift),
454 Self::Open(o) => o.shift_spans(shift),
455 Self::Close(c) => c.shift_spans(shift),
456 Self::Commodity(c) => c.shift_spans(shift),
457 Self::Pad(p) => p.shift_spans(shift),
458 Self::Event(e) => e.shift_spans(shift),
459 Self::Query(q) => q.shift_spans(shift),
460 Self::Note(n) => n.shift_spans(shift),
461 Self::Document(d) => d.shift_spans(shift),
462 Self::Price(p) => p.shift_spans(shift),
463 Self::Custom(c) => c.shift_spans(shift),
464 }
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use crate::{NaiveDate, Spanned};
472
473 #[test]
476 fn directive_shift_spans_propagates_into_posting_spans() {
477 use crate::Amount;
478 use rust_decimal_macros::dec;
479
480 let posting = Spanned::new(
481 Posting::new("Assets:Bank", Amount::new(dec!(100), "USD")),
482 Span::new(50, 75),
483 );
484 let txn = Transaction {
485 date: NaiveDate::new(2024, 1, 1).unwrap(),
486 flag: '*',
487 payee: None,
488 narration: "Test".into(),
489 tags: Vec::new(),
490 links: Vec::new(),
491 meta: crate::Metadata::default(),
492 postings: vec![posting],
493 trailing_comments: Vec::new(),
494 };
495 let mut d = Directive::Transaction(txn);
496 d.shift_spans(&|s: &mut Span| {
497 s.start += 10;
498 s.end += 10;
499 });
500 if let Directive::Transaction(t) = d {
501 assert_eq!(t.postings[0].span, Span::new(60, 85));
502 } else {
503 unreachable!();
504 }
505 }
506
507 #[test]
519 fn metadata_shift_spans_dispatches_via_value_impl() {
520 use crate::Amount;
521 use rust_decimal_macros::dec;
522
523 let mut meta = crate::Metadata::default();
524 meta.insert(
525 "amt".to_string(),
526 MetaValue::Amount(Amount::new(dec!(1), "USD")),
527 );
528
529 let shift = |_: &mut Span| {
535 };
538 meta.shift_spans(&shift);
539 }
540}