1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::{fmt, hash::Hash, path::PathBuf, time::SystemTime};
pub use semver;
use serde::{Deserialize, Serialize};
/// Used to specify from which particular revision of a repository.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum GitTarget {
/// The components is pointing to a specific revision in the repository.
#[serde(untagged)]
Revision {
#[serde(rename = "revision")]
hash: String,
},
/// The components is pointing to a specific tag in the repository.
#[serde(untagged)]
Tag {
#[serde(rename = "tag")]
name: String,
},
/// The components is pointing to a specific *branch* in the repository.
///
/// NOTE: When an update is issued, these type of components will trigger an update if the
/// branch they were pointing to had new commits since the time the component was installed.
/// This means that these components are _not_ deterministic and their behavior could change
/// in-between updates.
#[serde(untagged)]
Branch {
/// This is the name of the branch being tracked.
#[serde(rename = "branch")]
name: String,
/// This field represents the revision hash that is currently presently installed.
///
/// This is only meant to be used in the local manifest in order to check for updates.
latest_revision: Option<String>,
},
}
impl Default for GitTarget {
fn default() -> Self {
GitTarget::Branch {
name: String::from("main"),
latest_revision: None,
}
}
}
impl fmt::Display for GitTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
GitTarget::Branch { name, .. } => write!(f, "branch = \"{name}"),
GitTarget::Revision { hash } => write!(f, "rev = \"{hash}"),
GitTarget::Tag { name: tag } => write!(f, "tag = \"{tag}"),
}
}
}
impl GitTarget {
pub fn to_cargo_flag(&self) -> [String; 2] {
match &self {
GitTarget::Branch { name, .. } => [String::from("--branch"), String::from(name)],
GitTarget::Revision { hash } => [String::from("--rev"), String::from(hash)],
GitTarget::Tag { name: tag } => [String::from("--tag"), String::from(tag)],
}
}
}
/// Represents the canonical versioning authority for a tool or toolchain
#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
#[serde(rename_all = "snake_case")]
pub enum Authority {
/// The authority for this tool/toolchain is a local filesystem path
#[serde(untagged)]
Path {
/// The path to the crate.
path: PathBuf,
/// This is the name of the crate that holds the executable we're going to install.
///
/// This has to be specified because cargo needs the name of the crate to handle
/// uninstallation.
crate_name: String,
/// Represents the latest modification done inside this directory.
last_modification: Option<SystemTime>,
},
/// The authority for this tool/toolchain is a git repository.
#[serde(untagged)]
Git {
/// Points to the git repository containting the [crate::channel::Component].
repository_url: String,
/// This is the name of the crate that holds the executable we're going to install.
///
/// This has to be specified because some repositories hold multiple crates inside them.
crate_name: String,
/// If the target is missing from the [crate::manifest::Manifest], then we assume that it
/// is pointing to the tip of the `main` branch
#[serde(default)]
#[serde(flatten)]
target: GitTarget,
},
/// The authority for this tool/toolchain is crates.io
#[serde(untagged)]
Cargo {
/// The name of the crates.io package under which this tool is provided.
///
/// If `None`, then the package name is the same as the component
#[serde(skip_serializing_if = "Option::is_none")]
package: Option<String>,
/// The semantic versioning string for the package to fetch
version: semver::Version,
},
}
impl core::str::FromStr for Authority {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
impl fmt::Display for Authority {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Authority::Cargo { version, .. } => write!(f, "{version}"),
Authority::Git { repository_url, target, .. } => {
write!(f, "{repository_url}:{target}")
},
Authority::Path { path, .. } => write!(f, "{}", path.display()),
}
}
}