Compare commits
No commits in common. "a25092c7a015e984e9a458b6cf6ad29436c3866e" and "3232f2b1dad3983729c670ccbbcf82fd3b74babf" have entirely different histories.
a25092c7a0
...
3232f2b1da
@ -1,3 +1,3 @@
|
|||||||
pub mod passwd;
|
pub mod passwd;
|
||||||
pub mod userlib_error;
|
|
||||||
pub use passwd::{Password, Username};
|
pub use passwd::{Password, Username};
|
||||||
|
147
src/passwd.rs
147
src/passwd.rs
@ -1,11 +1,10 @@
|
|||||||
#![warn(
|
/*#![warn(
|
||||||
clippy::all,
|
clippy::all,
|
||||||
/* clippy::restriction,*/
|
clippy::restriction,
|
||||||
clippy::pedantic,
|
clippy::pedantic,
|
||||||
clippy::nursery,
|
clippy::nursery,
|
||||||
clippy::cargo
|
clippy::cargo
|
||||||
)]
|
)]*/
|
||||||
use crate::userlib_error::UserLibError;
|
|
||||||
use std::cmp::Eq;
|
use std::cmp::Eq;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
@ -73,105 +72,25 @@ pub struct Passwd<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Passwd<'a> {
|
impl<'a> Passwd<'a> {
|
||||||
/// Parse a line formatted like one in `/etc/passwd` and construct a matching `Passwd` instance
|
pub fn new_from_string(line: &'a str) -> Result<Self, &str> {
|
||||||
///
|
let elements: Vec<&str> = line.split(":").collect();
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// let pwd = adduser::passwd::Passwd::new_from_string(
|
|
||||||
/// "testuser:testpassword:1001:1001:full Name,,,,:/home/test:/bin/test"
|
|
||||||
/// ).unwrap();
|
|
||||||
/// assert_eq!(pwd.get_username(), "testuser");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// When parsing fails this function returns a `UserLibError::Message` containing some information as to why the function failed.
|
|
||||||
pub fn new_from_string(line: &'a str) -> Result<Self, UserLibError> {
|
|
||||||
let elements: Vec<&str> = line.split(':').collect();
|
|
||||||
if elements.len() == 7 {
|
if elements.len() == 7 {
|
||||||
Ok(Passwd {
|
Ok(Passwd {
|
||||||
username: Username::try_from(*elements.get(0).unwrap())?,
|
username: Username::try_from(*elements.get(0).unwrap())
|
||||||
password: Password::try_from(*elements.get(1).unwrap())?,
|
.expect("failed to parse username."),
|
||||||
uid: Uid::try_from(*elements.get(2).unwrap())?,
|
password: Password::try_from(*elements.get(1).unwrap())
|
||||||
gid: Gid::try_from(*elements.get(3).unwrap())?,
|
.expect("Failed to parse Password"),
|
||||||
gecos: Gecos::try_from(*elements.get(4).unwrap())?,
|
uid: Uid::try_from(*elements.get(2).unwrap()).expect("Failed to parse uid"),
|
||||||
home_dir: HomeDir::try_from(*elements.get(5).unwrap())?,
|
gid: Gid::try_from(*elements.get(3).unwrap()).expect("Failed to parse gid"),
|
||||||
shell_dir: ShellDir::try_from(*elements.get(6).unwrap())?,
|
gecos: Gecos::try_from(*elements.get(4).unwrap())
|
||||||
|
.expect("Failed to parse Gecos field"),
|
||||||
|
home_dir: HomeDir::try_from(*elements.get(5).unwrap())
|
||||||
|
.expect("Failed to parse home directory"),
|
||||||
|
shell_dir: ShellDir::try_from(*elements.get(6).unwrap())
|
||||||
|
.expect("Failed to parse shell directory"),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err("Failed to parse: not enough elements".into())
|
Err("Failed to parse: not enough elements")
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_username(&self) -> &'a str {
|
|
||||||
self.username.username
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_password(&self) -> &'a str {
|
|
||||||
self.password.password
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_uid(&self) -> u32 {
|
|
||||||
self.uid.uid
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_gid(&self) -> u32 {
|
|
||||||
self.gid.gid
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_comment(&self) -> &Gecos {
|
|
||||||
&self.gecos
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_home_dir(&self) -> &'a str {
|
|
||||||
self.home_dir.dir
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_shell_dir(&self) -> &'a str {
|
|
||||||
self.shell_dir.shell
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, .. } => Some(full_name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_room(&'a self) -> Option<&'a str> {
|
|
||||||
match *self {
|
|
||||||
Gecos::Simple { .. } => None,
|
|
||||||
Gecos::Detail { room, .. } => Some(room),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_phone_work(&'a self) -> Option<&'a str> {
|
|
||||||
match *self {
|
|
||||||
Gecos::Simple { .. } => None,
|
|
||||||
Gecos::Detail { phone_work, .. } => 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, .. } => Some(phone_home),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub const fn get_other(&'a self) -> Option<&'a str> {
|
|
||||||
match *self {
|
|
||||||
Gecos::Simple { .. } => None,
|
|
||||||
Gecos::Detail { other, .. } => Some(other),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,7 +140,7 @@ impl Display for Username<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for Username<'a> {
|
impl<'a> TryFrom<&'a str> for Username<'a> {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
||||||
Ok(Self { username: source })
|
Ok(Self { username: source })
|
||||||
}
|
}
|
||||||
@ -234,7 +153,7 @@ impl Display for Password<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for Password<'a> {
|
impl<'a> TryFrom<&'a str> for Password<'a> {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
||||||
Ok(Self { password: source })
|
Ok(Self { password: source })
|
||||||
}
|
}
|
||||||
@ -247,7 +166,7 @@ impl Display for Uid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for Uid {
|
impl TryFrom<&str> for Uid {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &str) -> std::result::Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
uid: source.parse::<u32>().unwrap(),
|
uid: source.parse::<u32>().unwrap(),
|
||||||
@ -262,7 +181,7 @@ impl Display for Gid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for Gid {
|
impl TryFrom<&str> for Gid {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &str) -> std::result::Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
gid: source.parse::<u32>().unwrap(),
|
gid: source.parse::<u32>().unwrap(),
|
||||||
@ -290,7 +209,7 @@ impl Display for Gecos<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for Gecos<'a> {
|
impl<'a> TryFrom<&'a str> for Gecos<'a> {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
||||||
let vals: Vec<&str> = source.split(',').collect();
|
let vals: Vec<&str> = source.split(',').collect();
|
||||||
if vals.len() == 5 {
|
if vals.len() == 5 {
|
||||||
@ -309,7 +228,8 @@ impl<'a> TryFrom<&'a str> for Gecos<'a> {
|
|||||||
phone_home: vals.get(3).unwrap(),
|
phone_home: vals.get(3).unwrap(),
|
||||||
other: "",
|
other: "",
|
||||||
})
|
})
|
||||||
} else if vals.len() == 1 {
|
}
|
||||||
|
else if vals.len() == 1 {
|
||||||
Ok(Gecos::Simple {
|
Ok(Gecos::Simple {
|
||||||
comment: vals.get(0).unwrap(),
|
comment: vals.get(0).unwrap(),
|
||||||
})
|
})
|
||||||
@ -326,7 +246,7 @@ impl Display for HomeDir<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for HomeDir<'a> {
|
impl<'a> TryFrom<&'a str> for HomeDir<'a> {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
||||||
Ok(Self { dir: source })
|
Ok(Self { dir: source })
|
||||||
}
|
}
|
||||||
@ -339,7 +259,7 @@ impl Display for ShellDir<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for ShellDir<'a> {
|
impl<'a> TryFrom<&'a str> for ShellDir<'a> {
|
||||||
type Error = UserLibError;
|
type Error = &'static str;
|
||||||
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
fn try_from(source: &'a str) -> std::result::Result<Self, Self::Error> {
|
||||||
Ok(Self { shell: source })
|
Ok(Self { shell: source })
|
||||||
}
|
}
|
||||||
@ -359,11 +279,11 @@ fn test_default_user() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_gecos() {
|
fn test_parse_gecos() {
|
||||||
// test if the Gecos field can be parsed and the resulting struct is populated correctly.
|
// test if the Gecos field can be parsed and the resulting struct is populated correctly.
|
||||||
let gcdetail = "Full Name,504,11345342,ä1-2312,myemail@test.com";
|
let gcd = "Full Name,504,11345342,ä1-2312,myemail@test.com";
|
||||||
let gcsimple = "A böring comment →";
|
let gcs = "A böring comment →";
|
||||||
let gc_no_other: &str = "systemd Network Management,,,";
|
let gc_no_other: &str = "systemd Network Management,,,";
|
||||||
let res_detail = Gecos::try_from(gcdetail).unwrap();
|
let res_detail = Gecos::try_from(gcd).unwrap();
|
||||||
let res_simple = Gecos::try_from(gcsimple).unwrap();
|
let res_simple = Gecos::try_from(gcs).unwrap();
|
||||||
let res_no_other = Gecos::try_from(gc_no_other).unwrap();
|
let res_no_other = Gecos::try_from(gc_no_other).unwrap();
|
||||||
match res_simple {
|
match res_simple {
|
||||||
Gecos::Simple { comment } => assert_eq!(comment, "A böring comment →"),
|
Gecos::Simple { comment } => assert_eq!(comment, "A böring comment →"),
|
||||||
@ -406,11 +326,6 @@ fn test_parse_gecos() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_new_from_string() {
|
fn test_new_from_string() {
|
||||||
// Test if a single line can be parsed and if the resulting struct is populated correctly.
|
// Test if a single line can be parsed and if the resulting struct is populated correctly.
|
||||||
let fail = Passwd::new_from_string("").err().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
fail,
|
|
||||||
UserLibError::Message("Failed to parse: not enough elements".into())
|
|
||||||
);
|
|
||||||
let pwd =
|
let pwd =
|
||||||
Passwd::new_from_string("testuser:testpassword:1001:1001:testcomment:/home/test:/bin/test")
|
Passwd::new_from_string("testuser:testpassword:1001:1001:testcomment:/home/test:/bin/test")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::fmt::{self, Display};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ParseError {
|
|
||||||
Username,
|
|
||||||
Password,
|
|
||||||
Uid,
|
|
||||||
Gid,
|
|
||||||
Gecos,
|
|
||||||
HomeDir,
|
|
||||||
ShellDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum UserLibError {
|
|
||||||
NotFound,
|
|
||||||
ParseError,
|
|
||||||
Message(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for UserLibError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::NotFound => write!(f, ""),
|
|
||||||
Self::ParseError => write!(f, "Failed to parse"), // TODO details
|
|
||||||
Self::Message(message) => write!(f, "{}", message),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for UserLibError {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::NotFound => "not found",
|
|
||||||
Self::ParseError => "failed to parse",
|
|
||||||
Self::Message(message) => message,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for UserLibError {
|
|
||||||
fn from(err: &str) -> Self {
|
|
||||||
Self::Message(err.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user