1use std::path::Path;
2
3use anyhow::Context;
4
5use crate::utils::{path_type, PathBufExt};
6
7path_type! {
8 ConfigDir: dir
10}
11
12impl ConfigDir {
13 pub fn jwt_priv_key(&self) -> PrivKeyPath {
14 PrivKeyPath(self.0.join("id_ecdsa"))
15 }
16 pub fn jwt_pub_key(&self) -> PubKeyPath {
17 PubKeyPath(self.0.join("id_ecdsa.pub"))
18 }
19 pub fn cli_toml(&self) -> CliTomlPath {
20 CliTomlPath(self.0.join("cli.toml"))
21 }
22}
23
24path_type!(#[non_exhaustive(any())] PrivKeyPath: file);
26path_type!(#[non_exhaustive(any())] PubKeyPath: file);
27
28path_type!(CliTomlPath: file);
29
30path_type!(BinFile: file);
31
32path_type!(BinDir: dir);
33
34impl BinDir {
35 pub fn version_dir(&self, version: &str) -> VersionBinDir {
36 VersionBinDir(self.0.join(version))
37 }
38
39 pub const CURRENT_VERSION_DIR_NAME: &str = "current";
40 pub fn current_version_dir(&self) -> VersionBinDir {
41 VersionBinDir(self.0.join(Self::CURRENT_VERSION_DIR_NAME))
42 }
43
44 pub fn set_current_version(&self, version: &str) -> anyhow::Result<()> {
45 self.current_version_dir().link_to(self.version_dir(version).as_ref())
46 }
47
48 pub fn current_version(&self) -> anyhow::Result<Option<String>> {
49 match std::fs::read_link(self.current_version_dir()) {
50 Ok(path) => path.into_os_string().into_string().ok().context("not utf8").map(Some),
51 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
52 Err(e) => Err(e.into()),
53 }
54 }
55
56 pub fn installed_versions(&self) -> anyhow::Result<Vec<String>> {
57 self.read_dir()?
58 .filter_map(|r| match r {
59 Ok(entry) => {
60 let name = entry.file_name();
61 if name == Self::CURRENT_VERSION_DIR_NAME {
62 None
63 } else {
64 entry.file_name().into_string().ok().map(Ok)
65 }
66 }
67 Err(e) => Some(Err(e.into())),
68 })
69 .collect()
70 }
71}
72
73path_type!(VersionBinDir: dir);
74
75impl VersionBinDir {
76 pub fn spacetimedb_cli(self) -> SpacetimedbCliBin {
77 SpacetimedbCliBin(self.0.joined("spacetimedb-cli").with_exe_ext())
78 }
79
80 pub fn create_custom(&self, path: &Path) -> anyhow::Result<()> {
81 if std::fs::symlink_metadata(self).is_ok_and(|m| m.file_type().is_dir()) {
82 anyhow::bail!("version already exists");
83 }
84 self.link_to(path)
85 }
86
87 fn link_to(&self, path: &Path) -> anyhow::Result<()> {
88 let rel_path = path.strip_prefix(self.0.parent().unwrap()).unwrap_or(path);
89 #[cfg(unix)]
90 {
91 std::fs::remove_file(self).ok();
93 std::os::unix::fs::symlink(rel_path, self)?;
94 }
95 #[cfg(windows)]
96 {
97 junction::delete(self).ok();
98 std::fs::remove_dir(self).ok();
99 junction::create(path, self)
102 .or_else(|err| std::os::windows::fs::symlink_dir(rel_path, self).or(Err(err)))?;
103 }
104 Ok(())
105 }
106}
107
108path_type!(SpacetimedbCliBin: file);