refactor, added ip validation
This commit is contained in:
39
Cargo.lock
generated
39
Cargo.lock
generated
@@ -17,6 +17,15 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
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]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -459,6 +468,35 @@ dependencies = [
|
|||||||
"bitflags",
|
"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]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.26"
|
version = "0.1.26"
|
||||||
@@ -585,6 +623,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
color-eyre = "0.6.5"
|
color-eyre = "0.6.5"
|
||||||
ratatui = "0.29.0"
|
ratatui = "0.29.0"
|
||||||
|
regex = "1.11.2"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
|
use crate::app::status::EntryCreation;
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::fmt::Display;
|
||||||
error::Error,
|
|
||||||
fmt::Display,
|
|
||||||
io::ErrorKind,
|
|
||||||
num::{IntErrorKind, ParseIntError},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
@@ -25,18 +22,26 @@ impl Display for Entry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
pub fn new(fromIP: String, toIP: String, fromPort: String, toPort: String) -> Option<Self> {
|
pub fn new(
|
||||||
if fromPort.parse::<i32>().is_ok_and(|a| a > 1 && a < 65535)
|
fromIP: String,
|
||||||
&& toPort.parse::<i32>().is_ok_and(|a| a > 1 && a < 65535)
|
toIP: String,
|
||||||
|
fromPort: String,
|
||||||
|
toPort: String,
|
||||||
|
) -> Result<Self, EntryCreation> {
|
||||||
|
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::<i32>().is_ok_and(|a| a > 1 && a < 65535)
|
||||||
|
|| !toPort.parse::<i32>().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,
|
fromIP,
|
||||||
toIP,
|
toIP,
|
||||||
fromPort,
|
fromPort,
|
||||||
toPort,
|
toPort,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ pub struct AppState {
|
|||||||
pub toIP: String,
|
pub toIP: String,
|
||||||
pub toPort: String,
|
pub toPort: String,
|
||||||
pub screen: CurrentScreen,
|
pub screen: CurrentScreen,
|
||||||
pub field: Option<EditingField>,
|
|
||||||
// pub current: Option<Entry>,
|
|
||||||
pub currentlyEditing: Option<EditingField>,
|
pub currentlyEditing: Option<EditingField>,
|
||||||
pub entries: Vec<Entry>,
|
pub entries: Vec<Entry>,
|
||||||
pub confDir: String,
|
pub confDir: String,
|
||||||
@@ -32,7 +30,6 @@ impl AppState {
|
|||||||
toPort: String::new(),
|
toPort: String::new(),
|
||||||
currentlyEditing: None,
|
currentlyEditing: None,
|
||||||
screen: CurrentScreen::Main,
|
screen: CurrentScreen::Main,
|
||||||
field: None,
|
|
||||||
entries: settings.entries,
|
entries: settings.entries,
|
||||||
confDir: confDir,
|
confDir: confDir,
|
||||||
}
|
}
|
||||||
@@ -45,7 +42,7 @@ impl AppState {
|
|||||||
self.fromPort.clone(),
|
self.fromPort.clone(),
|
||||||
self.toPort.clone(),
|
self.toPort.clone(),
|
||||||
) {
|
) {
|
||||||
Some(entry) => {
|
Ok(entry) => {
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
self.fromIP = String::new();
|
self.fromIP = String::new();
|
||||||
self.toIP = 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) {
|
pub fn nextField(&mut self) {
|
||||||
if let Some(currentField) = &self.currentlyEditing {
|
if let Some(currentField) = &self.currentlyEditing {
|
||||||
self.currentlyEditing = match currentField {
|
self.currentlyEditing = match currentField {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ impl Settings {
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
let newSet = Settings { entries: vec![] };
|
let newSet = Settings { entries: vec![] };
|
||||||
let payload = serde_json::to_string_pretty(&newSet).unwrap();
|
let payload = serde_json::to_string_pretty(&newSet).unwrap();
|
||||||
std::fs::write(config, payload);
|
let _ = std::fs::write(config, payload);
|
||||||
newSet
|
newSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,6 @@ impl Settings {
|
|||||||
|
|
||||||
pub fn save(&self, config: &String) {
|
pub fn save(&self, config: &String) {
|
||||||
let payload = serde_json::to_string(self).unwrap();
|
let payload = serde_json::to_string(self).unwrap();
|
||||||
std::fs::write(config, payload);
|
let _ = std::fs::write(config, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#[allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
mod app;
|
mod app;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ fn runApp<B: Backend>(app: &mut AppState, terminal: &mut Terminal<B>) -> Result<
|
|||||||
(KeyModifiers::NONE, KeyCode::Tab) => app.nextField(),
|
(KeyModifiers::NONE, KeyCode::Tab) => app.nextField(),
|
||||||
(KeyModifiers::SHIFT, KeyCode::Tab) => app.prevField(),
|
(KeyModifiers::SHIFT, KeyCode::Tab) => app.prevField(),
|
||||||
|
|
||||||
(m, KeyCode::Char(v)) => {
|
(_, KeyCode::Char(v)) => {
|
||||||
if let Some(e) = &app.currentlyEditing {
|
if let Some(e) = &app.currentlyEditing {
|
||||||
let mut isIP = false;
|
let mut isIP = false;
|
||||||
let opField = match e {
|
let opField = match e {
|
||||||
|
|||||||
23
src/ui/centeredRect.rs
Normal file
23
src/ui/centeredRect.rs
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
mod centeredRect;
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
Frame,
|
Frame,
|
||||||
layout::{Constraint, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
text::{Line, Span, Text},
|
text::{Line, Span, Text},
|
||||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
|
||||||
};
|
|
||||||
use ratatui::{
|
|
||||||
layout::{Direction, Rect},
|
|
||||||
widgets::Wrap,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::status::CurrentScreen;
|
use crate::app::status::CurrentScreen;
|
||||||
use crate::app::{AppState, status::EditingField};
|
use crate::app::{AppState, status::EditingField};
|
||||||
|
use crate::ui::centeredRect::centered_rect;
|
||||||
|
|
||||||
pub fn ui(frame: &mut Frame, app: &AppState) {
|
pub fn ui(frame: &mut Frame, app: &AppState) {
|
||||||
let chunks = Layout::default()
|
let chunks = Layout::default()
|
||||||
@@ -131,7 +130,6 @@ pub fn ui(frame: &mut Frame, app: &AppState) {
|
|||||||
EditingField::ToIP => "To IP",
|
EditingField::ToIP => "To IP",
|
||||||
EditingField::FromPort => "From Port",
|
EditingField::FromPort => "From Port",
|
||||||
EditingField::ToPort => "To Port",
|
EditingField::ToPort => "To Port",
|
||||||
_ => "Something ",
|
|
||||||
};
|
};
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!("Editing: {curEdit}"),
|
format!("Editing: {curEdit}"),
|
||||||
@@ -170,25 +168,3 @@ pub fn ui(frame: &mut Frame, app: &AppState) {
|
|||||||
frame.render_widget(helpFooter, footerChunks[0]);
|
frame.render_widget(helpFooter, footerChunks[0]);
|
||||||
frame.render_widget(keyBindFooter, footerChunks[1]);
|
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
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user