Author: Manos Pitsidianakis [manos@pitsidianak.is]
Hash: 7e7d59fb1038af7ae217195bfa091957e9688a04
Timestamp: Thu, 19 Oct 2023 07:32:28 +0000 (11 months ago)

+205 -205 +/-4 browse
core: move build module to build/ subdirectory
core: move build module to build/ subdirectory

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
1diff --git a/core/build.rs b/core/build.rs
2deleted file mode 100644
3index 44e41d2..0000000
4--- a/core/build.rs
5+++ /dev/null
6 @@ -1,95 +0,0 @@
7- /*
8- * This file is part of mailpot
9- *
10- * Copyright 2020 - Manos Pitsidianakis
11- *
12- * This program is free software: you can redistribute it and/or modify
13- * it under the terms of the GNU Affero General Public License as
14- * published by the Free Software Foundation, either version 3 of the
15- * License, or (at your option) any later version.
16- *
17- * This program is distributed in the hope that it will be useful,
18- * but WITHOUT ANY WARRANTY; without even the implied warranty of
19- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20- * GNU Affero General Public License for more details.
21- *
22- * You should have received a copy of the GNU Affero General Public License
23- * along with this program. If not, see <https://www.gnu.org/licenses/>.
24- */
25-
26- use std::{
27- fs::OpenOptions,
28- process::{Command, Stdio},
29- };
30-
31- // // Source: https://stackoverflow.com/a/64535181
32- // fn is_output_file_outdated<P1, P2>(input: P1, output: P2) -> io::Result<bool>
33- // where
34- // P1: AsRef<Path>,
35- // P2: AsRef<Path>,
36- // {
37- // let out_meta = metadata(output);
38- // if let Ok(meta) = out_meta {
39- // let output_mtime = meta.modified()?;
40- //
41- // // if input file is more recent than our output, we are outdated
42- // let input_meta = metadata(input)?;
43- // let input_mtime = input_meta.modified()?;
44- //
45- // Ok(input_mtime > output_mtime)
46- // } else {
47- // // output file not found, we are outdated
48- // Ok(true)
49- // }
50- // }
51-
52- include!("make_migrations.rs");
53-
54- const MIGRATION_RS: &str = "src/migrations.rs.inc";
55-
56- fn main() {
57- println!("cargo:rerun-if-changed=src/migrations.rs.inc");
58- println!("cargo:rerun-if-changed=migrations");
59- println!("cargo:rerun-if-changed=src/schema.sql.m4");
60-
61- let mut output = Command::new("m4")
62- .arg("./src/schema.sql.m4")
63- .output()
64- .unwrap();
65- if String::from_utf8_lossy(&output.stdout).trim().is_empty() {
66- panic!(
67- "m4 output is empty. stderr was {}",
68- String::from_utf8_lossy(&output.stderr)
69- );
70- }
71- let user_version: i32 = make_migrations("migrations", MIGRATION_RS, &mut output.stdout);
72- let mut verify = Command::new(std::env::var("SQLITE_BIN").unwrap_or("sqlite3".into()))
73- .stdin(Stdio::piped())
74- .stdout(Stdio::piped())
75- .stderr(Stdio::piped())
76- .spawn()
77- .unwrap();
78- println!(
79- "Verifying by creating an in-memory database in sqlite3 and feeding it the output schema."
80- );
81- verify
82- .stdin
83- .take()
84- .unwrap()
85- .write_all(&output.stdout)
86- .unwrap();
87- let exit = verify.wait_with_output().unwrap();
88- if !exit.status.success() {
89- panic!(
90- "sqlite3 could not read SQL schema: {}",
91- String::from_utf8_lossy(&exit.stdout)
92- );
93- }
94- let mut file = std::fs::File::create("./src/schema.sql").unwrap();
95- file.write_all(&output.stdout).unwrap();
96- file.write_all(
97- &format!("\n\n-- Set current schema version.\n\nPRAGMA user_version = {user_version};\n")
98- .as_bytes(),
99- )
100- .unwrap();
101- }
102 diff --git a/core/build/make_migrations.rs b/core/build/make_migrations.rs
103new file mode 100644
104index 0000000..91f3f2e
105--- /dev/null
106+++ b/core/build/make_migrations.rs
107 @@ -0,0 +1,110 @@
108+ /*
109+ * This file is part of mailpot
110+ *
111+ * Copyright 2023 - Manos Pitsidianakis
112+ *
113+ * This program is free software: you can redistribute it and/or modify
114+ * it under the terms of the GNU Affero General Public License as
115+ * published by the Free Software Foundation, either version 3 of the
116+ * License, or (at your option) any later version.
117+ *
118+ * This program is distributed in the hope that it will be useful,
119+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
120+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121+ * GNU Affero General Public License for more details.
122+ *
123+ * You should have received a copy of the GNU Affero General Public License
124+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
125+ */
126+
127+ use std::{fs::read_dir, io::Write, path::Path};
128+
129+ /// Scans migrations directory for file entries, and creates a rust file with an array containing
130+ /// the migration slices.
131+ ///
132+ ///
133+ /// If a migration is a data migration (not a CREATE, DROP or ALTER statement) it is appended to
134+ /// the schema file.
135+ ///
136+ /// Returns the current `user_version` PRAGMA value.
137+ pub fn make_migrations<M: AsRef<Path>, O: AsRef<Path>>(
138+ migrations_path: M,
139+ output_file: O,
140+ schema_file: &mut Vec<u8>,
141+ ) -> i32 {
142+ let migrations_folder_path = migrations_path.as_ref();
143+ let output_file_path = output_file.as_ref();
144+
145+ let mut paths = vec![];
146+ let mut undo_paths = vec![];
147+ for entry in read_dir(migrations_folder_path).unwrap() {
148+ let entry = entry.unwrap();
149+ let path = entry.path();
150+ if path.is_dir() || path.extension().map(|os| os.to_str().unwrap()) != Some("sql") {
151+ continue;
152+ }
153+ if path
154+ .file_name()
155+ .unwrap()
156+ .to_str()
157+ .unwrap()
158+ .ends_with("undo.sql")
159+ {
160+ undo_paths.push(path);
161+ } else {
162+ paths.push(path);
163+ }
164+ }
165+
166+ paths.sort();
167+ undo_paths.sort();
168+ let mut migr_rs = OpenOptions::new()
169+ .write(true)
170+ .create(true)
171+ .truncate(true)
172+ .open(output_file_path)
173+ .unwrap();
174+ migr_rs
175+ .write_all(b"\n//(user_version, redo sql, undo sql\n&[")
176+ .unwrap();
177+ for (i, (p, u)) in paths.iter().zip(undo_paths.iter()).enumerate() {
178+ // This should be a number string, padded with 2 zeros if it's less than 3
179+ // digits. e.g. 001, \d{3}
180+ let mut num = p.file_stem().unwrap().to_str().unwrap();
181+ let is_data = num.ends_with(".data");
182+ if is_data {
183+ num = num.strip_suffix(".data").unwrap();
184+ }
185+
186+ if !u.file_name().unwrap().to_str().unwrap().starts_with(num) {
187+ panic!("Undo file {u:?} should match with {p:?}");
188+ }
189+
190+ if num.parse::<u32>().is_err() {
191+ panic!("Migration file {p:?} should start with a number");
192+ }
193+ assert_eq!(num.parse::<usize>().unwrap(), i + 1, "migration sql files should start with 1, not zero, and no intermediate numbers should be missing. Panicked on file: {}", p.display());
194+ migr_rs.write_all(b"(").unwrap();
195+ migr_rs
196+ .write_all(num.trim_start_matches('0').as_bytes())
197+ .unwrap();
198+ migr_rs.write_all(b",r##\"").unwrap();
199+
200+ let redo = std::fs::read_to_string(p).unwrap();
201+ migr_rs.write_all(redo.trim().as_bytes()).unwrap();
202+ migr_rs.write_all(b"\"##,r##\"").unwrap();
203+ migr_rs
204+ .write_all(std::fs::read_to_string(u).unwrap().trim().as_bytes())
205+ .unwrap();
206+ migr_rs.write_all(b"\"##),").unwrap();
207+ if is_data {
208+ schema_file.extend(b"\n\n-- ".iter());
209+ schema_file.extend(num.as_bytes().iter());
210+ schema_file.extend(b".data.sql\n\n".iter());
211+ schema_file.extend(redo.into_bytes().into_iter());
212+ }
213+ }
214+ migr_rs.write_all(b"]").unwrap();
215+ migr_rs.flush().unwrap();
216+ paths.len() as i32
217+ }
218 diff --git a/core/build/mod.rs b/core/build/mod.rs
219new file mode 100644
220index 0000000..44e41d2
221--- /dev/null
222+++ b/core/build/mod.rs
223 @@ -0,0 +1,95 @@
224+ /*
225+ * This file is part of mailpot
226+ *
227+ * Copyright 2020 - Manos Pitsidianakis
228+ *
229+ * This program is free software: you can redistribute it and/or modify
230+ * it under the terms of the GNU Affero General Public License as
231+ * published by the Free Software Foundation, either version 3 of the
232+ * License, or (at your option) any later version.
233+ *
234+ * This program is distributed in the hope that it will be useful,
235+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
236+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
237+ * GNU Affero General Public License for more details.
238+ *
239+ * You should have received a copy of the GNU Affero General Public License
240+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
241+ */
242+
243+ use std::{
244+ fs::OpenOptions,
245+ process::{Command, Stdio},
246+ };
247+
248+ // // Source: https://stackoverflow.com/a/64535181
249+ // fn is_output_file_outdated<P1, P2>(input: P1, output: P2) -> io::Result<bool>
250+ // where
251+ // P1: AsRef<Path>,
252+ // P2: AsRef<Path>,
253+ // {
254+ // let out_meta = metadata(output);
255+ // if let Ok(meta) = out_meta {
256+ // let output_mtime = meta.modified()?;
257+ //
258+ // // if input file is more recent than our output, we are outdated
259+ // let input_meta = metadata(input)?;
260+ // let input_mtime = input_meta.modified()?;
261+ //
262+ // Ok(input_mtime > output_mtime)
263+ // } else {
264+ // // output file not found, we are outdated
265+ // Ok(true)
266+ // }
267+ // }
268+
269+ include!("make_migrations.rs");
270+
271+ const MIGRATION_RS: &str = "src/migrations.rs.inc";
272+
273+ fn main() {
274+ println!("cargo:rerun-if-changed=src/migrations.rs.inc");
275+ println!("cargo:rerun-if-changed=migrations");
276+ println!("cargo:rerun-if-changed=src/schema.sql.m4");
277+
278+ let mut output = Command::new("m4")
279+ .arg("./src/schema.sql.m4")
280+ .output()
281+ .unwrap();
282+ if String::from_utf8_lossy(&output.stdout).trim().is_empty() {
283+ panic!(
284+ "m4 output is empty. stderr was {}",
285+ String::from_utf8_lossy(&output.stderr)
286+ );
287+ }
288+ let user_version: i32 = make_migrations("migrations", MIGRATION_RS, &mut output.stdout);
289+ let mut verify = Command::new(std::env::var("SQLITE_BIN").unwrap_or("sqlite3".into()))
290+ .stdin(Stdio::piped())
291+ .stdout(Stdio::piped())
292+ .stderr(Stdio::piped())
293+ .spawn()
294+ .unwrap();
295+ println!(
296+ "Verifying by creating an in-memory database in sqlite3 and feeding it the output schema."
297+ );
298+ verify
299+ .stdin
300+ .take()
301+ .unwrap()
302+ .write_all(&output.stdout)
303+ .unwrap();
304+ let exit = verify.wait_with_output().unwrap();
305+ if !exit.status.success() {
306+ panic!(
307+ "sqlite3 could not read SQL schema: {}",
308+ String::from_utf8_lossy(&exit.stdout)
309+ );
310+ }
311+ let mut file = std::fs::File::create("./src/schema.sql").unwrap();
312+ file.write_all(&output.stdout).unwrap();
313+ file.write_all(
314+ &format!("\n\n-- Set current schema version.\n\nPRAGMA user_version = {user_version};\n")
315+ .as_bytes(),
316+ )
317+ .unwrap();
318+ }
319 diff --git a/core/make_migrations.rs b/core/make_migrations.rs
320deleted file mode 100644
321index 91f3f2e..0000000
322--- a/core/make_migrations.rs
323+++ /dev/null
324 @@ -1,110 +0,0 @@
325- /*
326- * This file is part of mailpot
327- *
328- * Copyright 2023 - Manos Pitsidianakis
329- *
330- * This program is free software: you can redistribute it and/or modify
331- * it under the terms of the GNU Affero General Public License as
332- * published by the Free Software Foundation, either version 3 of the
333- * License, or (at your option) any later version.
334- *
335- * This program is distributed in the hope that it will be useful,
336- * but WITHOUT ANY WARRANTY; without even the implied warranty of
337- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
338- * GNU Affero General Public License for more details.
339- *
340- * You should have received a copy of the GNU Affero General Public License
341- * along with this program. If not, see <https://www.gnu.org/licenses/>.
342- */
343-
344- use std::{fs::read_dir, io::Write, path::Path};
345-
346- /// Scans migrations directory for file entries, and creates a rust file with an array containing
347- /// the migration slices.
348- ///
349- ///
350- /// If a migration is a data migration (not a CREATE, DROP or ALTER statement) it is appended to
351- /// the schema file.
352- ///
353- /// Returns the current `user_version` PRAGMA value.
354- pub fn make_migrations<M: AsRef<Path>, O: AsRef<Path>>(
355- migrations_path: M,
356- output_file: O,
357- schema_file: &mut Vec<u8>,
358- ) -> i32 {
359- let migrations_folder_path = migrations_path.as_ref();
360- let output_file_path = output_file.as_ref();
361-
362- let mut paths = vec![];
363- let mut undo_paths = vec![];
364- for entry in read_dir(migrations_folder_path).unwrap() {
365- let entry = entry.unwrap();
366- let path = entry.path();
367- if path.is_dir() || path.extension().map(|os| os.to_str().unwrap()) != Some("sql") {
368- continue;
369- }
370- if path
371- .file_name()
372- .unwrap()
373- .to_str()
374- .unwrap()
375- .ends_with("undo.sql")
376- {
377- undo_paths.push(path);
378- } else {
379- paths.push(path);
380- }
381- }
382-
383- paths.sort();
384- undo_paths.sort();
385- let mut migr_rs = OpenOptions::new()
386- .write(true)
387- .create(true)
388- .truncate(true)
389- .open(output_file_path)
390- .unwrap();
391- migr_rs
392- .write_all(b"\n//(user_version, redo sql, undo sql\n&[")
393- .unwrap();
394- for (i, (p, u)) in paths.iter().zip(undo_paths.iter()).enumerate() {
395- // This should be a number string, padded with 2 zeros if it's less than 3
396- // digits. e.g. 001, \d{3}
397- let mut num = p.file_stem().unwrap().to_str().unwrap();
398- let is_data = num.ends_with(".data");
399- if is_data {
400- num = num.strip_suffix(".data").unwrap();
401- }
402-
403- if !u.file_name().unwrap().to_str().unwrap().starts_with(num) {
404- panic!("Undo file {u:?} should match with {p:?}");
405- }
406-
407- if num.parse::<u32>().is_err() {
408- panic!("Migration file {p:?} should start with a number");
409- }
410- assert_eq!(num.parse::<usize>().unwrap(), i + 1, "migration sql files should start with 1, not zero, and no intermediate numbers should be missing. Panicked on file: {}", p.display());
411- migr_rs.write_all(b"(").unwrap();
412- migr_rs
413- .write_all(num.trim_start_matches('0').as_bytes())
414- .unwrap();
415- migr_rs.write_all(b",r##\"").unwrap();
416-
417- let redo = std::fs::read_to_string(p).unwrap();
418- migr_rs.write_all(redo.trim().as_bytes()).unwrap();
419- migr_rs.write_all(b"\"##,r##\"").unwrap();
420- migr_rs
421- .write_all(std::fs::read_to_string(u).unwrap().trim().as_bytes())
422- .unwrap();
423- migr_rs.write_all(b"\"##),").unwrap();
424- if is_data {
425- schema_file.extend(b"\n\n-- ".iter());
426- schema_file.extend(num.as_bytes().iter());
427- schema_file.extend(b".data.sql\n\n".iter());
428- schema_file.extend(redo.into_bytes().into_iter());
429- }
430- }
431- migr_rs.write_all(b"]").unwrap();
432- migr_rs.flush().unwrap();
433- paths.len() as i32
434- }