use std::cmp::Ordering;
use crate::{Gid, Uid, UserInner};
pub struct User {
pub(crate) inner: UserInner,
}
impl PartialEq for User {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
&& self.group_id() == other.group_id()
&& self.name() == other.name()
}
}
impl Eq for User {}
impl PartialOrd for User {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for User {
fn cmp(&self, other: &Self) -> Ordering {
self.name().cmp(other.name())
}
}
impl User {
pub fn id(&self) -> &Uid {
self.inner.id()
}
pub fn group_id(&self) -> Gid {
self.inner.group_id()
}
pub fn name(&self) -> &str {
self.inner.name()
}
pub fn groups(&self) -> Vec<Group> {
self.inner.groups()
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) struct GroupInner {
pub(crate) id: Gid,
pub(crate) name: String,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Group {
pub(crate) inner: GroupInner,
}
impl Group {
pub fn id(&self) -> &Gid {
self.inner.id()
}
pub fn name(&self) -> &str {
self.inner.name()
}
}
pub struct Users {
users: Vec<User>,
}
impl Default for Users {
fn default() -> Self {
Self::new()
}
}
impl From<Users> for Vec<User> {
fn from(users: Users) -> Self {
users.users
}
}
impl From<Vec<User>> for Users {
fn from(users: Vec<User>) -> Self {
Self { users }
}
}
impl std::ops::Deref for Users {
type Target = [User];
fn deref(&self) -> &Self::Target {
self.list()
}
}
impl std::ops::DerefMut for Users {
fn deref_mut(&mut self) -> &mut Self::Target {
self.list_mut()
}
}
impl<'a> IntoIterator for &'a Users {
type Item = &'a User;
type IntoIter = std::slice::Iter<'a, User>;
fn into_iter(self) -> Self::IntoIter {
self.list().iter()
}
}
impl<'a> IntoIterator for &'a mut Users {
type Item = &'a mut User;
type IntoIter = std::slice::IterMut<'a, User>;
fn into_iter(self) -> Self::IntoIter {
self.list_mut().iter_mut()
}
}
impl Users {
pub fn new() -> Self {
Self { users: Vec::new() }
}
pub fn new_with_refreshed_list() -> Self {
let mut users = Self::new();
users.refresh_list();
users
}
pub fn list(&self) -> &[User] {
&self.users
}
pub fn list_mut(&mut self) -> &mut [User] {
&mut self.users
}
pub fn refresh_list(&mut self) {
crate::sys::get_users(&mut self.users);
}
#[cfg_attr(feature = "system", doc = "```no_run")]
#[cfg_attr(not(feature = "system"), doc = "```ignore")]
pub fn get_user_by_id(&self, user_id: &Uid) -> Option<&User> {
self.users.iter().find(|user| user.id() == user_id)
}
}
pub struct Groups {
groups: Vec<Group>,
}
impl Default for Groups {
fn default() -> Self {
Self::new()
}
}
impl From<Groups> for Vec<Group> {
fn from(groups: Groups) -> Self {
groups.groups
}
}
impl From<Vec<Group>> for Groups {
fn from(groups: Vec<Group>) -> Self {
Self { groups }
}
}
impl std::ops::Deref for Groups {
type Target = [Group];
fn deref(&self) -> &Self::Target {
self.list()
}
}
impl std::ops::DerefMut for Groups {
fn deref_mut(&mut self) -> &mut Self::Target {
self.list_mut()
}
}
impl<'a> IntoIterator for &'a Groups {
type Item = &'a Group;
type IntoIter = std::slice::Iter<'a, Group>;
fn into_iter(self) -> Self::IntoIter {
self.list().iter()
}
}
impl<'a> IntoIterator for &'a mut Groups {
type Item = &'a mut Group;
type IntoIter = std::slice::IterMut<'a, Group>;
fn into_iter(self) -> Self::IntoIter {
self.list_mut().iter_mut()
}
}
impl Groups {
pub fn new() -> Self {
Self { groups: Vec::new() }
}
pub fn new_with_refreshed_list() -> Self {
let mut groups = Self::new();
groups.refresh_list();
groups
}
pub fn list(&self) -> &[Group] {
&self.groups
}
pub fn list_mut(&mut self) -> &mut [Group] {
&mut self.groups
}
pub fn refresh_list(&mut self) {
crate::sys::get_groups(&mut self.groups);
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn check_list() {
let mut users = Users::new();
assert!(users.list().is_empty());
users.refresh_list();
assert!(users.list().len() >= MIN_USERS);
}
#[allow(clippy::unnecessary_fallible_conversions)]
#[test]
fn check_uid_gid_from_impls() {
use std::convert::TryFrom;
use std::str::FromStr;
#[cfg(not(windows))]
{
assert!(crate::Uid::try_from(0usize).is_ok());
assert!(crate::Uid::from_str("0").is_ok());
}
#[cfg(windows)]
{
assert!(crate::Uid::from_str("S-1-5-18").is_ok()); assert!(crate::Uid::from_str("0").is_err());
}
assert!(crate::Gid::try_from(0usize).is_ok());
assert!(crate::Gid::from_str("0").is_ok());
}
#[test]
fn check_groups() {
if !crate::IS_SUPPORTED_SYSTEM {
return;
}
assert!(!Groups::new_with_refreshed_list().is_empty());
}
}