pub mod scan;
pub mod store;
pub mod types;
pub use scan::{
FileInfo, MemoryScanner, QuotaScanner, ScanResult, rescan, rescan_group, rescan_user,
};
pub use store::{
check_create, check_write, clear_quotas, generate_report, get_quota, get_usage, has_quotas,
list_datasets, list_quotas, list_quotas_by_type, remove_quota, reset_usage, set_quota,
set_usage, update_usage,
};
pub use types::{
DEFAULT_GRACE_PERIOD, NO_LIMIT, QUOTA_BLOCK_SIZE, Quota, QuotaCheckResult, QuotaError,
QuotaKey, QuotaLimits, QuotaReport, QuotaReportEntry, QuotaResult, QuotaStatus, QuotaType,
QuotaUsage,
};
pub fn set_user_quota(
dataset: &str,
uid: u32,
soft_bytes: u64,
hard_bytes: u64,
) -> QuotaResult<()> {
set_quota(
dataset,
QuotaKey::user(uid),
QuotaLimits::bytes(soft_bytes, hard_bytes),
)
}
pub fn set_group_quota(
dataset: &str,
gid: u32,
soft_bytes: u64,
hard_bytes: u64,
) -> QuotaResult<()> {
set_quota(
dataset,
QuotaKey::group(gid),
QuotaLimits::bytes(soft_bytes, hard_bytes),
)
}
pub fn get_user_quota(dataset: &str, uid: u32) -> QuotaResult<Quota> {
get_quota(dataset, QuotaKey::user(uid))
}
pub fn get_group_quota(dataset: &str, gid: u32) -> QuotaResult<Quota> {
get_quota(dataset, QuotaKey::group(gid))
}
pub fn get_user_usage(dataset: &str, uid: u32) -> QuotaResult<QuotaUsage> {
get_usage(dataset, QuotaKey::user(uid))
}
pub fn get_group_usage(dataset: &str, gid: u32) -> QuotaResult<QuotaUsage> {
get_usage(dataset, QuotaKey::group(gid))
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn clean_test_dataset(name: &str) {
let _ = clear_quotas(name);
}
#[test]
fn test_exports_accessible() {
let _ = QuotaType::User;
let _ = QuotaKey::user(1000);
let _ = QuotaLimits::unlimited();
let _ = QuotaStatus::Ok;
}
#[test]
fn test_convenience_functions() {
let dataset = "test_conv";
clean_test_dataset(dataset);
set_user_quota(dataset, 1000, 1_000_000, 2_000_000).unwrap();
let quota = get_user_quota(dataset, 1000).unwrap();
assert_eq!(quota.limits.soft_bytes, 1_000_000);
set_group_quota(dataset, 100, 5_000_000, 10_000_000).unwrap();
let quota = get_group_quota(dataset, 100).unwrap();
assert_eq!(quota.limits.hard_bytes, 10_000_000);
clean_test_dataset(dataset);
}
#[test]
fn test_full_quota_workflow() {
let dataset = "test_workflow";
clean_test_dataset(dataset);
set_user_quota(dataset, 1000, 1000, 2000).unwrap();
set_group_quota(dataset, 100, 5000, 10000).unwrap();
let result = check_write(dataset, 1000, 100, 500, 0).unwrap();
assert!(result.is_allowed());
update_usage(dataset, 1000, 100, 500, 1, 0).unwrap();
let usage = get_user_usage(dataset, 1000).unwrap();
assert_eq!(usage.bytes_used, 500);
let result = check_write(dataset, 1000, 100, 300, 0).unwrap();
assert!(result.is_allowed());
update_usage(dataset, 1000, 100, 600, 0, 0).unwrap();
let result = check_write(dataset, 1000, 100, 100, 0).unwrap();
assert!(result.is_allowed()); assert_eq!(result.status, QuotaStatus::SoftLimitWarning);
update_usage(dataset, 1000, 100, 1000, 0, 0).unwrap();
let result = check_write(dataset, 1000, 100, 500, 0).unwrap();
assert!(!result.is_allowed());
assert_eq!(result.status, QuotaStatus::HardLimitExceeded);
clean_test_dataset(dataset);
}
#[test]
fn test_rescan_workflow() {
let dataset = "test_rescan_wf";
clean_test_dataset(dataset);
set_user_quota(dataset, 1000, 10000, 20000).unwrap();
let mut scanner = MemoryScanner::new();
scanner.add_files(
dataset,
vec![
FileInfo::new("/home/user/file1.txt", 1000, 100, 1000, false),
FileInfo::new("/home/user/file2.txt", 1000, 100, 2000, false),
FileInfo::new("/home/user/docs", 1000, 100, 0, true),
],
);
let result = rescan(dataset, &scanner).unwrap();
assert_eq!(result.files_scanned, 2);
assert_eq!(result.dirs_scanned, 1);
assert_eq!(result.total_bytes, 3000);
let usage = get_user_usage(dataset, 1000).unwrap();
assert_eq!(usage.bytes_used, 3000);
assert_eq!(usage.inodes_used, 3);
clean_test_dataset(dataset);
}
#[test]
fn test_quota_report() {
let dataset = "test_report";
clean_test_dataset(dataset);
set_user_quota(dataset, 1, 100, 200).unwrap();
set_user_quota(dataset, 2, 100, 200).unwrap();
set_group_quota(dataset, 1, 500, 1000).unwrap();
set_usage(dataset, QuotaKey::user(1), QuotaUsage::new(50, 5)).unwrap();
set_usage(dataset, QuotaKey::user(2), QuotaUsage::new(150, 10)).unwrap();
let report = generate_report(dataset, 0).unwrap();
assert_eq!(report.total_entries, 3);
assert!(report.over_soft > 0);
clean_test_dataset(dataset);
}
#[test]
fn test_check_create() {
let dataset = "test_create";
clean_test_dataset(dataset);
set_quota(dataset, QuotaKey::user(1000), QuotaLimits::inodes(10, 20)).unwrap();
let result = check_create(dataset, 1000, 100, 0).unwrap();
assert!(result.is_allowed());
set_usage(dataset, QuotaKey::user(1000), QuotaUsage::new(0, 20)).unwrap();
let result = check_create(dataset, 1000, 100, 0).unwrap();
assert!(!result.is_allowed());
assert_eq!(result.status, QuotaStatus::HardLimitExceeded);
clean_test_dataset(dataset);
}
#[test]
fn test_list_functions() {
let dataset = "test_list_fn";
clean_test_dataset(dataset);
assert!(!has_quotas(dataset));
set_user_quota(dataset, 1, 100, 200).unwrap();
set_user_quota(dataset, 2, 100, 200).unwrap();
set_group_quota(dataset, 1, 500, 1000).unwrap();
assert!(has_quotas(dataset));
let all = list_quotas(dataset).unwrap();
assert_eq!(all.len(), 3);
let users = list_quotas_by_type(dataset, QuotaType::User).unwrap();
assert_eq!(users.len(), 2);
let groups = list_quotas_by_type(dataset, QuotaType::Group).unwrap();
assert_eq!(groups.len(), 1);
clean_test_dataset(dataset);
}
#[test]
fn test_soft_limit_grace_period() {
let dataset = "test_grace";
clean_test_dataset(dataset);
let limits = QuotaLimits::bytes(100, 200).with_grace_period(10);
set_quota(dataset, QuotaKey::user(1000), limits).unwrap();
let mut usage = QuotaUsage::new(150, 5);
usage.soft_bytes_exceeded_at = 100; set_usage(dataset, QuotaKey::user(1000), usage).unwrap();
let result = check_write(dataset, 1000, 100, 10, 105).unwrap();
assert!(result.is_allowed());
assert_eq!(result.status, QuotaStatus::SoftLimitWarning);
let result = check_write(dataset, 1000, 100, 10, 115).unwrap();
assert!(!result.is_allowed());
assert_eq!(result.status, QuotaStatus::SoftLimitExceeded);
clean_test_dataset(dataset);
}
#[test]
fn test_constants() {
const { assert!(DEFAULT_GRACE_PERIOD > 0) };
const { assert!(NO_LIMIT == 0) };
const { assert!(QUOTA_BLOCK_SIZE > 0) };
}
}