use std::path::{Path, PathBuf};
pub const PICK_PREFIX: &str = "ek-pick-";
pub const PICK_SUFFIX: &str = ".list";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PickListErr {
Empty,
BadLine(usize, String),
Io(String),
}
impl std::fmt::Display for PickListErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Empty => write!(f, "pick list empty (select at least one plugin)"),
Self::BadLine(n, line) => {
write!(f, "pick list line {n} has unexpected whitespace: {line:?}")
}
Self::Io(e) => write!(f, "pick list io: {e}"),
}
}
}
impl std::error::Error for PickListErr {}
fn unique_name() -> String {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
format!("{PICK_PREFIX}{}{}{PICK_SUFFIX}", std::process::id(), nanos)
}
pub fn read(path: &Path) -> Result<Vec<String>, PickListErr> {
let text = std::fs::read_to_string(path).map_err(|e| PickListErr::Io(e.to_string()))?;
let text = text.strip_prefix('\u{feff}').unwrap_or(&text);
let mut names = Vec::new();
for (i, raw) in text.lines().enumerate() {
let line = raw.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if line.chars().any(|c| c.is_whitespace()) {
return Err(PickListErr::BadLine(i + 1, raw.to_string()));
}
names.push(line.to_string());
}
if names.is_empty() {
return Err(PickListErr::Empty);
}
Ok(names)
}
pub fn write_in(dir: &Path, names: &[&str]) -> Result<PathBuf, PickListErr> {
if names.is_empty() {
return Err(PickListErr::Empty);
}
for (i, n) in names.iter().enumerate() {
if n.chars().any(|c| c.is_whitespace()) || n.is_empty() || n.starts_with('#') {
return Err(PickListErr::BadLine(i + 1, (*n).to_string()));
}
}
std::fs::create_dir_all(dir).map_err(|e| PickListErr::Io(e.to_string()))?;
let path = dir.join(unique_name());
let mut text = String::from("# ek launch pick\n");
for n in names {
text.push_str(n);
text.push('\n');
}
std::fs::write(&path, text).map_err(|e| PickListErr::Io(e.to_string()))?;
Ok(path)
}