qualia_core_db/inference/runtime/artifacts/
budget.rs1use std::path::{Component, Path};
2
3#[derive(Debug)]
4pub enum ArtifactError {
5 InvalidLabel,
6 InvalidRelativePath,
7 BudgetExceeded {
8 budget_bytes: u64,
9 attempted_bytes: u64,
10 },
11 TargetExists,
12 Io(std::io::Error),
13 Cleanup {
14 path: std::path::PathBuf,
15 source: std::io::Error,
16 },
17 Promotion {
18 staging: std::path::PathBuf,
19 target: std::path::PathBuf,
20 source: std::io::Error,
21 },
22}
23
24impl std::fmt::Display for ArtifactError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::InvalidLabel => write!(
28 f,
29 "artifact run label must be non-empty ASCII [a-zA-Z0-9_-]"
30 ),
31 Self::InvalidRelativePath => {
32 write!(f, "artifact path must be a non-empty relative child path")
33 }
34 Self::BudgetExceeded {
35 budget_bytes,
36 attempted_bytes,
37 } => write!(
38 f,
39 "artifact byte budget exceeded: budget={budget_bytes}, attempted={attempted_bytes}"
40 ),
41 Self::TargetExists => write!(f, "artifact promotion target already exists"),
42 Self::Io(source) => write!(f, "artifact I/O: {source}"),
43 Self::Cleanup { path, source } => {
44 write!(
45 f,
46 "artifact cleanup failed for {}: {source}",
47 path.display()
48 )
49 }
50 Self::Promotion {
51 staging,
52 target,
53 source,
54 } => write!(
55 f,
56 "artifact promotion {} -> {} failed: {source}",
57 staging.display(),
58 target.display()
59 ),
60 }
61 }
62}
63
64impl std::error::Error for ArtifactError {
65 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
66 match self {
67 Self::Io(source) | Self::Cleanup { source, .. } | Self::Promotion { source, .. } => {
68 Some(source)
69 }
70 _ => None,
71 }
72 }
73}
74
75impl From<std::io::Error> for ArtifactError {
76 fn from(value: std::io::Error) -> Self {
77 Self::Io(value)
78 }
79}
80
81pub fn validate_relative_artifact_path(path: &Path) -> Result<(), ArtifactError> {
82 if path.as_os_str().is_empty() || path.is_absolute() {
83 return Err(ArtifactError::InvalidRelativePath);
84 }
85 for component in path.components() {
86 match component {
87 Component::Normal(_) => {}
88 _ => return Err(ArtifactError::InvalidRelativePath),
89 }
90 }
91 Ok(())
92}
93
94pub(super) fn validate_label(label: &str) -> Result<(), ArtifactError> {
95 if label.is_empty()
96 || !label
97 .bytes()
98 .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
99 {
100 return Err(ArtifactError::InvalidLabel);
101 }
102 Ok(())
103}