#![forbid(unsafe_code)]
mod options;
+mod utils;
mod views;
use crate::options::*;
))
.child(FormInputView::new(
"IP address (CIDR)",
- CidrAddressEditView::new().content(options.ip_addr, options.cidr_mask),
+ CidrAddressEditView::new().content(options.address),
))
.child(FormInputView::new(
"Gateway address",
.get_value()
}
- let (ip_addr, cidr_mask) = get_val::<CidrAddressEditView, _>(view, 2)?;
-
Some(NetworkOptions {
ifname: get_val::<SelectView, _>(view, 0)?,
fqdn: get_val::<EditView, _>(view, 1)?,
- ip_addr,
- cidr_mask,
+ address: get_val::<CidrAddressEditView, _>(view, 2)?,
gateway: get_val::<EditView, _>(view, 3).and_then(|s| s.parse().ok())?,
dns_server: get_val::<EditView, _>(view, 3).and_then(|s| s.parse().ok())?,
})
-use crate::SummaryOption;
+use crate::{utils::CidrAddress, SummaryOption};
use std::{
fmt, iter,
net::{IpAddr, Ipv4Addr},
pub struct NetworkOptions {
pub ifname: String,
pub fqdn: String,
- pub ip_addr: IpAddr,
- pub cidr_mask: usize,
+ pub address: CidrAddress,
pub gateway: IpAddr,
pub dns_server: IpAddr,
}
Self {
ifname: String::new(),
fqdn: "pve.example.invalid".to_owned(),
- ip_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
- cidr_mask: 0,
+ // Safety: The provided mask will always be valid.
+ address: CidrAddress::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0).unwrap(),
gateway: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
dns_server: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
}
SummaryOption::new("Administator email:", &self.password.email),
SummaryOption::new("Management interface:", &self.network.ifname),
SummaryOption::new("Hostname:", &self.network.fqdn),
- SummaryOption::new(
- "Host IP (CIDR):",
- format!("{}/{}", self.network.ip_addr, self.network.cidr_mask),
- ),
+ SummaryOption::new("Host IP (CIDR):", self.network.address.to_string()),
SummaryOption::new("Gateway", self.network.gateway.to_string()),
SummaryOption::new("DNS:", self.network.dns_server.to_string()),
]
--- /dev/null
+use std::{
+ fmt,
+ net::{AddrParseError, IpAddr},
+ num::ParseIntError,
+ str::FromStr,
+};
+
+#[derive(Debug)]
+pub enum CidrAddressParseError {
+ NoDelimiter,
+ InvalidAddr(AddrParseError),
+ InvalidMask(Option<ParseIntError>),
+}
+
+#[derive(Clone, Debug)]
+pub struct CidrAddress {
+ addr: IpAddr,
+ mask: usize,
+}
+
+impl CidrAddress {
+ pub fn new(addr: IpAddr, mask: usize) -> Result<Self, CidrAddressParseError> {
+ if mask > 32 {
+ Err(CidrAddressParseError::InvalidMask(None))
+ } else {
+ Ok(Self { addr, mask })
+ }
+ }
+
+ pub fn addr(&self) -> IpAddr {
+ self.addr
+ }
+
+ pub fn mask(&self) -> usize {
+ self.mask
+ }
+}
+
+impl FromStr for CidrAddress {
+ type Err = CidrAddressParseError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let (addr, mask) = s
+ .split_once('/')
+ .ok_or(CidrAddressParseError::NoDelimiter)?;
+
+ let mask = mask
+ .parse()
+ .map_err(|err| CidrAddressParseError::InvalidMask(Some(err)))?;
+
+ if mask > 32 {
+ Err(CidrAddressParseError::InvalidMask(None))
+ } else {
+ Ok(Self {
+ addr: addr.parse().map_err(CidrAddressParseError::InvalidAddr)?,
+ mask,
+ })
+ }
+ }
+}
+
+impl fmt::Display for CidrAddress {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}/{}", self.addr, self.mask)
+ }
+}
views::{DummyView, EditView, LinearLayout, ResizedView, SelectView, TextView},
View,
};
-use std::{marker::PhantomData, net::IpAddr, rc::Rc, str::FromStr};
+use std::{marker::PhantomData, rc::Rc, str::FromStr};
+
+use crate::utils::CidrAddress;
pub use self::table_view::*;
}
}
-impl FormInputViewGetValue<(IpAddr, usize)> for FormInputView<CidrAddressEditView> {
- fn get_value(&self) -> Option<(IpAddr, usize)> {
+impl FormInputViewGetValue<CidrAddress> for FormInputView<CidrAddressEditView> {
+ fn get_value(&self) -> Option<CidrAddress> {
self.inner_input().and_then(|v| v.get_values())
}
}
Self { view }
}
- pub fn content(mut self, addr: IpAddr, mask: usize) -> Self {
+ pub fn content(mut self, cidr: CidrAddress) -> Self {
if let Some(view) = self
.view
.get_child_mut(0)
.and_then(|v| v.downcast_mut::<ResizedView<EditView>>())
{
- *view = EditView::new().content(addr.to_string()).full_width();
+ *view = EditView::new()
+ .content(cidr.addr().to_string())
+ .full_width();
}
if let Some(view) = self
.get_child_mut(2)
.and_then(|v| v.downcast_mut::<ResizedView<IntegerEditView>>())
{
- *view = Self::mask_edit_view(mask);
+ *view = Self::mask_edit_view(cidr.mask());
}
self
.fixed_width(3)
}
- fn get_values(&self) -> Option<(IpAddr, usize)> {
- let ip_addr = self
+ fn get_values(&self) -> Option<CidrAddress> {
+ let addr = self
.view
.get_child(0)?
.downcast_ref::<ResizedView<EditView>>()?
.get_content()
.ok()?;
- Some((ip_addr, mask))
+ CidrAddress::new(addr, mask).ok()
}
}