diff --git a/Cargo.lock b/Cargo.lock index b2657c0..80b7ab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,7 @@ name = "configotron" version = "0.1.0" dependencies = [ "graphviz-rust", + "itertools", "pad", "rand", "serde", @@ -85,6 +86,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cfcded997a93eb31edd639361fa33fd229a8784e953b37d71035fe3890b7b" +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "equivalent" version = "1.0.2" @@ -194,6 +201,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" diff --git a/Cargo.toml b/Cargo.toml index dd57dab..a10289b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ serde_yml = "0.0.12" thiserror = "1" rand = "0.8" graphviz-rust = "0.9.4" +itertools = "0.14.0" diff --git a/docs/.configotron-whiteboard.autosave.xopp b/docs/.configotron-whiteboard.autosave.xopp new file mode 100644 index 0000000..c81c7a9 Binary files /dev/null and b/docs/.configotron-whiteboard.autosave.xopp differ diff --git a/src/configotron/conf.rs b/src/configotron/conf.rs index f101b5d..3760b3e 100644 --- a/src/configotron/conf.rs +++ b/src/configotron/conf.rs @@ -1,35 +1,33 @@ use serde::{Deserialize, Serialize}; -use std::{fs}; - +use std::fs; #[derive(Deserialize, Serialize, Debug)] pub enum UserConfigItem { String { name: String, - max_length: u64 + max_length: u64, }, IPv4 { name: String, }, Boolean { - name: String + name: String, }, Number { name: String, max: Option, - min: Option + min: Option, }, - Submenu(UserConfigData) + Submenu(UserConfigData), } #[derive(Deserialize, Serialize, Debug)] pub struct UserConfigData { pub name: String, - pub config_items: Vec + pub config_items: Vec, } - #[derive(thiserror::Error, Debug)] pub enum ConfigotronError { #[error("IO Operation failed: {}", ._0.to_string())] @@ -39,16 +37,16 @@ pub enum ConfigotronError { YamlParsingError(#[from] serde_yml::Error), #[error("Missing field {} (line {} col {}) {}", .missing_filed, .line, .col, .context)] - MissingFieldError{ + MissingFieldError { col: i32, line: i32, missing_filed: Box, - context: Box - } + context: Box, + }, } pub fn read_and_parse_yaml_file(input_file: &str) -> Result { let file_content = fs::read_to_string(&input_file)?; let config: UserConfigData = serde_yml::from_str(&file_content)?; return Ok(config); -} \ No newline at end of file +} diff --git a/src/configotron/doc/grid.rs b/src/configotron/doc/grid.rs index 6ad2d32..2b955c0 100644 --- a/src/configotron/doc/grid.rs +++ b/src/configotron/doc/grid.rs @@ -1,15 +1,15 @@ -use std::{fmt::format, usize}; use pad::PadStr; +use std::usize; -use crate::configotron::{conf::{UserConfigData, UserConfigItem}, doc::grid}; +use crate::configotron::conf::{UserConfigData, UserConfigItem}; #[derive(Clone, PartialEq, Debug)] -pub enum DocGridCellType{ +pub enum DocGridCellType { Boarder, - Empty, - SubMenu { text: Vec }, - Item { text: Vec }, - ItemJoin + Empty, + SubMenu { text: Vec }, + Item { text: Vec }, + ItemJoin, } impl DocGridCellType { @@ -19,44 +19,51 @@ impl DocGridCellType { DocGridCellType::Item { .. } => "I".to_string(), DocGridCellType::ItemJoin => "i".to_string(), DocGridCellType::SubMenu { .. } => "S".to_string(), - DocGridCellType::Boarder => "B".to_string() + DocGridCellType::Boarder => "B".to_string(), } } } pub enum ResultingGridCell { Grid(DocGrid), - Cell(DocGridCellType) + Cell(DocGridCellType), } fn split_string(s: &String) -> Vec { - s.split('\n').map(|s| s.to_string()).collect::>() -} + s.split('\n') + .map(|s| s.to_string()) + .collect::>() +} fn user_config_item_to_grid_cells(item: &UserConfigItem) -> ResultingGridCell { - match item { UserConfigItem::String { name, max_length } => { - ResultingGridCell::Cell(DocGridCellType::Item { text: vec![ - name.to_string(), format!("[length: 0..{}]", max_length) - ]} - )}, - UserConfigItem::IPv4 { name } => ResultingGridCell::Cell(DocGridCellType::Item { text: vec![ - name.to_string(), "[Address IPv4]".to_string() - ]}), - UserConfigItem::Boolean { name } => ResultingGridCell::Cell(DocGridCellType::Item { text: vec![ - name.to_string(), "[Yes/No]".to_string() - ] }), - UserConfigItem::Number { name, max, min } => ResultingGridCell::Cell(DocGridCellType::Item { text: vec![ - name.to_string(), - match (min, max) { - (None, None) => "[Integers]".to_string(), - (None, Some(h)) => format!("[Integers: ..{}]", h), - (Some(l), None) => format!("[Integers: {}..]", l), - (Some(l), Some(h)) => format!("[Integers: {}..{}]", l, h), - } - ] }), - UserConfigItem::Submenu(user_config_data) => ResultingGridCell::Grid(DocGrid::from(user_config_data)), + ResultingGridCell::Cell(DocGridCellType::Item { + text: vec![name.to_string(), format!("[length: 0..{}]", max_length)], + }) + } + UserConfigItem::IPv4 { name } => ResultingGridCell::Cell(DocGridCellType::Item { + text: vec![name.to_string(), "[Address IPv4]".to_string()], + }), + UserConfigItem::Boolean { name } => ResultingGridCell::Cell(DocGridCellType::Item { + text: vec![name.to_string(), "[Yes/No]".to_string()], + }), + UserConfigItem::Number { name, max, min } => { + ResultingGridCell::Cell(DocGridCellType::Item { + text: vec![ + name.to_string(), + match (min, max) { + (None, None) => "[Integers]".to_string(), + (None, Some(h)) => format!("[Integers: ..{}]", h), + (Some(l), None) => format!("[Integers: {}..]", l), + (Some(l), Some(h)) => format!("[Integers: {}..{}]", l, h), + }, + ], + }) + } + UserConfigItem::Submenu(user_config_data) => { + ResultingGridCell::Grid(DocGrid::from(user_config_data)) + } } } @@ -64,12 +71,16 @@ fn user_config_item_to_grid_cells(item: &UserConfigItem) -> ResultingGridCell { pub struct DocGrid { rows: usize, cols: usize, - array_cells: Vec + array_cells: Vec, } impl DocGrid { pub fn new() -> Self { - Self { rows: 0, cols: 2, array_cells: Vec::new() } + Self { + rows: 0, + cols: 2, + array_cells: Vec::new(), + } } pub fn len(&self) -> usize { @@ -81,23 +92,25 @@ impl DocGrid { } pub fn set_title(&mut self, cell: DocGridCellType) { - self.array_cells.resize(self.len() + self.cols, DocGridCellType::Empty); + self.array_cells + .resize(self.len() + self.cols, DocGridCellType::Empty); self.array_cells[0] = cell; } pub fn append_item(&mut self, item: ResultingGridCell) { match item { ResultingGridCell::Cell(cell_type) => { - self.array_cells.resize(self.len() + self.cols, DocGridCellType::Empty); - self.array_cells[self.rows*self.cols + 1] = cell_type; - + self.array_cells + .resize(self.len() + self.cols, DocGridCellType::Empty); + self.array_cells[self.rows * self.cols + 1] = cell_type; + self.rows += 1; - }, + } ResultingGridCell::Grid(grid) => { let new_col_size = std::cmp::max(self.cols, grid.cols + 1); let new_row_size = self.rows + grid.rows; - self.array_cells.resize(new_col_size * new_row_size, DocGridCellType::Empty); - + self.array_cells + .resize(new_col_size * new_row_size, DocGridCellType::Empty); let row_offset = self.rows; let old_cols = self.cols; @@ -121,10 +134,8 @@ impl DocGrid { // Copy in new stuff for row_index in 0..grid.rows { for col_index in 0..grid.cols { - let (target_row_index, target_col_index) = ( - row_index + row_offset, - col_index + 1 - ); + let (target_row_index, target_col_index) = + (row_index + row_offset, col_index + 1); let target_index = target_col_index + target_row_index * self.cols; let src_index = row_index * grid.cols + col_index; @@ -142,7 +153,10 @@ impl DocGrid { for row_index in 0..self.rows { s.push_str("| "); for col_index in 0..self.cols { - s.push_str(&format!("{} ", self.array_cells[row_index * self.cols + col_index].to_debug_string())); + s.push_str(&format!( + "{} ", + self.array_cells[row_index * self.cols + col_index].to_debug_string() + )); } s.push_str("|\n") } @@ -152,21 +166,19 @@ impl DocGrid { fn row_size_max(&self, c: usize) -> usize { let max = (0..self.rows) - .map(|r| { match &self.array_cells[r*self.cols + c] { + .map(|r| match &self.array_cells[r * self.cols + c] { DocGridCellType::Boarder => 1, DocGridCellType::Empty => 1, DocGridCellType::SubMenu { text: name } => { - name - .iter() - .map(|s| s.len()).max().unwrap_or(0) - }, + name.iter().map(|s| s.len()).max().unwrap_or(0) + } DocGridCellType::Item { text: name } => { - name - .iter() - .map(|s| s.len()).max().unwrap_or(0) - }, + name.iter().map(|s| s.len()).max().unwrap_or(0) + } DocGridCellType::ItemJoin => 1, - } }).max().unwrap_or(1); + }) + .max() + .unwrap_or(1); max } @@ -181,17 +193,15 @@ impl DocGrid { fn col_size_max(&self, r: usize) -> usize { let max = (0..self.cols) - .map(|c| { match &self.array_cells[r*self.cols + c] { + .map(|c| match &self.array_cells[r * self.cols + c] { DocGridCellType::Boarder => 1, DocGridCellType::Empty => 1, - DocGridCellType::SubMenu { text: name } => { - name.len() - }, - DocGridCellType::Item { text: name } => { - name.len() - }, + DocGridCellType::SubMenu { text: name } => name.len(), + DocGridCellType::Item { text: name } => name.len(), DocGridCellType::ItemJoin => 1, - } }).max().unwrap_or(1); + }) + .max() + .unwrap_or(1); max } @@ -208,7 +218,9 @@ impl DocGrid { impl From<&UserConfigData> for DocGrid { fn from(value: &UserConfigData) -> Self { let mut res = DocGrid::new(); - res.set_title(DocGridCellType::SubMenu { text: split_string(&value.name) }); + res.set_title(DocGridCellType::SubMenu { + text: split_string(&value.name), + }); value.config_items.iter().for_each(|i| { res.append_item(user_config_item_to_grid_cells(i)); }); @@ -217,31 +229,34 @@ impl From<&UserConfigData> for DocGrid { for row_index in 0..res.rows { for col_index in 2..res.cols { let index = col_index + row_index * res.cols; - match res.array_cells[col_index-1 + row_index * res.cols] { + match res.array_cells[col_index - 1 + row_index * res.cols] { DocGridCellType::Item { .. } => { res.array_cells[index] = DocGridCellType::ItemJoin; - }, + } DocGridCellType::ItemJoin => { res.array_cells[index] = DocGridCellType::ItemJoin; } _ => {} - } - + } } } res } } - #[derive(Clone)] pub enum DocGridLineType { - NONE, VERTICAL, HORIZONTAL + NONE, + VERTICAL, + HORIZONTAL, } #[derive(Clone)] pub enum DocGridLineCrossType { - NONE, FULL, VERTICAL, HORIZONTAL + NONE, + FULL, + VERTICAL, + HORIZONTAL, } pub struct DocGridLayout { @@ -251,18 +266,17 @@ pub struct DocGridLayout { lines_cross: Vec, } - impl DocGridLayout { fn map_line_to_char(l: &DocGridLineType) -> String { match l { DocGridLineType::HORIZONTAL => "-".to_string(), DocGridLineType::NONE => " ".to_string(), - DocGridLineType::VERTICAL => "|".to_string() + DocGridLineType::VERTICAL => "|".to_string(), } } fn map_cross_to_chr(c: &DocGridLineCrossType) -> String { - match c { + match c { DocGridLineCrossType::NONE => " ".to_string(), DocGridLineCrossType::FULL => "+".to_string(), DocGridLineCrossType::VERTICAL => "|".to_string(), @@ -270,7 +284,7 @@ impl DocGridLayout { } } - fn get_vert_line (&self, r: i32, c: i32) -> &DocGridLineType { + fn get_vert_line(&self, r: i32, c: i32) -> &DocGridLineType { if r < 0 || r >= (self.grid.rows as i32) || c < 0 || c > (self.grid.cols as i32) { &DocGridLineType::NONE } else { @@ -282,7 +296,7 @@ impl DocGridLayout { self.lines_vert[r as usize * (self.grid.cols + 1) + c as usize] = v; } - fn get_hort_line (&self, r: i32, c: i32) -> &DocGridLineType { + fn get_hort_line(&self, r: i32, c: i32) -> &DocGridLineType { if r < 0 || r > (self.grid.rows as i32) || c < 0 || c >= (self.grid.cols as i32) { &DocGridLineType::NONE } else { @@ -294,8 +308,7 @@ impl DocGridLayout { self.lines_hort[r as usize * (self.grid.cols) + c as usize] = v; } - - fn get_cross (&self, r: i32, c: i32) -> &DocGridLineCrossType { + fn get_cross(&self, r: i32, c: i32) -> &DocGridLineCrossType { if r < 0 || r > (self.grid.rows as i32) || c < 0 || c > (self.grid.cols as i32) { &DocGridLineCrossType::NONE } else { @@ -303,28 +316,42 @@ impl DocGridLayout { } } - fn set_cross (&mut self, r: usize, c: usize, v: DocGridLineCrossType) { + fn set_cross(&mut self, r: usize, c: usize, v: DocGridLineCrossType) { self.lines_cross[r * (self.grid.cols + 1) + c] = v; } - pub fn new(grid: &DocGrid, vertical_mapper: FLineV, horizontal_mapper: FLineH, cross_mapper: FCross) -> DocGridLayout - where FLineV: Fn(&DocGridCellType, &DocGridCellType) -> DocGridLineType, + pub fn new( + grid: &DocGrid, + vertical_mapper: FLineV, + horizontal_mapper: FLineH, + cross_mapper: FCross, + ) -> DocGridLayout + where + FLineV: Fn(&DocGridCellType, &DocGridCellType) -> DocGridLineType, FLineH: Fn(&DocGridCellType, &DocGridCellType) -> DocGridLineType, - FCross: Fn(&DocGridLineType, &DocGridLineType, &DocGridLineType, &DocGridLineType) -> DocGridLineCrossType + FCross: Fn( + &DocGridLineType, + &DocGridLineType, + &DocGridLineType, + &DocGridLineType, + ) -> DocGridLineCrossType, { let mut lines_hort = Vec::new(); let mut lines_vert = Vec::new(); let mut crosses = Vec::new(); - + lines_hort.resize(grid.cols * (1 + grid.rows), DocGridLineType::NONE); - lines_vert.resize((grid.cols + 1) * grid.rows , DocGridLineType::NONE ); - crosses.resize((grid.rows + 1) * (grid.cols + 1), DocGridLineCrossType::NONE); - + lines_vert.resize((grid.cols + 1) * grid.rows, DocGridLineType::NONE); + crosses.resize( + (grid.rows + 1) * (grid.cols + 1), + DocGridLineCrossType::NONE, + ); + let mut res: DocGridLayout = DocGridLayout { grid: grid.clone(), lines_cross: crosses, lines_hort: lines_hort, - lines_vert: lines_vert + lines_vert: lines_vert, }; let get_cell = |r: i32, c: i32| -> &DocGridCellType { @@ -334,28 +361,26 @@ impl DocGridLayout { &grid.array_cells[r as usize * grid.cols + c as usize] } }; - + // Calculating Lines Horizontal lines for row in 0..(grid.rows + 1) { for col in 0..grid.cols { - let top = get_cell(row as i32 - 1 , col as i32); - let bottom = get_cell(row as i32 , col as i32); + let top = get_cell(row as i32 - 1, col as i32); + let bottom = get_cell(row as i32, col as i32); res.set_hort_line(row, col, horizontal_mapper(top, bottom)); } } - + // Calculating Lines Vertical lines for row in 0..grid.get_row() { for col in 0..(grid.cols + 1) { let left = get_cell(row as i32, col as i32 - 1); - let right = get_cell(row as i32 , col as i32); + let right = get_cell(row as i32, col as i32); res.set_vert_line(row, col, vertical_mapper(left, right)); } } // Calculating Crossings - - for row in 0..(grid.rows + 1) { for col in 0..(grid.cols + 1) { let north = res.get_vert_line((row as i32) - 1, col as i32); @@ -376,30 +401,41 @@ impl DocGridLayout { for r in 0..self.grid.rows { for c in 0..self.grid.cols { - s.push_str(&format!("{}{}", - &Self::map_cross_to_chr(self.get_cross(r as i32, c as i32)), + s.push_str(&format!( + "{}{}", + &Self::map_cross_to_chr(self.get_cross(r as i32, c as i32)), &Self::map_line_to_char(self.get_hort_line(r as i32, c as i32)) )); } - s.push_str(&format!("{}\n", &Self::map_cross_to_chr(self.get_cross(r as i32, self.grid.cols as i32)))); + s.push_str(&format!( + "{}\n", + &Self::map_cross_to_chr(self.get_cross(r as i32, self.grid.cols as i32)) + )); for c in 0..self.grid.cols { - s.push_str(&format!("{}{}", + s.push_str(&format!( + "{}{}", &Self::map_line_to_char(self.get_vert_line(r as i32, c as i32)), &self.grid.array_cells[self.grid.cols * r + c].to_debug_string() )); } - s.push_str(&format!("{}\n", - &Self::map_line_to_char(self.get_vert_line(r as i32, self.grid.cols as i32)))); - }; + s.push_str(&format!( + "{}\n", + &Self::map_line_to_char(self.get_vert_line(r as i32, self.grid.cols as i32)) + )); + } for c in 0..self.grid.cols { - s.push_str(&format!("{}{}", - &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, c as i32)), + s.push_str(&format!( + "{}{}", + &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, c as i32)), &Self::map_line_to_char(self.get_hort_line(self.grid.rows as i32, c as i32)) )); } - s.push_str(&format!("{}\n", &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, self.grid.cols as i32)))); + s.push_str(&format!( + "{}\n", + &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, self.grid.cols as i32)) + )); s } @@ -415,71 +451,88 @@ impl DocGridLayout { match cell { DocGridCellType::Boarder => empty_string.clone(), DocGridCellType::Empty => empty_string.clone(), - DocGridCellType::SubMenu { text } => text.get(*sub_c).cloned().unwrap_or(empty_string.clone()), - DocGridCellType::Item { text } => text.get(*sub_c).cloned().unwrap_or(empty_string.clone()), + DocGridCellType::SubMenu { text } => { + text.get(*sub_c).cloned().unwrap_or(empty_string.clone()) + } + DocGridCellType::Item { text } => { + text.get(*sub_c).cloned().unwrap_or(empty_string.clone()) + } DocGridCellType::ItemJoin => empty_string.clone(), } }; for r in 0..self.grid.rows { for c in 0..self.grid.cols { - s.push_str(&format!("{}{}", - &Self::map_cross_to_chr(self.get_cross(r as i32, c as i32)), - &Self::map_line_to_char(self.get_hort_line(r as i32, c as i32)).repeat(row_sizes[c] + 2) + s.push_str(&format!( + "{}{}", + &Self::map_cross_to_chr(self.get_cross(r as i32, c as i32)), + &Self::map_line_to_char(self.get_hort_line(r as i32, c as i32)) + .repeat(row_sizes[c] + 2) )); } - s.push_str(&format!("{}\n", &Self::map_cross_to_chr(self.get_cross(r as i32, self.grid.cols as i32)))); + s.push_str(&format!( + "{}\n", + &Self::map_cross_to_chr(self.get_cross(r as i32, self.grid.cols as i32)) + )); for sub_c in 0..col_sizes[r] { for c in 0..self.grid.cols { - s.push_str(&format!("{} {} ", + s.push_str(&format!( + "{} {} ", &Self::map_line_to_char(self.get_vert_line(r as i32, c as i32)), - &map_cell(&self.grid.array_cells[self.grid.cols * r + c], &sub_c).pad_to_width(row_sizes[c]) + &map_cell(&self.grid.array_cells[self.grid.cols * r + c], &sub_c) + .pad_to_width(row_sizes[c]) )); } - s.push_str(&format!("{}\n", - &Self::map_line_to_char(self.get_vert_line(r as i32, self.grid.cols as i32)))); + s.push_str(&format!( + "{}\n", + &Self::map_line_to_char(self.get_vert_line(r as i32, self.grid.cols as i32)) + )); } - }; - + } + for c in 0..self.grid.cols { - s.push_str(&format!("{}{}", - &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, c as i32)), - &Self::map_line_to_char(self.get_hort_line(self.grid.rows as i32, c as i32)).repeat(row_sizes[c] + 2) + s.push_str(&format!( + "{}{}", + &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, c as i32)), + &Self::map_line_to_char(self.get_hort_line(self.grid.rows as i32, c as i32)) + .repeat(row_sizes[c] + 2) )); } - s.push_str(&format!("{}\n", &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, self.grid.cols as i32)))); + s.push_str(&format!( + "{}\n", + &Self::map_cross_to_chr(self.get_cross(self.grid.rows as i32, self.grid.cols as i32)) + )); s } } mod doc_grid_layout_mappers { - use super::{DocGridLineType, DocGridLineCrossType, DocGridCellType}; + use super::{DocGridCellType, DocGridLineCrossType, DocGridLineType}; pub fn vert_line_ascii(left: &DocGridCellType, right: &DocGridCellType) -> DocGridLineType { - use DocGridCellType::{ItemJoin, Item}; + use DocGridCellType::{Item, ItemJoin}; match (left, right) { (ItemJoin, Item { .. }) => DocGridLineType::NONE, (Item { .. }, ItemJoin) => DocGridLineType::NONE, - (_, _) => DocGridLineType::VERTICAL + (_, _) => DocGridLineType::VERTICAL, } } pub fn hort_line_ascii(top: &DocGridCellType, bottom: &DocGridCellType) -> DocGridLineType { - use DocGridCellType::{Boarder, Empty, ItemJoin, Item, SubMenu}; + use DocGridCellType::{Boarder, Empty, Item, ItemJoin, SubMenu}; match (top, bottom) { (Empty, Empty) => DocGridLineType::NONE, - // Sub Menu "Joins" + // Sub Menu "Joins" (Empty, SubMenu { .. }) => DocGridLineType::HORIZONTAL, (SubMenu { .. }, Empty) => DocGridLineType::NONE, (SubMenu { .. }, SubMenu { .. }) => DocGridLineType::HORIZONTAL, // Horizontal Rules - (Boarder, _) => DocGridLineType::HORIZONTAL, (_, Boarder) => DocGridLineType::HORIZONTAL, @@ -491,8 +544,13 @@ mod doc_grid_layout_mappers { } } - pub fn cross_ascii(n: &DocGridLineType, e: &DocGridLineType, s: &DocGridLineType, w: &DocGridLineType) -> DocGridLineCrossType{ - use DocGridLineType::{NONE, HORIZONTAL, VERTICAL}; + pub fn cross_ascii( + n: &DocGridLineType, + e: &DocGridLineType, + s: &DocGridLineType, + w: &DocGridLineType, + ) -> DocGridLineCrossType { + use DocGridLineType::{HORIZONTAL, NONE, VERTICAL}; match (n, e, s, w) { (VERTICAL, NONE, VERTICAL, NONE) => DocGridLineCrossType::VERTICAL, (NONE, HORIZONTAL, NONE, HORIZONTAL) => DocGridLineCrossType::HORIZONTAL, @@ -502,20 +560,22 @@ mod doc_grid_layout_mappers { } pub fn create_ascii_debug_string(grid: &DocGrid) -> String { - let layout = DocGridLayout::new(grid, + let layout = DocGridLayout::new( + grid, doc_grid_layout_mappers::vert_line_ascii, doc_grid_layout_mappers::hort_line_ascii, - doc_grid_layout_mappers::cross_ascii + doc_grid_layout_mappers::cross_ascii, ); return layout.to_debug_string(); } pub fn create_ascii_doc_string(grid: &DocGrid) -> String { - let layout = DocGridLayout::new(grid, + let layout = DocGridLayout::new( + grid, doc_grid_layout_mappers::vert_line_ascii, doc_grid_layout_mappers::hort_line_ascii, - doc_grid_layout_mappers::cross_ascii + doc_grid_layout_mappers::cross_ascii, ); return layout.to_ascii_doc_string(); @@ -523,44 +583,63 @@ pub fn create_ascii_doc_string(grid: &DocGrid) -> String { #[cfg(test)] mod tests { - use crate::configotron::{conf::{UserConfigData, UserConfigItem}}; use super::{DocGrid, DocGridCellType, ResultingGridCell}; + use crate::configotron::conf::{UserConfigData, UserConfigItem}; // Helper functino for creating elements for testing fn create_item() -> UserConfigItem { - UserConfigItem::Boolean { name: "Test".to_string() } + UserConfigItem::Boolean { + name: "Test".to_string(), + } } fn create_submenu(v: Vec) -> UserConfigItem { - UserConfigItem::Submenu(UserConfigData { name: "SubMenu".to_string(), config_items: v }) + UserConfigItem::Submenu(UserConfigData { + name: "SubMenu".to_string(), + config_items: v, + }) } fn create_menu(v: Vec) -> UserConfigData { - UserConfigData { name: "TestMenu".to_string(), config_items: v } + UserConfigData { + name: "TestMenu".to_string(), + config_items: v, + } } // Actuall Test #[test] fn grid_extend() { let mut grid = DocGrid::new(); - - grid.set_title(DocGridCellType::SubMenu { text: vec!["name".to_string()] }); - grid.append_item(ResultingGridCell::Cell(DocGridCellType::Item { text: vec!["name".to_string()] })); - grid.append_item(ResultingGridCell::Cell(DocGridCellType::ItemJoin)); - grid.append_item(ResultingGridCell::Cell(DocGridCellType::Item { text: vec!["name".to_string()] })); - grid.append_item(ResultingGridCell::Cell(DocGridCellType::Item { text: vec!["name".to_string()] })); - grid.append_item(ResultingGridCell::Cell(DocGridCellType::ItemJoin)); - grid.append_item(ResultingGridCell::Cell(DocGridCellType::SubMenu { text: vec!["name".to_string()] })); - assert_eq!(grid.to_debug_string(), r#"| S I | + grid.set_title(DocGridCellType::SubMenu { + text: vec!["name".to_string()], + }); + grid.append_item(ResultingGridCell::Cell(DocGridCellType::Item { + text: vec!["name".to_string()], + })); + grid.append_item(ResultingGridCell::Cell(DocGridCellType::ItemJoin)); + grid.append_item(ResultingGridCell::Cell(DocGridCellType::Item { + text: vec!["name".to_string()], + })); + grid.append_item(ResultingGridCell::Cell(DocGridCellType::Item { + text: vec!["name".to_string()], + })); + grid.append_item(ResultingGridCell::Cell(DocGridCellType::ItemJoin)); + grid.append_item(ResultingGridCell::Cell(DocGridCellType::SubMenu { + text: vec!["name".to_string()], + })); + + assert_eq!( + grid.to_debug_string(), + r#"| S I | | E i | | E I | | E I | | E i | | E S | -"#); - - +"# + ); } #[test] @@ -568,19 +647,17 @@ mod tests { let data = create_menu(vec![ create_item(), create_item(), - create_submenu(vec![ - create_item(), - create_item(), - create_item(), - ]), + create_submenu(vec![create_item(), create_item(), create_item()]), + create_item(), create_item(), create_item(), - create_item() ]); let grid = DocGrid::from(&data); - assert_eq!(grid.to_debug_string(), r#"| S I i | + assert_eq!( + grid.to_debug_string(), + r#"| S I i | | E I i | | E S I | | E E I | @@ -588,61 +665,55 @@ mod tests { | E I i | | E I i | | E I i | -"#); +"# + ); } #[test] fn grid_deep_assembly_from_data() { - let data = create_menu(vec![ - create_submenu(vec![ - create_submenu(vec![ - create_submenu(vec![ - create_item() - ]), - ]), - ]), - ]); + let data = create_menu(vec![create_submenu(vec![create_submenu(vec![ + create_submenu(vec![create_item()]), + ])])]); let grid = DocGrid::from(&data); - assert_eq!(grid.to_debug_string(), r#"| S S S S I | -"#); + assert_eq!( + grid.to_debug_string(), + r#"| S S S S I | +"# + ); } #[test] fn grid_complex_assembly_from_data() { let data = create_menu(vec![ - create_item(), - create_item(), - create_submenu(vec![ create_item(), create_item(), create_submenu(vec![ create_item(), create_item(), create_submenu(vec![ - create_item(), - create_item(), + create_item(), + create_item(), + create_submenu(vec![create_item(), create_item()]), + create_item(), ]), create_item(), - ]), - create_item(), - create_submenu(vec![ + create_submenu(vec![create_item(), create_item()]), create_item(), create_item(), ]), create_item(), create_item(), - ]), - create_item(), - create_item(), ]); let grid = DocGrid::from(&data); println!("{}", grid.to_debug_string()); - assert_eq!(grid.to_debug_string(), r#"| S I i i i | + assert_eq!( + grid.to_debug_string(), + r#"| S I i i i | | E I i i i | | E S I i i | | E E I i i | @@ -658,37 +729,38 @@ mod tests { | E E I i i | | E I i i i | | E I i i i | -"#); +"# + ); } mod ascii_docs { - use crate::configotron::{conf::{UserConfigData, UserConfigItem}, doc::grid::create_ascii_debug_string, doc::grid::create_ascii_doc_string}; - use super::{DocGrid, create_item, create_menu, create_submenu}; + use super::{create_item, create_menu, create_submenu, DocGrid}; + use crate::configotron::{ + doc::grid::create_ascii_debug_string, doc::grid::create_ascii_doc_string, + }; #[test] fn ascii_docs_debug() { - let data = create_menu(vec![ + let data = create_menu(vec![ create_item(), create_item(), - create_submenu(vec![ - create_item(), - create_item(), - create_item(), - ]), + create_submenu(vec![create_item(), create_item(), create_item()]), + create_item(), create_item(), create_item(), - create_item() ]); let grid = DocGrid::from(&data); - + println!("{}", grid.to_debug_string()); let s = create_ascii_debug_string(&grid); println!("{}", s); - assert_eq!(s, r#"+-+---+ + assert_eq!( + s, + r#"+-+---+ |S|I i| | +---+ |E|I i| @@ -705,51 +777,58 @@ mod tests { | +---+ |E|I i| +-+---+ -"#); - } +"# + ); + } #[test] fn ascii_docs_real() { - let data = create_menu(vec![ + let data = create_menu(vec![ create_item(), create_item(), - create_submenu(vec![ - create_item(), - create_item(), - create_item(), - ]), + create_submenu(vec![create_item(), create_item(), create_item()]), + create_item(), create_item(), create_item(), - create_item() ]); let grid = DocGrid::from(&data); - + println!("{}", grid.to_debug_string()); let s = create_ascii_doc_string(&grid); println!("{}", s); - assert_eq!(s, r#"+----------+----------------+ -| TestMenu | Test | -| +----------------+ -| | Test | -| +---------+------+ -| | SubMenu | Test | -| | +------+ -| | | Test | -| | +------+ -| | | Test | -| +---------+------+ -| | Test | -| +----------------+ -| | Test | -| +----------------+ -| | Test | -+----------+----------------+ -"#); + assert_eq!( + s, + r#"+----------+---------------------+ +| TestMenu | Test | +| | [Yes/No] | +| +---------------------+ +| | Test | +| | [Yes/No] | +| +----------+----------+ +| | SubMenu | Test | +| | | [Yes/No] | +| | +----------+ +| | | Test | +| | | [Yes/No] | +| | +----------+ +| | | Test | +| | | [Yes/No] | +| +----------+----------+ +| | Test | +| | [Yes/No] | +| +---------------------+ +| | Test | +| | [Yes/No] | +| +---------------------+ +| | Test | +| | [Yes/No] | ++----------+---------------------+ +"# + ); + } } - - } -} \ No newline at end of file +} diff --git a/src/configotron/fsm.rs b/src/configotron/fsm.rs index 4b4faba..435a8a4 100644 --- a/src/configotron/fsm.rs +++ b/src/configotron/fsm.rs @@ -1,137 +1,288 @@ -use std::{cell::RefCell, rc::{Weak, Rc}}; +use graphviz_rust::{ + dot_structures::{Attribute, Edge, EdgeTy, Graph, Id, NodeId, Stmt, Vertex}, + exec, + printer::PrinterContext, +}; +use std::{cell::RefCell, rc::Rc}; +#[derive(Debug, Clone)] pub struct FSM { - states: Vec>> + name: String, + states: Vec>>, } impl FSM { - pub fn new(state: Vec>>) -> Self { - FSM {states: state} + pub fn new(state: Vec>>, name: &str) -> Self { + FSM { + states: state, + name: name.to_string(), + } + } + + fn render_stmts(&self) -> Vec { + let mut stmts: Vec = vec![Stmt::Attribute(Attribute( + Id::Plain("rankdir".to_string()), + Id::Plain("LR".to_string()), + ))]; + for s in &self.states { + let s_borrowed = s.borrow(); + + stmts.push(Stmt::Node(graphviz_rust::dot_structures::Node { + id: NodeId(Id::Plain(s_borrowed.to_graph_label()), None), + attributes: { + let mut res = vec![Attribute( + Id::Plain("label".to_string()), + Id::Plain(s_borrowed.to_graph_label()), + )]; + match s_borrowed.action { + FSMStateAction::NONE => { + res.push(Attribute( + Id::Plain("shape".to_string()), + Id::Plain("circle".to_string()), + )); + } + FSMStateAction::TERMINAL => { + res.push(Attribute( + Id::Plain("shape".to_string()), + Id::Plain("doublecircle".to_string()), + )); + } + } + res + }, + })); + + match &s_borrowed.transition { + None => {} + Some(t) => { + for (t, range) in t.to_targets_vec() { + match t.upgrade() { + None => {} + Some(target) => { + stmts.push(Stmt::Edge(Edge { + ty: EdgeTy::Pair( + Vertex::N(NodeId( + Id::Plain(s_borrowed.to_graph_label()), + None, + )), + Vertex::N(NodeId( + Id::Plain(target.borrow().to_graph_label()), + None, + )), + ), + attributes: vec![Attribute( + Id::Plain("label".to_string()), + Id::Plain(format!("\"{}\"", range.to_string())), + )], + })); + } + } + } + } + } + } + + stmts + } + + fn render_graph(&self, format: graphviz_rust::cmd::Format) -> Result, std::io::Error> { + let g = Graph::DiGraph { + id: Id::Plain("root".to_string()), + strict: false, + stmts: self.render_stmts(), + }; + + let mut ctx: graphviz_rust::printer::PrinterContext = PrinterContext::default(); + + println!("{}", graphviz_rust::print(g.clone(), &mut ctx)); + + let data = exec(g, &mut ctx, vec![format.into()])?; + + Ok(data) } } #[derive(Debug, Clone, Copy)] pub enum FSMStateAction { - NONE, TERMINAL + NONE, + TERMINAL, } #[derive(Debug, Clone)] pub struct FSMState { name: String, action: FSMStateAction, - transition: Option + transition: Option, } -#[derive(Debug, Clone)] -pub struct FSMTranistion { - array: [Weak>; 128], -} - - impl FSMState { pub fn new(name: String, action: FSMStateAction) -> Self { - FSMState { name: name, action: action, transition: None } + FSMState { + name: name, + action: action, + transition: None, + } } - pub fn set_transitions(&mut self, t: FSMTranistion) { + pub fn set_transitions(&mut self, t: tranisitions::FSMTranistion) { self.transition = Some(t); } -} -impl FSMTranistion { - - pub fn new_from_mask(mask: TransitionMask, pos: Weak>, default: Weak>) -> Self { - let mut array: [Weak>; 128] = std::array::from_fn(|_| Weak::clone(&default)); - for i in 0..128 { - if mask.get_bit(i) { - array[i] = Weak::clone(&pos ); - } - } - - FSMTranistion { array } + pub fn to_graph_label(&self) -> String { + return format!("\"{}\"", self.name); } - pub fn new(mask: TransitionMask, default: Weak>) -> Self { - let mut array: [Weak>; 128] = std::array::from_fn(|_| Weak::clone(&default)); - FSMTranistion { array } - } - - pub fn apply(&mut self, mask: TransitionMask, state: Weak>) { - - for i in 0..128 { - if mask.get_bit(i) { - self.array[i] = Weak::clone(&state ); - } - } + pub fn to_graph_hash(&self) -> String { + self.to_graph_label() } } -use transition_masking::{TransitionMask}; +mod tranisitions { + use super::transition_masking::TransitionMask; + use super::FSMState; + use std::collections::HashMap; + use std::rc::Rc; + use std::{cell::RefCell, rc::Weak}; + + type StateRef = Weak>; + + #[derive(Debug, Clone)] + pub struct FSMTranistion { + array: [StateRef; 128], + } + + impl FSMTranistion { + pub fn new_from_mask(mask: TransitionMask, pos: StateRef, default: StateRef) -> Self { + let mut array: [StateRef; 128] = std::array::from_fn(|_| Weak::clone(&default)); + for i in 0..128 { + if mask.get_bit(i) { + array[i] = Weak::clone(&pos); + } + } + + FSMTranistion { array } + } + + pub fn new(default: StateRef) -> Self { + let array: [StateRef; 128] = std::array::from_fn(|_| Weak::clone(&default)); + + FSMTranistion { array } + } + + pub fn apply(&mut self, mask: TransitionMask, state: StateRef) { + for i in 0..128 { + if mask.get_bit(i) { + self.array[i] = Weak::clone(&state); + } + } + } + + pub fn to_targets_vec(&self) -> Vec<(StateRef, TransitionMask)> { + let mut target_maps: HashMap = HashMap::new(); + + self.array.iter().enumerate().for_each(|(index, target)| { + match target.upgrade() { + None => {} + Some(t) => { + let s_borrowd = t.borrow(); + let key = s_borrowd.to_graph_label(); + match target_maps.get_mut(&key) { + Some((_, mask)) => { + mask.set_bit(index); + } + None => { + let mut mask = TransitionMask::new(); + mask.set_bit(index); + target_maps.insert(key, (Rc::downgrade(&t), mask)); + } + }; + } + }; + }); + + let mut res: Vec<(StateRef, TransitionMask)> = Vec::new(); + + target_maps + .iter() + .for_each(|(_, (target, mask))| match target.upgrade() { + Some(t) => { + res.push((Rc::downgrade(&t), mask.clone())); + } + None => {} + }); + res + } + } +} mod transition_masking { - use std::ops::RangeInclusive; + use itertools::Itertools; + use std::{ops::RangeInclusive, usize}; pub enum MaskPresets { Char(char), - AnyCase, LowerCase, UpperCase, + AnyCase, + LowerCase, + UpperCase, AllHumanReadable, - Numbers, SpecialChars, - WhiteSpace, LineTermination, - NoChar + Numbers, + SpecialChars, + WhiteSpace, + LineTermination, + NoChar, } - + #[derive(Debug, Clone)] pub struct TransitionMask { - mask: u128 + mask: u128, } impl From for TransitionMask { fn from(value: MaskPresets) -> Self { let mut n: u128 = 0; - + let mut set_range = |r: RangeInclusive| { - r.for_each(|i| { n |= 1 << i }); + r.for_each(|i| n |= 1 << i); }; match value { MaskPresets::NoChar => {} MaskPresets::AnyCase => { - set_range(0x41..=0x5a); // UpperCase - set_range(0x61..=0x7a); // LowerCase - }, + set_range(0x41..=0x5a); // UpperCase + set_range(0x61..=0x7a); // LowerCase + } MaskPresets::LowerCase => { - set_range(0x61..=0x7a); // LowerCase - }, + set_range(0x61..=0x7a); // LowerCase + } MaskPresets::UpperCase => { - set_range(0x41..=0x5a); // UpperCase - }, + set_range(0x41..=0x5a); // UpperCase + } MaskPresets::AllHumanReadable => { - set_range(0x20..=0x7e); // All Ascii Chars + Space + Tab - n |= 1 << 0x9; // Tab - }, + set_range(0x20..=0x7e); // All Ascii Chars + Space + Tab + n |= 1 << 0x9; // Tab + } MaskPresets::Numbers => { - set_range(0x30..=0x39); // Numbers - }, + set_range(0x30..=0x39); // Numbers + } MaskPresets::SpecialChars => { - set_range(0x21..=0x2F); // ! to / - set_range(0x3A..=0x40); // : to @ - set_range(0x5B..=0x0D); // [ to ` - set_range(0x7B..=0x7E); // { to ~ - }, + set_range(0x21..=0x2F); // ! to / + set_range(0x3A..=0x40); // : to @ + set_range(0x5B..=0x0D); // [ to ` + set_range(0x7B..=0x7E); // { to ~ + } MaskPresets::WhiteSpace => { - n |= 1 << 0x9; // Tab - n |= 1 << 0x20; // Spaces - - }, + n |= 1 << 0x9; // Tab + n |= 1 << 0x20; // Spaces + } MaskPresets::LineTermination => { - n |= 1 << 0x0a; // Line Feed - n |= 1 << 0x0d; // Carriage Return - } + n |= 1 << 0x0a; // Line Feed + n |= 1 << 0x0d; // Carriage Return + } MaskPresets::Char(c) => { if c.is_ascii() { n |= 1 << (c as u32) } - }, + } } TransitionMask { mask: n } @@ -144,11 +295,15 @@ mod transition_masking { } pub fn or(&self, other: Self) -> TransitionMask { - TransitionMask { mask: other.mask | self.mask } + TransitionMask { + mask: other.mask | self.mask, + } } pub fn and(&self, other: Self) -> TransitionMask { - TransitionMask { mask: other.mask & self.mask } + TransitionMask { + mask: other.mask & self.mask, + } } pub fn inv(&self) -> TransitionMask { @@ -158,42 +313,190 @@ mod transition_masking { pub fn get_bit(&self, i: usize) -> bool { (self.mask & (1 << i)) > 0 } + + pub fn set_bit(&mut self, i: usize) { + self.mask |= 1 << i; + } + + pub fn to_string(&self) -> String { + let mut res: Vec = Vec::new(); + let mut strike: Option = None; + + fn num_to_str(i: usize) -> String { + if (0x21..=0x7E).contains(&i) { + format!("'{}'", ((i as u8) as char).to_string()) + } else { + match i { + 0x20 => "SP".to_string(), + 0x0D => "LF".to_string(), + 0x0A => "CR".to_string(), + 0x09 => "TAB".to_string(), + _ => format!("{:02x}", i), + } + } + } + + for (p, n) in (0..128).tuple_windows() { + match (strike, self.get_bit(p), self.get_bit(n)) { + (None, true, true) => { + // First two + strike = Some(p); + } + (None, true, false) => { + // First, only one + res.push(num_to_str(p)); + } + (None, false, true) => { + // Strik Start + strike = Some(n) + } + (None, false, false) => { + // No Strike + } + (Some(_), true, true) => { + // Stike ongoing + } + (Some(s), true, false) => { + // Strik end + if s == p { + // Single Number + res.push(num_to_str(p)); + } else { + // Range + res.push(format!("{}-{}", num_to_str(s), num_to_str(p))); + } + + strike = None; + } + (Some(_), false, true) => { + // This should no happend + strike = Some(n); + } + (Some(_), false, false) => { + // Should also no happend + strike = None; + } + } + } + + match strike { + None => (), + Some(s) => { + if s == 127 { + res.push(num_to_str(s)); + } else { + res.push(format!("{}-{}", num_to_str(s), num_to_str(127))); + } + } + }; + + res.join(",") + } } } #[cfg(test)] mod test { - use super::{transition_masking::*, *}; + use graphviz_rust::cmd::Format; + use std::io::Write; + + use super::{tranisitions::*, transition_masking::*, *}; #[test] - fn simple_fsm_building() { - let z0 = Rc::new(RefCell::new(FSMState::new("z0".to_string(), FSMStateAction::NONE))); - let z1 = Rc::new(RefCell::new(FSMState::new("z1".to_string(), FSMStateAction::NONE))); - let z2 = Rc::new(RefCell::new(FSMState::new("z2".to_string(), FSMStateAction::TERMINAL))); - let z3 = Rc::new(RefCell::new(FSMState::new("z3".to_string(), FSMStateAction::NONE))); + fn transition_mask_to_string() { + let mut mask1 = TransitionMask::new(); + + mask1.set_bit(0); + + (10..=16).for_each(|i| { + mask1.set_bit(i); + }); + + (50..=51).for_each(|i| { + mask1.set_bit(i); + }); + + mask1.set_bit(60); + mask1.set_bit(62); + mask1.set_bit(64); + + (70..=73).for_each(|i| { + mask1.set_bit(i); + }); + + (126..=127).for_each(|i| { + mask1.set_bit(i); + }); + + println!("{}", mask1.to_string()); + + assert_eq!( + mask1.to_string(), + "00,CR-10,'2'-'3','<','>','@','F'-'I','~'-7f" + ); + } + + #[test] + fn simple_fsm_render() { + let z0 = Rc::new(RefCell::new(FSMState::new( + "z0".to_string(), + FSMStateAction::NONE, + ))); + let z1 = Rc::new(RefCell::new(FSMState::new( + "z1".to_string(), + FSMStateAction::NONE, + ))); + let z2 = Rc::new(RefCell::new(FSMState::new( + "z2".to_string(), + FSMStateAction::TERMINAL, + ))); + let z3 = Rc::new(RefCell::new(FSMState::new( + "z3".to_string(), + FSMStateAction::NONE, + ))); let mut z0_transisions = FSMTranistion::new_from_mask( - TransitionMask::from(MaskPresets::Char('a')), Rc::downgrade(&z1), Rc::downgrade(&z3)); - z0_transisions.apply(TransitionMask::from(MaskPresets::Char('b')), Rc::downgrade(&z2)); + TransitionMask::from(MaskPresets::Char('a')), + Rc::downgrade(&z1), + Rc::downgrade(&z3), + ); + z0_transisions.apply( + TransitionMask::from(MaskPresets::Char('b')), + Rc::downgrade(&z2), + ); let mut z1_transisions = FSMTranistion::new_from_mask( - TransitionMask::from(MaskPresets::Char('a')), Rc::downgrade(&z1), Rc::downgrade(&z3)); - z1_transisions.apply(TransitionMask::from(MaskPresets::Char('b')), Rc::downgrade(&z2)); + TransitionMask::from(MaskPresets::Char('a')), + Rc::downgrade(&z1), + Rc::downgrade(&z3), + ); + z1_transisions.apply( + TransitionMask::from(MaskPresets::Char('b')), + Rc::downgrade(&z2), + ); let z2_transisions = FSMTranistion::new_from_mask( - TransitionMask::from(MaskPresets::Char('a')), Rc::downgrade(&z2), Rc::downgrade(&z3)); + TransitionMask::from(MaskPresets::Char('a')), + Rc::downgrade(&z2), + Rc::downgrade(&z3), + ); + + let z3_transisions = FSMTranistion::new(Rc::downgrade(&z3)); - let z3_transisions = FSMTranistion::new( - TransitionMask::from(MaskPresets::Char('a')), Rc::downgrade(&z3)); - z0.borrow_mut().set_transitions(z0_transisions); z1.borrow_mut().set_transitions(z1_transisions); z2.borrow_mut().set_transitions(z2_transisions); z3.borrow_mut().set_transitions(z3_transisions); + let fsm = FSM::new(vec![z0, z1, z2, z3], "Test FSM #1"); - let fsm = FSM::new(vec![z0, z1, z2, z3]); + let data = fsm.render_graph(Format::Svg).unwrap(); + let mut output_file = std::fs::File::create("/tmp/debug-simle_fsm_building.svg").unwrap(); + output_file.write_all(&data).unwrap(); + + + let svg_content = std::fs::read_to_string("/tmp/debug-simle_fsm_building.svg").unwrap(); + assert_eq!(svg_content, String::from_utf8(data).unwrap()); } - -} \ No newline at end of file +} diff --git a/src/configotron/mod.rs b/src/configotron/mod.rs index 0b6606e..41d30e4 100644 --- a/src/configotron/mod.rs +++ b/src/configotron/mod.rs @@ -1,3 +1,3 @@ pub mod conf; pub mod doc; -pub mod fsm; \ No newline at end of file +pub mod fsm; diff --git a/src/main.rs b/src/main.rs index 3c993c5..221c138 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ +#[allow(dead_code)] mod configotron; -use configotron::conf::{read_and_parse_yaml_file}; -use configotron::doc::grid::{DocGrid, create_ascii_doc_string}; +use configotron::conf::read_and_parse_yaml_file; +use configotron::doc::grid::{create_ascii_doc_string, DocGrid}; fn main() { - let data = match read_and_parse_yaml_file("example.yml") { Ok(d) => d, Err(error) => { diff --git a/tests/debug-simle_fsm_building.svg b/tests/debug-simle_fsm_building.svg new file mode 100644 index 0000000..681cb8b --- /dev/null +++ b/tests/debug-simle_fsm_building.svg @@ -0,0 +1,101 @@ + + + + + + +root + + + +z0 + +z0 + + + +z2 + + +z2 + + + +z0->z2 + + +'b' + + + +z1 + +z1 + + + +z0->z1 + + +'a' + + + +z3 + +z3 + + + +z0->z3 + + +00-'`','c'-7f + + + +z2->z2 + + +'a' + + + +z2->z3 + + +00-'`','b'-7f + + + +z1->z2 + + +'b' + + + +z1->z1 + + +'a' + + + +z1->z3 + + +00-'`','c'-7f + + + +z3->z3 + + +00-7f + + +