use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[non_exhaustive]
pub enum XattrNamespace {
User,
Trusted,
Security,
SystemPosixAcl,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[non_exhaustive]
pub enum Operation {
Read,
Write,
Execute,
Delete,
Create,
Stat,
Chmod,
ChownUid,
ChownGid,
SetXattr {
namespace: XattrNamespace,
},
}
impl Operation {
#[must_use]
pub fn is_metadata(&self) -> bool {
matches!(
self,
Self::Chmod | Self::ChownUid | Self::ChownGid | Self::SetXattr { .. }
)
}
#[must_use]
pub fn checks_parent(&self) -> bool {
matches!(self, Self::Delete | Self::Create)
}
#[must_use]
pub fn target_component(&self, walk_len: usize) -> Option<usize> {
if walk_len == 0 {
return None;
}
if self.checks_parent() {
walk_len.checked_sub(2)
} else {
Some(walk_len - 1)
}
}
}
#[derive(Debug, Default, Clone)]
pub struct MetadataParams {
pub new_mode: Option<u32>,
pub new_uid: Option<u32>,
pub new_gid: Option<u32>,
}
#[cfg(test)]
#[path = "operation_metadata_tests.rs"]
mod metadata_tests;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn checks_parent_true_for_delete() {
assert!(Operation::Delete.checks_parent());
}
#[test]
fn checks_parent_true_for_create() {
assert!(Operation::Create.checks_parent());
}
#[test]
fn checks_parent_false_for_read() {
assert!(!Operation::Read.checks_parent());
}
#[test]
fn checks_parent_false_for_write() {
assert!(!Operation::Write.checks_parent());
}
#[test]
fn checks_parent_false_for_execute() {
assert!(!Operation::Execute.checks_parent());
}
#[test]
fn checks_parent_false_for_stat() {
assert!(!Operation::Stat.checks_parent());
}
#[test]
fn target_component_last_for_read() {
assert_eq!(Operation::Read.target_component(3), Some(2));
}
#[test]
fn target_component_last_for_single_component() {
assert_eq!(Operation::Read.target_component(1), Some(0));
}
#[test]
fn target_component_second_to_last_for_delete() {
assert_eq!(Operation::Delete.target_component(3), Some(1));
}
#[test]
fn target_component_second_to_last_for_create() {
assert_eq!(Operation::Create.target_component(3), Some(1));
}
#[test]
fn target_component_none_for_empty_walk() {
assert_eq!(Operation::Read.target_component(0), None);
}
#[test]
fn target_component_none_for_parent_op_with_single_component() {
assert_eq!(Operation::Delete.target_component(1), None);
}
#[test]
fn target_component_parent_op_with_two_components() {
assert_eq!(Operation::Delete.target_component(2), Some(0));
}
}