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#[command(version)]
14#[deny(missing_docs)]
15pub struct Cli {
16 #[arg(default_value = ".")]
18 pub path: PathBuf,
19
20 #[arg(long = "color")]
22 pub color_choice: Option<ColorChoice>,
23
24 #[arg(short = 'L', long)]
26 pub level: Option<usize>,
27
28 #[arg(long, alias = "unset-level", conflicts_with = "level")]
32 pub max_level: bool,
33
34 #[arg(long, num_args = 0..=1, default_missing_value = "config")]
36 pub edit_config: Option<EditConfig>,
37}
38
39#[derive(ValueEnum, Clone, Copy)]
41pub enum EditConfig {
42 Config,
44 Icons,
46 Colors,
48}
49
50impl Cli {
51 const EDITOR_ENV_VAR: &str = "FANCY_TREE_EDITOR";
53
54 pub fn run(&self) -> crate::Result {
56 if let Some(edit_config) = self.edit_config {
58 return self.edit_file(edit_config);
59 }
60
61 self.run_tree()
62 }
63
64 fn run_tree(&self) -> crate::Result {
66 let git = Git::new(&self.path).expect("Should be able to read the git repository");
67
68 let lua_state = {
70 let mut builder = lua::state::Builder::new();
71 if let Some(ref git) = git {
72 builder = builder.with_git(git);
73 }
74 builder.build().expect("The lua state should be valid")
75 };
76
77 let config_dir = ConfigDir::new().expect("A config dir should be available");
79
80 let lua_inner = lua_state.to_inner();
81 let config = config_dir
82 .load_main(lua_inner)
83 .expect("The configuration should be valid");
84 let icons = config_dir
85 .load_icons(lua_inner)
86 .expect("The icon configuration should be valid");
87 let colors = config_dir
88 .load_colors(lua_inner)
89 .expect("The color configuration should be valid");
90
91 let mut builder = tree::Builder::new(&self.path);
92
93 if let Some(color_choice) = self.color_choice {
95 builder = builder.color_choice(color_choice);
96 }
97
98 if let Some(config) = config {
100 builder = builder.config(config);
101 }
102 if let Some(icons) = icons {
103 builder = builder.icons(icons);
104 }
105 if let Some(colors) = colors {
106 builder = builder.colors(colors);
107 }
108
109 if let Some(ref git) = git {
110 builder = builder.git(git);
111 }
112
113 if let Some(level) = self.level {
114 builder = builder.max_level(level);
115 } else if self.max_level {
116 builder = builder.unset_level();
117 }
118
119 let tree = builder.build();
120
121 lua_state.in_git_scope(|| tree.write_to_stdout().map_err(mlua::Error::external))?;
122
123 Ok(())
124 }
125
126 fn edit_file(&self, edit_config: EditConfig) -> crate::Result {
129 let config_dir = ConfigDir::new()?;
130 fs::create_dir_all(config_dir.path())?;
131
132 let (file_path, default_contents) = match edit_config {
133 EditConfig::Config => (config_dir.main_path(), config::Main::DEFAULT_MODULE),
134 EditConfig::Icons => (config_dir.icons_path(), config::Icons::DEFAULT_MODULE),
135 EditConfig::Colors => (config_dir.colors_path(), config::Colors::DEFAULT_MODULE),
136 };
137
138 if !file_path.try_exists().unwrap_or(false) {
140 let _ = fs::write(&file_path, default_contents);
143 }
144
145 println!("Opening `{}`", file_path.display());
146
147 let finder = find_editor::Finder::with_extra_environment_variables([Self::EDITOR_ENV_VAR]);
148 const WAIT: bool = true;
150 finder.open_editor(file_path, WAIT)?;
151
152 Ok(())
153 }
154}
155
156pub fn run() -> crate::Result {
159 Cli::parse().run()
160}