mirror of
https://github.com/uutils/rust-users.git
synced 2026-06-10 15:48:37 -07:00
Begin to move OS routines to a new module
This tries to mimic the Metadata struct in libc by providing a struct with hidden fields, then an OS-dependent trait that gives you accessors to those fields. Its goal is to remove any OS-specific things about users and groups -- which there will be a lot of -- from the default, publicly-accessible struct. This may not seem like a big problem, but you'll see it a lot in mocking code: "unimportant" fields, such as the user's home directory and shell, can now be mocked to include a "sensible" value even when they're not used.
This commit is contained in:
+50
-2
@@ -1,4 +1,5 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::path::Path;
|
||||
use std::ptr::read;
|
||||
use std::str::from_utf8_unchecked;
|
||||
use std::sync::Arc;
|
||||
@@ -11,6 +12,8 @@ use libc::{c_char, time_t};
|
||||
#[cfg(target_os = "linux")]
|
||||
use libc::c_char;
|
||||
|
||||
use os::*;
|
||||
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[repr(C)]
|
||||
@@ -75,10 +78,10 @@ pub struct User {
|
||||
pub primary_group: gid_t,
|
||||
|
||||
/// This user's home directory
|
||||
pub home_dir: String,
|
||||
home_dir: String,
|
||||
|
||||
/// This user's shell
|
||||
pub shell: String,
|
||||
shell: String,
|
||||
}
|
||||
|
||||
/// Information about a particular group.
|
||||
@@ -95,6 +98,51 @@ pub struct Group {
|
||||
pub members: Vec<String>,
|
||||
}
|
||||
|
||||
impl unix::UserExt for User {
|
||||
fn home_dir(&self) -> &Path {
|
||||
Path::new(&self.home_dir)
|
||||
}
|
||||
|
||||
fn with_home_dir(mut self, home_dir: &str) -> User {
|
||||
self.home_dir = home_dir.to_owned();
|
||||
self
|
||||
}
|
||||
|
||||
fn shell(&self) -> &Path {
|
||||
Path::new(&self.shell)
|
||||
}
|
||||
|
||||
fn with_shell(mut self, shell: &str) -> User {
|
||||
self.shell = shell.to_owned();
|
||||
self
|
||||
}
|
||||
|
||||
fn new(uid: uid_t, name: &str, primary_group: gid_t) -> User {
|
||||
User {
|
||||
uid: uid,
|
||||
name: Arc::new(name.to_owned()),
|
||||
primary_group: primary_group,
|
||||
home_dir: "/var/empty".to_owned(),
|
||||
shell: "/bin/false".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl unix::GroupExt for Group {
|
||||
fn members(&self) -> &[String] {
|
||||
&*self.members
|
||||
}
|
||||
|
||||
fn new(gid: gid_t, name: &str) -> Group {
|
||||
Group {
|
||||
gid: gid,
|
||||
name: Arc::new(name.to_owned()),
|
||||
members: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsafe fn from_raw_buf(p: *const i8) -> String {
|
||||
from_utf8_unchecked(CStr::from_ptr(p).to_bytes()).to_string()
|
||||
}
|
||||
|
||||
@@ -119,6 +119,8 @@ pub use cache::UsersCache;
|
||||
|
||||
pub mod mock;
|
||||
|
||||
pub mod os;
|
||||
|
||||
pub mod switch;
|
||||
|
||||
mod traits;
|
||||
|
||||
+16
-11
@@ -18,11 +18,13 @@
|
||||
//!
|
||||
//! ```rust
|
||||
//! use users::mock::{MockUsers, User, Group};
|
||||
//! use users::os::unix::{UserExt, GroupExt};
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! let mut users = MockUsers::with_current_uid(1000);
|
||||
//! users.add_user(User { uid: 1000, name: Arc::new("Bobbins".to_string()), primary_group: 100, home_dir: "/home/bobbins".to_string(), shell: "/bin/bash".to_string() });
|
||||
//! users.add_group(Group { gid: 100, name: Arc::new("funkyppl".to_string()), members: vec![ "other_person".to_string() ] });
|
||||
//! let bobbins = User::new(1000, "Bobbins", 1000).with_home_dir("/home/bobbins");
|
||||
//! users.add_user(bobbins);
|
||||
//! users.add_group(Group::new(100, "funkyppl"));
|
||||
//! ```
|
||||
//!
|
||||
//! The exports get re-exported into the mock module, for simpler `use` lines.
|
||||
@@ -38,6 +40,7 @@
|
||||
//!
|
||||
//! ```rust
|
||||
//! use users::{Users, UsersCache, User};
|
||||
//! use users::os::unix::UserExt;
|
||||
//! use users::mock::MockUsers;
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
@@ -46,7 +49,7 @@
|
||||
//! }
|
||||
//!
|
||||
//! let mut users = MockUsers::with_current_uid(1001);
|
||||
//! users.add_user(User { uid: 1001, name: Arc::new("fred".to_string()), primary_group: 101 , home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string()});
|
||||
//! users.add_user(User::new(1001, "fred", 101));
|
||||
//! print_current_username(&mut users);
|
||||
//!
|
||||
//! let mut actual_users = UsersCache::new();
|
||||
@@ -144,15 +147,17 @@ impl Groups for MockUsers {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{MockUsers};
|
||||
use super::MockUsers;
|
||||
use base::{User, Group};
|
||||
use traits::{Users, Groups};
|
||||
use std::sync::Arc;
|
||||
|
||||
use os::unix::{UserExt, GroupExt};
|
||||
|
||||
#[test]
|
||||
fn current_username() {
|
||||
let mut users = MockUsers::with_current_uid(1337);
|
||||
users.add_user(User { uid: 1337, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
|
||||
users.add_user(User::new(1337, "fred", 101));
|
||||
assert_eq!(Some(Arc::new("fred".into())), users.get_current_username())
|
||||
}
|
||||
|
||||
@@ -165,21 +170,21 @@ mod test {
|
||||
#[test]
|
||||
fn uid() {
|
||||
let mut users = MockUsers::with_current_uid(0);
|
||||
users.add_user(User { uid: 1337, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
|
||||
users.add_user(User::new(1337, "fred", 101));
|
||||
assert_eq!(Some(Arc::new("fred".into())), users.get_user_by_uid(1337).map(|u| u.name.clone()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn username() {
|
||||
let mut users = MockUsers::with_current_uid(1337);
|
||||
users.add_user(User { uid: 1440, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
|
||||
users.add_user(User::new(1440, "fred", 101));
|
||||
assert_eq!(Some(1440), users.get_user_by_name("fred").map(|u| u.uid))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_username() {
|
||||
let mut users = MockUsers::with_current_uid(1337);
|
||||
users.add_user(User { uid: 1440, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
|
||||
users.add_user(User::new(1337, "fred", 101));
|
||||
assert_eq!(None, users.get_user_by_name("criminy").map(|u| u.uid))
|
||||
}
|
||||
|
||||
@@ -192,21 +197,21 @@ mod test {
|
||||
#[test]
|
||||
fn gid() {
|
||||
let mut users = MockUsers::with_current_uid(0);
|
||||
users.add_group(Group { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
|
||||
users.add_group(Group::new(1337, "fred"));
|
||||
assert_eq!(Some(Arc::new("fred".into())), users.get_group_by_gid(1337).map(|g| g.name.clone()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_name() {
|
||||
let mut users = MockUsers::with_current_uid(0);
|
||||
users.add_group(Group { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
|
||||
users.add_group(Group::new(1337, "fred"));
|
||||
assert_eq!(Some(1337), users.get_group_by_name("fred").map(|g| g.gid))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_group_name() {
|
||||
let mut users = MockUsers::with_current_uid(0);
|
||||
users.add_group(Group { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
|
||||
users.add_group(Group::new(1337, "fred"));
|
||||
assert_eq!(None, users.get_group_by_name("santa").map(|g| g.gid))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
pub mod unix {
|
||||
use std::path::Path;
|
||||
use libc::{uid_t, gid_t};
|
||||
|
||||
pub trait UserExt {
|
||||
fn home_dir(&self) -> &Path;
|
||||
fn with_home_dir(mut self, home_dir: &str) -> Self;
|
||||
|
||||
fn shell(&self) -> &Path;
|
||||
fn with_shell(mut self, shell: &str) -> Self;
|
||||
|
||||
// TODO(ogham): Isn't it weird that the setters take a string slice, but
|
||||
// the getters return a Path?
|
||||
|
||||
fn new(uid: uid_t, name: &str, primary_group: gid_t) -> Self;
|
||||
}
|
||||
|
||||
pub trait GroupExt {
|
||||
fn members(&self) -> &[String];
|
||||
fn new(gid: gid_t, name: &str) -> Self;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user