///|
/// Errors produced by a filesystem adapter.
///
/// The loader translates these values into `LoadError`, so adapters never
/// leak platform-specific error types through the public loading API.
pub(all) suberror FileSystemError {
NotFound(path~ : String)
PermissionDenied(path~ : String)
InvalidPath(path~ : String, message~ : String)
ReadByteLimitExceeded(path~ : String, limit~ : Int)
GlobMatchLimitExceeded(path~ : String, limit~ : Int)
Other(message~ : String)
} derive(Debug, Eq)
///|
/// The backend boundary used by the portable configuration loader.
///
/// Implementations provide text, an identity used exclusively for cycle
/// detection, deterministic-enough glob candidates, and an explicitly
/// injected home directory. The loader sorts glob output itself and never
/// consults process state.
///
/// Implementations must honor supplied limits before returning an oversized
/// value. Adapters with streaming or metadata support should stop I/O before
/// allocation; adapters without it must reject before transferring the value
/// to the loader and document the residual backend allocation boundary.
pub(open) trait FileSystem {
fn read_text(Self, String, max_bytes? : Int) -> Result[
String,
FileSystemError,
]
fn canonical_identity(Self, String) -> Result[String, FileSystemError]
fn glob(Self, String, max_matches? : Int) -> Result[
Array[String],
FileSystemError,
]
fn home_dir(Self) -> String?
}
///|
fn filesystem_error_message(error : FileSystemError) -> String {
match error {
NotFound(path~) => "file not found: \{path}"
PermissionDenied(path~) => "permission denied: \{path}"
InvalidPath(path~, message~) => "invalid path \{path}: \{message}"
ReadByteLimitExceeded(path~, limit~) =>
"file exceeds byte limit \{limit}: \{path}"
GlobMatchLimitExceeded(path~, limit~) =>
"glob exceeds match limit \{limit}: \{path}"
Other(message~) => message
}
}