version_spec/
resolved_spec.rs1#![allow(clippy::from_over_into)]
2
3use crate::spec_error::SpecError;
4use crate::{UnresolvedVersionSpec, clean_version_string, is_alias_name, is_calver};
5use crate::{is_semver, version_types::*};
6use compact_str::CompactString;
7use semver::Version;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::str::FromStr;
11
12#[derive(Clone, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
14#[serde(untagged, into = "String", try_from = "String")]
15pub enum VersionSpec {
16 Canary,
18 Alias(CompactString),
20 Calendar(CalVer),
22 Semantic(SemVer),
24}
25
26impl VersionSpec {
27 pub fn parse<T: AsRef<str>>(value: T) -> Result<Self, SpecError> {
34 Self::from_str(value.as_ref())
35 }
36
37 pub fn as_version(&self) -> Option<&Version> {
39 match self {
40 Self::Calendar(inner) => Some(&inner.0),
41 Self::Semantic(inner) => Some(&inner.0),
42 _ => None,
43 }
44 }
45
46 pub fn is_alias<A: AsRef<str>>(&self, name: A) -> bool {
48 match self {
49 Self::Alias(alias) => alias == name.as_ref(),
50 _ => false,
51 }
52 }
53
54 pub fn is_canary(&self) -> bool {
56 match self {
57 Self::Canary => true,
58 Self::Alias(alias) => alias == "canary",
59 _ => false,
60 }
61 }
62
63 pub fn is_latest(&self) -> bool {
65 match self {
66 Self::Alias(alias) => alias == "latest",
67 _ => false,
68 }
69 }
70
71 pub fn to_unresolved_spec(&self) -> UnresolvedVersionSpec {
73 match self {
74 Self::Canary => UnresolvedVersionSpec::Canary,
75 Self::Alias(alias) => UnresolvedVersionSpec::Alias(alias.to_owned()),
76 Self::Calendar(version) => UnresolvedVersionSpec::Calendar(version.to_owned()),
77 Self::Semantic(version) => UnresolvedVersionSpec::Semantic(version.to_owned()),
78 }
79 }
80}
81
82#[cfg(feature = "schematic")]
83impl schematic::Schematic for VersionSpec {
84 fn schema_name() -> Option<String> {
85 Some("VersionSpec".into())
86 }
87
88 fn build_schema(mut schema: schematic::SchemaBuilder) -> schematic::Schema {
89 schema.set_description("Represents a resolved version or alias.");
90 schema.string_default()
91 }
92}
93
94impl Default for VersionSpec {
95 fn default() -> Self {
97 Self::Alias("latest".into())
98 }
99}
100
101impl FromStr for VersionSpec {
102 type Err = SpecError;
103
104 fn from_str(value: &str) -> Result<Self, Self::Err> {
105 if value == "canary" {
106 return Ok(VersionSpec::Canary);
107 }
108
109 let value = clean_version_string(value);
110
111 if is_alias_name(&value) {
112 return Ok(VersionSpec::Alias(CompactString::new(value)));
113 }
114
115 if is_calver(&value) {
116 return Ok(VersionSpec::Calendar(CalVer::parse(&value)?));
117 }
118
119 if is_semver(&value) {
120 return Ok(VersionSpec::Semantic(SemVer::parse(&value)?));
121 }
122
123 Err(SpecError::UnknownResolvedFormat(value.to_owned()))
124 }
125}
126
127impl TryFrom<String> for VersionSpec {
128 type Error = SpecError;
129
130 fn try_from(value: String) -> Result<Self, Self::Error> {
131 Self::from_str(&value)
132 }
133}
134
135impl Into<String> for VersionSpec {
136 fn into(self) -> String {
137 self.to_string()
138 }
139}
140
141impl fmt::Debug for VersionSpec {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 write!(f, "{self}")
145 }
146}
147
148impl fmt::Display for VersionSpec {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 match self {
151 Self::Canary => write!(f, "canary"),
152 Self::Alias(alias) => write!(f, "{alias}"),
153 Self::Calendar(version) => write!(f, "{version}"),
154 Self::Semantic(version) => write!(f, "{version}"),
155 }
156 }
157}
158
159impl PartialEq<&str> for VersionSpec {
160 fn eq(&self, other: &&str) -> bool {
161 match self {
162 Self::Canary => "canary" == *other,
163 Self::Alias(alias) => alias == other,
164 _ => &self.to_string() == other,
165 }
166 }
167}
168
169impl PartialEq<semver::Version> for VersionSpec {
170 fn eq(&self, other: &semver::Version) -> bool {
171 match self {
172 Self::Calendar(version) => &version.0 == other,
173 Self::Semantic(version) => &version.0 == other,
174 _ => false,
175 }
176 }
177}
178
179impl AsRef<VersionSpec> for VersionSpec {
180 fn as_ref(&self) -> &VersionSpec {
181 self
182 }
183}