Skip to main content

solar_core/tool/
github_workflows.rs

1mod parameters;
2mod workflow;
3mod workflow_file;
4
5use derive_getters::Getters;
6pub use parameters::Parameters;
7use serde::{Deserialize, Serialize};
8pub use workflow::Workflow;
9
10use crate::{Config, SolarError, ToolTrait, tool::github_workflows::workflow_file::WorkflowFile};
11use clap::Parser;
12use std::{
13    collections::HashMap,
14    fs::{self, DirEntry, File},
15    hash::Hash,
16    io::Write,
17    path::{Path, PathBuf},
18};
19
20#[derive(Parser, Clone, Default, PartialEq, Debug, Serialize, Deserialize, Getters)]
21pub struct GithubWorkflows {
22    /// The working directory to use for installation.
23    #[arg(short, long, default_value = ".")]
24    #[serde(skip)]
25    destination: PathBuf,
26
27    /// Use the release workflow in this project.
28    #[arg(short, long, num_args = 0..)]
29    workflows_list: Option<Vec<Workflow>>,
30}
31
32impl GithubWorkflows {
33    pub fn new(destination: PathBuf, workflows_list: Option<Vec<Workflow>>) -> Self {
34        Self {
35            destination,
36            workflows_list,
37        }
38    }
39
40    fn workflows_path(&self) -> PathBuf {
41        self.destination.join(".github/workflows")
42    }
43
44    fn get_current_workflows(&self) -> Result<Vec<DirEntry>, SolarError> {
45        let workflows_dir = self.workflows_path();
46        let mut workflow_entries = Vec::new();
47        for entry in fs::read_dir(workflows_dir)? {
48            workflow_entries.push(entry?);
49        }
50        Ok(workflow_entries)
51    }
52
53    fn workflow_list_to_map<'a>(
54        &self,
55        workflows_list: &'a Vec<Workflow>,
56    ) -> Result<HashMap<WorkflowFile, &'a Workflow>, SolarError> {
57        let mut workflow_map = HashMap::new();
58        for workflow in workflows_list {
59            let workflow_type = workflow.get().file();
60            workflow_map.entry(workflow_type).or_insert(workflow);
61        }
62        Ok(workflow_map)
63    }
64
65    fn extend_no_overwrite<K, V>(
66        &self,
67        mut hash_map_one: HashMap<K, V>,
68        hash_map_two: HashMap<K, V>,
69    ) -> HashMap<K, V>
70    where
71        K: Eq + Hash,
72    {
73        for (key, value) in hash_map_two {
74            hash_map_one.entry(key).or_insert(value);
75        }
76        hash_map_one
77    }
78
79    fn get_parameters(&self) -> Result<Parameters, SolarError> {
80        Ok(Parameters::new(
81            self.destination
82                .canonicalize()?
83                .file_name()
84                .ok_or("Could not get name of working directory")?
85                .to_str()
86                .ok_or("Could not convert directory name to string.")?
87                .to_string(),
88        ))
89    }
90}
91
92impl ToolTrait for GithubWorkflows {
93    fn set_dest(&mut self, dest: &Path) {
94        self.destination = dest.to_path_buf();
95    }
96
97    fn install(&mut self) -> Result<(), SolarError> {
98        let workflows_dir = self.workflows_path();
99        let parameters = self.get_parameters()?;
100        let workflows_list = self
101            .workflows_list
102            .as_ref()
103            .ok_or("At least one workflow must be given for installation.")?;
104
105        let mut workflows_map = self.workflow_list_to_map(workflows_list)?;
106
107        // Ensure github workspace folders exist.
108        fs::create_dir_all(&workflows_dir)?;
109
110        // Get or create configuration.
111        let config = Config::load_or_default(&self.destination);
112        if let Some(workflows_config) = config.github_workflows()
113            && let Some(config_workflows) = workflows_config.workflows_list()
114        {
115            let config_workflows_map = self.workflow_list_to_map(config_workflows)?;
116            workflows_map = self.extend_no_overwrite(workflows_map, config_workflows_map);
117        }
118
119        // Create the workflows.
120        for workflow in workflows_map.values() {
121            let workflow_type = workflow.get();
122            let workflow_path = workflows_dir.join(workflow_type.file().name());
123            if !fs::exists(&workflow_path)? {
124                File::create(&workflow_path)?;
125            }
126            let mut workflow_file = File::options()
127                .write(true)
128                .truncate(true)
129                .open(&workflow_path)?;
130            workflow_file.write_all(workflow_type.get(&parameters).as_bytes())?;
131        }
132
133        // Save configuration.
134        self.workflows_list = Some(Vec::from_iter(
135            workflows_map.values().map(|v| v.to_owned().to_owned()),
136        ));
137        config.set_github_workflows(Some(self.clone())).save()?;
138
139        Ok(())
140    }
141
142    fn uninstall(&mut self) -> Result<(), SolarError> {
143        // Get configuration
144        let config = Config::load_from(&self.destination)?;
145        let current_config = config.github_workflows().as_ref().ok_or(
146            "Cannot uninstall github_workflows - github_workflows not found in configuration.",
147        )?;
148
149        // Remove the workflows from the configuration.
150        let workflows_dir = self.workflows_path();
151        if let Some(workflows_list) = current_config.workflows_list() {
152            for workflow in workflows_list {
153                let workflow_path = workflows_dir.join(workflow.get().file().name());
154                if fs::exists(&workflow_path)? {
155                    fs::remove_file(workflow_path)?;
156                }
157            }
158        }
159
160        // If workflows directory is empty, remove it.
161        if self.get_current_workflows()?.is_empty() {
162            fs::remove_dir(workflows_dir)?;
163        }
164
165        // Update configuration, remove if empty.
166        let config = config.set_github_workflows(None);
167        match config.is_empty() {
168            true => fs::remove_file(config.path())?,
169            false => config.save()?,
170        }
171
172        Ok(())
173    }
174}