use std::path::PathBuf;
use anyhow::{Result, bail};
use clap::Args;
use org_core::{ImportStatus, Settings, default_config_path, parse_config_file};
use crate::importer::{ImportExecution, import_config};
const USUAL_LOCATIONS: [&str; 7] = [
"~/.config/nvim",
"~/.doom.d",
"~/.config/doom",
"~/.emacs.d",
"~/.config/emacs",
"~/.emacs",
"~/.spacemacs",
];
#[derive(Args, Debug)]
pub struct ImportCommand {
paths: Vec<PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
stdout: bool,
#[arg(long)]
force: bool,
#[arg(long)]
verify: bool,
}
impl ImportCommand {
pub fn execute(self, config: Option<String>, _root_directory: Option<String>) -> Result<()> {
if self.paths.is_empty() {
let home = PathBuf::from(std::env::var_os("HOME").unwrap_or_default());
let found: Vec<&str> = USUAL_LOCATIONS
.iter()
.copied()
.filter(|location| home.join(&location[2..]).exists())
.collect();
if found.is_empty() {
bail!("name your Emacs or Neovim config: agd setup import ~/.config/nvim");
}
bail!(
"name the config to import, for example: agd setup import {}\nfound: {}",
found[0],
found.join(", ")
);
}
let destination = match config {
Some(path) => PathBuf::from(path),
None => default_config_path()?,
};
let exists = destination.exists();
let base = if exists {
parse_config_file(&destination)?
} else {
Settings::default()
};
let preview = self.dry_run || self.stdout;
let write = !preview && (!exists || self.force);
let execution = import_config(
&self.paths,
&base,
!exists,
&destination,
write,
self.verify,
)?;
self.print(&execution);
if !preview && !write && !execution.diff.is_empty() {
bail!(
"{} exists; review the diff above and rerun with --force to replace it",
destination.display()
);
}
if write {
println!("wrote {}", destination.display());
}
Ok(())
}
fn print(&self, execution: &ImportExecution) {
if self.stdout {
print!("{}", execution.toml);
} else if !execution.diff.is_empty() {
print!("{}", execution.diff);
}
let sources = &execution.sources;
println!(
"read {} {} file(s):",
sources.editor.name(),
sources.files.len()
);
for file in &sources.files {
println!(" {}", file.display());
}
for note in &sources.notes {
println!(" {note}");
}
let settings = &execution.result.settings;
println!(
"imported roots {}, {} agenda entries, {} capture templates, {} views",
settings.roots.join(", "),
settings.agenda_files.len(),
settings.captures.len(),
settings.views.len()
);
for diagnostic in &execution.result.diagnostics {
println!(
" {} {} {}",
mark(&diagnostic.status),
diagnostic.key,
diagnostic.message
);
}
for result in &execution.verification {
println!(
" {} view {} {} {}",
mark(&result.status),
result.key,
result.description,
result.detail
);
}
}
}
fn mark(status: &ImportStatus) -> &'static str {
match status {
ImportStatus::Exact => "✓",
ImportStatus::Difference => "~",
ImportStatus::Unsupported => "✗",
}
}