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 meta,
337 } = self;
338 date.shift_spans(shift);
339 account.shift_spans(shift);
340 comment.shift_spans(shift);
341 meta.shift_spans(shift);
342 }
343}
344
345impl ShiftSpans for Document {
346 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
347 let Self {
348 date,
349 account,
350 path,
351 tags,
352 links,
353 meta,
354 } = self;
355 date.shift_spans(shift);
356 account.shift_spans(shift);
357 path.shift_spans(shift);
358 tags.shift_spans(shift);
359 links.shift_spans(shift);
360 meta.shift_spans(shift);
361 }
362}
363
364impl ShiftSpans for Price {
365 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
366 let Self {
367 date,
368 currency,
369 amount,
370 meta,
371 } = self;
372 date.shift_spans(shift);
373 currency.shift_spans(shift);
374 amount.shift_spans(shift);
375 meta.shift_spans(shift);
376 }
377}
378
379impl ShiftSpans for Custom {
380 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
381 let Self {
382 date,
383 custom_type,
384 values,
385 meta,
386 } = self;
387 date.shift_spans(shift);
388 custom_type.shift_spans(shift);
389 values.shift_spans(shift);
396 meta.shift_spans(shift);
397 }
398}
399
400impl ShiftSpans for Event {
401 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
402 let Self {
403 date,
404 event_type,
405 value,
406 meta,
407 } = self;
408 date.shift_spans(shift);
409 event_type.shift_spans(shift);
410 value.shift_spans(shift);
411 meta.shift_spans(shift);
412 }
413}
414
415impl ShiftSpans for Query {
416 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
417 let Self {
418 date,
419 name,
420 query,
421 meta,
422 } = self;
423 date.shift_spans(shift);
424 name.shift_spans(shift);
425 query.shift_spans(shift);
426 meta.shift_spans(shift);
427 }
428}
429
430impl ShiftSpans for Commodity {
431 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
432 let Self {
433 date,
434 currency,
435 meta,
436 } = self;
437 date.shift_spans(shift);
438 currency.shift_spans(shift);
439 meta.shift_spans(shift);
440 }
441}
442
443impl ShiftSpans for Directive {
446 fn shift_spans<F: Fn(&mut Span)>(&mut self, shift: &F) {
447 match self {
448 Self::Transaction(t) => t.shift_spans(shift),
449 Self::Balance(b) => b.shift_spans(shift),
450 Self::Open(o) => o.shift_spans(shift),
451 Self::Close(c) => c.shift_spans(shift),
452 Self::Commodity(c) => c.shift_spans(shift),
453 Self::Pad(p) => p.shift_spans(shift),
454 Self::Event(e) => e.shift_spans(shift),
455 Self::Query(q) => q.shift_spans(shift),
456 Self::Note(n) => n.shift_spans(shift),
457 Self::Document(d) => d.shift_spans(shift),
458 Self::Price(p) => p.shift_spans(shift),
459 Self::Custom(c) => c.shift_spans(shift),
460 }
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use crate::{NaiveDate, Spanned};
468
469 #[test]
472 fn directive_shift_spans_propagates_into_posting_spans() {
473 use crate::Amount;
474 use rust_decimal_macros::dec;
475
476 let posting = Spanned::new(
477 Posting::new("Assets:Bank", Amount::new(dec!(100), "USD")),
478 Span::new(50, 75),
479 );
480 let txn = Transaction {
481 date: NaiveDate::new(2024, 1, 1).unwrap(),
482 flag: '*',
483 payee: None,
484 narration: "Test".into(),
485 tags: Vec::new(),
486 links: Vec::new(),
487 meta: crate::Metadata::default(),
488 postings: vec![posting],
489 trailing_comments: Vec::new(),
490 };
491 let mut d = Directive::Transaction(txn);
492 d.shift_spans(&|s: &mut Span| {
493 s.start += 10;
494 s.end += 10;
495 });
496 if let Directive::Transaction(t) = d {
497 assert_eq!(t.postings[0].span, Span::new(60, 85));
498 } else {
499 unreachable!();
500 }
501 }
502
503 #[test]
515 fn metadata_shift_spans_dispatches_via_value_impl() {
516 use crate::Amount;
517 use rust_decimal_macros::dec;
518
519 let mut meta = crate::Metadata::default();
520 meta.insert(
521 "amt".to_string(),
522 MetaValue::Amount(Amount::new(dec!(1), "USD")),
523 );
524
525 let shift = |_: &mut Span| {
531 };
534 meta.shift_spans(&shift);
535 }
536}