0
0
mirror of https://github.com/TeFiLeDo/listload.git synced 2025-04-27 14:39:32 +02:00
listload/src/target_list.rs

130 lines
3.6 KiB
Rust

use std::{
fs::{create_dir_all, File, OpenOptions},
io::{BufReader, BufWriter},
path::{Path, PathBuf},
};
use anyhow::{ensure, Context, Result};
use regex::Regex;
use serde::{Deserialize, Serialize};
use crate::{target::Target, PROJ_DIRS};
#[derive(Debug)]
pub struct TargetList {
inner: InnerTargetList,
file: File,
}
impl TargetList {
pub fn name(&self) -> &str {
&self.inner.name
}
}
impl TargetList {
fn list_dir() -> PathBuf {
let dirs = PROJ_DIRS.get().expect("directories not initialized");
let mut path = dirs.data_dir().to_path_buf();
path.push("lists");
path
}
fn list_file(dir: &Path, name: &str) -> PathBuf {
let mut path = dir.to_path_buf();
path.push(name);
path.set_extension("json");
path
}
pub fn exists(name: &str) -> bool {
Self::exists_in(name, &Self::list_dir())
}
pub fn exists_in(name: &str, directory: &Path) -> bool {
Self::list_file(directory, name).is_file()
}
pub fn new(name: &str, comment: Option<&str>) -> anyhow::Result<Self> {
let dirs = PROJ_DIRS.get().expect("directories not initialized");
let dir = Self::list_dir();
ensure!(
dir.is_dir() || !dir.exists(),
"list directory is neither a directory nor nonexistant"
);
if !dir.exists() {
create_dir_all(&dir).context("failed to create list directory")?;
}
Self::new_in(name, comment, &dir)
}
pub fn new_in(name: &str, comment: Option<&str>, directory: &Path) -> anyhow::Result<Self> {
ensure!(directory.is_dir(), "directory isn't a directory");
ensure!(name != "none", "name is \"none\"");
let name_regex = Regex::new("^[a-z](_?[a-z0-9])+$").expect("correct name regex");
ensure!(name_regex.is_match(name), "name is forbidden");
let path = Self::list_file(directory, name);
ensure!(!path.is_file(), "target list file already exists");
ensure!(
!path.exists(),
"target list file is neither a file nor nonexistent",
);
let file = File::create(path).context("failed to create target list file")?;
let mut ret = Self {
file,
inner: InnerTargetList {
name: name.to_string(),
comment: comment.map(ToString::to_string),
targets: Vec::new(),
},
};
ret.save().context("failed to save no target list file")?;
Ok(ret)
}
pub fn load(name: &str) -> anyhow::Result<Self> {
Self::load_in(name, &Self::list_dir())
}
pub fn load_in(name: &str, directory: &Path) -> anyhow::Result<Self> {
ensure!(directory.is_dir(), "directory isn't a directory");
let path = Self::list_file(directory, name);
let file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.append(false)
.truncate(false)
.open(path)
.context("failed to open target list file")?;
let inner: InnerTargetList = serde_json::from_reader(BufReader::new(&file))
.context("failed to parse target list file")?;
ensure!(inner.name == name, "list name missmatch");
Ok(Self { inner, file })
}
pub fn save(&mut self) -> anyhow::Result<()> {
self.file.set_len(0);
serde_json::to_writer(BufWriter::new(&self.file), &self.inner)
.context("failed to save target list")
}
}
#[derive(Debug, Deserialize, Serialize)]
struct InnerTargetList {
name: String,
comment: Option<String>,
targets: Vec<Target>,
}