aboutsummaryrefslogtreecommitdiff
path: root/dotup_cli/src/commands/status.rs
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2021-12-24 21:01:11 +0000
committerdiogo464 <[email protected]>2021-12-24 21:01:11 +0000
commitffcb43df8f39a55be468cf4bdfecd72dd026d940 (patch)
tree252bb880ebb268a88c3c043f21ff4c923d905451 /dotup_cli/src/commands/status.rs
parent793b726b1393210bbfc934bb8236066ae52ebdab (diff)
initial implementation of status command
closes #1
Diffstat (limited to 'dotup_cli/src/commands/status.rs')
-rw-r--r--dotup_cli/src/commands/status.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/dotup_cli/src/commands/status.rs b/dotup_cli/src/commands/status.rs
new file mode 100644
index 0000000..e05ada9
--- /dev/null
+++ b/dotup_cli/src/commands/status.rs
@@ -0,0 +1,177 @@
1use std::path::{Path, PathBuf};
2
3use ansi_term::Colour;
4use clap::Parser;
5use dotup::{Depot, Link};
6
7use crate::{utils, Config};
8
9/// Shows information about links
10///
11/// If a link is created for a file that already had a link then the old link will be overwritten.
12/// By default creating a link to a directory will recursively link all files under that
13/// directory, to actually link a directory use the --directory flag.
14#[derive(Parser)]
15pub struct Opts {
16 /// The location where links will be installed to.
17 /// Defaults to the home directory.
18 #[clap(long)]
19 install_base: Option<PathBuf>,
20
21 /// The paths to show the status of
22 paths: Vec<PathBuf>,
23}
24
25#[derive(Debug)]
26struct State {
27 max_depth: u32,
28 install_base: PathBuf,
29}
30
31pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
32 let mut depot = utils::read_depot(&config.archive_path)?;
33
34 // walk dir
35 // if node is file:
36 // if linked
37 // print name in green and destination blue
38 // if invalid
39 // print name and destination red
40 // if not linked
41 // print name in gray
42 // if node is directory:
43 // if linked
44 // print name in green and destination blue
45 // if invalid
46 // print name and destination red
47 // if not linked:
48 // print name in gray
49 // if contains files that are linked/invalid:
50 // recurse into directory
51 //
52
53 let depot_base = depot.base_path();
54 let mut paths = Vec::new();
55 for path in opts.paths {
56 let canonical = dotup::utils::weakly_canonical(&path);
57 if canonical.starts_with(depot_base) {
58 paths.push(canonical);
59 } else {
60 log::warn!("Path '{}' is outside the depot", path.display());
61 }
62 }
63
64 if paths.is_empty() {
65 paths.push(PathBuf::from("."));
66 }
67
68 let state = State {
69 max_depth: u32::MAX,
70 install_base: opts
71 .install_base
72 .unwrap_or(utils::home_directory().unwrap()),
73 };
74
75 let (directories, files) = utils::collect_read_dir_split(".")?;
76 for path in directories.into_iter().chain(files.into_iter()) {
77 display_status_path(&depot, &state, &path, 0);
78 }
79
80 utils::write_depot(&depot)?;
81
82 Ok(())
83}
84
85fn display_status_path(depot: &Depot, state: &State, path: &Path, depth: u32) {
86 if depth == state.max_depth {
87 return;
88 }
89
90 if path.is_dir() {
91 display_status_directory(depot, state, path, depth);
92 } else {
93 display_status_file(depot, state, path, depth);
94 }
95}
96
97fn display_status_directory(depot: &Depot, state: &State, path: &Path, depth: u32) {
98 assert!(path.is_dir());
99 if depth == state.max_depth || !depot.subpath_has_links(path) {
100 return;
101 }
102
103 if let Some(link) = depot.get_link_by_path(path) {
104 print_link(depot, state, link, depth);
105 } else {
106 for entry in std::fs::read_dir(path).unwrap() {
107 let entry = match entry {
108 Ok(entry) => entry,
109 Err(_) => continue,
110 };
111 let entry_path = entry.path().canonicalize().unwrap();
112 let entry_path_stripped = entry_path
113 .strip_prefix(std::env::current_dir().unwrap())
114 .unwrap();
115
116 print_identation(depth);
117 println!(
118 "{}",
119 Colour::Yellow.paint(&format!("{}", path.file_name().unwrap().to_string_lossy()))
120 );
121
122 display_status_path(depot, state, &entry_path_stripped, depth + 1);
123 }
124 }
125}
126
127fn display_status_file(depot: &Depot, state: &State, path: &Path, depth: u32) {
128 print_identation(depth);
129 if let Some(link) = depot.get_link_by_path(path) {
130 print_link(depot, state, link, depth);
131 } else {
132 print_unlinked(path, depth);
133 }
134}
135
136fn print_link(depot: &Depot, state: &State, link: &Link, depth: u32) {
137 let origin = link.origin();
138 let dest = link.destination();
139 let filename = match origin.file_name() {
140 Some(filename) => filename,
141 None => return,
142 };
143
144 print_identation(depth);
145 if depot.is_link_installed(link, &state.install_base) {
146 println!(
147 "{} -> {}",
148 Colour::Green.paint(&format!("{}", filename.to_string_lossy())),
149 Colour::Blue.paint(&format!("{}", dest.display())),
150 );
151 } else {
152 println!(
153 "{}",
154 Colour::Red.paint(&format!(
155 "{} -> {}",
156 filename.to_string_lossy(),
157 dest.display()
158 ))
159 );
160 }
161}
162
163fn print_unlinked(path: &Path, depth: u32) {
164 let filename = match path.file_name() {
165 Some(filename) => filename,
166 None => return,
167 };
168
169 print_identation(depth);
170 println!("{}", filename.to_string_lossy());
171}
172
173fn print_identation(depth: u32) {
174 for i in 0..depth {
175 print!(" ");
176 }
177}