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