version_spec/
unresolved_spec.rs1#![allow(clippy::from_over_into)]
2
3use crate::spec_error::SpecError;
4use crate::unresolved_parser::*;
5use crate::version_types::*;
6use crate::{VersionSpec, clean_version_req_string, clean_version_string, is_alias_name};
7use compact_str::CompactString;
8use semver::VersionReq;
9use serde::{Deserialize, Serialize};
10use std::fmt::{Debug, Display};
11use std::str::FromStr;
12
13#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
16#[serde(untagged, into = "String", try_from = "String")]
17pub enum UnresolvedVersionSpec {
18 Canary,
20 Alias(CompactString),
22 Req(VersionReq),
24 ReqAny(Vec<VersionReq>),
26 Calendar(CalVer),
28 Semantic(SemVer),
30}
31
32impl UnresolvedVersionSpec {
33 pub fn parse<T: AsRef<str>>(value: T) -> Result<Self, SpecError> {
45 Self::from_str(value.as_ref())
46 }
47
48 pub fn is_alias<A: AsRef<str>>(&self, name: A) -> bool {
50 match self {
51 Self::Alias(alias) => alias == name.as_ref(),
52 _ => false,
53 }
54 }
55
56 pub fn is_canary(&self) -> bool {
58 match self {
59 Self::Canary => true,
60 Self::Alias(alias) => alias == "canary",
61 _ => false,
62 }
63 }
64
65 pub fn is_latest(&self) -> bool {
67 match self {
68 Self::Alias(alias) => alias == "latest",
69 _ => false,
70 }
71 }
72
73 pub fn to_resolved_spec(&self) -> VersionSpec {
80 match self {
81 Self::Canary => VersionSpec::Canary,
82 Self::Alias(alias) => VersionSpec::Alias(CompactString::new(alias)),
83 Self::Calendar(version) => VersionSpec::Calendar(version.to_owned()),
84 Self::Semantic(version) => VersionSpec::Semantic(version.to_owned()),
85 _ => VersionSpec::default(),
86 }
87 }
88}
89
90#[cfg(feature = "schematic")]
91impl schematic::Schematic for UnresolvedVersionSpec {
92 fn schema_name() -> Option<String> {
93 Some("UnresolvedVersionSpec".into())
94 }
95
96 fn build_schema(mut schema: schematic::SchemaBuilder) -> schematic::Schema {
97 schema.set_description("Represents an unresolved version or alias that must be resolved to a fully-qualified version.");
98 schema.string_default()
99 }
100}
101
102impl Default for UnresolvedVersionSpec {
103 fn default() -> Self {
105 Self::Alias("latest".into())
106 }
107}
108
109impl FromStr for UnresolvedVersionSpec {
110 type Err = SpecError;
111
112 fn from_str(value: &str) -> Result<Self, Self::Err> {
113 if value == "canary" {
114 return Ok(UnresolvedVersionSpec::Canary);
115 }
116
117 let value = clean_version_string(value);
118
119 if is_alias_name(&value) {
120 return Ok(UnresolvedVersionSpec::Alias(CompactString::new(value)));
121 }
122
123 let value = clean_version_req_string(&value);
124
125 if value.contains("||") {
127 let mut reqs = vec![];
128
129 for result in parse_multi(&value)? {
130 reqs.push(VersionReq::parse(&result)?);
131 }
132
133 return Ok(UnresolvedVersionSpec::ReqAny(reqs));
134 }
135
136 let (result, kind) = parse(value)?;
138
139 Ok(match kind {
140 ParseKind::Req => UnresolvedVersionSpec::Req(VersionReq::parse(&result)?),
141 ParseKind::Cal => UnresolvedVersionSpec::Calendar(CalVer::parse(&result)?),
142 _ => UnresolvedVersionSpec::Semantic(SemVer::parse(&result)?),
143 })
144 }
145}
146
147impl TryFrom<String> for UnresolvedVersionSpec {
148 type Error = SpecError;
149
150 fn try_from(value: String) -> Result<Self, Self::Error> {
151 Self::from_str(&value)
152 }
153}
154
155impl Into<String> for UnresolvedVersionSpec {
156 fn into(self) -> String {
157 self.to_string()
158 }
159}
160
161impl Display for UnresolvedVersionSpec {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 match self {
164 Self::Canary => write!(f, "canary"),
165 Self::Alias(alias) => write!(f, "{alias}"),
166 Self::Req(req) => write!(f, "{req}"),
167 Self::ReqAny(reqs) => write!(
168 f,
169 "{}",
170 reqs.iter()
171 .map(|req| req.to_string())
172 .collect::<Vec<_>>()
173 .join(" || ")
174 ),
175 Self::Calendar(version) => write!(f, "{version}"),
176 Self::Semantic(version) => write!(f, "{version}"),
177 }
178 }
179}
180
181impl PartialEq<VersionSpec> for UnresolvedVersionSpec {
182 fn eq(&self, other: &VersionSpec) -> bool {
183 match (self, other) {
184 (Self::Canary, VersionSpec::Alias(a)) => a == "canary",
185 (Self::Alias(a1), VersionSpec::Alias(a2)) => a1 == a2,
186 (Self::Calendar(v1), VersionSpec::Calendar(v2)) => v1 == v2,
187 (Self::Semantic(v1), VersionSpec::Semantic(v2)) => v1 == v2,
188 _ => false,
189 }
190 }
191}
192
193impl AsRef<UnresolvedVersionSpec> for UnresolvedVersionSpec {
194 fn as_ref(&self) -> &UnresolvedVersionSpec {
195 self
196 }
197}