#![cfg(unix)]
use crate::install_command::InstallCommand;
use dotfiles_core::action::Action;
use dotfiles_core::error::DotfilesError;
use dotfiles_core_macros::ConditionalAction;
use getset::Getters;
#[cfg(target_os = "macos")]
use log::info;
#[cfg(target_os = "macos")]
use std::fmt::Display;
use std::marker::PhantomData;
use subprocess::Exec;
#[cfg(target_os = "macos")]
#[derive(Getters, Eq, PartialEq, Debug, Clone)]
pub struct MacAppStoreItem {
#[getset(get)]
id: i64,
#[getset(get)]
name: String,
}
#[cfg(target_os = "macos")]
impl From<(i64, String)> for MacAppStoreItem {
fn from(value: (i64, String)) -> Self {
MacAppStoreItem {
id: value.0,
name: value.1,
}
}
}
#[cfg(target_os = "macos")]
impl Display for MacAppStoreItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "id: {}, name: {}", self.id, self.name)
}
}
#[cfg(target_os = "macos")]
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct MacAppStoreCommand {
items: Vec<MacAppStoreItem>,
args: Vec<String>,
}
#[cfg(target_os = "macos")]
impl From<Vec<MacAppStoreItem>> for MacAppStoreCommand {
fn from(items: Vec<MacAppStoreItem>) -> Self {
let mut args: Vec<String> = items.iter().map(|it| it.id().to_string()).collect();
args.insert(0, "install".into());
MacAppStoreCommand { items, args }
}
}
#[cfg(target_os = "macos")]
impl InstallCommand<MacAppStoreItem> for MacAppStoreCommand {
fn base_command(&self) -> Exec {
Exec::cmd("mas")
}
fn args(&self) -> &Vec<String> {
&self.args
}
fn action_description(&self) -> &str {
"Installing from Mac App Store"
}
fn items(&self) -> &Vec<MacAppStoreItem> {
&self.items
}
fn action_name(&self) -> &str {
"mas"
}
fn execute(&self) -> Result<(), DotfilesError> {
let item_list: String = self
.items()
.iter()
.map(|it| format!("{}", it))
.collect::<Vec<String>>()
.join(", ");
info!("{} {}", self.action_description(), item_list);
let mut cmd = self.base_command();
for arg in self.args().iter() {
cmd = cmd.arg(arg);
}
dotfiles_core::exec_wrapper::execute_commands(
vec![cmd],
format!("Couldn't {} {}", self.action_name(), item_list).as_str(),
format!(
"Unexpected error while {} {}",
self.action_description(),
&item_list
)
.as_str(),
)
}
}
struct BrewCommand {
items: Vec<String>,
args: Vec<String>,
action_name: String,
action_description: String,
}
impl InstallCommand<String> for BrewCommand {
fn base_command(&self) -> Exec {
Exec::cmd("brew")
}
fn args(&self) -> &Vec<String> {
&self.args
}
fn action_description(&self) -> &str {
&self.action_description
}
fn items(&self) -> &Vec<String> {
&self.items
}
fn action_name(&self) -> &str {
&self.action_name
}
}
impl BrewCommand {
fn tap(tap: &str) -> BrewCommand {
BrewCommand {
items: vec![tap.into()],
args: vec!["tap".into(), tap.into()],
action_name: "tap".into(),
action_description: "tapping".into(),
}
}
fn install_formulae(items: &Vec<String>) -> BrewCommand {
let mut args: Vec<String> = items.clone();
args.insert(0, "install".into());
BrewCommand {
items: items.clone(),
args: args,
action_name: "install formula".into(),
action_description: "installing formula".into(),
}
}
fn install_casks(items: &Vec<String>, force: &bool, adopt: &bool) -> BrewCommand {
let mut args = vec!["install".into(), "--cask".into()];
if *force {
args.push("--force".into())
}
if *adopt {
args.push("--adopt".into())
}
{
let mut items = items.clone();
args.append(&mut items)
}
let args = args;
let items = items.clone();
BrewCommand {
items,
args,
action_name: "install cask".into(),
action_description: "installing cask".into(),
}
}
}
#[derive(Eq, PartialEq, Debug, ConditionalAction, Getters)]
pub struct BrewAction<'a> {
#[get = "pub"]
skip_in_ci: bool,
#[get = "pub"]
force_casks: bool,
#[get = "pub"]
adopt_casks: bool,
#[get = "pub"]
taps: Vec<String>,
#[get = "pub"]
formulae: Vec<String>,
#[get = "pub"]
casks: Vec<String>,
#[cfg(target_os = "macos")]
#[get = "pub"]
mas_apps: Vec<MacAppStoreItem>,
phantom_data: PhantomData<&'a String>,
}
impl<'a> BrewAction<'a> {
pub fn new(
skip_in_ci: bool,
force_casks: bool,
adopt_casks: bool,
taps: Vec<String>,
formulae: Vec<String>,
casks: Vec<String>,
#[cfg(target_os = "macos")] mas_apps: Vec<MacAppStoreItem>,
) -> Self {
let action = BrewAction {
skip_in_ci,
force_casks,
adopt_casks,
taps,
formulae,
casks,
#[cfg(target_os = "macos")]
mas_apps,
phantom_data: PhantomData,
};
log::trace!("Creating new {:?}", action);
action
}
}
impl Action<'_> for BrewAction<'_> {
fn execute(&self) -> Result<(), DotfilesError> {
for tap in &self.taps {
BrewCommand::tap(tap).execute()?;
}
if !self.formulae.is_empty() {
BrewCommand::install_formulae(&self.formulae).execute()?;
}
if !self.casks.is_empty() {
BrewCommand::install_casks(&self.casks, self.force_casks(), self.adopt_casks()).execute()?;
}
Ok(())
}
}