aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs368
1 files changed, 316 insertions, 52 deletions
diff --git a/src/main.rs b/src/main.rs
index e2a659b..1559ede 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,7 @@
1#![feature(try_blocks)] 1#![feature(try_blocks)]
2
3// TODO: rewrite all errors so they start with lower case
4
2pub mod depot { 5pub mod depot {
3 use std::{ 6 use std::{
4 collections::HashSet, 7 collections::HashSet,
@@ -7,7 +10,7 @@ pub mod depot {
7 path::{Path, PathBuf}, 10 path::{Path, PathBuf},
8 }; 11 };
9 12
10 use slotmap::SlotMap; 13 use slotmap::{Key, SlotMap};
11 14
12 pub use disk::{read, write}; 15 pub use disk::{read, write};
13 16
@@ -134,6 +137,10 @@ pub mod depot {
134 } 137 }
135 } 138 }
136 139
140 /// moves the link specified by `link_id` to the path at `destination`.
141 /// if the link is already at the destination nothing is done.
142 /// if the destination is another link that that link is removed.
143 /// if the destination is under another link then an error is returned.
137 pub fn move_link( 144 pub fn move_link(
138 &mut self, 145 &mut self,
139 link_id: LinkID, 146 link_id: LinkID,
@@ -158,10 +165,12 @@ pub mod depot {
158 let node_parent_id = node.parent; 165 let node_parent_id = node.parent;
159 let node_link_id = *node_link_id; 166 let node_link_id = *node_link_id;
160 assert_ne!(link_id, node_link_id); 167 assert_ne!(link_id, node_link_id);
161 self.remove(node_link_id);
162 self.node_child_remove(link_parent_node_id, link_node_id); 168 self.node_child_remove(link_parent_node_id, link_node_id);
163 self.node_child_add(node_parent_id, link_node_id); 169 self.node_child_add(node_parent_id, link_node_id);
164 self.node_set_parent(link_node_id, node_parent_id); 170 self.node_set_parent(link_node_id, node_parent_id);
171 self.remove(node_link_id);
172 let new_origin = self.node_build_path(link_node_id);
173 self.links[link_id].origin = new_origin;
165 Ok(()) 174 Ok(())
166 } 175 }
167 NodeKind::Directory(..) => Err(anyhow::anyhow!( 176 NodeKind::Directory(..) => Err(anyhow::anyhow!(
@@ -176,8 +185,10 @@ pub mod depot {
176 )), 185 )),
177 NodeKind::Directory(_) => { 186 NodeKind::Directory(_) => {
178 let new_node_id = self.node_create_link(destination, link_id); 187 let new_node_id = self.node_create_link(destination, link_id);
188 let new_origin = self.node_build_path(new_node_id);
179 self.node_remove(link_node_id); 189 self.node_remove(link_node_id);
180 self.links[link_id].node_id = new_node_id; 190 self.links[link_id].node_id = new_node_id;
191 self.links[link_id].origin = new_origin;
181 Ok(()) 192 Ok(())
182 } 193 }
183 } 194 }
@@ -207,6 +218,14 @@ pub mod depot {
207 Ok(self.search_unchecked(&origin)) 218 Ok(self.search_unchecked(&origin))
208 } 219 }
209 220
221 /// finds the link at origin.
222 pub fn find(&self, origin: impl AsRef<Path>) -> anyhow::Result<Option<LinkID>> {
223 match self.search(origin)? {
224 SearchResult::Found(link_id) => Ok(Some(link_id)),
225 SearchResult::Ancestor(_) | SearchResult::NotFound => Ok(None),
226 }
227 }
228
210 /// returns an iterator for all the links at or under the given path. 229 /// returns an iterator for all the links at or under the given path.
211 pub fn links_under( 230 pub fn links_under(
212 &self, 231 &self,
@@ -391,6 +410,22 @@ pub mod depot {
391 } 410 }
392 } 411 }
393 412
413 /// build the path that references this node.
414 fn node_build_path(&self, node_id: NodeID) -> PathBuf {
415 fn recursive_helper(nodes: &SlotMap<NodeID, Node>, nid: NodeID, pbuf: &mut PathBuf) {
416 if nid.is_null() {
417 return;
418 }
419 let parent_id = nodes[nid].parent;
420 recursive_helper(nodes, parent_id, pbuf);
421 pbuf.push(&nodes[nid].comp);
422 }
423
424 let mut node_path = PathBuf::default();
425 recursive_helper(&self.nodes, node_id, &mut node_path);
426 node_path
427 }
428
394 fn node_set_parent(&mut self, node_id: NodeID, parent: NodeID) { 429 fn node_set_parent(&mut self, node_id: NodeID, parent: NodeID) {
395 self.nodes[node_id].parent = parent; 430 self.nodes[node_id].parent = parent;
396 } 431 }
@@ -461,6 +496,10 @@ pub mod depot {
461 } 496 }
462 } 497 }
463 498
499 /// a verified path is a path that:
500 /// + is not empty
501 /// + is relative
502 /// + does not contain Prefix/RootDir/ParentDir
464 fn verify_path(path: &Path) -> anyhow::Result<()> { 503 fn verify_path(path: &Path) -> anyhow::Result<()> {
465 // make sure the path is not empty 504 // make sure the path is not empty
466 // make sure the path is relative 505 // make sure the path is relative
@@ -523,11 +562,14 @@ pub mod depot {
523 let f1 = depot.create("d1/f1", "d1/f1").unwrap(); 562 let f1 = depot.create("d1/f1", "d1/f1").unwrap();
524 let _f2 = depot.create("d1/f2", "d1/f2").unwrap(); 563 let _f2 = depot.create("d1/f2", "d1/f2").unwrap();
525 564
565 depot.move_link(f1, "").unwrap_err();
526 depot.move_link(f1, "d1/f2/f1").unwrap_err(); 566 depot.move_link(f1, "d1/f2/f1").unwrap_err();
527 depot.move_link(f1, "d1").unwrap_err();
528 567
529 depot.move_link(f1, "").unwrap(); 568 depot.move_link(f1, "d1/f2").unwrap();
530 depot.move_link(f1, "d2/f1").unwrap(); 569 depot.move_link(f1, "f1").unwrap();
570 assert_eq!(depot.link_view(f1).origin(), Path::new("f1"));
571 depot.move_link(f1, "f2").unwrap();
572 assert_eq!(depot.link_view(f1).origin(), Path::new("f2"));
531 } 573 }
532 574
533 #[test] 575 #[test]
@@ -576,9 +618,13 @@ mod dotup {
576 path::{Path, PathBuf}, 618 path::{Path, PathBuf},
577 }; 619 };
578 620
621 use ansi_term::Color;
579 use anyhow::Context; 622 use anyhow::Context;
580 623
581 use crate::depot::{self, Depot, LinkID}; 624 use crate::{
625 depot::{self, Depot, LinkID},
626 utils,
627 };
582 628
583 #[derive(Debug)] 629 #[derive(Debug)]
584 struct CanonicalPair { 630 struct CanonicalPair {
@@ -658,7 +704,7 @@ mod dotup {
658 if already_linked.contains(&pair.link_id) { 704 if already_linked.contains(&pair.link_id) {
659 continue; 705 continue;
660 } 706 }
661 self.install_symlink(&pair.origin, &pair.destination)?; 707 self.symlink_install(&pair.origin, &pair.destination)?;
662 already_linked.insert(pair.link_id); 708 already_linked.insert(pair.link_id);
663 } 709 }
664 }; 710 };
@@ -674,7 +720,7 @@ mod dotup {
674 let origin = self.prepare_origin_path(origin.as_ref())?; 720 let origin = self.prepare_origin_path(origin.as_ref())?;
675 let canonical_pairs = self.canonical_pairs_under(&origin)?; 721 let canonical_pairs = self.canonical_pairs_under(&origin)?;
676 for pair in canonical_pairs { 722 for pair in canonical_pairs {
677 self.uninstall_symlink(&pair.origin, &pair.destination)?; 723 self.symlink_uninstall(&pair.origin, &pair.destination)?;
678 } 724 }
679 }; 725 };
680 if let Err(e) = uninstall_result { 726 if let Err(e) = uninstall_result {
@@ -686,50 +732,182 @@ mod dotup {
686 } 732 }
687 } 733 }
688 734
689 pub fn mv(&mut self, from: impl Iterator<Item = impl AsRef<Path>>, to: impl AsRef<Path>) { 735 pub fn mv(
690 let to = to.as_ref(); 736 &mut self,
691 let from: Vec<_> = from.map(|p| p.as_ref().to_owned()).collect(); 737 origins: impl Iterator<Item = impl AsRef<Path>>,
692 match from.as_slice() { 738 destination: impl AsRef<Path>,
693 [] => unreachable!(), 739 ) {
694 [from] => self.mv_one(from, to), 740 let origins = {
695 [from @ ..] => self.mv_many(from, to), 741 let mut v = Vec::new();
742 for origin in origins {
743 match self.prepare_origin_path(origin.as_ref()) {
744 Ok(origin) => v.push(origin),
745 Err(e) => {
746 println!("invalid link {} : {e}", origin.as_ref().display());
747 return;
748 }
749 }
750 }
751 v
752 };
753 let destination = destination.as_ref();
754
755 // if we are moving multiple links then the destination must be a directory
756 if origins.len() > 1 && destination.is_dir() {
757 println!("destination must be a directory");
758 return;
759 }
760
761 for origin in origins {
762 if let Err(e) = self.mv_one(&origin, destination) {
763 println!("error moving link {} : {e}", origin.display());
764 }
765 }
766 }
767
768 fn mv_one(&mut self, origin: &Path, destination: &Path) -> anyhow::Result<()> {
769 let link_id = match self.depot.find(origin)? {
770 Some(link_id) => link_id,
771 None => {
772 return Err(anyhow::anyhow!(format!(
773 "{} is not a link",
774 origin.display()
775 )))
776 }
777 };
778 let is_installed = self.symlink_is_installed_by_link_id(link_id)?;
779 let original_origin = self.depot.link_view(link_id).origin().to_owned();
780 self.depot.move_link(link_id, destination)?;
781 // move the actual file on disk
782 if let Err(e) = std::fs::rename(origin, destination).context("Failed to move file") {
783 // unwrap: moving the link back to its origin place has to work
784 self.depot.move_link(link_id, original_origin).unwrap();
785 return Err(e);
786 }
787 // reinstall because we just moved the origin
788 if is_installed {
789 self.symlink_install_by_link_id(link_id)
790 .context("failed to reinstall link while moving")?;
791 }
792 Ok(())
793 }
794
795 pub fn status(&self) {
796 let status_result: anyhow::Result<()> = try {
797 let curr_dir = utils::current_working_directory();
798 let (dirs, files) = utils::collect_read_dir_split(curr_dir)?;
799 };
800 if let Err(e) = status_result {
801 println!("error while displaying status : {e}");
696 } 802 }
697 } 803 }
698 804
699 fn mv_one(&mut self, from: &Path, to: &Path) {} 805 pub fn status2(&self) {
806 let status_result: anyhow::Result<()> = try {
807 let curr_dir = &std::env::current_dir()?;
808 let (dirs, files) = utils::collect_read_dir_split(curr_dir)?;
809 for path in dirs.iter().chain(files.iter()) {
810 self.print_status_for(&path, 0)?;
811 }
812 };
813 if let Err(e) = status_result {
814 println!("error while displaying status : {e}");
815 }
816 }
700 817
701 fn mv_many(&mut self, from: &[PathBuf], to: &Path) {} 818 fn print_status_for(&self, path: &Path, depth: u32) -> anyhow::Result<()> {
819 fn print_depth(d: u32) {
820 for _ in 0..d {
821 print!(" ");
822 }
823 }
824
825 let origin = self.prepare_origin_path(path)?;
826 if path.is_dir() {
827 print_depth(depth);
828 let file_name = path.file_name().unwrap().to_str().unwrap_or_default();
829 if let Some(link_id) = self.depot.find(&origin)? {
830 let installed = self.symlink_is_installed_by_link_id(link_id)?;
831 let link_view = self.depot.link_view(link_id);
832 let destination = link_view.destination().display().to_string();
833 let color = if installed { Color::Green } else { Color::Red };
834 println!(
835 "{}/ ---> {}",
836 color.paint(file_name),
837 Color::Blue.paint(destination)
838 );
839 } else {
840 println!("{}/", file_name);
841 let (dirs, files) = utils::collect_read_dir_split(path)?;
842 for path in dirs.iter().chain(files.iter()) {
843 self.print_status_for(&path, depth + 1)?;
844 }
845 }
846 } else if path.is_file() || path.is_symlink() {
847 print_depth(depth);
848 let file_name = path.file_name().unwrap().to_str().unwrap_or_default();
849 if let Some(link_id) = self.depot.find(&origin)? {
850 let installed = self.symlink_is_installed_by_link_id(link_id)?;
851 let link_view = self.depot.link_view(link_id);
852 let destination = link_view.destination().display().to_string();
853 let color = if installed { Color::Green } else { Color::Red };
854
855 println!(
856 "{} ---> {}",
857 color.paint(file_name),
858 Color::Blue.paint(destination)
859 );
860 } else {
861 println!("{}", file_name);
862 }
863 }
864 Ok(())
865 }
702 866
703 fn prepare_origin_path(&self, origin: &Path) -> anyhow::Result<PathBuf> { 867 fn prepare_origin_path(&self, origin: &Path) -> anyhow::Result<PathBuf> {
704 let canonical = origin 868 let canonical = utils::weakly_canonical(origin);
705 .canonicalize()
706 .context("Failed to canonicalize origin path")?;
707 let relative = canonical 869 let relative = canonical
708 .strip_prefix(&self.depot_dir) 870 .strip_prefix(&self.depot_dir)
709 .context("Invalid origin path, not under depot directory")?; 871 .context("Invalid origin path, not under depot directory")?;
710 Ok(relative.to_owned()) 872 Ok(relative.to_owned())
711 } 873 }
712 874
713 // returns the canonical pairs (origin, destination) for all links under `path`. 875 // returns the canonical pairs for all links under `path`.
714 fn canonical_pairs_under(&self, path: &Path) -> anyhow::Result<Vec<CanonicalPair>> { 876 fn canonical_pairs_under(&self, path: &Path) -> anyhow::Result<Vec<CanonicalPair>> {
715 let origin = self.prepare_origin_path(path)?; 877 let origin = self.prepare_origin_path(path)?;
716 let mut paths = Vec::new(); 878 let mut canonical_pairs = Vec::new();
717 for link_id in self.depot.links_under(origin)? { 879 for link_id in self.depot.links_under(origin)? {
718 let link_view = self.depot.link_view(link_id); 880 canonical_pairs.push(self.canonical_pair_from_link_id(link_id));
719 let relative_origin = link_view.origin();
720 let relative_destination = link_view.destination();
721 let canonical_origin = self.depot_dir.join(relative_origin);
722 let canonical_destination = self.install_base.join(relative_destination);
723 paths.push(CanonicalPair {
724 link_id,
725 origin: canonical_origin,
726 destination: canonical_destination,
727 });
728 } 881 }
729 Ok(paths) 882 Ok(canonical_pairs)
883 }
884
885 fn symlink_is_installed_by_link_id(&self, link_id: LinkID) -> anyhow::Result<bool> {
886 let canonical_pair = self.canonical_pair_from_link_id(link_id);
887 self.symlink_is_installed(&canonical_pair.origin, &canonical_pair.destination)
730 } 888 }
731 889
732 fn install_symlink(&self, origin: &Path, destination: &Path) -> anyhow::Result<()> { 890 fn symlink_is_installed(&self, origin: &Path, destination: &Path) -> anyhow::Result<bool> {
891 debug_assert!(origin.is_absolute());
892 debug_assert!(destination.is_absolute());
893
894 if destination.is_symlink() {
895 let symlink_destination = destination.read_link()?;
896 match symlink_destination.canonicalize() {
897 Ok(canonicalized) => Ok(origin == canonicalized),
898 Err(_) => Ok(false),
899 }
900 } else {
901 Ok(false)
902 }
903 }
904
905 fn symlink_install_by_link_id(&self, link_id: LinkID) -> anyhow::Result<()> {
906 let canonical_pair = self.canonical_pair_from_link_id(link_id);
907 self.symlink_install(&canonical_pair.origin, &canonical_pair.destination)
908 }
909
910 fn symlink_install(&self, origin: &Path, destination: &Path) -> anyhow::Result<()> {
733 debug_assert!(origin.is_absolute()); 911 debug_assert!(origin.is_absolute());
734 debug_assert!(destination.is_absolute()); 912 debug_assert!(destination.is_absolute());
735 913
@@ -753,7 +931,7 @@ mod dotup {
753 Ok(()) 931 Ok(())
754 } 932 }
755 933
756 fn uninstall_symlink(&self, origin: &Path, destination: &Path) -> anyhow::Result<()> { 934 fn symlink_uninstall(&self, origin: &Path, destination: &Path) -> anyhow::Result<()> {
757 debug_assert!(origin.is_absolute()); 935 debug_assert!(origin.is_absolute());
758 debug_assert!(destination.is_absolute()); 936 debug_assert!(destination.is_absolute());
759 937
@@ -766,6 +944,19 @@ mod dotup {
766 944
767 Ok(()) 945 Ok(())
768 } 946 }
947
948 fn canonical_pair_from_link_id(&self, link_id: LinkID) -> CanonicalPair {
949 let link_view = self.depot.link_view(link_id);
950 let relative_origin = link_view.origin();
951 let relative_destination = link_view.destination();
952 let canonical_origin = self.depot_dir.join(relative_origin);
953 let canonical_destination = self.install_base.join(relative_destination);
954 CanonicalPair {
955 link_id,
956 origin: canonical_origin,
957 destination: canonical_destination,
958 }
959 }
769 } 960 }
770 961
771 pub fn read(depot_path: PathBuf, install_base: PathBuf) -> anyhow::Result<Dotup> { 962 pub fn read(depot_path: PathBuf, install_base: PathBuf) -> anyhow::Result<Dotup> {
@@ -789,16 +980,26 @@ mod dotup {
789} 980}
790 981
791mod utils { 982mod utils {
792 use std::path::PathBuf; 983 use std::path::{Component, Path, PathBuf};
793 984
794 use crate::{ 985 use crate::{
795 depot::{self, Depot},
796 dotup::{self, Dotup}, 986 dotup::{self, Dotup},
797 Flags, 987 Flags,
798 }; 988 };
799 989
800 pub const DEFAULT_DEPOT_FILE_NAME: &str = ".depot"; 990 pub const DEFAULT_DEPOT_FILE_NAME: &str = ".depot";
801 991
992 /// collects the result of std::fs::read_dir into two vecs, the first one contains all the
993 /// directories and the second one all the files.
994 pub fn collect_read_dir_split(
995 dir: impl AsRef<Path>,
996 ) -> anyhow::Result<(Vec<PathBuf>, Vec<PathBuf>)> {
997 Ok(std::fs::read_dir(dir)?
998 .filter_map(|e| e.ok())
999 .map(|e| e.path())
1000 .partition(|p| p.is_dir()))
1001 }
1002
802 pub fn read_dotup(flags: &Flags) -> anyhow::Result<Dotup> { 1003 pub fn read_dotup(flags: &Flags) -> anyhow::Result<Dotup> {
803 let depot_path = depot_path_from_flags(flags)?; 1004 let depot_path = depot_path_from_flags(flags)?;
804 let install_base = install_base_from_flags(flags); 1005 let install_base = install_base_from_flags(flags);
@@ -809,18 +1010,6 @@ mod utils {
809 dotup::write(dotup) 1010 dotup::write(dotup)
810 } 1011 }
811 1012
812 pub fn read_depot(flags: &Flags) -> anyhow::Result<Depot> {
813 let depot_path = depot_path_from_flags(flags)?;
814 let depot = depot::read(&depot_path)?;
815 Ok(depot)
816 }
817
818 pub fn write_depot(flags: &Flags, depot: &Depot) -> anyhow::Result<()> {
819 let depot_path = depot_path_from_flags(flags)?;
820 depot::write(&depot_path, depot)?;
821 Ok(())
822 }
823
824 pub fn depot_path_from_flags(flags: &Flags) -> anyhow::Result<PathBuf> { 1013 pub fn depot_path_from_flags(flags: &Flags) -> anyhow::Result<PathBuf> {
825 match flags.depot { 1014 match flags.depot {
826 Some(ref path) => Ok(path.clone()), 1015 Some(ref path) => Ok(path.clone()),
@@ -855,10 +1044,76 @@ mod utils {
855 pub fn default_install_base() -> PathBuf { 1044 pub fn default_install_base() -> PathBuf {
856 PathBuf::from(std::env::var("HOME").expect("Failed to obtain HOME environment variable")) 1045 PathBuf::from(std::env::var("HOME").expect("Failed to obtain HOME environment variable"))
857 } 1046 }
1047 pub fn weakly_canonical(path: impl AsRef<Path>) -> PathBuf {
1048 let cwd = current_working_directory();
1049 weakly_canonical_cwd(path, cwd)
1050 }
1051
1052 fn weakly_canonical_cwd(path: impl AsRef<Path>, cwd: PathBuf) -> PathBuf {
1053 // Adapated from
1054 // https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61
1055 let path = path.as_ref();
1056
1057 let mut components = path.components().peekable();
1058 let mut canonical = cwd;
1059 let prefix = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
1060 components.next();
1061 PathBuf::from(c.as_os_str())
1062 } else {
1063 PathBuf::new()
1064 };
1065
1066 for component in components {
1067 match component {
1068 Component::Prefix(_) => unreachable!(),
1069 Component::RootDir => {
1070 canonical = prefix.clone();
1071 canonical.push(component.as_os_str())
1072 }
1073 Component::CurDir => {}
1074 Component::ParentDir => {
1075 canonical.pop();
1076 }
1077 Component::Normal(p) => canonical.push(p),
1078 };
1079 }
1080
1081 canonical
1082 }
858 1083
859 fn current_working_directory() -> PathBuf { 1084 pub fn current_working_directory() -> PathBuf {
860 std::env::current_dir().expect("Failed to obtain current working directory") 1085 std::env::current_dir().expect("Failed to obtain current working directory")
861 } 1086 }
1087
1088 #[cfg(test)]
1089 mod tests {
1090 use super::*;
1091
1092 #[test]
1093 fn weak_canonical_test() {
1094 let cwd = PathBuf::from("/home/user");
1095 assert_eq!(
1096 PathBuf::from("/home/dest"),
1097 weakly_canonical_cwd("../dest", cwd.clone())
1098 );
1099 assert_eq!(
1100 PathBuf::from("/home/dest/configs/init.vim"),
1101 weakly_canonical_cwd("../dest/configs/init.vim", cwd.clone())
1102 );
1103 assert_eq!(
1104 PathBuf::from("/dest/configs/init.vim"),
1105 weakly_canonical_cwd("/dest/configs/init.vim", cwd.clone())
1106 );
1107 assert_eq!(
1108 PathBuf::from("/home/user/configs/nvim/lua/setup.lua"),
1109 weakly_canonical_cwd("./configs/nvim/lua/setup.lua", cwd.clone())
1110 );
1111 assert_eq!(
1112 PathBuf::from("/home/user/configs/nvim/lua/setup.lua"),
1113 weakly_canonical_cwd("configs/nvim/lua/setup.lua", cwd.clone())
1114 );
1115 }
1116 }
862} 1117}
863 1118
864use std::path::PathBuf; 1119use std::path::PathBuf;
@@ -891,6 +1146,7 @@ enum SubCommand {
891 Install(InstallArgs), 1146 Install(InstallArgs),
892 Uninstall(UninstallArgs), 1147 Uninstall(UninstallArgs),
893 Mv(MvArgs), 1148 Mv(MvArgs),
1149 Status(StatusArgs),
894} 1150}
895 1151
896fn main() -> anyhow::Result<()> { 1152fn main() -> anyhow::Result<()> {
@@ -902,6 +1158,7 @@ fn main() -> anyhow::Result<()> {
902 SubCommand::Install(cmd_args) => command_install(args.flags, cmd_args), 1158 SubCommand::Install(cmd_args) => command_install(args.flags, cmd_args),
903 SubCommand::Uninstall(cmd_args) => command_uninstall(args.flags, cmd_args), 1159 SubCommand::Uninstall(cmd_args) => command_uninstall(args.flags, cmd_args),
904 SubCommand::Mv(cmd_args) => command_mv(args.flags, cmd_args), 1160 SubCommand::Mv(cmd_args) => command_mv(args.flags, cmd_args),
1161 SubCommand::Status(cmd_args) => command_status(args.flags, cmd_args),
905 } 1162 }
906} 1163}
907 1164
@@ -962,7 +1219,6 @@ struct InstallArgs {
962fn command_install(global_flags: Flags, args: InstallArgs) -> anyhow::Result<()> { 1219fn command_install(global_flags: Flags, args: InstallArgs) -> anyhow::Result<()> {
963 let dotup = utils::read_dotup(&global_flags)?; 1220 let dotup = utils::read_dotup(&global_flags)?;
964 dotup.install(args.paths.into_iter()); 1221 dotup.install(args.paths.into_iter());
965 utils::write_dotup(&dotup)?;
966 Ok(()) 1222 Ok(())
967} 1223}
968 1224
@@ -974,7 +1230,6 @@ struct UninstallArgs {
974fn command_uninstall(global_flags: Flags, args: UninstallArgs) -> anyhow::Result<()> { 1230fn command_uninstall(global_flags: Flags, args: UninstallArgs) -> anyhow::Result<()> {
975 let dotup = utils::read_dotup(&global_flags)?; 1231 let dotup = utils::read_dotup(&global_flags)?;
976 dotup.uninstall(args.paths.into_iter()); 1232 dotup.uninstall(args.paths.into_iter());
977 utils::write_dotup(&dotup)?;
978 Ok(()) 1233 Ok(())
979} 1234}
980 1235
@@ -995,3 +1250,12 @@ fn command_mv(global_flags: Flags, args: MvArgs) -> anyhow::Result<()> {
995 utils::write_dotup(&dotup)?; 1250 utils::write_dotup(&dotup)?;
996 Ok(()) 1251 Ok(())
997} 1252}
1253
1254#[derive(Parser, Debug)]
1255struct StatusArgs {}
1256
1257fn command_status(global_flags: Flags, _args: StatusArgs) -> anyhow::Result<()> {
1258 let dotup = utils::read_dotup(&global_flags)?;
1259 dotup.status();
1260 Ok(())
1261}