use camino::Utf8PathBuf;
use super::*;
use axoproject::platforms::{KNOWN_LINUX_TARGETS, KNOWN_MAC_TARGETS, KNOWN_WINDOWS_TARGETS};
const EXTS_FOR_TAR_BZIP2: &[&str] = &[".tar.bz2", ".tb2", ".tbz", ".tbz2", ".tz2"];
const EXTS_FOR_TAR_GZIP: &[&str] = &[".tar.gz", ".taz", ".tgz"];
const EXTS_FOR_TAR_LZIP: &[&str] = &[".tar.lz"];
const EXTS_FOR_TAR_LZMA: &[&str] = &[".tar.lzma", ".tlz"];
const EXTS_FOR_TAR_XZ: &[&str] = &[".tar.xz", ".txz"];
const EXTS_FOR_TAR_COMPRESS: &[&str] = &[".tar.Z", ".tZ", ".taZ"];
const EXTS_FOR_TAR_ZSTD: &[&str] = &[".tar.zst", ".tzst"];
const EXTS_FOR_TAR_BROTLI: &[&str] = &[".tar.br"];
const EXTS_FOR_ZIP: &[&str] = &[".zip"];
const EXTS_FOR_RAR: &[&str] = &[".rar"];
const EXTS_FOR_7ZIP: &[&str] = &[".7z"];
const KNOWN_ARCHIVE_EXTS: &[&[&str]] = &[
EXTS_FOR_TAR_BZIP2,
EXTS_FOR_TAR_GZIP,
EXTS_FOR_TAR_LZIP,
EXTS_FOR_TAR_LZMA,
EXTS_FOR_TAR_XZ,
EXTS_FOR_TAR_COMPRESS,
EXTS_FOR_TAR_ZSTD,
EXTS_FOR_TAR_BROTLI,
EXTS_FOR_ZIP,
EXTS_FOR_RAR,
EXTS_FOR_7ZIP,
];
const EXT_BUNDLE_MSI: &str = ".msi";
const EXT_BUNDLE_APP: &str = ".app";
const EXT_BUNDLE_DMG: &str = ".dmg";
const EXT_BUNDLE_DEB: &str = ".deb";
const EXT_BUNDLE_RPM: &str = ".rpm";
const EXT_BUNDLE_PACMAN: &str = ".pkg.tar.";
const EXT_BUNDLE_FLATPAK: &str = ".flatpak";
const EXT_BUNDLE_SNAP: &str = ".snap";
const KNOWN_WINDOWS_BUNDLE_EXTS: &[&str] = &[EXT_BUNDLE_MSI];
const KNOWN_MAC_BUNDLE_EXTS: &[&str] = &[EXT_BUNDLE_APP, EXT_BUNDLE_DMG];
const KNOWN_LINUX_BUNDLE_EXTS: &[&str] = &[
EXT_BUNDLE_DMG,
EXT_BUNDLE_DEB,
EXT_BUNDLE_RPM,
EXT_BUNDLE_PACMAN,
EXT_BUNDLE_FLATPAK,
EXT_BUNDLE_SNAP,
];
const KNOWN_BUNDLE_EXTS: &[&str] = &[
EXT_BUNDLE_MSI,
EXT_BUNDLE_APP,
EXT_BUNDLE_DMG,
EXT_BUNDLE_DEB,
EXT_BUNDLE_RPM,
EXT_BUNDLE_FLATPAK,
EXT_BUNDLE_SNAP,
EXT_BUNDLE_PACMAN,
];
const EXT_SCRIPT_SHELL: &str = ".sh";
const EXT_SCRIPT_POWERSHELL: &str = ".ps1";
const KNOWN_WINDOWS_SCRIPT_EXTS: &[&str] = &[EXT_SCRIPT_POWERSHELL];
const KNOWN_UNIX_SCRIPT_EXTS: &[&str] = &[EXT_SCRIPT_SHELL];
pub(crate) const KNOWN_SCRIPT_EXTS: &[&str] = &[EXT_SCRIPT_SHELL, EXT_SCRIPT_POWERSHELL];
impl ReleaseArtifacts {
pub fn add_inference(&mut self) {
let app_name = self.app_name.clone();
for file_idx in self.file_indices() {
let file = self.file_mut(file_idx);
if !file.infer {
continue;
}
if let Some(app_name) = &app_name {
if !file.name.contains(app_name) {
continue;
}
}
let mut targets = vec![];
for target in KNOWN_TARGET_TRIPLES.iter().copied().flatten().copied() {
if file.name.contains(target) {
targets.push(target.to_owned());
}
}
let label;
let description = String::new();
let method;
let preference;
if file.name.contains("install")
&& KNOWN_SCRIPT_EXTS.iter().any(|ext| file.name.ends_with(ext))
{
if targets.is_empty() {
targets = infer_targets_for_script(file);
}
let run_hint = infer_run_hint_for_script(file);
label = infer_label_for_script(file);
preference = InstallerPreference::Script;
method = InstallMethod::Run {
file: Some(file_idx),
run_hint,
};
} else if KNOWN_BUNDLE_EXTS.iter().any(|ext| file.name.ends_with(ext)) {
if targets.is_empty() {
targets = infer_targets_for_bundle(file);
}
label = infer_label_for_bundle(file);
preference = InstallerPreference::Native;
method = InstallMethod::Download { file: file_idx };
} else if KNOWN_ARCHIVE_EXTS
.iter()
.copied()
.flatten()
.any(|ext| file.name.ends_with(ext))
{
if targets.is_empty() {
continue;
}
label = infer_label_for_archive(file);
preference = InstallerPreference::Archive;
method = InstallMethod::Download { file: file_idx };
} else {
continue;
}
let targets = preference_to_targets(targets, preference);
let installer = Installer {
label,
description,
app_name: self.app_name.clone(),
targets,
method,
display: DisplayPreference::Preferred,
};
self.add_installer(installer);
}
}
}
fn infer_targets_for_bundle(file: &File) -> Vec<TargetTriple> {
let mut targets = vec![];
if KNOWN_WINDOWS_BUNDLE_EXTS
.iter()
.any(|ext| file.name.contains(ext))
{
targets.extend(KNOWN_WINDOWS_TARGETS.iter().copied().map(|t| t.to_owned()));
}
if KNOWN_MAC_BUNDLE_EXTS
.iter()
.any(|ext| file.name.contains(ext))
{
targets.extend(KNOWN_MAC_TARGETS.iter().copied().map(|t| t.to_owned()));
}
if KNOWN_LINUX_BUNDLE_EXTS
.iter()
.any(|ext| file.name.contains(ext))
{
targets.extend(
KNOWN_LINUX_TARGETS
.iter()
.copied()
.flatten()
.copied()
.map(|t| t.to_owned()),
);
}
targets
}
fn infer_targets_for_script(file: &File) -> Vec<TargetTriple> {
let mut targets = vec![];
if KNOWN_WINDOWS_SCRIPT_EXTS
.iter()
.any(|ext| file.name.contains(ext))
{
targets.extend(KNOWN_WINDOWS_TARGETS.iter().copied().map(|t| t.to_owned()));
}
if KNOWN_UNIX_SCRIPT_EXTS
.iter()
.any(|ext| file.name.contains(ext))
{
targets.extend(
KNOWN_LINUX_TARGETS
.iter()
.copied()
.flatten()
.copied()
.map(|t| t.to_owned()),
);
targets.extend(KNOWN_MAC_TARGETS.iter().copied().map(|t| t.to_owned()));
}
targets
}
fn infer_run_hint_for_script(file: &File) -> String {
if file.name.ends_with(EXT_SCRIPT_SHELL) {
format!(
"curl --proto '=https' --tlsv1.2 -LsSf {} | sh",
file.download_url
)
} else if file.name.ends_with(EXT_SCRIPT_POWERSHELL) {
format!(r#"powershell -c "irm {} | iex""#, file.download_url)
} else {
unimplemented!(
"Looks like someone added a new kind of script but didn't add a run hint for it?"
);
}
}
fn infer_label_for_bundle(file: &File) -> String {
Utf8PathBuf::from(&file.name)
.extension()
.expect("we determined a file was a bundle based on its extension, but it had none?")
.to_owned()
}
fn infer_label_for_archive(file: &File) -> String {
if EXTS_FOR_RAR.iter().any(|ext| file.name.ends_with(ext)) {
"rar".to_owned()
} else if EXTS_FOR_7ZIP.iter().any(|ext| file.name.ends_with(ext)) {
"7zip".to_owned()
} else if EXTS_FOR_ZIP.iter().any(|ext| file.name.ends_with(ext)) {
"zip".to_owned()
} else {
"tarball".to_owned()
}
}
fn infer_label_for_script(file: &File) -> String {
if file.name.ends_with(EXT_SCRIPT_POWERSHELL) {
"powershell".to_owned()
} else if file.name.ends_with(EXT_SCRIPT_SHELL) {
"shell".to_owned()
} else {
Utf8PathBuf::from(&file.name)
.extension()
.expect("we determined a file was a script based on its extension, but it had none?")
.to_owned()
}
}