use std::path::PathBuf;
use std::sync::Mutex;
use crate::format::Format;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum InstallScope {
User,
System,
}
impl InstallScope {
pub(crate) fn needs_sudo(self) -> bool {
match self {
Self::User => false,
Self::System => cfg!(target_os = "macos") || cfg!(target_os = "windows"),
}
}
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PkgScope {
User,
System,
Ask,
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
impl std::str::FromStr for PkgScope {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"user" => Ok(Self::User),
"system" => Ok(Self::System),
"ask" => Ok(Self::Ask),
other => Err(format!(
"[packaging] preferred_scope: unknown value {other:?} \
(expected \"user\", \"system\", or \"ask\")"
)),
}
}
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
impl PkgScope {
pub(crate) fn os_default() -> Self {
Self::Ask
}
pub(crate) fn label(self) -> &'static str {
match self {
Self::User => "user",
Self::System => "system",
Self::Ask => "ask",
}
}
pub(crate) fn dist_suffix(self) -> &'static str {
match self {
Self::User => "-user",
Self::System => "-system",
Self::Ask => "",
}
}
}
pub(crate) fn effective_scope(
format: Format,
requested: Option<InstallScope>,
) -> (InstallScope, Option<&'static str>) {
let hard_upgrade: Option<&'static str> = match format {
Format::Aax => Some("AAX is system-only; ignoring --user"),
Format::Au3 => Some("AU v3 is system-only; ignoring --user"),
Format::Vst2 if cfg!(target_os = "windows") => {
Some("VST2 on Windows is system-only; ignoring --user")
}
_ => None,
};
if let Some(msg) = hard_upgrade {
let note = if requested == Some(InstallScope::User) {
Some(msg)
} else {
None
};
return (InstallScope::System, note);
}
if let Some(s) = requested {
return (s, None);
}
if cfg!(target_os = "windows") && format == Format::Vst3 {
return (
InstallScope::System,
Some("VST3 on Windows defaults to system scope; pass --user for per-user install"),
);
}
(InstallScope::User, None)
}
pub(crate) fn set_cli_install_scope(
slot: &mut Option<InstallScope>,
want: InstallScope,
) -> crate::Res {
if let Some(prev) = *slot
&& prev != want
{
return Err("--user and --system are mutually exclusive".into());
}
*slot = Some(want);
Ok(())
}
pub(crate) fn note_once(message: &str) {
use std::collections::HashSet;
static SEEN: Mutex<Option<HashSet<String>>> = Mutex::new(None);
let mut g = SEEN.lock().unwrap();
let seen = g.get_or_insert_with(HashSet::new);
if seen.insert(message.to_string()) {
eprintln!("note: {message}");
}
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn home() -> PathBuf {
crate::dirs::require_home_dir().expect("home directory required")
}
#[cfg(target_os = "windows")]
fn local_appdata() -> PathBuf {
crate::dirs::require_local_appdata().expect("LOCALAPPDATA required")
}
#[cfg(target_os = "windows")]
fn appdata() -> PathBuf {
crate::dirs::require_appdata().expect("APPDATA required")
}
#[cfg(target_os = "macos")]
impl InstallScope {
pub(crate) fn clap_dir(self) -> PathBuf {
match self {
Self::User => home().join("Library/Audio/Plug-Ins/CLAP"),
Self::System => PathBuf::from("/Library/Audio/Plug-Ins/CLAP"),
}
}
pub(crate) fn vst3_dir(self) -> PathBuf {
match self {
Self::User => home().join("Library/Audio/Plug-Ins/VST3"),
Self::System => PathBuf::from("/Library/Audio/Plug-Ins/VST3"),
}
}
pub(crate) fn vst2_dir(self) -> PathBuf {
match self {
Self::User => home().join("Library/Audio/Plug-Ins/VST"),
Self::System => PathBuf::from("/Library/Audio/Plug-Ins/VST"),
}
}
pub(crate) fn lv2_dir(self) -> PathBuf {
match self {
Self::User => home().join("Library/Audio/Plug-Ins/LV2"),
Self::System => PathBuf::from("/Library/Audio/Plug-Ins/LV2"),
}
}
pub(crate) fn au_v2_dir(self) -> PathBuf {
match self {
Self::User => home().join("Library/Audio/Plug-Ins/Components"),
Self::System => PathBuf::from("/Library/Audio/Plug-Ins/Components"),
}
}
pub(crate) fn standalone_dir(self) -> PathBuf {
match self {
Self::User => home().join("Applications"),
Self::System => PathBuf::from("/Applications"),
}
}
}
#[cfg(target_os = "windows")]
impl InstallScope {
pub(crate) fn clap_dir(self) -> PathBuf {
match self {
Self::User => local_appdata().join(r"Programs\Common\CLAP"),
Self::System => crate::common_program_files().join("CLAP"),
}
}
pub(crate) fn vst3_dir(self) -> PathBuf {
match self {
Self::User => local_appdata().join(r"Programs\Common\VST3"),
Self::System => crate::common_program_files().join("VST3"),
}
}
#[allow(clippy::unused_self)]
pub(crate) fn vst2_dir(self) -> PathBuf {
crate::program_files().join("Steinberg").join("VstPlugins")
}
pub(crate) fn lv2_dir(self) -> PathBuf {
match self {
Self::User => appdata().join("LV2"),
Self::System => crate::common_program_files().join("LV2"),
}
}
}
#[cfg(target_os = "linux")]
impl InstallScope {
pub(crate) fn clap_dir(self) -> PathBuf {
let _ = self;
home().join(".clap")
}
pub(crate) fn vst3_dir(self) -> PathBuf {
let _ = self;
home().join(".vst3")
}
pub(crate) fn vst2_dir(self) -> PathBuf {
let _ = self;
home().join(".vst")
}
pub(crate) fn lv2_dir(self) -> PathBuf {
let _ = self;
home().join(".lv2")
}
}