mod toml_io;
use crate::config::Config;
use crate::diagnostic::{Diagnostic, DiagnosticCode};
use crate::model::{
AdrEntry, AdrSpec, GuardEntry, GuardSpec, ReleasesFile, WorkItemEntry, WorkItemSpec,
};
use crate::schema::ArtifactSchema;
use crate::write::WriteOp;
use std::path::Path;
pub struct LoadResult<T> {
pub items: Vec<T>,
pub warnings: Vec<Diagnostic>,
}
pub fn load_adrs(config: &Config) -> Result<Vec<AdrEntry>, Diagnostic> {
load_adrs_with_warnings(config).map(|r| r.items)
}
pub fn load_adrs_with_warnings(config: &Config) -> Result<LoadResult<AdrEntry>, Diagnostic> {
let adr_dir = config.adr_dir();
toml_io::load_toml_dir(
&adr_dir,
|path| load_adr(config, path),
|adrs| adrs.sort_by(|a, b| a.spec.govctl.id.cmp(&b.spec.govctl.id)),
)
}
pub fn load_adr(config: &Config, path: &Path) -> Result<AdrEntry, Diagnostic> {
let spec = toml_io::load_toml_spec(
config,
path,
ArtifactSchema::Adr,
DiagnosticCode::E0301AdrSchemaInvalid,
"Invalid TOML",
"Invalid ADR structure",
|_| {},
)?;
Ok(AdrEntry {
spec,
path: path.to_path_buf(),
})
}
pub fn write_adr(
path: &Path,
spec: &AdrSpec,
op: WriteOp,
display_path: Option<&Path>,
) -> Result<(), Diagnostic> {
toml_io::write_toml_spec(
path,
ArtifactSchema::Adr,
spec,
op,
display_path,
"Failed to serialize TOML",
)
}
pub fn load_work_items(config: &Config) -> Result<Vec<WorkItemEntry>, Diagnostic> {
load_work_items_with_warnings(config).map(|r| r.items)
}
pub fn load_work_items_with_warnings(
config: &Config,
) -> Result<LoadResult<WorkItemEntry>, Diagnostic> {
let work_dir = config.work_dir();
toml_io::load_toml_dir(
&work_dir,
|path| load_work_item(config, path),
|items| items.sort_by(|a, b| a.spec.govctl.id.cmp(&b.spec.govctl.id)),
)
}
pub fn load_guards(config: &Config) -> Result<Vec<GuardEntry>, Diagnostic> {
load_guards_with_warnings(config).map(|r| r.items)
}
pub fn load_guards_with_warnings(config: &Config) -> Result<LoadResult<GuardEntry>, Diagnostic> {
let guard_dir = config.guard_dir();
toml_io::load_toml_dir(
&guard_dir,
|path| load_guard(config, path),
|items| items.sort_by(|a, b| a.spec.govctl.id.cmp(&b.spec.govctl.id)),
)
}
pub fn load_guard(config: &Config, path: &Path) -> Result<GuardEntry, Diagnostic> {
let spec = toml_io::load_toml_spec(
config,
path,
ArtifactSchema::Guard,
DiagnosticCode::E1001GuardSchemaInvalid,
"Invalid TOML",
"Invalid verification guard structure",
|_| {},
)?;
Ok(GuardEntry {
spec,
path: path.to_path_buf(),
})
}
pub fn load_work_item(config: &Config, path: &Path) -> Result<WorkItemEntry, Diagnostic> {
let spec = toml_io::load_toml_spec(
config,
path,
ArtifactSchema::WorkItem,
DiagnosticCode::E0401WorkSchemaInvalid,
"Invalid TOML",
"Invalid work item structure",
strip_legacy_inline_history_for_schema,
)?;
Ok(WorkItemEntry {
spec,
path: path.to_path_buf(),
})
}
fn strip_legacy_inline_history_for_schema(raw: &mut toml::Value) {
let Some(content) = raw
.as_table_mut()
.and_then(|root| root.get_mut("content"))
.and_then(toml::Value::as_table_mut)
else {
return;
};
content.remove("journal");
}
pub fn write_work_item(
path: &Path,
spec: &WorkItemSpec,
op: WriteOp,
display_path: Option<&Path>,
) -> Result<(), Diagnostic> {
toml_io::write_toml_spec(
path,
ArtifactSchema::WorkItem,
spec,
op,
display_path,
"Failed to serialize TOML",
)
}
pub fn write_guard(
path: &Path,
spec: &GuardSpec,
op: WriteOp,
display_path: Option<&Path>,
) -> Result<(), Diagnostic> {
toml_io::write_toml_spec(
path,
ArtifactSchema::Guard,
spec,
op,
display_path,
"Failed to serialize TOML",
)
}
pub fn load_releases(config: &Config) -> Result<ReleasesFile, Diagnostic> {
let path = config.releases_path();
if !path.exists() {
return Ok(ReleasesFile::default());
}
let releases: ReleasesFile = toml_io::load_toml_spec(
config,
&path,
ArtifactSchema::Release,
DiagnosticCode::E0704ReleaseSchemaInvalid,
"Invalid releases.toml",
"Invalid release structure",
|_| {},
)?;
for release in &releases.releases {
semver::Version::parse(&release.version).map_err(|_| {
Diagnostic::new(
DiagnosticCode::E0701ReleaseInvalidSemver,
format!("Invalid semver version: {}", release.version),
path.display().to_string(),
)
})?;
}
Ok(releases)
}
pub fn validate_version(version: &str) -> Result<semver::Version, String> {
semver::Version::parse(version).map_err(|_| format!("Invalid semver: {version}"))
}
pub fn write_releases(
config: &Config,
releases: &ReleasesFile,
op: WriteOp,
) -> Result<(), Diagnostic> {
let path = config.releases_path();
let path_display = config.display_path(&path);
toml_io::write_toml_spec(
&path,
ArtifactSchema::Release,
releases,
op,
Some(&path_display),
"Failed to serialize releases",
)
}