use crate::cli::open::core::{Open, OpenOption};
use crate::log::Log;
use crate::tpl::doc::INIT_CONFIG;
use crate::utils::conf::read;
use anyhow::anyhow;
use dyn_fmt::AsStrFormatExt;
use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};
use std::fs;
use std::{
collections::BTreeMap,
io::{Error, ErrorKind},
path::{Path, PathBuf},
};
#[derive(Deserialize, Debug, Serialize)]
pub struct Config {
pub loc: Option<PathBuf>,
pub map: BTreeMap<String, Open>,
}
impl Config {
pub fn new(loc: Option<String>) -> anyhow::Result<Self> {
match loc {
Some(loc) => {
let path = Path::new(&loc).to_path_buf();
if path.is_file() {
Ok(read(&path)?)
} else {
Err(anyhow::Error::msg(
Log::Warn(&"Can not find config file by {}".format(&[path.display()]))
.to_string(),
))
}
}
None => {
let home = dirs::home_dir();
if let Some(home) = home {
Ok(read(&home.join(".doc.toml"))?)
} else {
Err(anyhow!("Can not find home dir."))
}
}
}
}
pub fn new_empty() -> Config {
Config { loc: None, map: BTreeMap::new() }
}
pub fn save(&self) -> anyhow::Result<()> {
let Config { loc, map } = self;
let mut content = String::from(INIT_CONFIG);
if !map.is_empty() {
content = self.to_string()?
}
if let Some(loc) = loc {
fs::write(loc, content)?
}
Ok(())
}
pub fn to_string(&self) -> Result<String, Error> {
toml::to_string(self).map_err(|err| Error::new(ErrorKind::InvalidData, err))
}
pub fn open(&self, name: &str, option: &OpenOption) -> anyhow::Result<()> {
match self.find(name) {
Some(doc) => {
doc.open(option)?;
Ok(())
}
None => {
Log::Warn(
&"Can not find any doc by '{}', similar docs found below:".format(&[name]),
)
.println();
self.walk(|n, doc| {
if doc.contains(n, &name[0..1]) {
println!("{}", doc.get_printed_name(n).green());
}
});
Ok(())
}
}
}
pub fn view(&self, name: &str, detail: bool) {
if let Some(doc) = self.map.get(name) {
doc.view(name, detail);
}
}
pub fn walk<T>(&self, mut call: T)
where
T: FnMut(&String, &Open),
{
for (n, doc) in &self.map {
call(n, doc)
}
}
pub fn find(&self, name: &str) -> Option<&Open> {
let mut doc_name = String::new();
match self.map.get(name) {
Some(_) => doc_name = name.to_string(),
None => {
self.walk(|n, doc| {
if let Open { full: Some(full), .. } = doc {
if full == name {
doc_name = n.to_string()
}
}
});
}
}
if doc_name.is_empty() {
None
} else {
Some(self.map.get(&doc_name).unwrap())
}
}
}
impl From<(PathBuf, String)> for Config {
fn from(value: (PathBuf, String)) -> Self {
let (loc, str) = value;
Config {
loc: Some(loc),
map: if let Ok(config) = toml::from_str(&str) { config } else { BTreeMap::new() },
}
}
}