fabro/lib/components/fabro-environment/src/error.rs
2026-07-23 17:59:34 -04:00

136 lines
4.2 KiB
Rust

use std::path::PathBuf;
use toml::de::Error as TomlDeError;
use toml::ser::Error as TomlSerError;
use crate::{EnvironmentId, EnvironmentRevision, EnvironmentRevisionParseError};
#[derive(Debug, thiserror::Error)]
pub enum EnvironmentValidationError {
#[error("environment id {value:?} must match [a-z0-9][a-z0-9-]{{0,62}}")]
InvalidEnvironmentId { value: String },
#[error("environment settings are invalid: {}", .errors.join("; "))]
InvalidSettings { errors: Vec<String> },
#[error("failed to read Dockerfile referenced by environment at {path:?}")]
DockerfileRead {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error(
"Dockerfile path sources are not supported for stored environments; use inline Dockerfile content"
)]
DockerfilePathUnsupported,
}
#[derive(Debug, thiserror::Error)]
pub enum EnvironmentStoreError {
#[error("environment not found: {id}")]
NotFound { id: EnvironmentId },
#[error("environment already exists: {id}")]
AlreadyExists { id: EnvironmentId },
#[error("environment revision is stale for {id}: expected {expected}, actual {actual}")]
StaleRevision {
id: EnvironmentId,
expected: EnvironmentRevision,
actual: EnvironmentRevision,
},
#[error("environment is reserved and cannot be modified: {id}")]
Reserved { id: EnvironmentId },
#[error("environment validation failed: {source}")]
Validation {
#[from]
source: EnvironmentValidationError,
},
#[error("invalid environment filename at {path:?}")]
InvalidFilename { path: PathBuf, reason: String },
#[error("failed to parse environment TOML at {path:?}")]
Parse {
path: PathBuf,
#[source]
source: TomlDeError,
},
#[error("invalid persisted environment revision for {id}")]
InvalidRevision {
id: EnvironmentId,
#[source]
source: EnvironmentRevisionParseError,
},
#[error("environment TOML at {path:?} is not UTF-8")]
InvalidUtf8 {
path: PathBuf,
#[source]
source: std::str::Utf8Error,
},
#[error("failed to serialize environment TOML")]
Serialize {
#[from]
source: TomlSerError,
},
#[error("I/O error at {path:?}")]
Io {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("failed to encode environment JSON for {field}")]
JsonEncode {
field: &'static str,
#[source]
source: serde_json::Error,
},
#[error("failed to decode environment JSON for {field}")]
JsonDecode {
field: &'static str,
#[source]
source: serde_json::Error,
},
#[error("database error")]
Db {
#[from]
source: sqlx::Error,
},
#[error("environment row count {count} exceeds SQLite integer range")]
RowCountOverflow { count: usize },
}
impl EnvironmentStoreError {
pub(crate) fn io(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
Self::Io {
path: path.into(),
source,
}
}
pub(crate) fn parse(path: impl Into<PathBuf>, source: TomlDeError) -> Self {
Self::Parse {
path: path.into(),
source,
}
}
pub(crate) fn invalid_utf8(path: impl Into<PathBuf>, source: std::str::Utf8Error) -> Self {
Self::InvalidUtf8 {
path: path.into(),
source,
}
}
#[must_use]
pub fn kind(&self) -> &'static str {
match self {
Self::NotFound { .. } => "not_found",
Self::AlreadyExists { .. } => "already_exists",
Self::StaleRevision { .. } => "stale_revision",
Self::Reserved { .. } => "reserved",
Self::Validation { .. } => "validation",
Self::InvalidFilename { .. } => "invalid_filename",
Self::Parse { .. } | Self::InvalidUtf8 { .. } | Self::InvalidRevision { .. } => "parse",
Self::Serialize { .. } => "serialize",
Self::JsonEncode { .. } | Self::JsonDecode { .. } => "json",
Self::Db { .. } => "db",
Self::RowCountOverflow { .. } => "row_count_overflow",
Self::Io { .. } => "io",
}
}
}