linting changes
This commit is contained in:
parent
647881f022
commit
467c8ee007
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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")),
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
24
src/error.rs
24
src/error.rs
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -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())?,
|
||||||
|
@ -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]
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()),
|
||||||
|
@ -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 {
|
||||||
|
@ -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),
|
||||||
|
@ -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,17 +124,17 @@ 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.";
|
||||||
|
error!("{}", error_msg);
|
||||||
Err(std::io::Error::new(
|
Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::InvalidInput,
|
std::io::ErrorKind::InvalidInput,
|
||||||
"Failed to remove the home directory! As the user did not have one.",
|
error_msg,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn get_group_pos_by_id(&self, id: u32) -> Option<(&crate::Group, usize)> {
|
fn get_group_pos_by_id(&self, id: u32) -> Option<(&crate::Group, usize)> {
|
||||||
for (i, group) in self.groups.iter().enumerate() {
|
for (i, group) in self.groups.iter().enumerate() {
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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"));
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user