commit 87266e7d54ac5ed59707ad2c1d5e12bab5140c67 Author: Matthieu Jolimaitre Date: Mon Jun 23 21:21:36 2025 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..495e905 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "boxon" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d972def --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "boxon" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..7af09ca --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# boxon + +Utility to convert ascii boxes into ansi boxes inside text files. diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..768ff5c --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +hard_tabs = true +max_width = 120 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..229a749 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,235 @@ +use std::{env::args, fs}; + +fn main() { + let file = args().nth(1).expect("Usage : boxon "); + let content = fs::read_to_string(&file).unwrap_or_else(|_| panic!("Could not read file '{file}'.")); + let mut grid = Grid::from(content); + grid.fix(); + let content: String = grid.into(); + fs::write(&file, content).unwrap_or_else(|_| panic!("Could not write result to file '{file}'.")); +} + +#[derive(Debug, Clone)] +struct Grid(Vec>); + +impl> From for Grid { + fn from(value: T) -> Self { + Self(value.as_ref().lines().map(str::chars).map(Iterator::collect).collect()) + } +} + +impl From for String { + fn from(val: Grid) -> Self { + val.0 + .iter() + .map(|l| l.iter().collect::()) + .collect::>() + .join("\n") + } +} + +impl Grid { + fn fix(&mut self) -> usize { + let mut result = 0; + let copy = self.clone(); + for (y, line) in self.0.iter_mut().enumerate() { + for (x, character) in line.iter_mut().enumerate() { + if let Some(cross) = copy.cross_at(x as i32, y as i32) { + let new_char = match cross { + Cross( + ((), MM, ()), // + (MM, MM, MM), // + ((), MM, ()), // + ) => '┼', + Cross( + ((), MM, ()), // + (F_, MM, MM), // + ((), MM, ()), // + ) => '├', + Cross( + ((), MM, ()), // + (MM, MM, F_), // + ((), MM, ()), // + ) => '┤', + Cross( + ((), MM, ()), // + (MM, MM, MM), // + ((), F_, ()), // + ) => '┴', + Cross( + ((), F_, ()), // + (MM, MM, MM), // + ((), MM, ()), // + ) => '┬', + Cross( + ((), F_, ()), // + (MM, MM, MM), // + ((), F_, ()), // + ) => '─', + Cross( + ((), MM, ()), // + (F_, MM, F_), // + ((), MM, ()), // + ) => '│', + Cross( + ((), MM, ()), // + (MM, MM, F_), // + ((), F_, ()), // + ) => '┘', + Cross( + ((), F_, ()), // + (MM, MM, F_), // + ((), MM, ()), // + ) => '┐', + Cross( + ((), F_, ()), // + (F_, MM, MM), // + ((), MM, ()), // + ) => '┌', + Cross( + ((), MM, ()), // + (F_, MM, MM), // + ((), F_, ()), // + ) => '└', + _ => continue, + }; + *character = new_char; + result += 1; + } + } + } + result + } + + #[rustfmt::skip] + fn cross_at(&self, x: i32, y: i32) -> Option { + fn at(g: &Grid, x: i32, y: i32) -> Option { + if x < 0 || y < 0 { + None + } else { + Some(connects(*g.0.get(y as usize)?.get(x as usize)?)) + } + } + let get = |x: i32, y: i32| -> Cross { + at(self, x, y).unwrap_or(connects(' ')) + }; + let c = get(x, y); + let its_y = y; + Some(Cross( + ( (), get(x, y - 1).d() && c.u(), ()), + (get(x - 1, y).r() && c.l(), get(x, its_y).c(), get(x + 1, y).l() && c.r()), + ( (), get(x, y + 1).u() && c.d(), ()), + )) + } +} + +fn connects(c: char) -> Cross { + match c { + '-' => Cross( + ((), F_, ()), // + (MM, MM, MM), // + ((), F_, ()), // + ), + '|' => Cross( + ((), MM, ()), // + (F_, MM, F_), // + ((), MM, ()), // + ), + '+' => Cross( + ((), MM, ()), // + (MM, MM, MM), // + ((), MM, ()), // + ), + '┼' => Cross( + ((), MM, ()), // + (MM, MM, MM), // + ((), MM, ()), // + ), + '├' => Cross( + ((), MM, ()), // + (F_, MM, MM), // + ((), MM, ()), // + ), + '┤' => Cross( + ((), MM, ()), // + (MM, MM, F_), // + ((), MM, ()), // + ), + '┴' => Cross( + ((), MM, ()), // + (MM, MM, MM), // + ((), F_, ()), // + ), + '┬' => Cross( + ((), F_, ()), // + (MM, MM, MM), // + ((), MM, ()), // + ), + '─' => Cross( + ((), F_, ()), // + (MM, MM, MM), // + ((), F_, ()), // + ), + '│' => Cross( + ((), MM, ()), // + (F_, MM, F_), // + ((), MM, ()), // + ), + '┘' => Cross( + ((), MM, ()), // + (MM, MM, F_), // + ((), F_, ()), // + ), + '┐' => Cross( + ((), F_, ()), // + (MM, MM, F_), // + ((), MM, ()), // + ), + '┌' => Cross( + ((), F_, ()), // + (F_, MM, MM), // + ((), MM, ()), // + ), + '└' => Cross( + ((), MM, ()), // + (F_, MM, MM), // + ((), F_, ()), // + ), + _ => Cross( + ((), F_, ()), // + (F_, F_, F_), // + ((), F_, ()), // + ), + } +} + +const F_: bool = false; +const MM: bool = true; +type Empt = (); +struct Cross( + (Empt, bool, Empt), // + (bool, bool, bool), // + (Empt, bool, Empt), // +); + +impl Cross { + fn u(&self) -> bool { + self.0.1 + } + + fn d(&self) -> bool { + self.2.1 + } + + fn l(&self) -> bool { + self.1.0 + } + + fn r(&self) -> bool { + self.1.2 + } + + fn c(&self) -> bool { + self.1.1 + } +} diff --git a/test/out.txt b/test/out.txt new file mode 100644 index 0000000..1abe12b --- /dev/null +++ b/test/out.txt @@ -0,0 +1,21 @@ + +this is a normalized data table diagram. + ++----------------+ +|USERS BY EMAIL | ++--+-----+-------+ +|id|email|user_id| ++--+-----+-------+ +| 1|b@o.b| 1| +| 2|c@r.l| 2| ++--+-----+-------+ + +---------------+ + |USERS | + +--+--------+---+ + |id| name|age| + +--+--------+---+ + | 1| bob| 19| + | 2| charlie| 23| + +--+--------+---+ + + diff --git a/test/source.txt b/test/source.txt new file mode 100644 index 0000000..a6132ea --- /dev/null +++ b/test/source.txt @@ -0,0 +1,21 @@ + +this is a normalized data table diagram. + ++----------------+ +|USERS BY EMAIL | ++--+-----+-------+ +|id|email|user_id| ++--+-----+-------+ +| 1|b@o.b| 1| +| 2|c@r.l| 2| ++--+-----+-------+ ++---------------+ +|USERS | ++--+--------+---+ +|id| name|age| ++--+--------+---+ +| 1| bob| 19| +| 2| charlie| 23| ++--+--------+---+ + +