passionfruitdev_cli/
cli.rs

1use chrono::{Datelike, Timelike, Utc};
2use clap::{Subcommand, ValueEnum};
3use std::process::Command as RunCommand;
4use std::{fmt, fs};
5use toml_edit::{value, Document};
6
7pub use clap::Parser;
8
9#[derive(Parser)]
10#[command(author, version, about, long_about = None)]
11pub struct Cli {
12    #[command(subcommand)]
13    command: Command,
14}
15
16#[derive(Subcommand)]
17enum Command {
18    Install {
19        #[arg(short, long, value_enum, default_value_t = Architecture::Aarch64AppleDarwin)]
20        architecture: Architecture,
21        #[arg(long, value_enum, value_enum, default_value_t = OperatingSystem::MacOS)]
22        os: OperatingSystem,
23    },
24    New {
25        name: String,
26    },
27    Publish {
28        #[arg(short, long, value_enum, default_value_t = Lifecycle::Nightly)]
29        lifecycle: Lifecycle,
30    },
31}
32
33#[derive(ValueEnum, Debug, Clone)]
34#[clap(rename_all = "kebab_case")]
35enum Architecture {
36    Aarch64AppleDarwin,
37}
38
39#[derive(ValueEnum, Debug, Clone)]
40enum OperatingSystem {
41    MacOS,
42}
43
44#[derive(ValueEnum, Debug, Clone, PartialEq)]
45enum Lifecycle {
46    Manual,
47    Pull,
48    Merge,
49    Nightly,
50    Candidate,
51    Official,
52}
53
54impl fmt::Display for Lifecycle {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        match self {
57            Lifecycle::Manual => write!(f, "manual"),
58            Lifecycle::Pull => write!(f, "pull"),
59            Lifecycle::Merge => write!(f, "merge"),
60            Lifecycle::Nightly => write!(f, "nightly"),
61            Lifecycle::Candidate => write!(f, "rc"),
62            Lifecycle::Official => write!(f, "official"),
63        }
64    }
65}
66
67pub fn execute(cli: Cli) {
68    match &cli.command {
69        Command::Install { architecture, os } => match (architecture, os) {
70            (Architecture::Aarch64AppleDarwin, OperatingSystem::MacOS) => {
71                println!("Installing Aarch64AppleDarwin on MacOS dependencies");
72            }
73        },
74        Command::New { name } => {
75            println!("Creating project: {}", name)
76        }
77        Command::Publish { lifecycle } => publish(lifecycle),
78    }
79}
80
81fn publish(lifecycle: &Lifecycle) {
82    let datetime = Utc::now();
83    let datetime = format!(
84        "{:02}{:02}{:02}{:02}{:02}{:02}",
85        datetime.year(),
86        datetime.month(),
87        datetime.day(),
88        datetime.hour(),
89        datetime.minute(),
90        datetime.second()
91    );
92    let git_hash = RunCommand::new("git")
93        .args(&["rev-parse", "HEAD"])
94        .output()
95        .unwrap();
96    let git_hash = String::from_utf8(git_hash.stdout).unwrap();
97    let git_hash = git_hash.lines().next().unwrap();
98
99    let dryrun = RunCommand::new("cargo")
100        .args(&["publish", "--dry-run"])
101        .output()
102        .unwrap();
103    if dryrun.status.success() {
104        let current_directory = std::env::current_dir().unwrap();
105        let cargo_toml_path = format!("{}/Cargo.toml", current_directory.display());
106        let cargo_toml_backup_path = format!("{}/Cargo.toml.bak", current_directory.display());
107        let _ = RunCommand::new("cp")
108            .args(&[&cargo_toml_path, &cargo_toml_backup_path])
109            .output()
110            .unwrap();
111        let cargo_toml = fs::read_to_string(&cargo_toml_path).unwrap();
112        let mut cargo_toml = cargo_toml.parse::<Document>().unwrap();
113        let version = cargo_toml["package"]["version"].as_str().unwrap();
114
115        let version = format!(
116            "{}-{}.{}+{}",
117            &version,
118            lifecycle.to_string(),
119            &datetime,
120            &git_hash
121        );
122        cargo_toml["package"]["version"] = value(&version);
123        let _ = fs::write(&cargo_toml_path, cargo_toml.to_string()).unwrap();
124        let _ = RunCommand::new("cargo")
125            .args(&["publish", "--allow-dirty"])
126            .output()
127            .unwrap();
128        let _ = RunCommand::new("mv")
129            .args(&[&cargo_toml_backup_path, &cargo_toml_path])
130            .output()
131            .unwrap();
132        if lifecycle.to_string() == Lifecycle::Official.to_string() {
133            let _ = RunCommand::new("cargo")
134                .args(&["publish"])
135                .output()
136                .unwrap();
137            // TODO: Bump semver after official release.
138        }
139    } else {
140        println!(
141            "Publish failed dry run with error:\n{}",
142            String::from_utf8_lossy(&dryrun.stderr)
143        );
144    }
145    ()
146}