1use std::path::PathBuf;
5use std::process::Command;
6
7use derive_setters::Setters;
8
9use crate::error::{Error, Result};
10use crate::Workflow;
11
12#[derive(Setters, Clone)]
13#[setters(strip_option, into)]
14pub struct Generate {
15 workflow: Workflow,
16 name: String,
17}
18
19impl Generate {
20 pub fn new(workflow: Workflow) -> Self {
21 Self { workflow, name: "ci.yml".to_string() }
22 }
23
24 fn check_file(&self, path: &PathBuf, content: &str) -> Result<()> {
25 if let Ok(prev) = std::fs::read_to_string(path) {
26 if content != prev {
27 Err(Error::OutdatedWorkflow)
28 } else {
29 Ok(())
30 }
31 } else {
32 Err(Error::MissingWorkflowFile(path.clone()))
33 }
34 }
35
36 pub fn generate(&self) -> Result<()> {
37 let comment = include_str!("./comment.yml");
38
39 let root_dir = String::from_utf8(
40 Command::new("git")
41 .args(["rev-parse", "--show-toplevel"])
42 .output()?
43 .stdout,
44 )?;
45
46 let path = PathBuf::from(root_dir.trim())
47 .join(".github")
48 .join("workflows")
49 .join(self.name.as_str());
50
51 let content = format!("{}\n{}", comment, self.workflow.to_string()?);
52
53 let result = self.check_file(&path, &content);
54
55 if std::env::var("CI").is_ok() {
56 result
57 } else {
58 match result {
59 Ok(()) => {
60 println!("Workflow file is up-to-date: {}", path.display());
61 Ok(())
62 }
63 Err(Error::OutdatedWorkflow) => {
64 std::fs::write(path.clone(), content)?;
65 println!("Updated workflow file: {}", path.display());
66 Ok(())
67 }
68 Err(Error::MissingWorkflowFile(path)) => {
69 std::fs::create_dir_all(path.parent().ok_or(Error::IO(
70 std::io::Error::other("Invalid parent dir(s) path"),
71 ))?)?;
72 std::fs::write(path.clone(), content)?;
73 println!("Generated workflow file: {}", path.display());
74 Ok(())
75 }
76 Err(e) => Err(e),
77 }
78 }
79 }
80}