Skip to main content

ironwork_rt/
edit.rs

1//! Editing: a numeric value laid into a numeric-edited PICTURE, alphanumeric data into an
2//! alphanumeric-edited one, and de-editing back to a number.
3
4use crate::picture::Sym;
5
6/// The characters a numeric-edited item holds for a value. `magnitude` is already aligned to the
7/// PICTURE's decimal places and within its digit positions. `point` is what a decimal point
8/// position shows: a period, or a comma under DECIMAL-POINT IS COMMA.
9pub fn numeric(syms: &[Sym], digits: u32, negative: bool, magnitude: u128, blank_when_zero: bool, point: char) -> String {
10    let size: usize = syms.iter().map(Sym::width).sum();
11    let digit_syms: Vec<&Sym> = syms.iter().filter(|s| s.is_digit()).collect();
12    if magnitude == 0 && (blank_when_zero || digit_syms.iter().all(|s| matches!(s, Sym::Z | Sym::Float(_)))) {
13        return " ".repeat(size);
14    }
15    if magnitude == 0 && digit_syms.iter().all(|s| matches!(s, Sym::Star)) {
16        return syms.iter().flat_map(|s| std::iter::repeat_n(if *s == Sym::Point { point } else { '*' }, s.width())).collect();
17    }
18    let text = format!("{magnitude:0width$}", width = digits as usize);
19    let mut digit_chars = text.chars().skip(text.len() - digits as usize);
20    let mut out: Vec<char> = Vec::with_capacity(size);
21    let (mut significant, mut fill) = (false, None);
22    let (mut float_char, mut float_slot) = (None, None);
23    for s in syms {
24        match *s {
25            Sym::Nine => {
26                significant = true;
27                out.push(digit_chars.next().unwrap_or('0'));
28            }
29            Sym::Z | Sym::Star => {
30                let blank = if *s == Sym::Star { '*' } else { ' ' };
31                fill = Some(blank);
32                let d = digit_chars.next().unwrap_or('0');
33                significant |= d != '0';
34                out.push(if significant { d } else { blank });
35            }
36            Sym::FloatLead(c) => {
37                float_char = Some(c);
38                float_slot = Some(out.len());
39                out.push(' ');
40            }
41            Sym::Float(_) => {
42                let d = digit_chars.next().unwrap_or('0');
43                significant |= d != '0';
44                if significant {
45                    out.push(d);
46                } else {
47                    float_slot = Some(out.len());
48                    out.push(' ');
49                }
50            }
51            Sym::Point | Sym::Implied => {
52                significant = true;
53                if *s == Sym::Point {
54                    out.push(point);
55                }
56            }
57            Sym::Insert(c) => {
58                if significant {
59                    out.push(c);
60                } else if float_char.is_some() {
61                    float_slot = Some(out.len());
62                    out.push(' ');
63                } else {
64                    out.push(fill.unwrap_or(c));
65                }
66            }
67            Sym::Sign(c) => out.push(match (c, negative) {
68                ('+', false) => '+',
69                (_, true) => '-',
70                _ => ' ',
71            }),
72            Sym::Currency => out.push('$'),
73            Sym::Cr => out.extend(if negative { ['C', 'R'] } else { [' ', ' '] }),
74            Sym::Db => out.extend(if negative { ['D', 'B'] } else { [' ', ' '] }),
75            Sym::Char => out.push(' '),
76        }
77    }
78    if let (Some(c), Some(slot)) = (float_char, float_slot) {
79        out[slot] = match (c, negative) {
80            ('$', _) => '$',
81            ('+', false) => '+',
82            (_, true) => '-',
83            _ => ' ',
84        };
85    }
86    out.into_iter().collect()
87}
88
89/// Alphanumeric data laid into an alphanumeric-edited PICTURE: characters fill the X, A and 9
90/// positions in order; insertion characters stand where they are written.
91pub fn alphanumeric(syms: &[Sym], data: &[u8], space: u8, encode: impl Fn(char) -> u8) -> Vec<u8> {
92    let mut source = data.iter();
93    syms.iter()
94        .map(|s| match s {
95            Sym::Insert(c) => encode(*c),
96            _ => source.next().copied().unwrap_or(space),
97        })
98        .collect()
99}
100
101/// A numeric-edited item's value: the digits in its digit positions, negative when it shows a
102/// minus sign, CR or DB.
103pub fn de_edit(syms: &[Sym], text: &str) -> (bool, u128) {
104    let chars: Vec<char> = text.chars().collect();
105    let (mut at, mut magnitude, mut negative) = (0usize, 0u128, false);
106    for s in syms {
107        let c = chars.get(at).copied().unwrap_or(' ');
108        match s {
109            Sym::Implied => continue,
110            Sym::Cr | Sym::Db => {
111                negative |= c != ' ';
112                at += 2;
113                continue;
114            }
115            _ if s.is_digit() => magnitude = magnitude * 10 + c.to_digit(10).unwrap_or(0) as u128,
116            Sym::Sign(_) | Sym::FloatLead(_) | Sym::Float(_) => negative |= c == '-',
117            _ => {}
118        }
119        if matches!(s, Sym::Float(_)) {
120            negative |= c == '-';
121        }
122        at += 1;
123    }
124    (negative, magnitude)
125}