1use crate::{codec_enum, codec_struct};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum Mode {
9 In,
10 Out,
11 InOut,
12}
13
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum Initial {
16 Text(String),
17 Bytes(Vec<u8>),
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Protection {
22 Askip,
23 Prot,
24 Unprot,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum Intensity {
29 Norm,
30 Brt,
31 Drk,
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub struct Attrb {
36 pub protection: Protection,
37 pub numeric: bool,
38 pub intensity: Intensity,
39 pub detectable: bool,
40 pub cursor: bool,
41 pub fset: bool,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct Field {
46 pub name: Option<String>,
48 pub line: u16,
50 pub column: u16,
51 pub length: u16,
52 pub attrb: Attrb,
53 pub initial: Option<Initial>,
55 pub picin: Option<String>,
56 pub picout: Option<String>,
57 pub occurs: u16,
58 pub group: Option<String>,
59 pub justify_right: bool,
60 pub fill_zero: bool,
61 pub color: Option<String>,
62 pub hilight: Option<String>,
63}
64
65#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct Map {
67 pub name: String,
68 pub lines: u16,
69 pub columns: u16,
70 pub line: u16,
71 pub column: u16,
72 pub ctrl: Vec<String>,
73 pub tioapfx: bool,
74 pub dsatts: Vec<String>,
75 pub fields: Vec<Field>,
76}
77
78#[derive(Clone, Debug, PartialEq, Eq)]
79pub struct Mapset {
80 pub name: String,
81 pub mode: Mode,
82 pub ctrl: Vec<String>,
83 pub maps: Vec<Map>,
84}
85
86codec_enum!(Mode { In = 0, Out = 1, InOut = 2 });
87codec_enum!(Protection { Askip = 0, Prot = 1, Unprot = 2 });
88codec_enum!(Intensity { Norm = 0, Brt = 1, Drk = 2 });
89codec_enum!(Initial { Text(text) = 0, Bytes(bytes) = 1 });
90codec_struct!(Attrb { protection, numeric, intensity, detectable, cursor, fset });
91codec_struct!(Field { name, line, column, length, attrb, initial, picin, picout, occurs, group, justify_right, fill_zero, color, hilight });
92codec_struct!(Map { name, lines, columns, line, column, ctrl, tioapfx, dsatts, fields });
93codec_struct!(Mapset { name, mode, ctrl, maps });
94
95pub const EXTENDED: &[(&str, char, bool)] = &[
97 ("COLOR", 'C', true),
98 ("PS", 'P', true),
99 ("HILIGHT", 'H', true),
100 ("VALIDN", 'V', true),
101 ("OUTLINE", 'U', false),
102 ("SOSI", 'M', false),
103 ("TRANSP", 'T', false),
104];
105
106pub fn picture_size(pic: &str) -> u32 {
108 let chars: Vec<char> = pic.chars().collect();
109 let (mut size, mut i) = (0, 0);
110 while i < chars.len() {
111 let c = chars[i].to_ascii_uppercase();
112 i += 1;
113 let mut repeat = 1;
114 if chars.get(i) == Some(&'(')
115 && let Some(close) = chars[i..].iter().position(|&c| c == ')')
116 {
117 repeat = chars[i + 1..i + close].iter().collect::<String>().parse().unwrap_or(1);
118 i += close + 1;
119 }
120 if !matches!(c, 'S' | 'V') {
121 size += repeat;
122 }
123 }
124 size
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131pub struct Slot {
132 pub field: usize,
133 pub occurrence: u16,
134 pub control: Option<usize>,
135 pub extended: usize,
136 pub data: usize,
137 pub size: usize,
138}
139
140impl Slot {
141 pub fn length_at(&self) -> Option<usize> {
143 self.control
144 }
145
146 pub fn attribute_at(&self) -> Option<usize> {
147 self.control.map(|c| c + 2)
148 }
149}
150
151pub fn extended_attributes(map: &Map) -> Vec<&'static str> {
153 EXTENDED.iter().filter(|(a, _, _)| map.dsatts.iter().any(|d| d == a)).map(|(a, _, _)| *a).collect()
154}
155
156pub fn slots(map: &Map, input: bool) -> Vec<Slot> {
159 let k = extended_attributes(map).len();
160 let mut at = if map.tioapfx { 12 } else { 0 };
161 let mut out = Vec::new();
162 let mut group: Option<&str> = None;
163 for (i, f) in map.fields.iter().enumerate().filter(|(_, f)| f.name.is_some()) {
164 let lead = match &f.group {
165 Some(g) => {
166 let first = group != Some(g.as_str());
167 group = Some(g);
168 first
169 }
170 None => {
171 group = None;
172 true
173 }
174 };
175 let picture = if input { &f.picin } else { &f.picout };
176 let size = picture.as_deref().map_or(usize::from(f.length), |p| picture_size(p) as usize);
177 let copies = if f.group.is_none() { f.occurs.max(1) } else { 1 };
178 for occurrence in 0..copies {
179 let control = lead.then_some(at);
180 if lead {
181 at += 3 + k;
182 }
183 out.push(Slot { field: i, occurrence, control, extended: at - k, data: at, size });
184 at += size;
185 }
186 }
187 out
188}