rust_actions/
workflow_registry.rs

1use 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}