use std::{path::PathBuf, process::Command};
use clingwrap::runner::{CommandError, CommandRunner};
use serde::{Deserialize, Serialize};
use crate::{
action::{ActionError, Context},
action_impl::ActionImpl,
runlog::RunLogSource,
util::{mkdir, UtilError},
};
pub const RUSTUP_DIR: &str = "rustup";
const DEFAULT_CHANNEL: &str = "stable";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Rustup {
channel: String,
target: Option<String>,
components: Option<Vec<String>>,
profile: Option<String>,
}
impl Rustup {
pub fn new(
channel: &Option<String>,
target: &Option<String>,
components: &Option<Vec<String>>,
profile: &Option<String>,
) -> Self {
Self {
channel: channel.clone().unwrap_or(DEFAULT_CHANNEL.to_string()),
target: target.to_owned(),
components: components.to_owned(),
profile: profile.to_owned(),
}
}
}
impl ActionImpl for Rustup {
fn execute(&self, context: &mut Context) -> Result<(), ActionError> {
context
.runlog()
.debug(RunLogSource::PrePlan, format!("rustup {self:?}"));
let dir = context.deps_dir().join(RUSTUP_DIR);
if !dir.exists() {
mkdir(&dir).map_err(|err| RustupError::RustupDir(dir.clone(), err))?;
}
context.set_plan_env("RUSTUP_HOME", format!("/ci/deps/{RUSTUP_DIR}"));
let mut cmd = Command::new("rustup");
cmd.env("RUSTUP_HOME", dir.as_os_str());
cmd.arg("install");
cmd.arg(&self.channel);
if let Some(v) = &self.target {
cmd.arg("--target");
cmd.arg(v);
}
if let Some(v) = &self.profile {
cmd.arg("--profile");
cmd.arg(v);
}
if let Some(list) = &self.components {
cmd.arg("--component");
let mut v = String::new();
for c in list {
if !v.is_empty() {
v.push(',');
}
v.push_str(c);
}
cmd.arg(v);
}
context.runlog().start_program(RunLogSource::PrePlan, &cmd);
let mut runner = CommandRunner::new(cmd);
runner.capture_stdout();
runner.capture_stderr();
match runner.execute() {
Ok(output) => {
context
.runlog()
.program_succeeded(RunLogSource::PrePlan, &output);
Ok(())
}
Err(err) => {
context.runlog().program_failed(RunLogSource::PrePlan, &err);
Err(RustupError::Rustup(err).into())
}
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum RustupError {
#[error("failed to run rustup")]
Rustup(#[source] CommandError),
#[error("failed to create rustup directory {0}")]
RustupDir(PathBuf, #[source] UtilError),
}
impl From<RustupError> for ActionError {
fn from(value: RustupError) -> Self {
Self::Rustup(value)
}
}