release_kit/depend/
version.rs1use serde::Serialize;
8
9use super::source::{Source, TagStyle};
10use crate::error::RkError;
11
12#[derive(Debug, Clone, Serialize)]
14pub struct Resolved {
15 pub version: String,
17 pub tag: String,
19 pub origin: &'static str,
21}
22
23pub fn resolve(source: &Source, argument: Option<&str>) -> Result<Resolved, RkError> {
30 if let Some(raw) = argument {
31 let Some(tag) = crate::devshell::normalize_tag(raw) else {
32 return Err(RkError::Usage(format!(
33 "--pin {raw} is not a version: pass 1.2.3, v1.2.3, or the release URL"
34 )));
35 };
36 let version = tag.trim_start_matches('v').to_owned();
37 return Ok(Resolved {
38 tag: tag_for(&version, source.tag_style),
39 version,
40 origin: "argument",
41 });
42 }
43 let Some(version) = source.version.clone() else {
44 return Err(RkError::Usage(
45 "the source declares no version; pass --pin".into(),
46 ));
47 };
48 Ok(Resolved {
49 tag: tag_for(&version, source.tag_style),
50 version,
51 origin: "source-tree",
52 })
53}
54
55#[must_use]
58pub fn tag_for(version: &str, style: TagStyle) -> String {
59 match style {
60 TagStyle::Bare => version.to_owned(),
61 TagStyle::Prefixed | TagStyle::Unknown => format!("v{version}"),
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 #![allow(clippy::expect_used)]
68
69 use camino::Utf8PathBuf;
70
71 use super::{TagStyle, resolve, tag_for};
72 use crate::depend::source::Source;
73 use crate::error::RkError;
74
75 fn source(version: Option<&str>, style: TagStyle) -> Source {
76 Source {
77 path: Utf8PathBuf::from("/srv/sample"),
78 tech: Some("rust"),
79 name: Some("sample-tool".into()),
80 version: version.map(str::to_owned),
81 bins: Vec::new(),
82 owner_repo: None,
83 host: None,
84 flake_package: false,
85 dist_github: false,
86 binstall_github: false,
87 tag_style: style,
88 channels: Vec::new(),
89 }
90 }
91
92 #[test]
94 fn the_tag_follows_the_source_tag_style() {
95 assert_eq!(tag_for("1.4.0", TagStyle::Prefixed), "v1.4.0");
96 assert_eq!(tag_for("1.4.0", TagStyle::Bare), "1.4.0");
97 assert_eq!(tag_for("1.4.0", TagStyle::Unknown), "v1.4.0");
98 let resolved = resolve(&source(Some("1.4.0"), TagStyle::Bare), None).expect("resolves");
99 assert_eq!(resolved.version, "1.4.0");
100 assert_eq!(resolved.tag, "1.4.0");
101 assert_eq!(resolved.origin, "source-tree");
102 }
103
104 #[test]
106 fn the_argument_overrides_the_tree() {
107 let tree = source(Some("1.4.0"), TagStyle::Prefixed);
108 for raw in [
109 "2.0.0",
110 "v2.0.0",
111 "https://github.com/acme/sample/releases/tag/v2.0.0",
112 ] {
113 let resolved = resolve(&tree, Some(raw)).expect("resolves");
114 assert_eq!(resolved.version, "2.0.0", "{raw}");
115 assert_eq!(resolved.tag, "v2.0.0", "{raw}");
116 assert_eq!(resolved.origin, "argument");
117 }
118 assert!(matches!(
119 resolve(&tree, Some("latest")),
120 Err(RkError::Usage(_))
121 ));
122 }
123
124 #[test]
125 fn no_version_is_a_usage_error() {
126 assert!(matches!(
127 resolve(&source(None, TagStyle::Unknown), None),
128 Err(RkError::Usage(_))
129 ));
130 }
131}