github_workflows_update/
resource.rs1use regex::Regex;
8use std::fmt;
9use tracing::instrument;
10use url::Url;
11
12use crate::error::Error;
13use crate::error::Result;
14use crate::updater;
15use crate::version::Version;
16
17#[derive(Debug, PartialEq, Eq, Hash, Clone)]
18pub enum Resource {
19 Docker {
20 container: String,
21 },
22 GhAction {
23 user: String,
24 repo: String,
25 },
26 GhWorkflow {
27 user: String,
28 repo: String,
29 workflow: String,
30 },
31}
32
33impl Resource {
34 #[instrument(level = "debug")]
35 pub fn new_docker(container: String) -> Resource {
36 Resource::Docker { container }
37 }
38
39 #[instrument(level = "debug")]
40 pub fn new_ghaction(user: String, repo: String) -> Resource {
41 Resource::GhAction { user, repo }
42 }
43
44 #[instrument(level = "debug")]
45 pub fn new_ghworkflow(user: String, repo: String, workflow: String) -> Resource {
46 Resource::GhWorkflow {
47 user,
48 repo,
49 workflow,
50 }
51 }
52
53 pub fn is_docker(&self) -> bool {
54 matches!(self, Resource::Docker { .. })
55 }
56
57 pub fn is_github(&self) -> bool {
58 matches!(
59 self,
60 Resource::GhAction { .. } | Resource::GhWorkflow { .. }
61 )
62 }
63
64 #[instrument(level = "debug")]
65 pub fn parse(input: &str) -> Result<(Self, Version), Error> {
66 let re_docker = Regex::new(r"^docker://(?P<resource>[^:]+):(?P<version>[^:]+)$").unwrap();
67 if let Some(m) = re_docker.captures(input) {
68 let version_str = m.name("version").unwrap().as_str();
69 let version = Version::new(version_str)
70 .ok_or_else(|| Error::VersionParsing(version_str.into()))?;
71 return Ok((
72 Resource::new_docker(m.name("resource").unwrap().as_str().into()),
73 version,
74 ));
75 }
76 let re_ghworkflow = Regex::new(r"^(?P<user>[^/]+)/(?P<repo>[^/]+)/\.github/workflows/(?P<workflow>[^@]+)@(?P<version>[^@]+)$").unwrap();
77 if let Some(m) = re_ghworkflow.captures(input) {
78 let version_str = m.name("version").unwrap().as_str();
79 let version = Version::new(version_str)
80 .ok_or_else(|| Error::VersionParsing(version_str.into()))?;
81 return Ok((
82 Resource::new_ghworkflow(
83 m.name("user").unwrap().as_str().into(),
84 m.name("repo").unwrap().as_str().into(),
85 m.name("workflow").unwrap().as_str().into(),
86 ),
87 version,
88 ));
89 }
90 let re_github =
91 Regex::new(r"^(?P<user>[^/]+)/(?P<repo>[^@/]+)@(?P<version>[^@]+)$").unwrap();
92 if let Some(m) = re_github.captures(input) {
93 let version_str = m.name("version").unwrap().as_str();
94 let version = Version::new(version_str)
95 .ok_or_else(|| Error::VersionParsing(version_str.into()))?;
96 return Ok((
97 Resource::new_ghaction(
98 m.name("user").unwrap().as_str().into(),
99 m.name("repo").unwrap().as_str().into(),
100 ),
101 version,
102 ));
103 }
104 Err(Error::ResourceParseError(input.into()))
105 }
106
107 #[instrument(level = "debug")]
108 pub fn url(&self) -> Result<Url> {
109 let url_string = match self {
110 Resource::Docker { container } => {
111 format!("https://registry.hub.docker.com/v2/repositories/{container}/tags")
112 }
113 Resource::GhAction { user, repo } => {
114 format!("https://api.github.com/repos/{user}/{repo}/git/matching-refs/tags")
115 }
116 Resource::GhWorkflow { user, repo, .. } => {
117 format!("https://api.github.com/repos/{user}/{repo}/git/matching-refs/tags")
118 }
119 };
120 Ok(Url::parse(&url_string)?)
121 }
122
123 #[instrument(level = "debug")]
124 pub fn versioned_string(&self, version: &Version) -> String {
125 if self.is_docker() {
126 format!("{self}:{version}")
127 } else if self.is_github() {
128 format!("{self}@{version}")
129 } else {
130 panic!("unknown resource type");
131 }
132 }
133
134 #[instrument(level = "debug")]
135 pub async fn get_versions(&self) -> Result<Vec<Version>> {
136 if self.is_docker() {
137 updater::docker::get_versions(&self.url()?).await
138 } else if self.is_github() {
139 updater::github::get_versions(&self.url()?).await
140 } else {
141 panic!("unknown resource type");
142 }
143 }
144}
145
146impl fmt::Display for Resource {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(
149 f,
150 "{}",
151 match self {
152 Resource::Docker { container } => format!("docker://{container}"),
153 Resource::GhAction { user, repo } => format!("{user}/{repo}"),
154 Resource::GhWorkflow {
155 user,
156 repo,
157 workflow,
158 } => format!("{user}/{repo}/.github/workflows/{workflow}"),
159 }
160 )
161 }
162}