use std::{
env::current_dir,
fs::{create_dir_all, File},
io::Write,
path::PathBuf,
time::Duration,
};
use anyhow::Context;
use clap::{Args, Subcommand};
use git2::Repository;
use indicatif::{ProgressBar, ProgressStyle};
use log::{info, warn};
use crate::{
config::{add_plugin_to_config, get_config, remove_plugin_from_config},
get_home,
plugin::config::PluginToml,
MULTI_PRPGRESS_BAR,
};
use super::Submodule;
#[derive(Clone, Args)]
pub struct PluginArgs {
#[command(subcommand)]
command: PluginSubcommands,
}
#[derive(Clone, Subcommand)]
pub enum PluginSubcommands {
Create(CreateArgs),
Remove(RemoveArgs),
Use(UseArgs),
Fetch,
}
#[derive(Clone, Args)]
pub struct CreateArgs {
name: String,
version: String,
path: Option<PathBuf>,
#[arg(short, long, action)]
local: bool,
}
#[derive(Clone, Args)]
pub struct RemoveArgs {
name: String,
}
#[derive(Clone, Args)]
pub struct UseArgs {
name: String,
version: String,
location: String,
}
pub struct Plugin<'a> {
args: &'a PluginArgs,
}
impl<'a> Plugin<'a> {
pub fn new(args: &'a PluginArgs) -> Self {
Plugin { args }
}
}
impl<'a> Submodule for Plugin<'a> {
fn run(&mut self) -> anyhow::Result<()> {
match &self.args.command {
PluginSubcommands::Create(arg) => {
create_new_plugin(
arg.name.clone(),
arg.version.clone(),
arg.path.clone(),
arg.local,
)
.context("Failed to create the new plugin")?;
}
PluginSubcommands::Remove(arg) => {
remove_plugin_from_config(arg.name.clone())
.context("Failed to remove plugin from config")?;
}
PluginSubcommands::Use(arg) => {
fetch_plugin(
arg.name.clone(),
arg.version.clone(),
arg.location.clone(),
true,
)
.context("Failed to configure plugin.")?;
}
PluginSubcommands::Fetch => {
fetch_plugins_from_config().context("Failed to fetch plugins")?;
}
}
Ok(())
}
}
pub fn fetch_plugin(
name: String,
version: String,
location: String,
update_config: bool,
) -> anyhow::Result<()> {
let path = PathBuf::from(&location);
if path.exists() {
if update_config {
add_plugin_to_config(name, version, location)
.context("Failed to add plugin to project config")?;
}
return Ok(());
}
let mut path = get_home().context("Failed to get Labt Home")?;
path.push("plugins");
path.push(format!("{}-{}", name, version));
let mut conf = path.clone();
conf.push("plugin.toml");
if conf.exists() {
if update_config {
add_plugin_to_config(name, version, location)
.context("Failed to add plugin to project config")?;
}
return Ok(());
}
create_dir_all(&path).context(format!(
"Failed to create plugin directory for {}-{}",
name, version
))?;
let spinner = MULTI_PRPGRESS_BAR.with(|multi| multi.borrow().add(ProgressBar::new_spinner()));
spinner.enable_steady_tick(Duration::from_millis(100));
spinner.set_style(ProgressStyle::with_template("{spinner} {prefix:.blue} {wide_msg}").unwrap());
spinner.set_prefix("Plugin");
spinner.set_message(format!("Clonning {}", location));
let _repo = Repository::clone(location.as_str(), path)
.context("Failed to clone plugin to local directory")?;
if update_config {
add_plugin_to_config(name.clone(), version.clone(), location)
.context("Failed to add plugin to project config")?;
}
spinner.finish_and_clear();
info!(target: "plugin", "Installed plugin: {}-{}", name, version);
Ok(())
}
pub fn fetch_plugins_from_config() -> anyhow::Result<()> {
let config = get_config().context("Failed reading project configuration")?;
if let Some(plugins) = config.plugins {
for (name, plugin) in plugins {
fetch_plugin(
name.clone(),
plugin.version.clone(),
plugin.location.unwrap_or(
get_home() .context("Failed to get Labt home")?
.to_str()
.unwrap_or("")
.to_string(),
),
false,
)
.context(format!(
"Failed to fetch plugin: {}-{}",
name, plugin.version
))?;
}
}
Ok(())
}
pub fn create_new_plugin(
name: String,
version: String,
path: Option<PathBuf>,
local_plugin: bool,
) -> anyhow::Result<()> {
warn!("This is an unstable feature, Things may not work correctly");
let plugin = PluginToml {
name: name.clone(),
version: version.clone(),
stage: crate::plugin::config::Stage {
pre: None,
aapt: None,
compile: None,
dex: None,
bundle: None,
post: None,
},
path: PathBuf::new(),
package_paths: None,
};
let mut path = if local_plugin {
let mut cwd = current_dir().context("Failed to get current working directory.")?;
cwd.push("plugins");
cwd.push(format!("{}-{}", name, version));
create_dir_all(&cwd).context("Failed creating plugin directory on project folder")?;
cwd
} else {
if let Some(path) = path {
path
} else {
let mut path = get_home().context("Failed to get Labt Home")?;
path.push("plugins");
path.push(format!("{}-{}", name, version));
path
}
};
let doc = toml_edit::ser::to_document(&plugin).context("Failed to serialize plugin config")?;
path.push("plugin.toml");
let mut file =
File::create(&path).context(format!("Failed to create plugin file at {:?}", path))?;
file.write_all(doc.to_string().as_bytes())
.context(format!("Failed to write plugin file {:?}", path))?;
info!(target: "plugin", "Created a plugin at {:?}", path);
Ok(())
}