1use crate::{
2 Pkg,
3 db::{Db, PkgType},
4};
5use miette::{Diagnostic, IntoDiagnostic, Result};
6use std::path::PathBuf;
7use thiserror::Error;
8
9#[derive(Debug)]
10pub struct Fs {
11 target_dir: PathBuf,
12 load_path: PathBuf,
13 db: Db,
14}
15
16#[derive(Error, Debug, Diagnostic)]
17pub enum FsError {
18 #[error(transparent)]
19 #[diagnostic(code(fs::io_error))]
20 IoError(#[from] std::io::Error),
21
22 #[error("The given load path is exist and is a file {0}")]
23 #[diagnostic(code(fs::load_path_is_file))]
24 LoadPathIsFile(PathBuf),
25}
26
27impl Fs {
28 pub fn new(target_dir: PathBuf, load_path: PathBuf, db_path: &PathBuf) -> Self {
29 let db = Db::new(db_path).unwrap();
30
31 let _ = std::fs::create_dir_all(&target_dir);
32 let _ = std::fs::create_dir_all(&load_path);
33
34 Self {
35 target_dir,
36 load_path,
37 db,
38 }
39 }
40
41 pub fn link(&self) -> Result<()> {
42 let pkgs = self.db.get_pkgs()?;
43
44 if !self.load_path.exists() {
45 std::fs::create_dir_all(&self.load_path).into_diagnostic()?;
46 } else if self.load_path.is_dir() {
47 std::fs::remove_dir_all(&self.load_path).into_diagnostic()?;
48 std::fs::create_dir_all(&self.load_path).into_diagnostic()?;
49 } else {
50 return Err(FsError::LoadPathIsFile(self.load_path.clone())).into_diagnostic()?;
51 }
52
53 for pkg in pkgs {
54 let target = self.load_path.join(pkg.name);
55
56 if target.exists() {
57 std::fs::remove_file(&target).into_diagnostic()?;
58 }
59
60 match pkg.pkg_type {
61 PkgType::SingleExecutable => {
62 std::os::unix::fs::symlink(&pkg.path, &target).into_diagnostic()?;
63 }
64 PkgType::Directory(ref entry_point) => {
65 std::os::unix::fs::symlink(entry_point, &target).into_diagnostic()?;
66 }
67 }
68 }
69
70 Ok(())
71 }
72
73 pub fn store_or_overwrite(
74 &self,
75 pkgs: &mut [&mut Pkg],
76 bridge_name: Option<&str>,
77 ) -> Result<()> {
78 if !self.target_dir.exists() {
79 std::fs::create_dir_all(&self.target_dir).into_diagnostic()?;
80 }
81
82 for pkg in pkgs {
83 let target_dir = self.target_dir.join(bridge_name.unwrap_or(""));
84
85 if !target_dir.exists() {
86 std::fs::create_dir_all(&target_dir).into_diagnostic()?;
87 }
88
89 let target = target_dir.join(&pkg.name);
90
91 if target.exists() {
92 if target.is_dir() {
93 std::fs::remove_dir_all(&target).into_diagnostic()?;
94 } else {
95 std::fs::remove_file(&target).into_diagnostic()?;
96 }
97 }
98
99 std::fs::rename(&pkg.path, &target).into_diagnostic()?;
100
101 if let PkgType::Directory(ref entry_point) = pkg.pkg_type {
102 let entry_point_str = entry_point.to_str().unwrap();
104 let old_path_str = pkg.path.to_str().unwrap();
105 let target_str = target.to_str().unwrap();
106
107 let new_entry_point_str = entry_point_str.replace(old_path_str, target_str);
108 pkg.pkg_type = PkgType::Directory(PathBuf::from(new_entry_point_str))
109 };
110
111 pkg.path = target;
112 }
113
114 Ok(())
115 }
116
117 pub fn remove_pkgs(&self, pkgs: &[&String]) -> Result<bool> {
118 let pkgs = pkgs.iter().map(|s| s.to_string()).collect::<Vec<String>>();
119 let pkgs = pkgs.as_slice();
120
121 let mut removed = false;
122
123 let pkgs = self.db.get_pkgs_by_name(pkgs)?;
124
125 for pkg in pkgs {
126 let target = self.target_dir.join(&pkg.name);
127
128 if target.exists() {
129 if target.is_dir() {
130 std::fs::remove_dir_all(&target).into_diagnostic()?;
131 } else {
132 std::fs::remove_file(&target).into_diagnostic()?;
133 }
134 removed = true;
135 }
136 }
137 Ok(removed)
138 }
139}