diff --git a/Cargo.lock b/Cargo.lock index 5b39223..29db703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -459,6 +468,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -585,6 +623,7 @@ version = "0.1.0" dependencies = [ "color-eyre", "ratatui", + "regex", "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 2bb7a23..4aa4a4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] color-eyre = "0.6.5" ratatui = "0.29.0" +regex = "1.11.2" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" diff --git a/src/app/entry.rs b/src/app/entry.rs index db0beb1..5b59d4e 100644 --- a/src/app/entry.rs +++ b/src/app/entry.rs @@ -1,10 +1,7 @@ +use crate::app::status::EntryCreation; +use regex::Regex; use serde::{Deserialize, Serialize}; -use std::{ - error::Error, - fmt::Display, - io::ErrorKind, - num::{IntErrorKind, ParseIntError}, -}; +use std::fmt::Display; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Entry { @@ -25,18 +22,26 @@ impl Display for Entry { } impl Entry { - pub fn new(fromIP: String, toIP: String, fromPort: String, toPort: String) -> Option { - if fromPort.parse::().is_ok_and(|a| a > 1 && a < 65535) - && toPort.parse::().is_ok_and(|a| a > 1 && a < 65535) + pub fn new( + fromIP: String, + toIP: String, + fromPort: String, + toPort: String, + ) -> Result { + let ip = Regex::new("/^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$/").unwrap(); + if !fromPort.parse::().is_ok_and(|a| a > 1 && a < 65535) + || !toPort.parse::().is_ok_and(|a| a > 1 && a < 65535) { - Some(Entry { + Err(EntryCreation::PortValidationError) + } else if !ip.is_match(&fromIP) || !ip.is_match(&toIP) { + Err(EntryCreation::IPValidationError) + } else { + Ok(Entry { fromIP, toIP, fromPort, toPort, }) - } else { - None } } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 15c75fb..b00fed2 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -15,8 +15,6 @@ pub struct AppState { pub toIP: String, pub toPort: String, pub screen: CurrentScreen, - pub field: Option, - // pub current: Option, pub currentlyEditing: Option, pub entries: Vec, pub confDir: String, @@ -32,7 +30,6 @@ impl AppState { toPort: String::new(), currentlyEditing: None, screen: CurrentScreen::Main, - field: None, entries: settings.entries, confDir: confDir, } @@ -45,7 +42,7 @@ impl AppState { self.fromPort.clone(), self.toPort.clone(), ) { - Some(entry) => { + Ok(entry) => { self.entries.push(entry); self.fromIP = String::new(); self.toIP = String::new(); @@ -59,17 +56,6 @@ impl AppState { } } - pub fn startEditing(&mut self) { - if let Some(currentField) = &self.currentlyEditing { - match currentField { - EditingField::ToIP => {} - _ => self.currentlyEditing = Some(EditingField::FromIP), - } - } else { - self.currentlyEditing = Some(EditingField::FromIP); - } - } - pub fn nextField(&mut self) { if let Some(currentField) = &self.currentlyEditing { self.currentlyEditing = match currentField { diff --git a/src/app/settings.rs b/src/app/settings.rs index 5868c3c..38dac23 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -25,7 +25,7 @@ impl Settings { Err(_) => { let newSet = Settings { entries: vec![] }; let payload = serde_json::to_string_pretty(&newSet).unwrap(); - std::fs::write(config, payload); + let _ = std::fs::write(config, payload); newSet } } @@ -33,6 +33,6 @@ impl Settings { pub fn save(&self, config: &String) { let payload = serde_json::to_string(self).unwrap(); - std::fs::write(config, payload); + let _ = std::fs::write(config, payload); } } diff --git a/src/main.rs b/src/main.rs index 9747484..7fa7d64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#[allow(non_snake_case)] +#![allow(non_snake_case)] mod app; mod ui; @@ -114,7 +114,7 @@ fn runApp(app: &mut AppState, terminal: &mut Terminal) -> Result< (KeyModifiers::NONE, KeyCode::Tab) => app.nextField(), (KeyModifiers::SHIFT, KeyCode::Tab) => app.prevField(), - (m, KeyCode::Char(v)) => { + (_, KeyCode::Char(v)) => { if let Some(e) = &app.currentlyEditing { let mut isIP = false; let opField = match e { diff --git a/src/ui/centeredRect.rs b/src/ui/centeredRect.rs new file mode 100644 index 0000000..0e3222b --- /dev/null +++ b/src/ui/centeredRect.rs @@ -0,0 +1,23 @@ +use ratatui::layout::{Constraint, Direction, Layout, Rect}; + +pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + // Cut the given rectangle into three vertical pieces + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(r); + + // Then cut the middle vertical piece into three width-wise pieces + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_layout[1])[1] // Return the middle chunk +} diff --git a/src/ui.rs b/src/ui/mod.rs similarity index 85% rename from src/ui.rs rename to src/ui/mod.rs index 916e3f4..bd62afb 100644 --- a/src/ui.rs +++ b/src/ui/mod.rs @@ -1,18 +1,17 @@ #![allow(non_snake_case)] +mod centeredRect; + use ratatui::{ Frame, - layout::{Constraint, Layout}, + layout::{Constraint, Direction, Layout}, style::{Color, Style}, text::{Line, Span, Text}, - widgets::{Block, Borders, List, ListItem, Paragraph}, -}; -use ratatui::{ - layout::{Direction, Rect}, - widgets::Wrap, + widgets::{Block, Borders, List, ListItem, Paragraph, Wrap}, }; use crate::app::status::CurrentScreen; use crate::app::{AppState, status::EditingField}; +use crate::ui::centeredRect::centered_rect; pub fn ui(frame: &mut Frame, app: &AppState) { let chunks = Layout::default() @@ -131,7 +130,6 @@ pub fn ui(frame: &mut Frame, app: &AppState) { EditingField::ToIP => "To IP", EditingField::FromPort => "From Port", EditingField::ToPort => "To Port", - _ => "Something ", }; Span::styled( format!("Editing: {curEdit}"), @@ -170,25 +168,3 @@ pub fn ui(frame: &mut Frame, app: &AppState) { frame.render_widget(helpFooter, footerChunks[0]); frame.render_widget(keyBindFooter, footerChunks[1]); } - -fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { - // Cut the given rectangle into three vertical pieces - let popup_layout = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Percentage((100 - percent_y) / 2), - Constraint::Percentage(percent_y), - Constraint::Percentage((100 - percent_y) / 2), - ]) - .split(r); - - // Then cut the middle vertical piece into three width-wise pieces - Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage((100 - percent_x) / 2), - Constraint::Percentage(percent_x), - Constraint::Percentage((100 - percent_x) / 2), - ]) - .split(popup_layout[1])[1] // Return the middle chunk -}