use std::fs;
use std::path::{Path, PathBuf};
use crate::StoreError;
use crate::fs::write_outputs;
use crate::repository::GitCommand;
pub const ATTRIBUTES: &str = ".gitattributes";
pub const ATTRIBUTE: &str = "spec/**/*.yaml merge=loft";
pub const DRIVER: &str = "merge.loft.driver";
pub const LAUNCHER: &str = "loft";
pub const NAME: (&str, &str) = ("merge.loft.name", "Loft Specification Format merge driver");
#[must_use]
pub fn driver(launcher: &str) -> String {
format!(
"if {launcher} --version >/dev/null 2>&1; then {launcher} merge-driver %O %A %B %P; \
else echo \"loft merge-driver could not run, so %P was merged as text with conflict \
markers; make it run where git runs it, then run git checkout -m %P\" >&2; \
git merge-file -L ours -L base -L theirs %A %O %B; exit 1; fi"
)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Installed {
pub written: Vec<String>,
pub version: String,
}
pub fn install(git: &GitCommand, root: &Path, launcher: &str) -> Result<Installed, StoreError> {
git.prefix(root)?;
let version = git
.launcher_version(root, launcher)?
.ok_or_else(|| StoreError::Launcher(launcher.to_owned()))?;
let mut written = Vec::new();
let path = root.join(ATTRIBUTES);
let current = match fs::read_to_string(&path) {
Ok(text) => text,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => String::new(),
Err(error) => return Err(crate::error::io_error(&path)(error)),
};
if !current.lines().any(|line| line.trim() == ATTRIBUTE) {
let separator = if current.is_empty() || current.ends_with('\n') {
""
} else {
"\n"
};
let text = format!("{current}{separator}{ATTRIBUTE}\n");
write_outputs(root, &[(PathBuf::from(ATTRIBUTES), text.into_bytes())])?;
written.push(ATTRIBUTES.to_owned());
}
for (name, value) in [(DRIVER, driver(launcher).as_str()), NAME] {
if git.config_get(root, name)?.as_deref() != Some(value) {
git.config_set(root, name, value)?;
written.push(name.to_owned());
}
}
Ok(Installed { written, version })
}