use std::{
fs,
path::{Path, PathBuf},
};
use crate::azure::rigg::{GeneratedRiggFiles, ownership};
use crate::config::{RiggConfig, RiggOwnership};
#[derive(Debug, Default)]
pub struct WriteOutcome {
pub written: Vec<PathBuf>,
pub skipped: Vec<PathBuf>,
}
#[derive(Debug, thiserror::Error)]
pub enum WriteError {
#[error("io: {0}")]
Io(#[from] std::io::Error),
}
pub fn write_to_disk(
files: &GeneratedRiggFiles,
rigg_config: &RiggConfig,
root: &Path,
) -> Result<WriteOutcome, WriteError> {
let mut outcome = WriteOutcome::default();
let groups: &[(&str, &std::collections::HashMap<String, String>)] = &[
("indexes", &files.indexes),
("datasources", &files.datasources),
("skillsets", &files.skillsets),
("indexers", &files.indexers),
("knowledge_sources", &files.knowledge_sources),
("knowledge_bases", &files.knowledge_bases),
];
for (subdir, map) in groups {
for (name, yaml) in *map {
let path = root.join(subdir).join(format!("{name}.yaml"));
if should_skip(&path, rigg_config) {
outcome.skipped.push(path);
} else {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&path, yaml.as_bytes())?;
outcome.written.push(path);
}
}
}
Ok(outcome)
}
fn should_skip(path: &Path, rigg_config: &RiggConfig) -> bool {
if !path.exists() {
return false;
}
if ownership::is_managed_by_user(path) {
return true;
}
matches!(rigg_config.ownership, RiggOwnership::ManagedByUser)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::tempdir;
fn rigg_config(ownership: RiggOwnership) -> RiggConfig {
RiggConfig {
dir: "./rigg".to_string(),
ownership,
}
}
fn single_index_files(name: &str, content: &str) -> GeneratedRiggFiles {
let mut files = GeneratedRiggFiles::default();
files.indexes.insert(name.to_string(), content.to_string());
files
}
fn one_of_each_files() -> GeneratedRiggFiles {
let mut files = GeneratedRiggFiles::default();
files
.indexes
.insert("idx".to_string(), "index: yaml\n".to_string());
files
.datasources
.insert("ds".to_string(), "datasource: yaml\n".to_string());
files
.skillsets
.insert("ss".to_string(), "skillset: yaml\n".to_string());
files
.indexers
.insert("ir".to_string(), "indexer: yaml\n".to_string());
files
.knowledge_sources
.insert("ks".to_string(), "ks: yaml\n".to_string());
files
.knowledge_bases
.insert("kb".to_string(), "kb: yaml\n".to_string());
files
}
#[test]
fn write_to_disk_creates_files_in_correct_subdirs() {
let dir = tempdir().unwrap();
let files = one_of_each_files();
let cfg = rigg_config(RiggOwnership::Generated);
let outcome = write_to_disk(&files, &cfg, dir.path()).unwrap();
assert_eq!(outcome.skipped.len(), 0);
assert_eq!(outcome.written.len(), 6);
assert!(dir.path().join("indexes/idx.yaml").exists());
assert!(dir.path().join("datasources/ds.yaml").exists());
assert!(dir.path().join("skillsets/ss.yaml").exists());
assert!(dir.path().join("indexers/ir.yaml").exists());
assert!(dir.path().join("knowledge_sources/ks.yaml").exists());
assert!(dir.path().join("knowledge_bases/kb.yaml").exists());
}
#[test]
fn write_to_disk_overwrites_when_ownership_is_generated() {
let dir = tempdir().unwrap();
let indexes_dir = dir.path().join("indexes");
fs::create_dir_all(&indexes_dir).unwrap();
let file_path = indexes_dir.join("jira-issues.yaml");
let mut f = std::fs::File::create(&file_path).unwrap();
f.write_all(b"old content\n").unwrap();
drop(f);
let files = single_index_files("jira-issues", "new content\n");
let cfg = rigg_config(RiggOwnership::Generated);
let outcome = write_to_disk(&files, &cfg, dir.path()).unwrap();
assert_eq!(outcome.skipped.len(), 0);
assert_eq!(outcome.written.len(), 1);
let on_disk = fs::read_to_string(&file_path).unwrap();
assert_eq!(on_disk, "new content\n");
}
#[test]
fn write_to_disk_preserves_managed_by_user_files() {
let dir = tempdir().unwrap();
let indexes_dir = dir.path().join("indexes");
fs::create_dir_all(&indexes_dir).unwrap();
let file_path = indexes_dir.join("jira-issues.yaml");
let original = "# rigg:managed-by-user\nname: custom\n";
let mut f = std::fs::File::create(&file_path).unwrap();
f.write_all(original.as_bytes()).unwrap();
drop(f);
let files = single_index_files("jira-issues", "should not be written\n");
let cfg = rigg_config(RiggOwnership::Generated);
let outcome = write_to_disk(&files, &cfg, dir.path()).unwrap();
assert_eq!(outcome.written.len(), 0);
assert_eq!(outcome.skipped.len(), 1);
assert_eq!(outcome.skipped[0], file_path);
let on_disk = fs::read_to_string(&file_path).unwrap();
assert_eq!(on_disk, original);
}
#[test]
fn write_to_disk_preserves_existing_files_when_ownership_is_managed_by_user() {
let dir = tempdir().unwrap();
let indexes_dir = dir.path().join("indexes");
fs::create_dir_all(&indexes_dir).unwrap();
let file_path = indexes_dir.join("jira-issues.yaml");
let original = "name: existing\n"; let mut f = std::fs::File::create(&file_path).unwrap();
f.write_all(original.as_bytes()).unwrap();
drop(f);
let files = single_index_files("jira-issues", "should not be written\n");
let cfg = rigg_config(RiggOwnership::ManagedByUser);
let outcome = write_to_disk(&files, &cfg, dir.path()).unwrap();
assert_eq!(outcome.written.len(), 0);
assert_eq!(outcome.skipped.len(), 1);
let on_disk = fs::read_to_string(&file_path).unwrap();
assert_eq!(on_disk, original);
}
#[test]
fn write_to_disk_creates_missing_files_when_ownership_is_managed_by_user() {
let dir = tempdir().unwrap();
let files = single_index_files("jira-issues", "generated content\n");
let cfg = rigg_config(RiggOwnership::ManagedByUser);
let outcome = write_to_disk(&files, &cfg, dir.path()).unwrap();
assert_eq!(outcome.written.len(), 1);
assert_eq!(outcome.skipped.len(), 0);
let on_disk = fs::read_to_string(dir.path().join("indexes/jira-issues.yaml")).unwrap();
assert_eq!(on_disk, "generated content\n");
}
}