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 }
147 }
148}
149
150impl ShiftSpans for BookedCost {
151 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
152 let Self { per_unit, total } = self;
153 per_unit.shift_spans(shift);
154 total.shift_spans(shift);
155 }
156}
157
158impl ShiftSpans for PriceAnnotation {
161 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
162 let Self { kind, amount } = self;
163 kind.shift_spans(shift);
164 amount.shift_spans(shift);
165 }
166}
167
168impl ShiftSpans for PriceKind {
169 fn shift_spans<F: Fn(&mut Span)>(&mut self, _: &F) {
170 match self {
173 Self::Unit | Self::Total => {}
174 }
175 }
176}
177
178impl ShiftSpans for MetaValue {
181 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
182 match self {
197 Self::String(s) => s.shift_spans(shift),
198 Self::Account(a) => a.shift_spans(shift),
199 Self::Currency(c) => c.shift_spans(shift),
200 Self::Tag(t) => t.shift_spans(shift),
201 Self::Link(l) => l.shift_spans(shift),
202 Self::Date(d) => d.shift_spans(shift),
203 Self::Number(n) => n.shift_spans(shift),
204 Self::Bool(b) => b.shift_spans(shift),
205 Self::Amount(a) => a.shift_spans(shift),
206 Self::None => {}
207 }
208 }
209}
210
211impl ShiftSpans for Posting {
214 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
215 let Self {
216 account,
217 units,
218 cost,
219 price,
220 flag,
221 meta,
222 comments,
223 trailing_comments,
224 } = self;
225 account.shift_spans(shift);
226 units.shift_spans(shift);
227 cost.shift_spans(shift);
228 price.shift_spans(shift);
229 flag.shift_spans(shift);
230 meta.shift_spans(shift);
231 comments.shift_spans(shift);
232 trailing_comments.shift_spans(shift);
233 }
234}
235
236impl ShiftSpans for Transaction {
239 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
240 let Self {
241 date,
242 flag,
243 payee,
244 narration,
245 tags,
246 links,
247 meta,
248 postings,
249 trailing_comments,
250 } = self;
251 date.shift_spans(shift);
252 flag.shift_spans(shift);
253 payee.shift_spans(shift);
254 narration.shift_spans(shift);
255 tags.shift_spans(shift);
256 links.shift_spans(shift);
257 meta.shift_spans(shift);
258 postings.shift_spans(shift);
259 trailing_comments.shift_spans(shift);
260 }
261}
262
263impl ShiftSpans for Open {
264 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
265 let Self {
266 date,
267 account,
268 currencies,
269 booking,
270 meta,
271 } = self;
272 date.shift_spans(shift);
273 account.shift_spans(shift);
274 currencies.shift_spans(shift);
275 booking.shift_spans(shift);
276 meta.shift_spans(shift);
277 }
278}
279
280impl ShiftSpans for Close {
281 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
282 let Self {
283 date,
284 account,
285 meta,
286 } = self;
287 date.shift_spans(shift);
288 account.shift_spans(shift);
289 meta.shift_spans(shift);
290 }
291}
292
293impl ShiftSpans for Balance {
294 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
295 let Self {
296 date,
297 account,
298 amount,
299 tolerance,
300 meta,
301 } = self;
302 date.shift_spans(shift);
303 account.shift_spans(shift);
304 amount.shift_spans(shift);
305 tolerance.shift_spans(shift);
306 meta.shift_spans(shift);
307 }
308}
309
310impl ShiftSpans for Pad {
311 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
312 let Self {
313 date,
314 account,
315 source_account,
316 meta,
317 } = self;
318 date.shift_spans(shift);
319 account.shift_spans(shift);
320 source_account.shift_spans(shift);
321 meta.shift_spans(shift);
322 }
323}
324
325impl ShiftSpans for Note {
326 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
327 let Self {
328 date,
329 account,
330 comment,
331 meta,
332 } = self;
333 date.shift_spans(shift);
334 account.shift_spans(shift);
335 comment.shift_spans(shift);
336 meta.shift_spans(shift);
337 }
338}
339
340impl ShiftSpans for Document {
341 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
342 let Self {
343 date,
344 account,
345 path,
346 tags,
347 links,
348 meta,
349 } = self;
350 date.shift_spans(shift);
351 account.shift_spans(shift);
352 path.shift_spans(shift);
353 tags.shift_spans(shift);
354 links.shift_spans(shift);
355 meta.shift_spans(shift);
356 }
357}
358
359impl ShiftSpans for Price {
360 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
361 let Self {
362 date,
363 currency,
364 amount,
365 meta,
366 } = self;
367 date.shift_spans(shift);
368 currency.shift_spans(shift);
369 amount.shift_spans(shift);
370 meta.shift_spans(shift);
371 }
372}
373
374impl ShiftSpans for Custom {
375 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
376 let Self {
377 date,
378 custom_type,
379 values,
380 meta,
381 } = self;
382 date.shift_spans(shift);
383 custom_type.shift_spans(shift);
384 values.shift_spans(shift);
391 meta.shift_spans(shift);
392 }
393}
394
395impl ShiftSpans for Event {
396 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
397 let Self {
398 date,
399 event_type,
400 value,
401 meta,
402 } = self;
403 date.shift_spans(shift);
404 event_type.shift_spans(shift);
405 value.shift_spans(shift);
406 meta.shift_spans(shift);
407 }
408}
409
410impl ShiftSpans for Query {
411 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
412 let Self {
413 date,
414 name,
415 query,
416 meta,
417 } = self;
418 date.shift_spans(shift);
419 name.shift_spans(shift);
420 query.shift_spans(shift);
421 meta.shift_spans(shift);
422 }
423}
424
425impl ShiftSpans for Commodity {
426 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
427 let Self {
428 date,
429 currency,
430 meta,
431 } = self;
432 date.shift_spans(shift);
433 currency.shift_spans(shift);
434 meta.shift_spans(shift);
435 }
436}
437
438impl ShiftSpans for Directive {
441 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
442 match self {
443 Self::Transaction(t) => t.shift_spans(shift),
444 Self::Balance(b) => b.shift_spans(shift),
445 Self::Open(o) => o.shift_spans(shift),
446 Self::Close(c) => c.shift_spans(shift),
447 Self::Commodity(c) => c.shift_spans(shift),
448 Self::Pad(p) => p.shift_spans(shift),
449 Self::Event(e) => e.shift_spans(shift),
450 Self::Query(q) => q.shift_spans(shift),
451 Self::Note(n) => n.shift_spans(shift),
452 Self::Document(d) => d.shift_spans(shift),
453 Self::Price(p) => p.shift_spans(shift),
454 Self::Custom(c) => c.shift_spans(shift),
455 }
456 }
457}
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462 use crate::{NaiveDate, Spanned};
463
464 #[test]
467 fn directive_shift_spans_propagates_into_posting_spans() {
468 use crate::Amount;
469 use rust_decimal_macros::dec;
470
471 let posting = Spanned::new(
472 Posting::new("Assets:Bank", Amount::new(dec!(100), "USD")),
473 Span::new(50, 75),
474 );
475 let txn = Transaction {
476 date: NaiveDate::new(2024, 1, 1).unwrap(),
477 flag: '*',
478 payee: None,
479 narration: "Test".into(),
480 tags: Vec::new(),
481 links: Vec::new(),
482 meta: crate::Metadata::default(),
483 postings: vec![posting],
484 trailing_comments: Vec::new(),
485 };
486 let mut d = Directive::Transaction(txn);
487 d.shift_spans(&|s: &mut Span| {
488 s.start += 10;
489 s.end += 10;
490 });
491 if let Directive::Transaction(t) = d {
492 assert_eq!(t.postings[0].span, Span::new(60, 85));
493 } else {
494 unreachable!();
495 }
496 }
497
498 #[test]
510 fn metadata_shift_spans_dispatches_via_value_impl() {
511 use crate::Amount;
512 use rust_decimal_macros::dec;
513
514 let mut meta = crate::Metadata::default();
515 meta.insert(
516 "amt".to_string(),
517 MetaValue::Amount(Amount::new(dec!(1), "USD")),
518 );
519
520 let shift = |_: &mut Span| {
526 };
529 meta.shift_spans(&shift);
530 }
531}