1use crate::color::ColorChoice;
3use crate::config::{self, ConfigDir, ConfigFile as _};
4use crate::git::Git;
5use crate::lua;
6use crate::tree;
7use clap::{Parser, ValueEnum};
8use std::fs;
9use std::path::PathBuf;
10
11#[derive(Parser)]
13#[deny(missing_docs)]
14pub struct Cli {
15 #[arg(default_value = ".")]
17 pub path: PathBuf,
18
19 #[arg(long = "color")]
21 pub color_choice: Option<ColorChoice>,
22
23 #[arg(short = 'L', long)]
25 pub level: Option<usize>,
26
27 #[arg(long, num_args = 0..=1, default_missing_value = "config")]
29 pub edit_config: Option<EditConfig>,
30}
31
32#[derive(ValueEnum, Clone, Copy)]
34pub enum EditConfig {
35 Config,
37 Icons,
39 Colors,
41}
42
43impl Cli {
44 const EDITOR_ENV_VAR: &str = "FANCY_TREE_EDITOR";
46
47 pub fn run(&self) -> crate::Result {
49 if self.should_edit_file() {
50 self.edit_file()?;
51 return Ok(());
52 }
53
54 let git = Git::new(&self.path).expect("Should be able to read the git repository");
55
56 let lua_state = {
58 let mut builder = lua::state::Builder::new();
59 if let Some(ref git) = git {
60 builder = builder.with_git(git);
61 }
62 builder.build().expect("The lua state should be valid")
63 };
64
65 let config_dir = ConfigDir::new().expect("A config dir should be available");
67 let config = config_dir
68 .load_main(lua_state.to_inner())
69 .expect("The configuration should be valid");
70 let icons = config_dir
71 .load_icons(lua_state.to_inner())
72 .expect("The icon configuration should be valid");
73 let colors = config_dir
74 .load_colors(lua_state.to_inner())
75 .expect("The color configuration should be valid");
76
77 let color_choice = self
78 .color_choice
79 .or_else(|| config.as_ref().and_then(|config| config.color_choice()))
80 .unwrap_or_default();
81 let mut builder = tree::Builder::new(&self.path, color_choice);
82 if let Some(config) = config {
83 builder = builder.config(config);
84 }
85 if let Some(icons) = icons {
86 builder = builder.icons(icons);
87 }
88 if let Some(colors) = colors {
89 builder = builder.colors(colors);
90 }
91 if let Some(ref git) = git {
92 builder = builder.git(git);
93 }
94 if let Some(level) = self.level {
95 builder = builder.max_level(level);
96 }
97 let tree = builder.build();
98
99 lua_state.in_git_scope(|| tree.write_to_stdout().map_err(mlua::Error::external))?;
100
101 Ok(())
102 }
103
104 #[inline]
107 fn should_edit_file(&self) -> bool {
108 self.edit_config.is_some()
109 }
110
111 fn edit_file(&self) -> crate::Result {
114 let config_dir = ConfigDir::new()?;
115 fs::create_dir_all(config_dir.path())?;
116
117 let edit_config = self.edit_config.expect("Should have checked if Some");
118
119 let (file_path, contents) = match edit_config {
120 EditConfig::Config => (config_dir.main_path(), config::Main::DEFAULT_MODULE),
121 EditConfig::Icons => (config_dir.icons_path(), config::Icons::DEFAULT_MODULE),
122 EditConfig::Colors => (config_dir.colors_path(), config::Colors::DEFAULT_MODULE),
123 };
124
125 if !file_path.try_exists().unwrap_or(false) {
127 let _ = fs::write(&file_path, contents);
130 }
131
132 println!("Opening `{}`", file_path.display());
133
134 let finder = find_editor::Finder::with_extra_environment_variables([Self::EDITOR_ENV_VAR]);
135 const WAIT: bool = true;
137 finder.open_editor(file_path, WAIT)?;
138
139 Ok(())
140 }
141}
142
143pub fn run() -> crate::Result {
146 let cli = Cli::parse();
147 cli.run()
148}