use crate::model::settings;
use artifact::{layout::Layout, stamp};
use std::{collections::HashSet, path::PathBuf};
fn dir() -> Option<PathBuf> {
settings::dir().ok().map(|dir| dir.join("layouts"))
}
fn file(id: &str) -> Option<PathBuf> {
Some(dir()?.join(format!("{id}.toml")))
}
pub fn all() -> Vec<Layout> {
let Some(dir) = dir() else {
return Vec::new();
};
let Ok(entries) = std::fs::read_dir(dir) else {
return Vec::new();
};
let mut layouts: Vec<Layout> = entries
.flatten()
.map(|entry| entry.path())
.filter(|path| path.extension().is_some_and(|ext| ext == "toml"))
.filter_map(|path| {
let body = std::fs::read_to_string(&path).ok()?;
let mut layout: Layout = toml::from_str(&body).ok()?;
layout.touched = stamp::of(&path);
if layout.id.is_empty() {
layout.id = path.file_stem()?.to_str()?.to_owned();
}
Some(layout)
})
.collect();
layouts.sort_by_key(|layout| std::cmp::Reverse(layout.touched));
layouts
}
pub fn read(id: &str) -> Option<Layout> {
let path = file(id)?;
let body = std::fs::read_to_string(&path).ok()?;
let mut layout: Layout = toml::from_str(&body).ok()?;
layout.touched = stamp::of(&path);
if layout.id.is_empty() {
layout.id = id.to_owned();
}
Some(layout)
}
pub fn create(name: &str, over: artifact::layout::Member) -> Option<Layout> {
let dir = dir()?;
std::fs::create_dir_all(&dir).ok()?;
let taken: HashSet<String> = all().into_iter().map(|layout| layout.name).collect();
let name = match name.is_empty() {
false => name.to_owned(),
true => artifact::layout::next_name(&taken),
};
let id = (stamp::now()..)
.map(|stamp| stamp.to_string())
.find(|id| !dir.join(format!("{id}.toml")).exists())?;
let mut layout = Layout::new(id, &name, over);
save(&mut layout);
Some(layout)
}
pub fn save(layout: &mut Layout) {
let (Some(path), Ok(body)) = (file(&layout.id), toml::to_string_pretty(&*layout)) else {
return;
};
if let Some(dir) = path.parent() {
let _ = std::fs::create_dir_all(dir);
}
if std::fs::write(&path, body).is_ok() {
layout.touched = stamp::now();
}
}
pub fn remove(id: &str) {
if let Some(path) = file(id) {
let _ = std::fs::remove_file(path);
}
}