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 } => format!(
111 "https://registry.hub.docker.com/v2/repositories/{}/tags",
112 container
113 ),
114 Resource::GhAction { user, repo } => format!(
115 "https://api.github.com/repos/{}/{}/git/matching-refs/tags",
116 user, repo
117 ),
118 Resource::GhWorkflow { user, repo, .. } => format!(
119 "https://api.github.com/repos/{}/{}/git/matching-refs/tags",
120 user, repo
121 ),
122 };
123 Ok(Url::parse(&url_string)?)
124 }
125
126 #[instrument(level = "debug")]
127 pub fn versioned_string(&self, version: &Version) -> String {
128 if self.is_docker() {
129 format!("{}:{}", self, version)
130 } else if self.is_github() {
131 format!("{}@{}", self, version)
132 } else {
133 panic!("unknown resource type");
134 }
135 }
136
137 #[instrument(level = "debug")]
138 pub async fn get_versions(&self) -> Result<Vec<Version>> {
139 if self.is_docker() {
140 updater::docker::get_versions(&self.url()?).await
141 } else if self.is_github() {
142 updater::github::get_versions(&self.url()?).await
143 } else {
144 panic!("unknown resource type");
145 }
146 }
147}
148
149impl fmt::Display for Resource {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(
152 f,
153 "{}",
154 match self {
155 Resource::Docker { container } => format!("docker://{}", container),
156 Resource::GhAction { user, repo } => format!("{}/{}", user, repo),
157 Resource::GhWorkflow {
158 user,
159 repo,
160 workflow,
161 } => format!("{}/{}/.github/workflows/{}", user, repo, workflow),
162 }
163 )
164 }
165}