snm_core/version/
dist_version.rs

1use std::{fmt::Display, fs::read_dir};
2
3use console::style;
4use semver::Version;
5use serde::Deserialize;
6
7use crate::{
8    fetcher::{Lts, Release},
9    types::ReleaseDir,
10    SnmRes,
11};
12
13use super::{ParseVersion, UserVersion};
14
15/// `DistVersion` represents full semver range according to the node release
16#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct DistVersion(pub(super) Version);
18
19impl ParseVersion for DistVersion {
20    type Item = Self;
21    fn parse(v: &str) -> SnmRes<Self::Item> {
22        Ok(DistVersion(Version::parse(v)?))
23    }
24}
25
26impl<'de> Deserialize<'de> for DistVersion {
27    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
28    where
29        D: serde::Deserializer<'de>,
30    {
31        let v: String = String::deserialize(deserializer)?;
32
33        Self::parse(v.trim_start_matches('v')).map_err(serde::de::Error::custom)
34    }
35}
36
37impl Display for DistVersion {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        self.0.fmt(f)
40    }
41}
42
43impl AsRef<Version> for DistVersion {
44    fn as_ref(&self) -> &Version {
45        &self.0
46    }
47}
48
49impl DistVersion {
50    /// To list all the installed versions
51    pub fn list_versions(release_dir: &ReleaseDir) -> SnmRes<Vec<Self>> {
52        let mut versions: Vec<Self> = vec![];
53
54        let entries = read_dir(release_dir.as_ref())?;
55
56        for entry in entries {
57            let entry = entry?.path();
58            let entry = entry.strip_prefix(release_dir.as_ref())?;
59
60            if let Some(e) = entry.to_str() {
61                let dist_ver = Self::parse(e)?;
62
63                versions.push(dist_ver);
64            }
65        }
66
67        Ok(versions)
68    }
69
70    /// To match multiple installed versions with a provided user version
71    pub fn match_versions(r_dir: &ReleaseDir, version: &UserVersion) -> SnmRes<Vec<Self>> {
72        let mut versions: Vec<Self> = vec![];
73
74        let entries = read_dir(r_dir.as_ref())?;
75
76        for entry in entries {
77            let entry = entry?.path();
78            let entry = entry.strip_prefix(r_dir.as_ref())?;
79
80            if let Some(e) = entry.to_str() {
81                let dist_ver = Self::parse(e)?;
82
83                let release = Release {
84                    version: dist_ver,
85                    lts: Lts::No,
86                };
87
88                let is_match = version.match_release(&release);
89
90                if is_match {
91                    versions.push(release.version);
92                }
93            }
94        }
95
96        // Sorting the list descending order
97        versions.sort_by(|a, b| b.cmp(a));
98
99        Ok(versions)
100    }
101
102    /// To match a installed version with the user provided version
103    pub fn match_version(r_dir: &ReleaseDir, version: &UserVersion) -> SnmRes<Self> {
104        let versions = Self::match_versions(r_dir, version)?;
105
106        // NOTE: version list is already sorted, so I am returning the first element
107        let max = versions.into_iter().next().ok_or_else(|| {
108            anyhow::anyhow!("Version {} not found locally", style(version).bold())
109        })?;
110
111        Ok(max)
112    }
113}