workflow_utils/
version.rs1use crate::imports::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Version {
6 pub major: u64,
8 pub minor: u64,
10 pub patch: u64,
12}
13
14impl AsRef<Version> for Version {
15 fn as_ref(&self) -> &Version {
16 self
17 }
18}
19
20impl FromStr for Version {
21 type Err = Error;
22
23 fn from_str(s: &str) -> Result<Self> {
24 let mut parts = s.split('.');
25 let major = parts
26 .next()
27 .ok_or_else(|| Error::custom("Invalid version"))?
28 .chars()
29 .filter(|c| c.is_ascii_digit())
30 .collect::<String>()
31 .parse()?;
32 let minor = parts
33 .next()
34 .ok_or_else(|| Error::custom("Invalid version"))?
35 .chars()
36 .filter(|c| c.is_ascii_digit())
37 .collect::<String>()
38 .parse()?;
39 let patch = parts
40 .next()
41 .ok_or_else(|| Error::custom("Invalid version"))?
42 .chars()
43 .filter(|c| c.is_ascii_digit())
44 .collect::<String>()
45 .parse()?;
46 Ok(Version {
47 major,
48 minor,
49 patch,
50 })
51 }
52}
53
54impl Display for Version {
55 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
56 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
57 }
58}
59
60impl Version {
61 pub fn is_greater_than<V>(&self, other: V) -> bool
64 where
65 V: AsRef<Version>,
66 {
67 use std::cmp::Ordering;
68
69 let other = other.as_ref();
70
71 matches!(
72 (
73 self.major.cmp(&other.major),
74 self.minor.cmp(&other.minor),
75 self.patch.cmp(&other.patch),
76 ),
77 (Ordering::Greater, _, _)
78 | (Ordering::Equal, Ordering::Greater, _)
79 | (Ordering::Equal, Ordering::Equal, Ordering::Greater)
80 )
81 }
82}
83
84#[derive(Debug, Deserialize)]
85struct CrateResponse {
86 #[serde(rename = "crate")]
87 crate_: Crate,
88}
89
90#[derive(Debug, Deserialize)]
91struct Crate {
92 max_version: String,
93}
94
95pub async fn latest_crate_version<S: Display, U: Display>(
98 crate_name: S,
99 user_agent: U,
100) -> Result<Version> {
101 let url = format!("https://crates.io/api/v1/crates/{crate_name}");
102 let response = http::Request::new(url)
103 .with_user_agent(user_agent.to_string())
104 .get_json::<CrateResponse>()
105 .await?;
106 response.crate_.max_version.parse()
107}
108
109#[cfg(not(target_arch = "wasm32"))]
111pub mod blocking {
112 use super::*;
113 use reqwest::blocking::Client;
114 use reqwest::header::*;
115
116 pub fn latest_crate_version<S: Display, U: Display>(
119 crate_name: S,
120 user_agent: U,
121 ) -> Result<Version> {
122 let url = format!("https://crates.io/api/v1/crates/{crate_name}");
123 let mut headers = HeaderMap::new();
124 headers.insert(
125 USER_AGENT,
126 HeaderValue::from_str(user_agent.to_string().as_str())?,
127 );
128 let response = Client::builder()
129 .default_headers(headers)
130 .build()?
131 .get(url)
132 .send()?
133 .json::<CrateResponse>()?;
134 response.crate_.max_version.parse()
135 }
136}