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 os_default() -> Self {
Self::User
}
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: InstallScope,
) -> (InstallScope, Option<&'static str>) {
if requested == InstallScope::System {
return (InstallScope::System, None);
}
match format {
Format::Aax => (
InstallScope::System,
Some("AAX is system-only; ignoring --user"),
),
Format::Au3 => (
InstallScope::System,
Some("AU v3 is system-only; ignoring --user"),
),
Format::Vst2 if cfg!(target_os = "windows") => (
InstallScope::System,
Some("VST2 on Windows is system-only; ignoring --user"),
),
_ => (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")
}
}