solar_core/tool/
github_workflows.rs1mod 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 #[arg(short, long, default_value = ".")]
24 #[serde(skip)]
25 destination: PathBuf,
26
27 #[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 fs::create_dir_all(&workflows_dir)?;
109
110 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 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(¶meters).as_bytes())?;
131 }
132
133 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 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 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 self.get_current_workflows()?.is_empty() {
162 fs::remove_dir(workflows_dir)?;
163 }
164
165 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}