phpup/commands/
list_remote.rs

1use super::{Command, Config};
2use crate::release;
3use crate::version;
4use crate::version::Local;
5use crate::version::Version;
6use clap;
7use colored::Colorize;
8use itertools::Itertools;
9use thiserror::Error;
10
11#[derive(clap::Parser, Debug)]
12pub struct ListRemote {
13    version: Option<Version>,
14    #[clap(
15        long = "latest-patch",
16        visible_alias = "lp",
17        help = "List latest patch release (avairable only if patch number is NOT specified)"
18    )]
19    only_latest_patch: bool,
20}
21
22#[derive(Error, Debug)]
23pub enum Error {
24    #[error(transparent)]
25    FailedFetchRelease(#[from] release::FetchError),
26}
27
28impl Command for ListRemote {
29    type Error = Error;
30
31    fn run(&self, config: &Config) -> Result<(), Error> {
32        let query_versions = match &self.version {
33            Some(version) => {
34                if self.only_latest_patch && version.patch_version().is_some() {
35                    println!(
36                        "{}: '--latest-patch' is available only if patch number is NOT specified: {}",
37                        "warning".yellow().bold(),
38                        version
39                    );
40                }
41                vec![*version]
42            }
43            None => {
44                vec![
45                    Version::from_major(3),
46                    Version::from_major(4),
47                    Version::from_major(5),
48                    Version::from_major(7),
49                    Version::from_major(8),
50                ]
51            }
52        };
53
54        let installed_versions = version::installed(config).collect_vec();
55        let current_version = Local::current(config);
56
57        for query_version in query_versions {
58            let releases = release::fetch_all(query_version)?;
59            let remote_versions = releases.keys();
60
61            let remote_versions = if self.only_latest_patch {
62                filter_latest_patch(remote_versions).collect_vec()
63            } else {
64                remote_versions.collect_vec()
65            };
66
67            for &remote_version in remote_versions {
68                let installed = installed_versions.contains(&remote_version);
69                let remote_version = Local::Installed(remote_version);
70                let used = Some(&remote_version) == current_version.as_ref();
71                println!("{}", remote_version.to_string_by(installed, used))
72            }
73        }
74        Ok(())
75    }
76}
77
78fn filter_latest_patch<'a, I>(versions: I) -> impl Iterator<Item = &'a Version>
79where
80    I: Iterator<Item = &'a Version> + DoubleEndedIterator,
81{
82    let mut latest_patch: Option<&'a Version> = None;
83    let mut latest_patches = versions
84        .rev()
85        .filter_map(|version| {
86            if latest_patch.is_none()
87                || latest_patch.unwrap().minor_version() != version.minor_version()
88            {
89                latest_patch.replace(version)
90            } else {
91                None
92            }
93        })
94        .collect::<Vec<_>>();
95    latest_patches.push(latest_patch.unwrap());
96    latest_patches.into_iter().rev()
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn version_not_specified() {
105        let base_dir = tempfile::tempdir().unwrap();
106        let config = Config::default().with_base_dir(base_dir);
107        let cmd = ListRemote {
108            version: None,
109            only_latest_patch: false,
110        };
111        assert!(cmd.run(&config).is_ok());
112    }
113    #[test]
114    fn version_specified() {
115        let base_dir = tempfile::tempdir().unwrap();
116        let config = Config::default().with_base_dir(base_dir);
117        let cmd = ListRemote {
118            version: Some("7.2".parse().unwrap()),
119            only_latest_patch: false,
120        };
121        assert!(cmd.run(&config).is_ok());
122    }
123}
124
125// TODO: can't get last itm
126// fn filter_latest_patch<'a, T>(versions: T) -> impl Iterator<Item = &'a Version>
127// where
128//     T: Iterator<Item = &'a Version> + DoubleEndedIterator,
129// {
130//     let mut latest_patch: Option<&'a Version> = None;
131//     versions
132//         .map(move |version| match latest_patch {
133//             Some(latest) if latest.minor_version().unwrap() == version.minor_version().unwrap() => {
134//                 latest_patch.replace(version);
135//                 None
136//             }
137//             _ => latest_patch.replace(version),
138//         })
139//         .filter_map(|e| e)
140// }