linting changes

This commit is contained in:
Dietrich 2020-11-09 09:56:24 +01:00
parent 647881f022
commit 467c8ee007
15 changed files with 142 additions and 143 deletions

View File

@ -1,3 +1,4 @@
#![allow(clippy::default_trait_access)]
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -12,7 +13,6 @@ pub enum DeletePrimaryGroup {
Keep, Keep,
DeleteIfEmpty, DeleteIfEmpty,
} }
#[derive(Debug, Builder, Eq, PartialEq)] #[derive(Debug, Builder, Eq, PartialEq)]
#[builder(public)] #[builder(public)]
#[builder(default)] #[builder(default)]
@ -23,6 +23,7 @@ pub struct NewUserArgs<'a> {
} }
impl<'a> NewUserArgs<'a> { impl<'a> NewUserArgs<'a> {
#[must_use]
pub fn builder() -> NewUserArgsBuilder<'a> { pub fn builder() -> NewUserArgsBuilder<'a> {
NewUserArgsBuilder::default() NewUserArgsBuilder::default()
} }

View File

@ -2,6 +2,9 @@ use std::path::PathBuf;
extern crate adduser; extern crate adduser;
use adduser::api::UserDBWrite;
use adduser::api::UserRead;
extern crate env_logger; extern crate env_logger;
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
@ -9,9 +12,6 @@ use log::{debug, error, info, trace, warn};
fn main() { fn main() {
env_logger::init(); env_logger::init();
use adduser::api::UserDBWrite;
use adduser::api::UserRead;
let mf = adduser::Files { let mf = adduser::Files {
passwd: Some(PathBuf::from("./passwd")), passwd: Some(PathBuf::from("./passwd")),
shadow: Some(PathBuf::from("./shadow")), shadow: Some(PathBuf::from("./shadow")),

View File

@ -1,4 +1,5 @@
extern crate adduser; extern crate adduser;
use adduser::api::UserDBRead;
fn main() { fn main() {
simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new( simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new(
@ -7,7 +8,6 @@ fn main() {
simplelog::TerminalMode::Mixed, simplelog::TerminalMode::Mixed,
)]) )])
.unwrap(); .unwrap();
use adduser::api::UserDBRead;
let db = adduser::UserDBLocal::load_files(adduser::Files::default()).unwrap(); let db = adduser::UserDBLocal::load_files(adduser::Files::default()).unwrap();

View File

@ -1,6 +1,7 @@
use std::error::Error; use std::error::Error;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ParseError { pub enum ParseError {
Username, Username,
@ -12,6 +13,7 @@ pub enum ParseError {
ShellDir, ShellDir,
} }
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum UserLibError { pub enum UserLibError {
NotFound, NotFound,
@ -36,8 +38,8 @@ impl PartialEq for MyMessage {
impl Display for MyMessage { impl Display for MyMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
MyMessage::Simple(m) => write!(f, "{}", m), Self::Simple(m) => write!(f, "{}", m),
MyMessage::IOError(m, e) => write!(f, "{},{}", m, e), Self::IOError(m, e) => write!(f, "{},{}", m, e),
} }
} }
} }
@ -47,7 +49,7 @@ impl Display for UserLibError {
match self { match self {
Self::NotFound => write!(f, "not found"), Self::NotFound => write!(f, "not found"),
Self::ParseError => write!(f, "failed to parse"), Self::ParseError => write!(f, "failed to parse"),
UserLibError::FilesRequired => write!( Self::FilesRequired => write!(
f, f,
"File locking is only possible if some files are specified" "File locking is only possible if some files are specified"
), ),
@ -63,12 +65,12 @@ impl Display for UserLibError {
impl Error for UserLibError { impl Error for UserLibError {
fn source(&self) -> Option<&(dyn Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self { match *self {
UserLibError::NotFound Self::NotFound
| UserLibError::ParseError | Self::ParseError
| UserLibError::FilesChanged | Self::FilesChanged
| UserLibError::FilesRequired => None, | Self::FilesRequired
UserLibError::Message(MyMessage::IOError(_, ref e)) => Some(e), | Self::Message(MyMessage::Simple(_)) => None,
UserLibError::Message(MyMessage::Simple(_)) => None, Self::Message(MyMessage::IOError(_, ref e)) => Some(e),
} }
} }
} }
@ -87,13 +89,13 @@ impl From<String> for UserLibError {
impl From<std::io::Error> for UserLibError { impl From<std::io::Error> for UserLibError {
fn from(e: std::io::Error) -> Self { fn from(e: std::io::Error) -> Self {
UserLibError::Message(MyMessage::Simple(e.to_string())) Self::Message(MyMessage::Simple(e.to_string()))
} }
} }
impl From<(String, std::io::Error)> for UserLibError { impl From<(String, std::io::Error)> for UserLibError {
fn from((m, e): (String, std::io::Error)) -> Self { fn from((m, e): (String, std::io::Error)) -> Self {
UserLibError::Message(MyMessage::IOError(m, e)) Self::Message(MyMessage::IOError(m, e))
} }
} }
/* /*

View File

@ -1,10 +1,3 @@
#![warn(
clippy::all,
/* clippy::restriction,*/
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::non_ascii_literal)] #![allow(clippy::non_ascii_literal)]
use crate::userlib::NewFromString; use crate::userlib::NewFromString;
@ -57,10 +50,11 @@ pub struct Group {
} }
impl Group { impl Group {
#[must_use]
pub fn remove_in(&self, content: &str) -> String { pub fn remove_in(&self, content: &str) -> String {
content content
.split(&self.source) .split(&self.source)
.map(|x| x.trim()) .map(str::trim)
.collect::<Vec<&str>>() .collect::<Vec<&str>>()
.join("\n") .join("\n")
} }
@ -75,7 +69,7 @@ impl GroupRead for Group {
#[must_use] #[must_use]
fn get_member_names(&self) -> Option<Vec<&str>> { fn get_member_names(&self) -> Option<Vec<&str>> {
let mut r: Vec<&str> = Vec::new(); let mut r: Vec<&str> = Vec::new();
for u in self.members.iter() { for u in &self.members {
r.push(&u.username); r.push(&u.username);
} }
Some(r) Some(r)
@ -128,7 +122,7 @@ impl NewFromString for Group {
if elements.len() == 4 { if elements.len() == 4 {
Ok(Self { Ok(Self {
pos: position, pos: position,
source: line.clone(), source: line,
groupname: Groupname::try_from(elements.get(0).unwrap().to_string())?, groupname: Groupname::try_from(elements.get(0).unwrap().to_string())?,
password: crate::Password::Disabled, password: crate::Password::Disabled,
gid: crate::Gid::try_from(elements.get(2).unwrap().to_string())?, gid: crate::Gid::try_from(elements.get(2).unwrap().to_string())?,

View File

@ -1,3 +1,12 @@
#![warn(
clippy::all,
//clippy::restriction,
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
//#![allow(clippy::non_ascii_literal)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
#[macro_use] #[macro_use]

View File

@ -1,3 +1,4 @@
#![allow(clippy::non_ascii_literal)]
use crate::UserLibError; use crate::UserLibError;
use std::cmp::Eq; use std::cmp::Eq;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -26,19 +27,19 @@ impl Gecos {
#[must_use] #[must_use]
pub fn get_comment(&self) -> Option<&str> { pub fn get_comment(&self) -> Option<&str> {
match &self { match &self {
Gecos::Simple { comment, .. } => Some(&comment), Self::Simple { comment, .. } => Some(comment),
Gecos::Detail { .. } => None, Self::Detail { .. } => None,
} }
} }
#[must_use] #[must_use]
pub fn get_full_name(&self) -> Option<&str> { pub fn get_full_name(&self) -> Option<&str> {
match &self { match &self {
Gecos::Simple { .. } => None, Self::Simple { .. } => None,
Gecos::Detail { full_name, .. } => { Self::Detail { full_name, .. } => {
if full_name.is_empty() { if full_name.is_empty() {
None None
} else { } else {
Some(&full_name) Some(full_name)
} }
} }
} }
@ -46,12 +47,12 @@ impl Gecos {
#[must_use] #[must_use]
pub fn get_room(&self) -> Option<&str> { pub fn get_room(&self) -> Option<&str> {
match &self { match &self {
Gecos::Simple { .. } => None, Self::Simple { .. } => None,
Gecos::Detail { room, .. } => { Self::Detail { room, .. } => {
if room.is_empty() { if room.is_empty() {
None None
} else { } else {
Some(&room) Some(room)
} }
} }
} }
@ -59,12 +60,12 @@ impl Gecos {
#[must_use] #[must_use]
pub fn get_phone_work(&self) -> Option<&str> { pub fn get_phone_work(&self) -> Option<&str> {
match &self { match &self {
Gecos::Simple { .. } => None, Self::Simple { .. } => None,
Gecos::Detail { phone_work, .. } => { Self::Detail { phone_work, .. } => {
if phone_work.is_empty() { if phone_work.is_empty() {
None None
} else { } else {
Some(&phone_work) Some(phone_work)
} }
} }
} }
@ -72,12 +73,12 @@ impl Gecos {
#[must_use] #[must_use]
pub fn get_phone_home(&self) -> Option<&str> { pub fn get_phone_home(&self) -> Option<&str> {
match &self { match &self {
Gecos::Simple { .. } => None, Self::Simple { .. } => None,
Gecos::Detail { phone_home, .. } => { Self::Detail { phone_home, .. } => {
if phone_home.is_empty() { if phone_home.is_empty() {
None None
} else { } else {
Some(&phone_home) Some(phone_home)
} }
} }
} }
@ -85,8 +86,8 @@ impl Gecos {
#[must_use] #[must_use]
pub const fn get_other(&self) -> Option<&Vec<String>> { pub const fn get_other(&self) -> Option<&Vec<String>> {
match self { match self {
Gecos::Simple { .. } => None, Self::Simple { .. } => None,
Gecos::Detail { other, .. } => match other { Self::Detail { other, .. } => match other {
None => None, None => None,
Some(comments) => Some(comments), Some(comments) => Some(comments),
}, },
@ -97,8 +98,8 @@ impl Gecos {
impl Display for Gecos { impl Display for Gecos {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self { match &self {
Gecos::Simple { comment } => write!(f, "{}", comment), Self::Simple { comment } => write!(f, "{}", comment),
Gecos::Detail { Self::Detail {
full_name, full_name,
room, room,
phone_work, phone_work,
@ -125,7 +126,7 @@ impl TryFrom<String> for Gecos {
fn try_from(source: String) -> std::result::Result<Self, Self::Error> { fn try_from(source: String) -> std::result::Result<Self, Self::Error> {
let vals: Vec<String> = source.split(',').map(ToString::to_string).collect(); let vals: Vec<String> = source.split(',').map(ToString::to_string).collect();
if vals.len() > 3 { if vals.len() > 3 {
Ok(Gecos::Detail { Ok(Self::Detail {
full_name: vals[0].clone(), full_name: vals[0].clone(),
room: vals[1].clone(), room: vals[1].clone(),
phone_work: vals[2].clone(), phone_work: vals[2].clone(),
@ -137,7 +138,7 @@ impl TryFrom<String> for Gecos {
}, },
}) })
} else if vals.len() == 1 { } else if vals.len() == 1 {
Ok(Gecos::Simple { Ok(Self::Simple {
comment: vals.get(0).unwrap().into(), comment: vals.get(0).unwrap().into(),
}) })
} else { } else {
@ -145,7 +146,6 @@ impl TryFrom<String> for Gecos {
} }
} }
} }
#[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.

View File

@ -24,11 +24,11 @@ pub struct User {
} }
impl User { impl User {
pub fn get_shadow(&self) -> Option<&crate::Shadow> { #[must_use]
pub const fn get_shadow(&self) -> Option<&crate::Shadow> {
match self.password { match self.password {
crate::Password::Encrypted(_) => None, crate::Password::Encrypted(_) | crate::Password::Disabled => None,
crate::Password::Shadow(ref s) => Some(s), crate::Password::Shadow(ref s) => Some(s),
crate::Password::Disabled => None,
} }
} }
/*fn get_nth_line(content: &str, n: u32) -> (String, u64) { /*fn get_nth_line(content: &str, n: u32) -> (String, u64) {
@ -49,17 +49,16 @@ impl User {
trace!("Olduser:\n\t{}\nNewuser\n\t{}", &self.source, &line); trace!("Olduser:\n\t{}\nNewuser\n\t{}", &self.source, &line);
(offset, line.len(), line == self.source) (offset, line.len(), line == self.source)
}*/ }*/
#[must_use]
pub fn remove_in(&self, content: &str) -> String { pub fn remove_in(&self, content: &str) -> String {
content content
.split(&self.source) .split(&self.source)
.map(|x| x.trim()) .map(str::trim)
.collect::<Vec<&str>>() .collect::<Vec<&str>>()
.join("\n") .join("\n")
} }
pub fn username(&mut self, name: String) -> &mut Self { pub fn username(&mut self, name: String) -> &mut Self {
self.username = crate::Username { self.username = crate::Username { username: name };
username: name.into(),
};
self self
} }
pub fn disable_password(&mut self) -> &mut Self { pub fn disable_password(&mut self) -> &mut Self {
@ -131,7 +130,7 @@ impl crate::api::UserRead for User {
#[must_use] #[must_use]
fn get_password(&self) -> Option<&str> { fn get_password(&self) -> Option<&str> {
match &self.password { match &self.password {
crate::Password::Encrypted(crate::EncryptedPassword { password }) => Some(&password), crate::Password::Encrypted(crate::EncryptedPassword { password }) => Some(password),
crate::Password::Shadow(crate::Shadow { ref password, .. }) => Some(&password.password), crate::Password::Shadow(crate::Shadow { ref password, .. }) => Some(&password.password),
crate::Password::Disabled => None, crate::Password::Disabled => None,
} }
@ -287,6 +286,7 @@ fn test_new_from_string() {
#[test] #[test]
fn test_parse_passwd() { fn test_parse_passwd() {
// Test wether the passwd file can be parsed and recreated without throwing an exception // Test wether the passwd file can be parsed and recreated without throwing an exception
use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::{prelude::*, BufReader}; use std::io::{prelude::*, BufReader};
let file = File::open("/etc/passwd").unwrap(); let file = File::open("/etc/passwd").unwrap();
@ -295,7 +295,7 @@ fn test_parse_passwd() {
for (n, line) in reader.lines().enumerate() { for (n, line) in reader.lines().enumerate() {
let lineorig: String = line.unwrap(); let lineorig: String = line.unwrap();
let linecopy = lineorig.clone(); let linecopy = lineorig.clone();
let pass_struc = User::new_from_string(linecopy, n as u32).unwrap(); let pass_struc = User::new_from_string(linecopy, n.try_into().unwrap()).unwrap();
assert_eq!( assert_eq!(
// ignoring the numbers of `,` since the implementation does not (yet) reproduce a missing comment field. // ignoring the numbers of `,` since the implementation does not (yet) reproduce a missing comment field.
lineorig, lineorig,

View File

@ -1,10 +1,3 @@
#![warn(
clippy::all,
/* clippy::restriction,*/
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::non_ascii_literal)] #![allow(clippy::non_ascii_literal)]
use log::warn; use log::warn;
@ -65,8 +58,7 @@ impl Display for Password {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Encrypted(EncryptedPassword { password }) => write!(f, "{}", password,), Self::Encrypted(EncryptedPassword { password }) => write!(f, "{}", password,),
Self::Shadow(_) => write!(f, "x"), Self::Shadow(_) | Self::Disabled => write!(f, "x"),
Self::Disabled => write!(f, "x"),
} }
} }
} }
@ -85,11 +77,11 @@ impl Display for EncryptedPassword {
impl TryFrom<String> for EncryptedPassword { impl TryFrom<String> for EncryptedPassword {
type Error = UserLibError; type Error = UserLibError;
fn try_from(source: String) -> std::result::Result<Self, Self::Error> { fn try_from(source: String) -> std::result::Result<Self, Self::Error> {
if source == "x" { /*if source == "x" {
//warn!("password from shadow not loaded!") //warn!("password from shadow not loaded!")
} else { } else {
//warn!("Password field has an unexpected value") //warn!("Password field has an unexpected value")
}; };*/
Ok(Self { password: source }) Ok(Self { password: source })
} }
} }
@ -149,6 +141,7 @@ impl Gid {
self.gid < 1000 self.gid < 1000
} }
#[must_use]
pub const fn get_gid(&self) -> u32 { pub const fn get_gid(&self) -> u32 {
self.gid self.gid
} }

View File

@ -1,14 +1,4 @@
#![warn(
clippy::all,
/* clippy::restriction,*/
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::non_ascii_literal)]
use crate::userlib::NewFromString; use crate::userlib::NewFromString;
use log::warn;
use crate::UserLibError; use crate::UserLibError;
use std::cmp::Eq; use std::cmp::Eq;
@ -40,10 +30,11 @@ impl Shadow {
pub fn get_password(&self) -> &str { pub fn get_password(&self) -> &str {
&self.password.password &self.password.password
} }
#[must_use]
pub fn remove_in(&self, content: &str) -> String { pub fn remove_in(&self, content: &str) -> String {
content content
.split(&self.source) .split(&self.source)
.map(|x| x.trim()) .map(str::trim)
.collect::<Vec<&str>>() .collect::<Vec<&str>>()
.join("\n") .join("\n")
} }
@ -108,7 +99,7 @@ impl NewFromString for Shadow {
let extra = elements.get(8).unwrap(); let extra = elements.get(8).unwrap();
Ok(Self { Ok(Self {
pos: position, pos: position,
source: line.clone(), source: line,
username: crate::Username::try_from(elements.get(0).unwrap().to_string())?, username: crate::Username::try_from(elements.get(0).unwrap().to_string())?,
password: crate::EncryptedPassword::try_from(elements.get(1).unwrap().to_string())?, password: crate::EncryptedPassword::try_from(elements.get(1).unwrap().to_string())?,
last_change: date_since_epoch(elements.get(2).unwrap()), last_change: date_since_epoch(elements.get(2).unwrap()),

View File

@ -26,7 +26,8 @@ impl Default for Files {
impl Files { impl Files {
/// Check if all the files are defined. Because some operations require the files to be present /// Check if all the files are defined. Because some operations require the files to be present
pub fn is_virtual(&self) -> bool { #[must_use]
pub const fn is_virtual(&self) -> bool {
!(self.group.is_some() & self.passwd.is_some() & self.shadow.is_some()) !(self.group.is_some() & self.passwd.is_some() & self.shadow.is_some())
} }
pub fn lock_and_get_passwd(&self) -> Result<LockedFileGuard, crate::UserLibError> { pub fn lock_and_get_passwd(&self) -> Result<LockedFileGuard, crate::UserLibError> {
@ -89,7 +90,7 @@ impl LockedFileGuard {
Ok(_) => (), Ok(_) => (),
Err(e) => return Err(("Could not write (all) users. ".to_owned(), e).into()), Err(e) => return Err(("Could not write (all) users. ".to_owned(), e).into()),
}; };
let _ = self.file.write("\n".as_bytes()); let _ = self.file.write(b"\n");
Ok(()) Ok(())
} }
@ -155,12 +156,19 @@ impl LockedFileGuard {
); );
// write the pid into the tempfile // write the pid into the tempfile
{ {
let mut tempfile = File::create(&*tempfilepath) let mut tempfile = File::create(&*tempfilepath).unwrap_or_else(|e| {
.expect(&format!("Failed to open {}", filename.to_str().unwrap())); panic!("Failed to open {} error: {}", filename.to_str().unwrap(), e)
match write!(tempfile, "{}", pid) { });
Ok(_) => {} write!(tempfile, "{}", pid).or_else(|e| {
Err(_) => error!("could not write to {}", filename.to_string_lossy()), let error_msg = format!(
}; "could not write to {} error {}",
filename.to_string_lossy(),
e
);
error!("{}", error_msg);
let err: crate::UserLibError = error_msg.into();
Err(err)
})?;
} }
// try to make a hardlink from the lockfile to the tempfile // try to make a hardlink from the lockfile to the tempfile
@ -198,12 +206,9 @@ impl LockedFileGuard {
} }
}; };
let mut content = String::new(); let mut content = String::new();
match lf.read_to_string(&mut content) { lf.read_to_string(&mut content)
Ok(_) => {} .unwrap_or_else(|e| panic!("failed to read the lockfile{}", e));
Err(_) => {
panic!("failed to read the lockfile{}", e);
}
}
let content = content.trim().trim_matches(char::from(0)); let content = content.trim().trim_matches(char::from(0));
let lock_pid = content.parse::<u32>(); let lock_pid = content.parse::<u32>();
match lock_pid { match lock_pid {

View File

@ -5,11 +5,13 @@ pub struct SourceHash {
} }
impl SourceHash { impl SourceHash {
#[must_use]
pub fn new(src: &str) -> Self { pub fn new(src: &str) -> Self {
Self { Self {
hashvalue: src.to_owned(), hashvalue: src.to_owned(),
} }
} }
#[must_use]
pub fn has_changed(&self, new: &str) -> bool { pub fn has_changed(&self, new: &str) -> bool {
trace!( trace!(
"Old and new lengths: {}, {}", "Old and new lengths: {}, {}",
@ -27,6 +29,7 @@ pub struct Hashes {
} }
impl Hashes { impl Hashes {
#[must_use]
pub fn new(passwd: &str, shadow: &str, group: &str) -> Self { pub fn new(passwd: &str, shadow: &str, group: &str) -> Self {
Self { Self {
passwd: SourceHash::new(passwd), passwd: SourceHash::new(passwd),

View File

@ -1,10 +1,3 @@
#![warn(
clippy::all,
//clippy::restriction,
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::non_ascii_literal)] #![allow(clippy::non_ascii_literal)]
pub mod files; pub mod files;
@ -33,11 +26,11 @@ impl UserDBLocal {
shadow_content: &str, shadow_content: &str,
group_content: &str, group_content: &str,
) -> Self { ) -> Self {
let shadow_entries: Vec<crate::Shadow> = string_to(&shadow_content); let shadow_entries: Vec<crate::Shadow> = string_to(shadow_content);
let mut users = user_vec_to_hashmap(string_to(&passwd_content)); let mut users = user_vec_to_hashmap(string_to(passwd_content));
let groups = string_to(&group_content); let groups = string_to(group_content);
shadow_to_users(&mut users, shadow_entries); shadow_to_users(&mut users, shadow_entries);
let res = Self { Self {
source_files: files::Files { source_files: files::Files {
passwd: None, passwd: None,
group: None, group: None,
@ -45,13 +38,11 @@ impl UserDBLocal {
}, },
users, users,
groups, groups,
source_hashes: hashes::Hashes::new(&passwd_content, &shadow_content, &group_content), source_hashes: hashes::Hashes::new(passwd_content, shadow_content, group_content),
}; }
res
} }
/// Import the database from a [`Files`] struct /// Import the database from a [`Files`] struct
#[must_use]
pub fn load_files(files: files::Files) -> Result<Self, crate::UserLibError> { pub fn load_files(files: files::Files) -> Result<Self, crate::UserLibError> {
// Get the Strings for the files use an inner block to drop references after read. // Get the Strings for the files use an inner block to drop references after read.
let (my_passwd_lines, my_shadow_lines, my_group_lines) = { let (my_passwd_lines, my_shadow_lines, my_group_lines) = {
@ -77,10 +68,10 @@ impl UserDBLocal {
} }
fn delete_from_passwd( fn delete_from_passwd(
user: &crate::User, user: &crate::User,
passwd_file_content: String, passwd_file_content: &str,
locked_p: &mut files::LockedFileGuard, locked_p: &mut files::LockedFileGuard,
) -> Result<(), UserLibError> { ) -> Result<(), UserLibError> {
let modified_p = user.remove_in(&passwd_file_content); let modified_p = user.remove_in(passwd_file_content);
// write the new content to the file. // write the new content to the file.
let ncont = locked_p.replace_contents(modified_p); let ncont = locked_p.replace_contents(modified_p);
@ -92,13 +83,13 @@ impl UserDBLocal {
fn delete_from_shadow( fn delete_from_shadow(
user: &crate::User, user: &crate::User,
shadow_file_content: String, shadow_file_content: &str,
locked_s: &mut files::LockedFileGuard, locked_s: &mut files::LockedFileGuard,
) -> Result<(), UserLibError> { ) -> Result<(), UserLibError> {
let shad = user.get_shadow(); let shad = user.get_shadow();
match shad { match shad {
Some(shadow) => { Some(shadow) => {
let modified_s = shadow.remove_in(&shadow_file_content); let modified_s = shadow.remove_in(shadow_file_content);
let ncont = locked_s.replace_contents(modified_s); let ncont = locked_s.replace_contents(modified_s);
match ncont { match ncont {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@ -116,10 +107,10 @@ impl UserDBLocal {
fn delete_from_group( fn delete_from_group(
group: &crate::Group, group: &crate::Group,
group_file_content: String, group_file_content: &str,
locked_g: &mut files::LockedFileGuard, locked_g: &mut files::LockedFileGuard,
) -> Result<(), UserLibError> { ) -> Result<(), UserLibError> {
let modified_g = group.remove_in(&group_file_content); let modified_g = group.remove_in(group_file_content);
let replace_result = locked_g.replace_contents(modified_g); let replace_result = locked_g.replace_contents(modified_g);
match replace_result { match replace_result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@ -133,15 +124,15 @@ impl UserDBLocal {
} }
fn delete_home(user: &crate::User) -> std::io::Result<()> { fn delete_home(user: &crate::User) -> std::io::Result<()> {
match user.get_home_dir() { if let Some(dir) = user.get_home_dir() {
Some(dir) => std::fs::remove_dir_all(dir), std::fs::remove_dir_all(dir)
None => { } else {
error!("Failed to remove the home directory! As the user did not have one."); let error_msg = "Failed to remove the home directory! As the user did not have one.";
Err(std::io::Error::new( error!("{}", error_msg);
std::io::ErrorKind::InvalidInput, Err(std::io::Error::new(
"Failed to remove the home directory! As the user did not have one.", std::io::ErrorKind::InvalidInput,
)) error_msg,
} ))
} }
} }
@ -190,8 +181,8 @@ impl UserDBWrite for UserDBLocal {
error!("The source files have changed. Deleting the user could corrupt the userdatabase. Aborting!"); error!("The source files have changed. Deleting the user could corrupt the userdatabase. Aborting!");
Err(format!("The userdatabase has been changed {}", args.username).into()) Err(format!("The userdatabase has been changed {}", args.username).into())
} else { } else {
Self::delete_from_passwd(user, passwd_file_content, &mut locked_p)?; Self::delete_from_passwd(user, &passwd_file_content, &mut locked_p)?;
Self::delete_from_shadow(user, shadow_file_content, &mut locked_s)?; Self::delete_from_shadow(user, &shadow_file_content, &mut locked_s)?;
if args.delete_home == DeleteHome::Delete { if args.delete_home == DeleteHome::Delete {
Self::delete_home(user)?; Self::delete_home(user)?;
} }
@ -206,7 +197,7 @@ impl UserDBWrite for UserDBLocal {
{ {
UserDBLocal::delete_from_group( UserDBLocal::delete_from_group(
group, group,
group_file_content, &group_file_content,
&mut locked_g, &mut locked_g,
)?; )?;
let _gres = self.groups.remove(id); let _gres = self.groups.remove(id);
@ -297,9 +288,9 @@ impl UserDBRead for UserDBLocal {
fn get_user_by_id(&self, uid: u32) -> Option<&crate::User> { fn get_user_by_id(&self, uid: u32) -> Option<&crate::User> {
// could probably be more efficient - on the other hand its no problem to loop a thousand users. // could probably be more efficient - on the other hand its no problem to loop a thousand users.
for (_, user) in self.users.iter() { for user in self.users.values() {
if user.get_uid() == uid { if user.get_uid() == uid {
return Some(&user); return Some(user);
} }
} }
None None
@ -310,7 +301,7 @@ impl UserDBRead for UserDBLocal {
} }
fn get_group_by_name(&self, name: &str) -> Option<&crate::Group> { fn get_group_by_name(&self, name: &str) -> Option<&crate::Group> {
for group in self.groups.iter() { for group in &self.groups {
if group.get_groupname()? == name { if group.get_groupname()? == name {
return Some(group); return Some(group);
} }
@ -319,7 +310,7 @@ impl UserDBRead for UserDBLocal {
} }
fn get_group_by_id(&self, id: u32) -> Option<&crate::Group> { fn get_group_by_id(&self, id: u32) -> Option<&crate::Group> {
for group in self.groups.iter() { for group in &self.groups {
if group.get_gid()? == id { if group.get_gid()? == id {
return Some(group); return Some(group);
} }
@ -376,7 +367,7 @@ fn shadow_to_users(
for pass in shadow { for pass in shadow {
let user = users let user = users
.get_mut(pass.get_username()) .get_mut(pass.get_username())
.expect(&format!("the user {} does not exist", pass.get_username())); .unwrap_or_else(|| panic!("the user {} does not exist", pass.get_username()));
user.password = crate::Password::Shadow(pass); user.password = crate::Password::Shadow(pass);
} }
users users
@ -397,7 +388,7 @@ fn user_vec_to_hashmap(users: Vec<crate::User>) -> HashMap<String, crate::User>
.collect() .collect()
} }
/// Try to parse a String into some Object /// Try to parse a String into some Object.
/// ///
/// # Errors /// # Errors
/// if the parsing failed a [`UserLibError::Message`](crate::userlib_error::UserLibError::Message) is returned containing a more detailed error message. /// if the parsing failed a [`UserLibError::Message`](crate::userlib_error::UserLibError::Message) is returned containing a more detailed error message.
@ -412,12 +403,20 @@ fn string_to<T>(source: &str) -> Vec<T>
where where
T: NewFromString, T: NewFromString,
{ {
use std::convert::TryInto;
source source
.lines() .lines()
.enumerate() .enumerate()
.filter_map(|(n, line)| { .filter_map(|(n, line)| {
if line.len() > 5 { if line.len() > 5 {
Some(T::new_from_string(line.to_owned(), n as u32).expect("failed to read lines")) Some(
T::new_from_string(
line.to_owned(),
n.try_into()
.unwrap_or_else(|e| panic!("Failed to convert usize to u32 {}", e)),
)
.expect("failed to read lines"),
)
} else { } else {
None None
} }
@ -484,10 +483,10 @@ fn test_user_db_write_implementation() {
assert_eq!(data.get_all_users().len(), 1); assert_eq!(data.get_all_users().len(), 1);
assert!(data assert!(data
.delete_user(NewUserArgs::builder().username(&user).build().unwrap()) .delete_user(NewUserArgs::builder().username(user).build().unwrap())
.is_ok()); .is_ok());
assert!(data assert!(data
.delete_user(NewUserArgs::builder().username(&user).build().unwrap()) .delete_user(NewUserArgs::builder().username(user).build().unwrap())
.is_err()); .is_err());
assert_eq!(data.get_all_users().len(), 0); assert_eq!(data.get_all_users().len(), 0);
} }

View File

@ -17,8 +17,8 @@ fn test_test() {
let mf = adduser::Files { let mf = adduser::Files {
passwd: Some(p.path.clone()), passwd: Some(p.path.clone()),
shadow: Some(s.path.clone()), shadow: Some(s.path),
group: Some(g.path.clone()), group: Some(g.path),
}; };
let mut db = adduser::UserDBLocal::load_files(mf).unwrap(); let mut db = adduser::UserDBLocal::load_files(mf).unwrap();
@ -32,9 +32,9 @@ fn test_test() {
); );
let pf2 = fs::read_to_string(&p.path).unwrap(); let pf2 = fs::read_to_string(&p.path).unwrap();
assert_eq!(user_res.unwrap().get_username().unwrap(), "teste"); assert_eq!(user_res.unwrap().get_username().unwrap(), "teste");
let pfl = pf.lines(); let pflines = pf.lines();
let pfl2 = pf2.lines(); let pflines2 = pf2.lines();
for (l1, l2) in pfl.zip(pfl2) { for (l1, l2) in pflines.zip(pflines2) {
if l1 != l2 { if l1 != l2 {
dbg!(l1, l2); dbg!(l1, l2);
assert!(l1.starts_with("teste")); assert!(l1.starts_with("teste"));

View File

@ -12,6 +12,7 @@ pub struct Fixture {
} }
impl Fixture { impl Fixture {
#[must_use]
pub fn blank(fixture_filename: &str) -> Self { pub fn blank(fixture_filename: &str) -> Self {
// First, figure out the right file in `tests/fixtures/`: // First, figure out the right file in `tests/fixtures/`:
let root_dir = &env::var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR"); let root_dir = &env::var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR");
@ -24,14 +25,15 @@ impl Fixture {
let mut path = PathBuf::from(&tempdir.path()); let mut path = PathBuf::from(&tempdir.path());
path.push(&fixture_filename); path.push(&fixture_filename);
Fixture { Self {
_tempdir: tempdir, _tempdir: tempdir,
source, source,
path, path,
} }
} }
#[must_use]
pub fn copy(fixture_filename: &str) -> Self { pub fn copy(fixture_filename: &str) -> Self {
let fixture = Fixture::blank(fixture_filename); let fixture = Self::blank(fixture_filename);
fs::copy(&fixture.source, &fixture.path).unwrap(); fs::copy(&fixture.source, &fixture.path).unwrap();
fixture fixture
} }
@ -41,6 +43,6 @@ impl Deref for Fixture {
type Target = Path; type Target = Path;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.path.deref() &self.path
} }
} }