From 93e61a4bd6ad8b5db1083bdd21994bf73b0b90ba Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Mon, 17 Apr 2023 20:30:16 +0200 Subject: added clap cli, modified (basically) all user interface functions; changed some functions to easier string handling --- src/commands/download.rs | 12 +- src/commands/io.rs | 29 ++--- src/commands/list.rs | 53 +++------ src/commands/modification.rs | 44 +++---- src/commands/update.rs | 22 +--- src/config.rs | 2 +- src/db.rs | 10 +- src/lib.rs | 20 ++-- src/main.rs | 278 ++++++++++++++++++++++++++++++++++--------- 9 files changed, 289 insertions(+), 181 deletions(-) (limited to 'src') diff --git a/src/commands/download.rs b/src/commands/download.rs index 2714630..4baecee 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,10 +1,10 @@ use crate::{files::{get_downloaded_versions, download_versions, delete_version, disable_version, clean_list_dir}, db::{userlist_get_all_current_versions_with_mods, lists_get_all_ids, lists_get}, modrinth::get_raw_versions, error::{MLE, ErrorType, MLError}}; -use crate::{List, get_current_list, config::Cfg, input::Input}; +use crate::{List, get_current_list, config::Cfg}; -pub async fn download(config: Cfg, input: Input) -> MLE<()> { +pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: bool) -> MLE<()> { let mut liststack: Vec = vec![]; - if input.all_lists { + if all_lists { let list_ids = lists_get_all_ids(config.clone())?; for id in list_ids { liststack.push(lists_get(config.clone(), id)?); @@ -33,7 +33,7 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> { let current_download = downloaded_versions.get(&mod_id); - if current_download.is_none() || input.clean { + if current_download.is_none() || clean { to_download.push(current_version); } else { let downloaded_version = current_download.ok_or("SOMETHING_HAS_REALLY_GONE_WRONG").unwrap(); @@ -44,7 +44,7 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> { } } - if input.clean { clean_list_dir(¤t_list)? }; + if clean { clean_list_dir(¤t_list)? }; if !to_download.is_empty() { download_versions(current_list.clone(), config.clone(), get_raw_versions(&config.apis.modrinth, to_download).await).await?; @@ -54,7 +54,7 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> { if !to_disable.is_empty() { for ver in to_disable { - if input.delete_old { + if delete_old { println!("Deleting version {} for mod {}", ver.1, ver.0); delete_version(current_list.clone(), ver.1)?; } else { diff --git a/src/commands/io.rs b/src/commands/io.rs index a3d056f..5de8dd1 100644 --- a/src/commands/io.rs +++ b/src/commands/io.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::prelude::*; use serde::{Serialize, Deserialize}; -use crate::{input::{Input, IoOptions}, db::{lists_get, userlist_get_all_ids, lists_get_all_ids, lists_insert}, config::Cfg, Modloader, List, devdir, error::MLE, mods_add, IDSelector}; +use crate::{db::{lists_get, userlist_get_all_ids, lists_get_all_ids, lists_insert}, config::Cfg, Modloader, List, devdir, error::MLE, mod_add, IDSelector}; #[derive(Debug, Serialize, Deserialize)] struct Export { @@ -32,22 +32,12 @@ impl ExportList { } } -pub async fn io(config: Cfg, input: Input) -> MLE<()> { - - match input.clone().io_options.unwrap() { - IoOptions::Export => { export(config, input)? }, - IoOptions::Import => { import(config, input).await? }, - } - - Ok(()) -} - -fn export(config: Cfg, input: Input) -> MLE<()> { +pub fn export(config: Cfg, list: Option) -> MLE<()> { let mut list_ids: Vec = vec![]; - if input.all_lists { + if list.is_none() { list_ids = lists_get_all_ids(config.clone())?; } else { - list_ids.push(lists_get(config.clone(), input.list.unwrap().id)?.id); + list_ids.push(lists_get(config.clone(), list.unwrap())?.id); } let mut lists: Vec = vec![]; for list_id in list_ids { @@ -64,14 +54,9 @@ fn export(config: Cfg, input: Input) -> MLE<()> { Ok(()) } -async fn import(config: Cfg, input: Input) -> MLE<()> { +pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE<()> { - let filestr: String = match input.file { - Some(args) => args, - None => devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str()), - }; - - let mut file = File::open(filestr)?; + let mut file = File::open(file_str)?; let mut content = String::new(); file.read_to_string(&mut content)?; let export: Export = toml::from_str(&content)?; @@ -86,7 +71,7 @@ async fn import(config: Cfg, input: Input) -> MLE<()> { }; //TODO impl set_version and good direct download //TODO impl all at once, dafuck - mods_add(config.clone(), mod_ids, list, input.direct_download, false).await?; + mod_add(config.clone(), mod_ids, list, direct_download, false).await?; } Ok(()) } diff --git a/src/commands/list.rs b/src/commands/list.rs index 8e86973..80e801a 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,4 +1,4 @@ -use crate::{db::{lists_insert, lists_remove, config_change_current_list, config_get_current_list, lists_get, lists_version}, Modloader, config::Cfg, input::{Input, ListOptions}, cmd_update, error::MLE}; +use crate::{db::{lists_insert, lists_remove, config_change_current_list, config_get_current_list, lists_get, lists_version}, Modloader, config::Cfg, update, error::MLE}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct List { @@ -8,44 +8,23 @@ pub struct List { pub download_folder: String, } -pub async fn list(config: Cfg, input: Input) -> MLE<()> { - - match input.clone().list_options.unwrap() { - ListOptions::Add => { - add(config, input) - }, - ListOptions::Change => { - change(config, input) - }, - ListOptions::Remove => { - remove(config, input) - }, - ListOptions::Version => { - version(config, input).await - } - } -} - pub fn get_current_list(config: Cfg) -> MLE { let id = config_get_current_list(config.clone())?; lists_get(config, id) } -fn add(config: Cfg, input: Input) -> MLE<()> { - let id = input.list_id.unwrap(); - let mc_version = input.list_mcversion.unwrap(); - let mod_loader = input.modloader.unwrap(); - let download_folder = input.directory.unwrap(); - lists_insert(config, id, mc_version, mod_loader, download_folder) +pub fn list_add(config: Cfg, id: String, mc_version: String, modloader: Modloader, directory: String) -> MLE<()> { + lists_insert(config, id, mc_version, modloader, directory) } -fn change(config: Cfg, input: Input) -> MLE<()> { - println!("Change default list to: {}", input.clone().list.unwrap().id); - config_change_current_list(config, input.list.unwrap().id) +pub fn list_change(config: Cfg, id: String) -> MLE<()> { + //TODO check if list exists + println!("Change default list to: {}", id); + config_change_current_list(config, id) } -fn remove(config: Cfg, input: Input) -> MLE<()> { - lists_remove(config, input.list.unwrap().id) +pub fn list_remove(config: Cfg, id: String) -> MLE<()> { + lists_remove(config, id) } ///Changing the current lists version and updating it @@ -54,14 +33,12 @@ fn remove(config: Cfg, input: Input) -> MLE<()> { /// /// * `config` - The current config /// * `args` - All args, to extract the new version -async fn version(config: Cfg, input: Input) -> MLE<()> { - println!("Change version for list {} to minecraft version: {}", input.clone().list.unwrap().id, input.clone().list_mcversion.unwrap()); +pub async fn list_version(config: Cfg, id: String, mc_version: String, download: bool, delete: bool) -> MLE<()> { + println!("Change version for list {} to minecraft version: {}", id, mc_version); - lists_version(config.clone(), input.clone().list.ok_or("").unwrap().id, input.clone().list_mcversion.ok_or("").unwrap())?; - - //Linebreak readability - println!(""); + lists_version(config.clone(), &id, &mc_version)?; - println!("Check for updates for new minecraft version in list {}", input.clone().list.unwrap().id); - cmd_update(config, vec![input.list.ok_or("").unwrap()], true, input.direct_download, input.delete_old).await + println!("\nCheck for updates for new minecraft version in list {}", id); + let list = lists_get(config.clone(), id)?; + update(config, vec![list], true, download, delete).await } diff --git a/src/commands/modification.rs b/src/commands/modification.rs index 31e50af..454e148 100644 --- a/src/commands/modification.rs +++ b/src/commands/modification.rs @@ -1,4 +1,4 @@ -use crate::{modrinth::{versions, extract_current_version, Version, projects, get_raw_versions, project}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, input::{Input, ModOptions}, files::{delete_version, download_versions}, List, error::{MLE, ErrorType, MLError}}; +use crate::{modrinth::{versions, extract_current_version, Version, projects, get_raw_versions, project}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, files::{delete_version, download_versions}, List, error::{MLE, ErrorType, MLError}}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum IDSelector { @@ -6,24 +6,6 @@ pub enum IDSelector { VersionID(String) } -pub async fn modification(config: Cfg, input: Input) -> MLE<()> { - match input.clone().mod_options.ok_or("").unwrap() { - ModOptions::Add => { - add(config, input).await - }, - ModOptions::Remove => { - remove(config, input) - }, - } -} - -async fn add(config: Cfg, input: Input) -> MLE<()> { - - mods_add(config, vec![input.mod_id.unwrap()], input.list.unwrap(), input.direct_download, input.set_version).await?; - - Ok(()) -} - #[derive(Debug, Clone)] pub struct ProjectInfo { pub mod_id: String, @@ -34,7 +16,7 @@ pub struct ProjectInfo { pub download_link: String, } -pub async fn mods_add(config: Cfg, ids: Vec, list: List, direct_download: bool, set_version: bool) -> MLE<()> { +pub async fn mod_add(config: Cfg, ids: Vec, list: List, direct_download: bool, set_version: bool) -> MLE<()> { println!("Add mods to {}", list.id); println!(" └Add mods:"); @@ -156,22 +138,24 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec) -> MLE Ok(projectinfo) } -fn remove(config: Cfg, input: Input) -> MLE<()> { - - let id = match input.clone().mod_id.unwrap() { - IDSelector::ModificationID(id) => id, - IDSelector::VersionID(..) => return Err(MLError::new(ErrorType::ArgumentError, "NO_MOD_ID")), - }; +/// Remove mod from a list +/// # Arguments +/// +/// * `config` - config struct +/// * `id` - name, slug or id of the mod +/// * `list` - List struct +pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> { - let mod_id = mods_get_id(&config.data, &id)?; + let mod_id = mods_get_id(&config.data, id)?; - let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; + let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?; - userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; - delete_version(input.list.unwrap(), version)?; + userlist_remove(config.clone(), &list.id, &mod_id)?; + delete_version(list, version)?; let list_ids = lists_get_all_ids(config.clone())?; + // Remove mod from main list if not used elsewhere let mut mod_used = false; for id in list_ids { let mods = userlist_get_all_ids(config.clone(), id)?; diff --git a/src/commands/update.rs b/src/commands/update.rs index 75bee39..e5751c0 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,21 +1,6 @@ -use crate::{config::Cfg, modrinth::{versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, userlist_get_set_version, mods_get_info}, List, input::Input, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}}; - -pub async fn update(config: Cfg, input: Input) -> MLE<()> { - let mut liststack: Vec = vec![]; - if input.all_lists { - let list_ids = lists_get_all_ids(config.clone())?; - for id in list_ids { - liststack.push(lists_get(config.clone(), id)?); - } - } else { - let current = get_current_list(config.clone())?; - println!("Update list {}:", current.id); - liststack.push(current) - } - cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await -} +use crate::{config::Cfg, modrinth::{versions, extract_current_version, Version}, db::{userlist_get_all_ids, userlist_get_applicable_versions, userlist_change_versions, userlist_get_current_version, userlist_get_set_version, mods_get_info}, List, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}}; -pub async fn cmd_update(config: Cfg, liststack: Vec, clean: bool, direct_download: bool, delete_old: bool) -> MLE<()> { +pub async fn update(config: Cfg, liststack: Vec, clean: bool, direct_download: bool, delete_old: bool) -> MLE<()> { for current_list in liststack { let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?; @@ -34,7 +19,7 @@ pub async fn cmd_update(config: Cfg, liststack: Vec, clean: bool, direct_d } //Getting current installed version for disable or delete - let disable_version = userlist_get_current_version(config.clone(), String::from(¤t_list.id), String::from(&id))?; + let disable_version = userlist_get_current_version(config.clone(), ¤t_list.id, &id)?; updatestack.push( match specific_update(config.clone(), clean, current_list.clone(), String::from(&id)).await { @@ -110,6 +95,7 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML }?; current.push(current_ver.clone()); + //TODO implement version selection if no primary let link = match current_ver.files.into_iter().find(|f| f.primary).ok_or("!no primary in links") { Ok(p) => Ok(p), Err(e) => Err(MLError::new(ErrorType::Other, e)), diff --git a/src/config.rs b/src/config.rs index ded0062..075d884 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,7 +27,7 @@ impl Cfg { let default_cfg = Cfg { data: String::from("./"), apis: Apis { modrinth: String::from("https://api.modrinth.com/v2/") } }; let mut file = File::create(devdir(configfile.to_str().unwrap()))?; println!("Created config file"); - file.write_all(&toml::to_string(&default_cfg)?.as_bytes())?; + file.write_all(toml::to_string(&default_cfg)?.as_bytes())?; File::open(devdir(configfile.to_str().unwrap()))? } else { return Err(err.into()); diff --git a/src/db.rs b/src/db.rs index 9428466..2c48cab 100644 --- a/src/db.rs +++ b/src/db.rs @@ -49,6 +49,9 @@ pub fn mods_get_all_ids(config: Cfg) -> Result, Box MLE { + + //TODO check if "slug" is id + let data = devdir(format!("{}/data.db", data).as_str()); let connection = Connection::open(data)?; @@ -192,8 +195,7 @@ pub fn userlist_get_all_ids(config: Cfg, list_id: String) -> MLE> { } } - -pub fn userlist_remove(config: Cfg, list_id: String, mod_id: String) -> MLE<()> { +pub fn userlist_remove(config: Cfg, list_id: &str, mod_id: &str) -> MLE<()> { let data = devdir(format!("{}/data.db", config.data).as_str()); let connection = Connection::open(data)?; @@ -242,7 +244,7 @@ pub fn userlist_get_all_applicable_versions_with_mods(config: Cfg, list_id: Stri Ok(versions) } -pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String) -> MLE { +pub fn userlist_get_current_version(config: Cfg, list_id: &str, mod_id: &str) -> MLE { let data = devdir(format!("{}/data.db", config.data).as_str()); let connection = Connection::open(data).unwrap(); @@ -425,7 +427,7 @@ pub fn lists_get(config: Cfg, list_id: String) -> MLE { Ok(list) } -pub fn lists_version(config: Cfg, list_id: String, version: String) -> MLE<()> { +pub fn lists_version(config: Cfg, list_id: &str, version: &str) -> MLE<()> { let data = devdir(format!("{}/data.db", config.data).as_str()); let connection = Connection::open(data).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index eb845d8..8d97d1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ pub mod db; pub mod error; pub mod files; -use std::path::Path; +use std::{path::Path, fmt::Display}; pub use apis::*; pub use commands::*; @@ -19,14 +19,7 @@ pub enum Modloader { } impl Modloader { - fn to_string(&self) -> String { - match self { - Modloader::Fabric => String::from("fabric"), - Modloader::Forge => String::from("forge"), - } - } - - fn from(string: &str) -> MLE { + pub fn from(string: &str) -> MLE { match string { "forge" => Ok(Modloader::Forge), "fabric" => Ok(Modloader::Fabric), @@ -35,6 +28,15 @@ impl Modloader { } } +impl Display for Modloader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Modloader::Fabric => write!(f, "fabric"), + Modloader::Forge => write!(f, "forge"), + } + } +} + pub fn devdir(path: &str) -> String { let p = Path::new(path); let dev = std::env::var("DEV"); diff --git a/src/main.rs b/src/main.rs index 32727c7..0dfc190 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,65 +1,237 @@ -use std::{env, process}; +use clap::{Parser, Subcommand}; +use modlist::{config::Cfg, mod_add, mod_remove, db::{lists_get, config_get_current_list, lists_get_all_ids}, IDSelector, download, update, List, get_current_list, import, devdir, export, list_add, Modloader, list_version, list_remove, list_change}; -use modlist::{config::Cfg, input::{get_input, Cmd}, update, download, list, io, modification, setup}; +//TODO make default list optional + +#[derive(Parser)] +#[command(author, version, about)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + r#Mod { + #[command(subcommand)] + command: ModCommands, + }, + List { + #[command(subcommand)] + command: ListCommands + }, + Download { + /// download all lists + #[arg(short, long)] + all: bool, + + /// clean all mods before downloading them + #[arg(short, long)] + clean: bool, + + /// remove disabled versions + #[arg(short, long)] + remove: bool, + }, + Update { + /// download all lists + #[arg(short, long)] + all: bool, + + /// directly download updated mods + #[arg(short, long)] + download: bool, + + /// clean all mods before downloading them + #[arg(short, long)] + clean: bool, + + /// delete disabled versions + #[arg(short, long)] + remove: bool, + }, + Import { + #[arg(short, long)] + file: Option, + + /// directly download imported mods + #[arg(short, long)] + download: bool, + }, + Export { + /// the list you want to export + list: Option + } +} + +#[derive(Subcommand)] +enum ModCommands { + Add { + /// id of the mod/version + id: String, + + /// set id mode to version + #[arg(short, long)] + version: bool, + + /// directly download the mod + #[arg(short, long)] + download: bool, + + /// lock the version added + #[arg(/* short , */long)] + lock: bool, + + /// optional List selection, else default list will be used + #[arg(short, long)] + list: Option + }, + Remove { + /// id, name or title of the mod + id: String, + + /// optional List selection, else default list will be used + #[arg(short, long)] + list: Option + } +} + +#[derive(Subcommand)] +enum ListCommands { + Add { + /// list id + id: String, + + directory: String, + + modloader: Option, + + version: Option, + }, + Remove { + /// id, name or title of the list + id: String + }, + List, + Change { + /// id of the list to change to + id: String + }, + Version { + /// list id + id: String, + /// desired minecraft version + version: String, + + /// directly download updated mods + #[arg(long, short)] + download: bool, + + /// delete disabled versions + #[arg(short, long)] + remove: bool, + } +} #[tokio::main] async fn main() { + + let cli = Cli::parse(); + let config = Cfg::init("modlist.toml").unwrap(); - - let mut args: Vec = env::args().collect(); - args.reverse(); - args.pop(); - args.reverse(); - - if args.is_empty() { - println!("Please enter an argument"); - process::exit(1); - }; - - let input = match get_input(config.clone(), args).await { - Ok(i) => i, - Err(e) => { - println!("{}", e); - process::exit(1); - } - }; - - match input.clone().command.unwrap() { - Cmd::Mod => { - modification(config, input).await - }, - Cmd::List => { - list(config, input).await - }, - Cmd::Update => { - update(config, input).await - }, - Cmd::Download => { - download(config, input).await + println!("{:?}", config); + + //TODO setup? maybe setup on install + match cli.command { + Commands::Mod { command } => { + + match command { + #[allow(unused_variables)] + ModCommands::Add { id, version, list, download, lock } => { + let listf = match list { + Some(list) => lists_get(config.clone(), list).unwrap(), + None => lists_get(config.clone(), config_get_current_list(config.clone()).unwrap()).unwrap(), + }; + + let marked_id = match version { + true => IDSelector::VersionID(id), + false => IDSelector::ModificationID(id), + }; + + mod_add(config, vec![marked_id], listf, download, lock).await + } + ModCommands::Remove { id, list } => { + //TODO add output + //TODO add success even if no file found + let listf = match list { + Some(list) => lists_get(config.clone(), list).unwrap(), + None => lists_get(config.clone(), config_get_current_list(config.clone()).unwrap()).unwrap(), + }; + mod_remove(config, &id, listf) + } + } }, - Cmd::Io => { - io(config, input).await + Commands::List { command } => { + match command { + ListCommands::Add { id, directory, modloader, version } => { + let ml = match modloader { + Some(ml) => Modloader::from(&ml).unwrap(), + //TODO add default modloader to config + None => Modloader::Fabric, + }; + + let ver = match version { + Some(ver) => ver, + //TODO get latest version + //TODO impl config for specific version or latest or latest snap + None => "1.19.4".to_string(), + }; + + list_add(config, id, ver, ml, directory) + }, + ListCommands::Remove { id } => { + list_remove(config, id) + }, + ListCommands::List => { + todo!() + }, + ListCommands::Change { id } => { + list_change(config, id) + }, + ListCommands::Version { id, version, download, remove } => { + list_version(config, id, version, download, remove).await + } + } }, - Cmd::Version => { - show_version(); - Ok(()) + //TODO a add specific list + Commands::Update { all, download, clean, remove } => { + let mut liststack: Vec = vec![]; + if all { + let list_ids = lists_get_all_ids(config.clone()).unwrap(); + for id in list_ids { + liststack.push(lists_get(config.clone(), id).unwrap()); + } + } else { + let current = get_current_list(config.clone()).unwrap(); + println!("Update list {}:", current.id); + liststack.push(current) + } + update(config, liststack, clean, download, remove).await }, - Cmd::Setup => { - setup(config).await + //TODO add specific list + Commands::Download { all, clean, remove } => { + download(config, all, clean, remove).await }, - }.unwrap() -} + Commands::Import { file, download } => { + let filestr: String = match file { + Some(args) => args, + None => devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str()), + }; -fn show_version() { - match std::env::var("DEV") { - Ok(dev) => { - let devint = dev.parse::().unwrap(); - if devint >= 1 { - println!("Modlist by FxQnLr v{} (DEV)", env!("CARGO_PKG_VERSION")); - } else { - println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")); - } + import(config, filestr, download).await }, - Err(..) => println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")), - } + Commands::Export { list } => { + export(config, list) + }, + }.unwrap(); } -- cgit v1.2.3