use crate::pkg::{config, local, resolve, types};
use anyhow::{Result, anyhow};
use mlua::LuaSerdeExt;
use std::fs;
pub fn add(
ext_name: &str,
_yes: bool,
plugin_manager: &crate::pkg::plugin::PluginManager,
) -> Result<()> {
println!("Adding extension: {}", ext_name);
let (pkg, _, _, _, registry_handle) =
resolve::resolve_package_and_version(ext_name, false, false)?;
if pkg.package_type != types::PackageType::Extension {
return Err(anyhow!("'{}' is not an extension package.", ext_name));
}
let pkg_val = plugin_manager
.lua
.to_value(&pkg)
.map_err(|e: mlua::Error| anyhow!(e.to_string()))?;
plugin_manager.trigger_hook("on_pre_extension_add", Some(pkg_val))?;
if let Some(extension_info) = pkg.extension {
if extension_info.extension_type != "zoi" {
return Err(anyhow!(
"Unsupported extension type: {}",
extension_info.extension_type
));
}
println!("Applying extension changes...");
for change in extension_info.changes {
match change {
types::ExtensionChange::RepoGit { add } => {
println!("Adding git repository: {}", add);
config::clone_git_repo(&add)?;
}
types::ExtensionChange::RegistryRepo { add } => {
println!("Setting registry to: {}", add);
config::set_default_registry(&add)?;
}
types::ExtensionChange::RegistryAdd { add } => {
println!("Adding registry: {}", add);
config::add_added_registry(&add)?;
}
types::ExtensionChange::RepoAdd { add } => {
println!("Adding repository: {}", add);
config::add_repo(&add)?;
}
types::ExtensionChange::Project { add } => {
println!("Creating zoi.yaml...");
if std::path::Path::new("zoi.yaml").exists() {
return Err(anyhow!(
"A 'zoi.yaml' file already exists in the current directory. Please remove it first."
));
}
fs::write("zoi.yaml", add)?;
}
types::ExtensionChange::Pgp { name, key } => {
println!("Adding PGP key: {} from {}", name, key);
if key.starts_with("http") {
crate::pkg::pgp::add_key_from_url(&key, &name)?;
} else {
crate::pkg::pgp::add_key_from_fingerprint(&key, &name)?;
}
}
types::ExtensionChange::Plugin { name, script } => {
println!("Adding plugin: {}", name);
let plugin_dir = crate::pkg::plugin::get_plugin_dir()?;
let plugin_path = plugin_dir.join(format!("{}.lua", name));
fs::write(plugin_path, script)?;
}
types::ExtensionChange::Hook { name, content } => {
println!("Adding global hook: {}", name);
let hooks_dir = crate::pkg::hooks::global::get_user_hooks_dir()?;
let hook_path = hooks_dir.join(format!("{}.hook.yaml", name));
fs::write(hook_path, content)?;
}
}
}
} else {
return Err(anyhow!(
"'{}' is an extension package but contains no extension data.",
ext_name
));
}
let manifest = types::InstallManifest {
name: pkg.name.clone(),
version: pkg.version.clone().unwrap_or_default(),
sub_package: None,
repo: pkg.repo.clone(),
registry_handle: registry_handle.unwrap_or_default(),
package_type: pkg.package_type,
reason: types::InstallReason::Direct,
scope: pkg.scope,
bins: None,
conflicts: None,
replaces: None,
provides: None,
backup: None,
installed_dependencies: vec![],
chosen_options: vec![],
chosen_optionals: vec![],
install_method: None,
service: None,
installed_files: vec![],
installed_size: pkg.installed_size,
};
local::write_manifest(&manifest)?;
let manifest_val = plugin_manager
.lua
.to_value(&manifest)
.map_err(|e: mlua::Error| anyhow!(e.to_string()))?;
plugin_manager.trigger_hook("on_post_extension_add", Some(manifest_val))?;
println!("Successfully added extension '{}'.", ext_name);
Ok(())
}
pub fn remove(
ext_name: &str,
_yes: bool,
plugin_manager: &crate::pkg::plugin::PluginManager,
) -> Result<()> {
println!("Removing extension: {}", ext_name);
let (pkg, _, _, _, _) = resolve::resolve_package_and_version(ext_name, false, false)?;
let (manifest, scope) = if let Some(m) =
local::is_package_installed(&pkg.name, None, types::Scope::User)?
{
(m, types::Scope::User)
} else if let Some(m) = local::is_package_installed(&pkg.name, None, types::Scope::System)? {
(m, types::Scope::System)
} else {
return Err(anyhow!("Extension '{}' is not installed.", ext_name));
};
let manifest_val = plugin_manager
.lua
.to_value(&manifest)
.map_err(|e: mlua::Error| anyhow!(e.to_string()))?;
plugin_manager.trigger_hook("on_pre_extension_remove", Some(manifest_val.clone()))?;
if pkg.package_type != types::PackageType::Extension {
return Err(anyhow!("'{}' is not an extension package.", ext_name));
}
if let Some(extension_info) = pkg.extension {
if extension_info.extension_type != "zoi" {
return Err(anyhow!(
"Unsupported extension type: {}",
extension_info.extension_type
));
}
println!("Reverting extension changes...");
for change in extension_info.changes.iter().rev() {
match change {
types::ExtensionChange::RepoGit { add } => {
let repo_name = add
.trim_end_matches('/')
.split('/')
.next_back()
.unwrap_or_default()
.trim_end_matches(".git");
if !repo_name.is_empty() {
println!("Removing git repository: {}", repo_name);
if let Err(e) = config::remove_git_repo(repo_name) {
eprintln!("Warning: failed to remove git repo '{}': {}", repo_name, e);
}
}
}
types::ExtensionChange::RegistryRepo { add: _ } => {
let default_registry = "https://gitlab.com/Zillowe/Zillwen/Zusty/Zoidberg.git";
println!("Setting registry back to default");
if let Err(e) = config::set_default_registry(default_registry) {
eprintln!("Warning: failed to set registry to default: {}", e);
}
}
types::ExtensionChange::RegistryAdd { add } => {
println!("Removing registry: {}", add);
if let Err(e) = config::remove_added_registry(add) {
eprintln!("Warning: failed to remove registry '{}': {}", add, e);
}
}
types::ExtensionChange::RepoAdd { add } => {
println!("Removing repository: {}", add);
if let Err(e) = config::remove_repo(add) {
eprintln!("Warning: failed to remove repo '{}': {}", add, e);
}
}
types::ExtensionChange::Project { add: _ } => {
println!("Removing zoi.yaml...");
if let Err(e) = fs::remove_file("zoi.yaml") {
eprintln!("Warning: failed to remove 'zoi.yaml': {}", e);
}
}
types::ExtensionChange::Pgp { name, key: _ } => {
println!("Removing PGP key: {}", name);
if let Err(e) = crate::pkg::pgp::remove_key_by_name(name) {
eprintln!("Warning: failed to remove PGP key '{}': {}", name, e);
}
}
types::ExtensionChange::Plugin { name, script: _ } => {
println!("Removing plugin: {}", name);
let plugin_dir = crate::pkg::plugin::get_plugin_dir()?;
let plugin_path = plugin_dir.join(format!("{}.lua", name));
if plugin_path.exists()
&& let Err(e) = fs::remove_file(plugin_path)
{
eprintln!("Warning: failed to remove plugin '{}': {}", name, e);
}
}
types::ExtensionChange::Hook { name, content: _ } => {
println!("Removing global hook: {}", name);
let hooks_dir = crate::pkg::hooks::global::get_user_hooks_dir()?;
let hook_path = hooks_dir.join(format!("{}.hook.yaml", name));
if hook_path.exists()
&& let Err(e) = fs::remove_file(hook_path)
{
eprintln!("Warning: failed to remove global hook '{}': {}", name, e);
}
}
}
}
} else {
return Err(anyhow!(
"'{}' is an extension package but contains no extension data.",
ext_name
));
}
let package_dir = local::get_package_dir(
scope,
&manifest.registry_handle,
&manifest.repo,
&manifest.name,
)?;
if package_dir.exists() {
fs::remove_dir_all(&package_dir)?;
}
plugin_manager.trigger_hook("on_post_extension_remove", Some(manifest_val))?;
println!("Successfully removed extension '{}'.", ext_name);
Ok(())
}