rust_actions/
workflow_registry.rs1use crate::parser::{parse_workflows, Workflow};
2use crate::{Error, Result};
3use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5
6const FILE_REF_PREFIX: &str = "@file:";
7
8pub struct WorkflowRegistry {
9 base_path: PathBuf,
10 workflows: HashMap<PathBuf, Workflow>,
11}
12
13impl WorkflowRegistry {
14 pub fn build(workflows_path: impl AsRef<Path>) -> Result<Self> {
15 let base_path = workflows_path.as_ref().to_path_buf();
16 let parsed = parse_workflows(&base_path)?;
17 let workflows: HashMap<PathBuf, Workflow> = parsed.into_iter().collect();
18
19 Ok(Self {
20 base_path,
21 workflows,
22 })
23 }
24
25 pub fn get(&self, path: &Path) -> Option<&Workflow> {
26 self.workflows.get(path)
27 }
28
29 pub fn get_by_str(&self, path: &str) -> Option<&Workflow> {
30 self.workflows.get(&PathBuf::from(path))
31 }
32
33 pub fn is_reusable(&self, path: &Path) -> bool {
34 self.workflows
35 .get(path)
36 .map(|w| w.is_reusable())
37 .unwrap_or(false)
38 }
39
40 pub fn resolve_file_ref(&self, uses: &str) -> Result<&Workflow> {
41 let path = parse_file_ref(uses)?;
42 self.get_by_str(path).ok_or_else(|| {
43 Error::WorkflowNotFound {
44 path: path.to_string(),
45 }
46 })
47 }
48
49 pub fn runnable_workflows(&self) -> impl Iterator<Item = (&PathBuf, &Workflow)> {
50 self.workflows.iter().filter(|(_, w)| !w.is_reusable())
51 }
52
53 pub fn reusable_workflows(&self) -> impl Iterator<Item = (&PathBuf, &Workflow)> {
54 self.workflows.iter().filter(|(_, w)| w.is_reusable())
55 }
56
57 pub fn all_workflows(&self) -> impl Iterator<Item = (&PathBuf, &Workflow)> {
58 self.workflows.iter()
59 }
60
61 pub fn base_path(&self) -> &Path {
62 &self.base_path
63 }
64
65 pub fn workflow_count(&self) -> usize {
66 self.workflows.len()
67 }
68
69 pub fn runnable_count(&self) -> usize {
70 self.workflows.values().filter(|w| !w.is_reusable()).count()
71 }
72
73 pub fn reusable_count(&self) -> usize {
74 self.workflows.values().filter(|w| w.is_reusable()).count()
75 }
76}
77
78pub fn is_file_ref(uses: &str) -> bool {
79 uses.starts_with(FILE_REF_PREFIX)
80}
81
82pub fn parse_file_ref(uses: &str) -> Result<&str> {
83 if !uses.starts_with(FILE_REF_PREFIX) {
84 return Err(Error::InvalidFileRef {
85 uses: uses.to_string(),
86 });
87 }
88 Ok(&uses[FILE_REF_PREFIX.len()..])
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_is_file_ref() {
97 assert!(is_file_ref("@file:setup/user-setup.yaml"));
98 assert!(!is_file_ref("user/create"));
99 assert!(!is_file_ref("file:something"));
100 }
101
102 #[test]
103 fn test_parse_file_ref() {
104 let path = parse_file_ref("@file:setup/user-setup.yaml").unwrap();
105 assert_eq!(path, "setup/user-setup.yaml");
106 }
107
108 #[test]
109 fn test_parse_file_ref_invalid() {
110 let result = parse_file_ref("user/create");
111 assert!(result.is_err());
112 }
113}