use super::{Commit, FileIo, load_snapshot, stage};
use crate::xdg::Env;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct LernieGlobal {
root: PathBuf,
}
impl LernieGlobal {
pub fn resolve(env: &Env) -> Self {
Self {
root: env.lernie_config_root(),
}
}
pub fn models(&self) -> PathBuf {
self.root.join("models.yaml")
}
pub fn workflows_dir(&self) -> PathBuf {
self.root.join("workflows")
}
pub fn workflows(&self, io: &dyn FileIo) -> std::io::Result<Vec<PathBuf>> {
let mut files: Vec<PathBuf> = io
.list_dir(&self.workflows_dir())?
.into_iter()
.filter(|p| p.extension().and_then(|e| e.to_str()) == Some("yaml"))
.collect();
files.sort();
Ok(files)
}
pub fn new_workflow(&self, name: &str) -> Result<PathBuf, WorkflowNameError> {
validate_workflow_name(name)?;
Ok(self.workflows_dir().join(format!("{name}.yaml")))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkflowNameError {
Empty,
Separator,
DotLeading,
}
fn validate_workflow_name(name: &str) -> Result<(), WorkflowNameError> {
if name.is_empty() {
return Err(WorkflowNameError::Empty);
}
if name.starts_with('.') {
return Err(WorkflowNameError::DotLeading);
}
if name.contains('/') || name.contains('\\') {
return Err(WorkflowNameError::Separator);
}
Ok(())
}
#[derive(Debug, Clone)]
pub struct Editor {
path: PathBuf,
draft: String,
loaded: Option<u64>,
}
impl Editor {
pub fn load(path: PathBuf, io: &dyn FileIo) -> std::io::Result<Self> {
let (draft, loaded) = load_snapshot(io, &path)?;
Ok(Self {
path,
draft,
loaded,
})
}
pub fn seeded(path: PathBuf, seed: &[u8]) -> Self {
Self {
path,
draft: String::from_utf8_lossy(seed).into_owned(),
loaded: None,
}
}
pub(crate) fn path(&self) -> &Path {
&self.path
}
#[cfg(test)]
pub(crate) fn draft(&self) -> &str {
&self.draft
}
pub(crate) fn draft_mut(&mut self) -> &mut String {
&mut self.draft
}
pub fn set_draft(&mut self, text: String) {
self.draft = text;
}
pub fn is_new(&self) -> bool {
self.loaded.is_none()
}
pub fn reload(&mut self, io: &dyn FileIo) -> std::io::Result<()> {
let (draft, loaded) = load_snapshot(io, &self.path)?;
self.draft = draft;
self.loaded = loaded;
Ok(())
}
pub fn apply(&mut self, io: &dyn FileIo) -> Saved {
match self.apply_inner(io) {
Ok(saved) => saved,
Err(e) => Saved::Io {
error: e.to_string(),
},
}
}
fn apply_inner(&mut self, io: &dyn FileIo) -> std::io::Result<Saved> {
let staged = stage(io, &self.path, self.draft.as_bytes())?;
match staged.commit(io, self.loaded)? {
Commit::Ok(hash) => {
self.loaded = Some(hash);
Ok(Saved::Ok)
}
Commit::Conflict => Ok(Saved::Conflict),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Saved {
Ok,
Conflict,
Io { error: String },
}
#[cfg(test)]
mod tests;