1use crate::messages::Refusal;
19use crate::sheet::{A1Range, Workbook};
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub struct Seam {
24 pub name: &'static str,
26 pub entrance: &'static str,
29 pub oracle: &'static str,
31 pub implementations: &'static [&'static str],
34 pub deferred_to: &'static str,
36}
37
38pub const SEAMS: [Seam; 5] = [
40 Seam {
41 name: "sentences",
42 entrance: "a phrasebook file (.vla) with its test: proofs, loaded through api::vocab_gate",
43 oracle: "frazaro prove passes every proof; the translate and refusal goldens hold",
44 implementations: &["the phrasebooks a door loads, english.vla first"],
45 deferred_to: "",
46 },
47 Seam {
48 name: "paragraphs",
49 entrance: "a library of sentences with named slots, imported by a Use sentence",
50 oracle: "the build golden, and the stamp hashing every file used",
51 implementations: &[],
52 deferred_to: "G-USE",
53 },
54 Seam {
55 name: "engines",
56 entrance: "an implementation of kernel::Engine under a head-table symbol",
57 oracle: "a proof file of its kind, with clingo beside it where it applies",
58 implementations: &[],
59 deferred_to: "PORT.9",
60 },
61 Seam {
62 name: "formats and hosts",
63 entrance: "an implementation of reflect::Source to read, a writer beside it, and a kernel::HostProfile per door",
64 oracle: "the reflect, diff and audit goldens for a reader; the build golden for a writer",
65 implementations: &["xlsx (OOXML), read and written", "ods (OpenDocument), read"],
66 deferred_to: "",
67 },
68 Seam {
69 name: "projections",
70 entrance: "an implementation of kernel::Projection",
71 oracle: "a view golden per projection",
72 implementations: &[],
73 deferred_to: "KERNEL.4",
74 },
75];
76
77pub fn seams() -> &'static [Seam] {
79 &SEAMS
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub struct Format {
85 pub name: &'static str,
86 pub extension: &'static str,
87 pub reads: bool,
88 pub writes: bool,
89}
90
91pub const FORMATS: [Format; 2] = [
94 Format {
95 name: "OOXML",
96 extension: "xlsx",
97 reads: true,
98 writes: true,
99 },
100 Format {
101 name: "OpenDocument",
102 extension: "ods",
103 reads: true,
104 writes: false,
105 },
106];
107
108pub trait Engine {
114 type Table;
116 fn head(&self) -> &str;
118 fn proof_kind(&self) -> &str;
120 fn run(&self, tables: &[Self::Table]) -> Result<Self::Table, Refusal>;
122}
123
124#[derive(Clone, Debug, PartialEq, Eq)]
126pub struct Window {
127 pub sheet: String,
128 pub range: A1Range,
129}
130
131pub trait Projection {
136 fn name(&self) -> &str;
138 fn project(&self, model: &Workbook, window: &Window, out: &mut dyn FnMut(&str));
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145pub struct HostProfile {
146 pub door: &'static str,
147 pub forms: &'static [&'static str],
149 pub scaffold: &'static [&'static str],
151 pub refuses_with: &'static str,
153}
154
155pub const FILE_DOOR: HostProfile = HostProfile {
161 door: "file",
162 forms: &[
163 "at-line",
164 "begin",
165 "dim",
166 "on-error",
167 "label",
168 "exit-sub",
169 "exit-function",
170 "set!",
171 "if",
172 ".",
173 ],
174 scaffold: &["vla-report-error", "vlaensuresheet"],
175 refuses_with: "build-not-representable",
176};
177
178pub const HOST_PROFILES: [HostProfile; 1] = [FILE_DOOR];
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::headtable;
187 use crate::reflect;
188
189 const FIXTURE_XLSX: &[u8] = include_bytes!("../../scripts/reflect/fixture.xlsx");
190 const FIXTURE_ODS: &[u8] = include_bytes!("../../scripts/reflect/opendocument.ods");
191
192 #[test]
193 fn five_seams_each_with_an_entrance_and_an_oracle() {
194 assert_eq!(seams().len(), 5);
195 let names: Vec<&str> = seams().iter().map(|s| s.name).collect();
196 assert_eq!(
197 names,
198 [
199 "sentences",
200 "paragraphs",
201 "engines",
202 "formats and hosts",
203 "projections"
204 ]
205 );
206 for s in seams() {
207 assert!(!s.entrance.is_empty(), "{} names no entrance", s.name);
208 assert!(!s.oracle.is_empty(), "{} names no oracle", s.name);
209 assert!(
210 !s.implementations.is_empty() || !s.deferred_to.is_empty(),
211 "{} has neither an implementation nor the item that lands one",
212 s.name
213 );
214 }
215 }
216
217 #[test]
218 fn the_engines_and_projections_seams_are_named_and_empty_today() {
219 let engines = seams().iter().find(|s| s.name == "engines").unwrap();
220 assert!(engines.implementations.is_empty());
221 assert_eq!(engines.deferred_to, "PORT.9");
222 let projections = seams().iter().find(|s| s.name == "projections").unwrap();
223 assert!(projections.implementations.is_empty());
224 assert_eq!(projections.deferred_to, "KERNEL.4");
225 let paragraphs = seams().iter().find(|s| s.name == "paragraphs").unwrap();
226 assert_eq!(paragraphs.deferred_to, "G-USE");
227 }
228
229 #[test]
230 fn the_formats_registry_matches_the_readers_and_the_writer() {
231 assert_eq!(FORMATS.len(), 2);
232 assert!(reflect::open(FIXTURE_XLSX, "fixture.xlsx").is_ok());
233 assert!(reflect::open(FIXTURE_ODS, "opendocument.ods").is_ok());
234 let readers: Vec<&str> = FORMATS
235 .iter()
236 .filter(|f| f.reads)
237 .map(|f| f.extension)
238 .collect();
239 assert_eq!(readers, ["xlsx", "ods"]);
240 let writers: Vec<&str> = FORMATS
241 .iter()
242 .filter(|f| f.writes)
243 .map(|f| f.extension)
244 .collect();
245 assert_eq!(writers, ["xlsx"], "the one writer is the OOXML writer");
246 let formats = seams()
247 .iter()
248 .find(|s| s.name == "formats and hosts")
249 .unwrap();
250 assert_eq!(formats.implementations.len(), FORMATS.len());
251 }
252
253 #[test]
254 fn the_file_door_holds_only_core_forms_and_refuses_from_the_catalogue() {
255 assert_eq!(HOST_PROFILES.len(), 1);
256 assert_eq!(FILE_DOOR.door, "file");
257 for sym in FILE_DOOR.forms {
258 let canonical = headtable::resolve_head_alias(sym);
259 assert!(
260 headtable::row(&canonical).is_some(),
261 "{sym} is not a form of the head table"
262 );
263 }
264 for call in FILE_DOOR.scaffold {
265 assert!(
266 headtable::row(call).is_none(),
267 "{call} is a scaffold call, not a core form"
268 );
269 }
270 assert_eq!(FILE_DOOR.refuses_with, "build-not-representable");
271 }
272
273 #[test]
274 fn a_window_is_one_sheet_and_one_rectangle() {
275 let w = Window {
276 sheet: "Model".to_string(),
277 range: A1Range {
278 top: 1,
279 left: 1,
280 bottom: 20,
281 right: 6,
282 },
283 };
284 assert_eq!(w.range.cells(), 120);
285 assert_eq!(w.sheet, "Model");
286 }
287}