1use crate::picture::Sym;
5use zarch::ebcdic;
6
7pub fn numeric(syms: &[Sym], digits: u32, negative: bool, magnitude: u128, blank_when_zero: bool, point: char, currency: &str) -> String {
12 let width = |s: &Sym| width(s, currency);
13 let size: usize = syms.iter().map(width).sum();
14 let digit_syms: Vec<&Sym> = syms.iter().filter(|s| s.is_digit()).collect();
15 if magnitude == 0 && (blank_when_zero || digit_syms.iter().all(|s| matches!(s, Sym::Z | Sym::Float(_)))) {
16 return " ".repeat(size);
17 }
18 if magnitude == 0 && digit_syms.iter().all(|s| matches!(s, Sym::Star)) {
19 return syms.iter().flat_map(|s| std::iter::repeat_n(if *s == Sym::Point { point } else { '*' }, width(s))).collect();
20 }
21 let text = format!("{magnitude:0width$}", width = digits as usize);
22 let mut digit_chars = text.chars().skip(text.len() - digits as usize);
23 let mut out: Vec<char> = Vec::with_capacity(size);
24 let (mut significant, mut fill) = (false, None);
25 let (mut float_char, mut float_slot) = (None, None);
26 for s in syms {
27 match *s {
28 Sym::Nine => {
29 significant = true;
30 out.push(digit_chars.next().unwrap_or('0'));
31 }
32 Sym::Z | Sym::Star => {
33 let blank = if *s == Sym::Star { '*' } else { ' ' };
34 fill = Some(blank);
35 let d = digit_chars.next().unwrap_or('0');
36 significant |= d != '0';
37 out.push(if significant { d } else { blank });
38 }
39 Sym::FloatLead(c) => {
40 float_char = Some(c);
41 out.extend(std::iter::repeat_n(' ', width(s)));
42 float_slot = Some(out.len() - 1);
43 }
44 Sym::Float(_) => {
45 let d = digit_chars.next().unwrap_or('0');
46 significant |= d != '0';
47 if significant {
48 out.push(d);
49 } else {
50 float_slot = Some(out.len());
51 out.push(' ');
52 }
53 }
54 Sym::Point | Sym::Implied => {
55 significant = true;
56 if *s == Sym::Point {
57 out.push(point);
58 }
59 }
60 Sym::Insert(c) => {
61 if significant {
62 out.push(c);
63 } else if float_char.is_some() {
64 float_slot = Some(out.len());
65 out.push(' ');
66 } else {
67 out.push(fill.unwrap_or(c));
68 }
69 }
70 Sym::Sign(c) => out.push(match (c, negative) {
71 ('+', false) => '+',
72 (_, true) => '-',
73 _ => ' ',
74 }),
75 Sym::Currency => out.extend(currency.chars()),
76 Sym::Cr => out.extend(if negative { ['C', 'R'] } else { [' ', ' '] }),
77 Sym::Db => out.extend(if negative { ['D', 'B'] } else { [' ', ' '] }),
78 Sym::Char => out.push(' '),
79 }
80 }
81 match (float_char, float_slot) {
82 (Some('$'), Some(slot)) => {
83 let value: Vec<char> = currency.chars().collect();
84 out[slot + 1 - value.len()..=slot].copy_from_slice(&value);
85 }
86 (Some(c), Some(slot)) => {
87 out[slot] = match (c, negative) {
88 ('+', false) => '+',
89 (_, true) => '-',
90 _ => ' ',
91 }
92 }
93 _ => {}
94 }
95 out.into_iter().collect()
96}
97
98pub fn alphanumeric(syms: &[Sym], data: &[u8], space: u8, encode: impl Fn(char) -> u8) -> Vec<u8> {
101 let mut source = data.iter();
102 syms.iter()
103 .map(|s| match s {
104 Sym::Insert(c) => encode(*c),
105 _ => source.next().copied().unwrap_or(space),
106 })
107 .collect()
108}
109
110pub fn dbcs(syms: &[Sym], data: &[u8]) -> Vec<u8> {
113 let mut source = data.as_chunks::<2>().0.iter();
114 syms.iter()
115 .flat_map(|s| match s {
116 Sym::Insert(_) => [ebcdic::SPACE; 2],
117 _ => source.next().copied().unwrap_or([ebcdic::SPACE; 2]),
118 })
119 .collect()
120}
121
122pub fn de_edit(syms: &[Sym], text: &str, currency: &str) -> (bool, u128) {
125 let chars: Vec<char> = text.chars().collect();
126 let (mut at, mut magnitude, mut negative) = (0usize, 0u128, false);
127 for s in syms {
128 let c = chars.get(at).copied().unwrap_or(' ');
129 match s {
130 Sym::Implied => continue,
131 Sym::Cr | Sym::Db => {
132 negative |= c != ' ';
133 at += 2;
134 continue;
135 }
136 _ if s.is_digit() => magnitude = magnitude * 10 + c.to_digit(10).unwrap_or(0) as u128,
137 Sym::Sign(_) | Sym::FloatLead(_) | Sym::Float(_) => negative |= c == '-',
138 _ => {}
139 }
140 if matches!(s, Sym::Float(_)) {
141 negative |= c == '-';
142 }
143 at += width(s, currency);
144 }
145 (negative, magnitude)
146}
147
148fn width(s: &Sym, currency: &str) -> usize {
150 match s {
151 Sym::Currency | Sym::FloatLead('$') => currency.chars().count().max(1),
152 _ => s.width(),
153 }
154}