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