restructure

This commit is contained in:
2020-10-01 09:38:13 +02:00
parent 1ab0adbc06
commit 063fb41708
5 changed files with 305 additions and 159 deletions
+9 -1
View File
@@ -5,11 +5,19 @@ use std::fs::File;
use std::io::{prelude::*, BufReader};
fn main() {
simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new(
simplelog::LevelFilter::Warn,
simplelog::Config::default(),
simplelog::TerminalMode::Mixed,
)])
.unwrap();
let file = File::open("/etc/passwd").unwrap();
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", Passwd::new_from_string(&line.unwrap()).unwrap());
let line = line.unwrap();
println!("{}", line);
println!("{}", Passwd::new_from_string(&line).unwrap());
}
// let pwd = Passwd::default();
+1
View File
@@ -1,5 +1,6 @@
#[macro_use]
extern crate lazy_static;
extern crate log;
pub mod passwd;
pub mod userlib_error;
+164 -158
View File
@@ -7,6 +7,7 @@
)]
#![allow(clippy::non_ascii_literal)]
use log::warn;
use regex::Regex;
use crate::userlib_error::UserLibError;
@@ -25,11 +26,50 @@ pub struct Username<'a> {
username: &'a str,
}
impl Display for Username<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.username,)
}
}
impl<'a> TryFrom<&'a str> for Username<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
lazy_static! {
static ref USERVALIDATION: Regex =
Regex::new("^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\\$)$").unwrap();
}
if USERVALIDATION.is_match(source) {
Ok(Self { username: source })
} else {
Err(UserLibError::Message("Invalid username".into()))
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Password<'a> {
password: &'a str,
}
impl Display for Password<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.password,)
}
}
impl<'a> TryFrom<&'a str> for Password<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
if source == "x" {
warn!("password from shadow not loaded!")
} else {
warn!("Password field has an unexpected value")
};
Ok(Self { password: source })
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Uid {
uid: u32,
@@ -39,6 +79,7 @@ pub struct Uid {
pub struct Gid {
gid: u32,
}
/// The gecos field of a user.
///
/// In the `/etc/passwd` file this field is a `,` sepparated list of items.
@@ -59,6 +100,129 @@ pub enum Gecos<'a> {
},
}
impl<'a> Gecos<'a> {
#[must_use]
pub const fn get_comment(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { comment, .. } => Some(comment),
Gecos::Detail { .. } => None,
}
}
#[must_use]
pub const fn get_full_name(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { full_name, .. } => {
if full_name.is_empty() {
None
} else {
Some(full_name)
}
}
}
}
#[must_use]
pub const fn get_room(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { room, .. } => {
if room.is_empty() {
None
} else {
Some(room)
}
}
}
}
#[must_use]
pub const fn get_phone_work(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_work, .. } => {
if phone_work.is_empty() {
None
} else {
Some(phone_work)
}
}
}
}
#[must_use]
pub const fn get_phone_home(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_home, .. } => {
if phone_home.is_empty() {
None
} else {
Some(phone_home)
}
}
}
}
#[must_use]
pub const fn get_other(&'a self) -> Option<&Vec<&'a str>> {
match self {
Gecos::Simple { .. } => None,
Gecos::Detail { other, .. } => match other {
None => None,
Some(comments) => Some(comments),
},
}
}
}
impl Display for Gecos<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Gecos::Simple { comment } => write!(f, "{}", comment),
Gecos::Detail {
full_name,
room,
phone_work,
phone_home,
other,
} => write!(
f,
"{},{},{},{}{}",
full_name,
room,
phone_work,
phone_home,
match other {
None => "".to_string(),
Some(cont) => format!(",{}", cont.join(",")),
}
),
}
}
}
impl<'a> TryFrom<&'a str> for Gecos<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
let vals: Vec<&str> = source.split(',').collect();
if vals.len() > 3 {
Ok(Gecos::Detail {
full_name: vals[0],
room: vals[1],
phone_work: vals[2],
phone_home: vals[3],
other: if vals.len() == 4 {
None
} else {
Some(vals[4..].to_vec())
},
})
} else if vals.len() == 1 {
Ok(Gecos::Simple {
comment: vals.get(0).unwrap(),
})
} else {
panic!(format!("Could not parse this string: {}", source))
}
}
}
/// The home directory of a user
#[derive(Debug, PartialEq, Eq)]
pub struct HomeDir<'a> {
@@ -142,78 +306,6 @@ impl<'a> Passwd<'a> {
}
}
impl<'a> Gecos<'a> {
#[must_use]
pub const fn get_comment(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { comment, .. } => Some(comment),
Gecos::Detail { .. } => None,
}
}
#[must_use]
pub const fn get_full_name(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { full_name, .. } => {
if full_name.is_empty() {
None
} else {
Some(full_name)
}
}
}
}
#[must_use]
pub const fn get_room(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { room, .. } => {
if room.is_empty() {
None
} else {
Some(room)
}
}
}
}
#[must_use]
pub const fn get_phone_work(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_work, .. } => {
if phone_work.is_empty() {
None
} else {
Some(phone_work)
}
}
}
}
#[must_use]
pub const fn get_phone_home(&'a self) -> Option<&'a str> {
match *self {
Gecos::Simple { .. } => None,
Gecos::Detail { phone_home, .. } => {
if phone_home.is_empty() {
None
} else {
Some(phone_home)
}
}
}
}
#[must_use]
pub const fn get_other(&'a self) -> Option<&Vec<&'a str>> {
match self {
Gecos::Simple { .. } => None,
Gecos::Detail { other, .. } => match other {
None => None,
Some(comments) => Some(comments),
},
}
}
}
impl Default for Passwd<'_> {
fn default() -> Self {
Passwd {
@@ -252,40 +344,6 @@ impl Display for Passwd<'_> {
}
}
impl Display for Username<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.username,)
}
}
impl<'a> TryFrom<&'a str> for Username<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
lazy_static! {
static ref USERVALIDATION: Regex =
Regex::new("^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\\$)$").unwrap();
}
if USERVALIDATION.is_match(source) {
Ok(Self { username: source })
} else {
Err(UserLibError::Message("Invalid username".into()))
}
}
}
impl Display for Password<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.password,)
}
}
impl<'a> TryFrom<&'a str> for Password<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
Ok(Self { password: source })
}
}
impl Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.uid,)
@@ -316,58 +374,6 @@ impl TryFrom<&str> for Gid {
}
}
impl Display for Gecos<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Gecos::Simple { comment } => write!(f, "{}", comment),
Gecos::Detail {
full_name,
room,
phone_work,
phone_home,
other,
} => write!(
f,
"{},{},{},{}{}",
full_name,
room,
phone_work,
phone_home,
match other {
None => "".to_string(),
Some(cont) => format!(",{}", cont.join(",")),
}
),
}
}
}
impl<'a> TryFrom<&'a str> for Gecos<'a> {
type Error = UserLibError;
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
let vals: Vec<&str> = source.split(',').collect();
if vals.len() > 3 {
Ok(Gecos::Detail {
full_name: vals[0],
room: vals[1],
phone_work: vals[2],
phone_home: vals[3],
other: if vals.len() == 4 {
None
} else {
Some(vals[4..].to_vec())
},
})
} else if vals.len() == 1 {
Ok(Gecos::Simple {
comment: vals.get(0).unwrap(),
})
} else {
panic!(format!("Could not parse this string: {}", source))
}
}
}
impl Display for HomeDir<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.dir,)