From f7a6d2e9c67c1fdf8fc17fa0461a201fd2720537 Mon Sep 17 00:00:00 2001
From: fxqnlr <felixquinn03@gmail.com>
Date: Thu, 19 Jan 2023 18:37:42 +0100
Subject: input mostly inplemented, mods missing

---
 src/apis/modrinth.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/apis/modrinth.rs')

diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index 78073e6..9890cf2 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -3,7 +3,7 @@ use chrono::{DateTime, FixedOffset};
 use reqwest::Client;
 use serde::Deserialize;
 
-use crate::{Modloader, List};
+use crate::{Modloader, List, error::{MLE, MLError, ErrorType}};
 
 #[derive(Debug, Deserialize, Clone)]
 pub struct Project {
@@ -177,9 +177,9 @@ pub async fn get_raw_versions(api: String, versions: Vec<String>) -> Vec<Version
     serde_json::from_slice(&data).unwrap()
 }
 
-pub fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> {
+pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
     match versions.len() {
-        0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))),
+        0 => Err(MLError::new(ErrorType::ModError, "NO_VERSIONS_AVAILABLE")),
         1.. => {
             let mut times: Vec<(String, DateTime<FixedOffset>)> = vec![];
             for ver in versions {
-- 
cgit v1.2.3


From 1890d59428dfcca861ea1b7820411d80cc60d713 Mon Sep 17 00:00:00 2001
From: fxqnlr <felixquinn03@gmail.com>
Date: Sun, 22 Jan 2023 22:34:17 +0100
Subject: Added list version cmd, fixed some todos

---
 .gitignore                   |   1 +
 planmodlist.xopp             | Bin 322225 -> 322154 bytes
 src/apis/modrinth.rs         |  10 +++++--
 src/commands/download.rs     |  13 ++-------
 src/commands/list.rs         |  30 +++++++++----------
 src/commands/mod.rs          |   4 +--
 src/commands/modification.rs |  45 +++++++++++------------------
 src/commands/setup.rs        |   2 +-
 src/commands/update.rs       |  67 ++++++++++++++++++++++---------------------
 src/config.rs                |   1 -
 src/db.rs                    |  26 ++++++++---------
 src/error.rs                 |   2 +-
 src/files.rs                 |  29 +++++++++++++++----
 src/input.rs                 |  46 ++++++++++++++++++++---------
 src/main.rs                  |   2 +-
 15 files changed, 151 insertions(+), 127 deletions(-)

(limited to 'src/apis/modrinth.rs')

diff --git a/.gitignore b/.gitignore
index 693fb15..8713145 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ data.db.cp
 export.toml
 config.toml
 /dev
+/.fleet
\ No newline at end of file
diff --git a/planmodlist.xopp b/planmodlist.xopp
index e8920e0..aeb2fa7 100644
Binary files a/planmodlist.xopp and b/planmodlist.xopp differ
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index 9890cf2..36ab5df 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -1,4 +1,3 @@
-use std::io::{Error, ErrorKind};
 use chrono::{DateTime, FixedOffset};
 use reqwest::Client;
 use serde::Deserialize;
@@ -142,7 +141,7 @@ pub async fn project(api: String, name: &str) -> Project {
 }
 
 pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> {
-    println!("Getting versions for all mods from modrinth");
+    println!("\tGet versions from modrinth\n");
     let all = ids.join(r#"",""#);
     let url = format!(r#"projects?ids=["{}"]"#, all);
 
@@ -188,7 +187,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
             }
             times.sort_by_key(|t| t.1);
             times.reverse();
-            println!("Current Version: {}", times[0].0);
+            println!("\t └New current version: {}", times[0].0);
             Ok(times[0].0.to_string())
         },
         _ => panic!("available_versions should never be negative"),
@@ -198,6 +197,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
 pub enum MCVersionType {
     Release,
     Latest,
+    Specific,
 }
 
 #[derive(Debug, Deserialize)]
@@ -220,6 +220,10 @@ pub async fn get_minecraft_version(api: String, version: MCVersionType) -> Strin
             &mc_versions[i]
         },
         MCVersionType::Latest => &mc_versions[0],
+        MCVersionType::Specific => {
+            println!("Not inplemented");
+            &mc_versions[0]
+        }
     };
     String::from(&ver.version)
 }
diff --git a/src/commands/download.rs b/src/commands/download.rs
index 0f63876..7748d15 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,4 +1,4 @@
-use crate::{files::{get_downloaded_versions, download_versions, delete_version, disable_version}, db::{userlist_get_all_current_versions_with_mods, lists_get_all_ids, lists_get}, modrinth::get_raw_versions, error::{MLE, ErrorType, MLError}};
+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};
 
 pub async fn download(config: Cfg, input: Input) -> MLE<()> {
@@ -44,17 +44,10 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> {
             }
         }
         
-        if input.clean {
-            let dl_path = &current_list.download_folder;
-            println!("Cleaning {}", dl_path);
-            for entry in std::fs::read_dir(dl_path)? {
-                let entry = entry?;
-                std::fs::remove_file(entry.path())?;
-            }
-        }
+        if input.clean { clean_list_dir(&current_list)? };
 
         if !to_download.is_empty() {
-            download_versions(current_list.clone(), get_raw_versions(String::from(&config.apis.modrinth), to_download).await).await?;
+            download_versions(current_list.clone(), config.clone(), get_raw_versions(String::from(&config.apis.modrinth), to_download).await).await?;
         } else {
             println!("There are no new versions to download");
         }
diff --git a/src/commands/list.rs b/src/commands/list.rs
index bc58787..eaf6fa1 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}, Modloader, config::Cfg, input::{Input, ListOptions}, /*cmd_update,*/ error::MLE, /*modrinth::MCVersionType*/};
+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};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct List {
@@ -20,13 +20,9 @@ pub async fn list(config: Cfg, input: Input) -> MLE<()> {
         ListOptions::Remove => {
             remove(config, input)
         },
-        /*
-        Subcmd::Version => {
-            match version(config, Some(input.args.ok_or("NO_VERSION")?), Some(MCVersionType::Release)).await {
-                Ok(..) => Ok(()),
-                Err(e) => Err(Box::new(e))
-            }
-        }*/
+        ListOptions::Version => {
+            version(config, input).await
+        }
     }
 }
 
@@ -44,7 +40,7 @@ fn add(config: Cfg, input: Input) -> MLE<()> {
 }
 
 fn change(config: Cfg, input: Input) -> MLE<()> {
-    //TODO reimplement current list
+    println!("Change default list to: {}", input.clone().list.unwrap().id);
     config_change_current_list(config, input.list.unwrap().id)
 }
 
@@ -52,17 +48,19 @@ fn remove(config: Cfg, input: Input) -> MLE<()> {
     lists_remove(config, input.list.unwrap().id)
 }
 
-/*
 ///Changing the current lists version and updating it
 /// #Arguments
 /// 
 /// * `config` - The current config
 /// * `args` - All args, to extract the new version
-async fn version(config: Cfg, args: Option<Vec<String>>, version_type: Option<MCVersionType>) -> MLE<()> {
-    let current_list = lists_get(config.clone(), config_get_current_list(config.clone())?)?;
+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());
 
-    lists_version(config.clone(), String::from(&current_list.id), String::from(&args.unwrap()[0]))?;
-    //update the list & with -- args
-    cmd_update(config, vec![current_list], true, true, false).await
+    lists_version(config.clone(), input.clone().list.ok_or("").unwrap().id, input.clone().list_mcversion.ok_or("").unwrap())?;
+    
+    //Linebreak readability
+    println!("");
+
+    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
 }
-*/
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 527afc7..38139f9 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,11 +1,11 @@
-//pub mod modification;
+pub mod modification;
 pub mod list;
 pub mod update;
 //pub mod setup;
 pub mod download;
 pub mod io;
 
-//pub use modification::*;
+pub use modification::*;
 pub use list::*;
 pub use update::*;
 //pub use setup::*;
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 7d4be8d..6a03b35 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,33 +1,24 @@
-use std::io::{Error, ErrorKind};
+use crate::{modrinth::{project, versions, extract_current_version, Version, projects}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, 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::{project, versions, extract_current_version, Version, projects}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, input::{Input, Subcmd}, get_current_list, files::{delete_version, download_versions}, List};
-
-pub async fn modification(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> {
-    match input.subcommand.as_ref().ok_or("")? {
-        Subcmd::Add => {
+pub async fn modification(config: Cfg, input: Input) -> MLE<()> {
+    match input.clone().mod_options.ok_or("").unwrap() {
+        ModOptions::Add => {
             add(config, input).await
         },
-        Subcmd::Remove => {
-            remove(config, input.args.ok_or("")?)
+        ModOptions::Remove => {
+            remove(config, input)
         },
-        _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "SUBCOMMAND_NOT_AVAILABLE")))
     }
 }
 
-async fn add(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> {
-
-    let args = input.args.ok_or("")?;
+async fn add(config: Cfg, input: Input) -> MLE<()> {
 
-    if args.is_empty() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
-    
-    let current_list = get_current_list(config.clone())?;
-    
-    mod_add(config, vec![String::from(&args[0])], current_list, input.disable_download).await?;
+    mod_add(config, vec![String::from(input.mod_id.unwrap())], input.list.unwrap(), input.direct_download).await?;
 
     Ok(())
 }
 
-pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_download: bool) -> Result<(), Box<dyn std::error::Error>> {
+pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_download: bool) -> MLE<()> {
     
     println!("Adding mod(s) {:?}", mod_id);
     let projects = if mod_id.len() == 1 {
@@ -50,7 +41,7 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_downl
 
             current_version_id = current_version.clone().unwrap().id;
 
-            file = current_version.clone().ok_or("VERSION_CORRUPTED")?.files.into_iter().find(|f| f.primary).unwrap().url;
+            file = current_version.clone().ok_or("").unwrap().files.into_iter().find(|f| f.primary).unwrap().url;
 
             for ver in available_versions {
                 available_versions_vec.push(ver.id);
@@ -67,7 +58,7 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_downl
         match userlist_get_all_ids(config.clone(), list.clone().id) {
             Ok(mods) => {
                 if mods.contains(&project.id) {
-                    return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_ON_LIST"))); } 
+                    return Err(MLError::new(ErrorType::ModError, "MOD_ALREADY_ON_LIST")); }
                 else {
                     userlist_insert(config.clone(), String::from(&list.id), String::from(&project.id), String::from(&current_version_id), available_versions_vec, file)?;
                 } 
@@ -88,24 +79,22 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_downl
             },
         };
 
-        if !disable_download && current_version.is_some() { download_versions(list.clone(), vec![current_version.unwrap()]).await?; };
+        if !disable_download && current_version.is_some() { download_versions(list.clone(), config.clone(), vec![current_version.unwrap()]).await?; };
             
     }
 
     Ok(())
 }
 
-fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
-    if args.is_empty() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
+fn remove(config: Cfg, input: Input) -> MLE<()> {
 
-    let current_list = get_current_list(config.clone())?;
-    let mod_id = mods_get_id(config.clone(), String::from(&args[0]))?;
+    let mod_id = mods_get_id(config.clone(), input.clone().mod_id.unwrap())?;
     
-    let version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&mod_id))?;
+    let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?;
 
     //TODO implement remove from modlist if not in any other lists && config clean is true
-    userlist_remove(config.clone(), String::from(&current_list.id), String::from(&mod_id))?;
-    delete_version(current_list, version)?;
+    userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?;
+    delete_version(input.list.unwrap(), version)?;
     
     let list_ids = lists_get_all_ids(config.clone())?;
     
diff --git a/src/commands/setup.rs b/src/commands/setup.rs
index e4fa801..cc7472c 100644
--- a/src/commands/setup.rs
+++ b/src/commands/setup.rs
@@ -63,4 +63,4 @@ fn to_04(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
         s_insert_column(config.clone(), list_id, String::from("disabled_versions"), String::from("TEXT"), Some(String::from("NONE")))?;
     }
     s_config_update_version(config, String::from("0.4"))
-}
+}
\ No newline at end of file
diff --git a/src/commands/update.rs b/src/commands/update.rs
index 068c3f3..f8bdb82 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,7 +1,6 @@
-use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, mods_get_versions, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_change_versions}, List, input::Input, files::{delete_version, download_versions, disable_version}, error::{MLE, MLError, ErrorType}};
+use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, mods_get_versions, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_change_versions}, 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<List> = vec![];
     if input.all_lists {
         let list_ids = lists_get_all_ids(config.clone())?;
@@ -10,10 +9,9 @@ pub async fn update(config: Cfg, input: Input) -> MLE<()> {
         }
     } else {
         let current = get_current_list(config.clone())?;
-        println!("Checking for updates of mods in {}", current.id);
+        println!("Check for updates of mods in list {}", current.id);
         liststack.push(current)
     }
-    
     cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await
 }
 
@@ -29,6 +27,7 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
         let mut projects = projects(String::from(&config.apis.modrinth), mods).await;
         projects.sort_by_key(|pro| pro.id.clone());
 
+        println!("Comparing mod versions:");
         let mut updatestack: Vec<Version> = vec![];
         for (index, project) in projects.into_iter().enumerate() {
             //Get versions for project and check if they match up
@@ -37,6 +36,8 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
             let v_id = &current_version.mod_id;
             if &p_id != v_id { return Err(MLError::new(ErrorType::Other, "SORTING_ERROR")) };
             
+            println!("\t({}) Check for update", project.title);
+    
             //Getting current installed version for disable or delete
             let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&project.id))?;
             
@@ -49,40 +50,44 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
                         current_versions.push((disable_version, p_id));
                         ver
                     },
-                    //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE")
-                    Err(..) => {
-                        //Updating versions in modlist for no repeating version calls
-                        mods_change_versions(config.clone(), version_db_string, project.id)?;
-                        println!("({}) No new version found for the specified", project.title);
+                    Err(e) => {
+                        //Catch no update available
+                        if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
+                            mods_change_versions(config.clone(), version_db_string, project.id)?;
+                            println!("\t └No new version found for the specified minecraft version");
+                        } else {
+                            return Err(e);
+                        };
                         continue;
                     },
                 });
             } else {
-                println!("({}) No new version found", project.title);
+                println!("\t └No new version found");
             };
         };
+        
+        //Linebreak readability
+        println!("");
 
-        if clean {
-            let dl_path = &current_list.download_folder;
-            println!("Cleaning {}", dl_path);
-            for entry in std::fs::read_dir(dl_path)? {
-                let entry = entry?;
-                std::fs::remove_file(entry.path())?;
-            }
-        }
+        if clean { clean_list_dir(&current_list)? };
         
+        //Linebreak readability
+        println!("");
+
         if direct_download {
-            download_versions(current_list.clone(), updatestack).await?;
+            download_versions(current_list.clone(), config.clone(), updatestack).await?;
 
             //Disable old versions
-            for ver in current_versions {
-                if delete_old {
-                    println!("Deleting version {} for mod {}", ver.0, ver.1);
-                    delete_version(current_list.clone(), ver.0)?;
-                } else if ver.0 != "NONE" { 
-                    println!("Disabling version {} for mod {}", ver.0, ver.1);
-                    disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
-                };
+            if !clean {
+                for ver in current_versions {
+                    if delete_old {
+                        println!("Deleting version {} for mod {}", ver.0, ver.1);
+                        delete_version(current_list.clone(), ver.0)?;
+                    } else if ver.0 != "NONE" { 
+                        println!("Disabling version {} for mod {}", ver.0, ver.1);
+                        disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
+                    };
+                }
             }
         };
         
@@ -92,8 +97,6 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
 }
 
 async fn specific_update(config: Cfg, clean: bool, list: List, project: Project) -> MLE<Version> {
-    println!("Checking update for '{}' in {}", project.title, list.id);
-    
     let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await;
     
     let mut versions: Vec<String> = vec![];
@@ -110,7 +113,7 @@ async fn specific_update(config: Cfg, clean: bool, list: List, project: Project)
     let mut current: Vec<Version> = vec![];
     if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) {
         //get new versions
-        print!(" | getting new version");
+        println!("\t └Get versions for specified minecraft versions");
         let current_str = extract_current_version(applicable_versions.clone())?;
         let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") {
             Ok(v) => Ok(v),
@@ -122,12 +125,12 @@ async fn specific_update(config: Cfg, clean: bool, list: List, project: Project)
             Ok(p) => Ok(p),
             Err(e) => Err(MLError::new(ErrorType::Other, e)),
         }?.url;
-        userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id);
+        userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id)?;
     }
 
     if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) };
     
-    println!(" | ✔️");
+    //println!(" └✔️");
     Ok(current[0].clone())
 }
 
diff --git a/src/config.rs b/src/config.rs
index 383e7ee..ded0062 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -25,7 +25,6 @@ impl Cfg {
                 if err.kind() == std::io::ErrorKind::NotFound {
                     println!("No config file found, creating one");
                     let default_cfg = Cfg { data: String::from("./"), apis: Apis { modrinth: String::from("https://api.modrinth.com/v2/") } };
-                    //TODO Error
                     let mut file = File::create(devdir(configfile.to_str().unwrap()))?;
                     println!("Created config file");
                     file.write_all(&toml::to_string(&default_cfg)?.as_bytes())?;
diff --git a/src/db.rs b/src/db.rs
index 119b2a5..f47bda6 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -5,7 +5,7 @@ use rusqlite::Connection;
 use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}};
 
 //mods
-pub fn mods_insert(config: Cfg, id: String, name: String, versions: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
+pub fn mods_insert(config: Cfg, id: String, name: String, versions: Vec<String>) -> MLE<()> {
 
     println!("Inserting mod {}({}) into database", name, id);
 
@@ -41,7 +41,7 @@ pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::
     }
 }
 
-pub fn mods_get_id(config: Cfg, name: String) -> Result<String, Box<dyn std::error::Error>> {
+pub fn mods_get_id(config: Cfg, name: String) -> MLE<String> {
     let data = devdir(format!("{}/data.db", config.data).as_str());
     let connection = Connection::open(data)?;
     
@@ -56,12 +56,12 @@ pub fn mods_get_id(config: Cfg, name: String) -> Result<String, Box<dyn std::err
     };
 
     match mod_id.is_empty() {
-        true => Err(Box::new(Error::new(ErrorKind::NotFound, "MOD_NOT_FOUND"))),
+        true => Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND")),
         false => Ok(mod_id),
     }
 }
 
-pub fn mods_get_name(config: Cfg, id: String) -> Result<String, Box<dyn std::error::Error>> {
+pub fn mods_get_name(config: Cfg, id: &str) -> MLE<String> {
     let data = devdir(format!("{}/data.db", config.data).as_str());
     let connection = Connection::open(data)?;
     
@@ -76,14 +76,14 @@ pub fn mods_get_name(config: Cfg, id: String) -> Result<String, Box<dyn std::err
     };
 
     match mod_name.is_empty() {
-        true => Err(Box::new(Error::new(ErrorKind::NotFound, "MOD_NOT_FOUND"))),
+        true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")),
         false => Ok(mod_name),
     }
 }
 
 pub fn mods_change_versions(config: Cfg, versions: String, mod_id: String) -> MLE<()> {
 
-    println!("Updating versions for {} with \n {}", mod_id, versions);
+    //println!("Updating versions for {} with \n {}", mod_id, versions);
 
     let data = devdir(format!("{}/data.db", config.data).as_str());
     let connection = Connection::open(data)?;
@@ -92,7 +92,7 @@ pub fn mods_change_versions(config: Cfg, versions: String, mod_id: String) -> ML
     Ok(())
 }
 
-pub fn mods_remove(config: Cfg, id: String) -> Result<(), Box<dyn std::error::Error>> {
+pub fn mods_remove(config: Cfg, id: String) -> MLE<()> {
 
     println!("Removing mod {} from database", id);
 
@@ -131,7 +131,7 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
 
     for ver in id_iter {
         let version = ver?;
-        println!("Getting versions for {} from the database", String::from(&version[2]));
+        println!("\t({}) Get versions from the database", String::from(&version[2]));
         //println!("Found versions {} for mod {}", version[1], version[0]);
         versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]) })
     };
@@ -143,7 +143,7 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
 }
 
 //userlist
-pub fn userlist_insert(config: Cfg, list_id: String, mod_id: String, current_version: String, applicable_versions: Vec<String>, current_link: String) -> Result<(), Box<dyn std::error::Error>> {
+pub fn userlist_insert(config: Cfg, list_id: String, mod_id: String, current_version: String, applicable_versions: Vec<String>, current_link: String) -> MLE<()> {
     println!("Inserting {} into current list({})", mod_id, list_id);
 
     let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -201,7 +201,7 @@ pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: St
     };
 
     match version.is_empty() {
-        true => Err(MLError::new(ErrorType::DBError, "MOD_NOT_FOUND")),
+        true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")),
         false => Ok(version),
     }
 }
@@ -241,7 +241,7 @@ pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String
     };
 
     match version.is_empty() {
-        true => Err(MLError::new(ErrorType::DBError, "MOD_NOT_FOUND")),
+        true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")),
         false => Ok(version),
     }
 }
@@ -285,7 +285,7 @@ pub fn userlist_get_all_current_versions_with_mods(config: Cfg, list_id: String)
     Ok(versions)
 }
 
-pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> Result<(), Box<dyn std::error::Error>> {
+pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> MLE<()> {
     let data = devdir(format!("{}/data.db", config.data).as_str());
     let connection = Connection::open(data)?;
 
@@ -322,7 +322,7 @@ pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: Strin
     };
 
     match version.is_empty() {
-        true => Err(MLError::new(ErrorType::DBError, "MOD_NOT_FOUND")),
+        true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")),
         false => Ok(version),
     }
 }
diff --git a/src/error.rs b/src/error.rs
index a1f5f2e..612a2e2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -36,7 +36,7 @@ impl fmt::Display for MLError {
             ErrorType::ArgumentError => write!(f, "User input not accepted: {}", self.message),
             ErrorType::ArgumentCountError => write!(f, "Too many/too few arguments"),
             ErrorType::ConfigError => write!(f, "CONFIG"),
-            ErrorType::DBError => write!(f, "DATABASE"),
+            ErrorType::DBError => write!(f, "Database: {}", self.message),
             ErrorType::ModError => write!(f, "Mod: {}", self.message),
             ErrorType::LibToml => write!(f, "TOML"),
             ErrorType::LibSql => write!(f, "SQL"),
diff --git a/src/files.rs b/src/files.rs
index 14e5636..0d7dc0a 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -2,13 +2,19 @@ use std::{fs::{File, read_dir, remove_file, rename}, io::Write, collections::Has
 use futures_util::StreamExt;
 use reqwest::Client;
 
-use crate::{List, modrinth::Version, db::userlist_add_disabled_versions, config::Cfg, error::{MLE, MLError, ErrorType}};
+use crate::{List, modrinth::Version, db::{userlist_add_disabled_versions, mods_get_name}, config::Cfg, error::{MLE, MLError, ErrorType}};
 
-pub async fn download_versions(current_list: List, versions: Vec<Version>) -> MLE<String> {
+pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> {
 
-    let dl_path = String::from(&current_list.download_folder);
+    let dl_path = String::from(&list.download_folder);
+
+    println!("Download to directory from: {} ({})", list.id, dl_path);
 
     for ver in versions {
+        //TODO get project name instead of projectid from db
+        let project_name = mods_get_name(config.clone(), &ver.project_id)?;
+        print!("\t({})Download version {}", project_name, ver.id);
+        std::io::stdout().flush().unwrap();
         let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap();
         let mut splitname: Vec<&str> = primary_file.filename.split('.').collect();
         let extension = match splitname.pop().ok_or("") {
@@ -16,14 +22,15 @@ pub async fn download_versions(current_list: List, versions: Vec<Version>) -> ML
             Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")),
         };
         let filename = format!("{}.mr.{}.{}.{}", splitname.join("."), ver.project_id, ver.id, extension);
-        download_file(primary_file.url, current_list.clone().download_folder, filename).await?;
+        download_file(primary_file.url, list.clone().download_folder, filename).await?;
+        tokio::time::sleep(std::time::Duration::new(3, 0)).await;
+        println!(" ✓");
     }
 
     Ok(dl_path)
 }
 
 async fn download_file(url: String, path: String, name: String) -> MLE<()> {
-    println!("Downloading {}", url);
     let dl_path_file = format!("{}/{}", path, name);
     let res = Client::new()
         .get(String::from(&url))
@@ -43,7 +50,7 @@ async fn download_file(url: String, path: String, name: String) -> MLE<()> {
 }
 
 pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_id: String) -> MLE<()> {
-    println!("Disabling version {} for mod {}", versionid, mod_id);
+    //println!("Disabling version {} for mod {}", versionid, mod_id);
     let file = get_file_path(current_list.clone(), String::from(&versionid))?;
     let disabled = format!("{}.disabled", file);
 
@@ -97,3 +104,13 @@ pub fn get_downloaded_versions(list: List) -> MLE<HashMap<String, String>> {
     }
     Ok(versions)
 }
+
+pub fn clean_list_dir(list: &List) -> MLE<()> {
+    let dl_path = &list.download_folder;
+    println!("Clean directory for: {}", list.id);
+    for entry in std::fs::read_dir(dl_path)? {
+        let entry = entry?;
+        std::fs::remove_file(entry.path())?;
+    };
+    Ok(())
+}
diff --git a/src/input.rs b/src/input.rs
index be24660..a41f671 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -42,6 +42,7 @@ pub enum ListOptions {
     Add,
     Remove,
     Change,
+    Version,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -119,14 +120,18 @@ impl Input {
                 "clean" => {
                     clean = true;
                 },
-                "direct-download" => {
-                    direct_download = true;
+                "no-download" => {
+                    direct_download = false;
                 },
                 "delete_old" => {
                     delete_old = true;
                 },
                 "l" => {
-                    list = Some(lists_get(config.clone(), String::from(arg_split[1]))?);
+                    if arg_split.len() == 2 {
+                        list = Some(lists_get(config.clone(), String::from(arg_split[1]))?);
+                    } else {
+                        return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list via it's id"));
+                    }
                 }
                 "la" => {
                     command = Some(Cmd::List);
@@ -136,18 +141,26 @@ impl Input {
                 "lr" => {
                     command = Some(Cmd::List);
                     list_options = Some(ListOptions::Remove);
-                    if arg_split.len() == 2 {
-                        list_id = Some(String::from(arg_split[1]));
-                        list = Some(lists_get(config.clone(), list_id.clone().unwrap())?)
-                    }
                 },
                 "lc" => {
                     command = Some(Cmd::List);
                     list_options = Some(ListOptions::Change);
-                    list_id = Some(String::from(arg_split[1]));
                 },
                 "lv" => {
-                    list_mcversion = Some(String::from(arg_split[1]));
+                    command = Some(Cmd::List);
+                    list_options = Some(ListOptions::Version);
+                    if arg_split.len() == 2 {
+                        list_mcversion = Some(String::from(arg_split[1]));
+                    } else {
+                        return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
+                    }
+                },
+                "mcv" => {
+                    if arg_split.len() == 2 {
+                        list_mcversion = Some(String::from(arg_split[1]));
+                    } else {
+                        return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
+                    }
                 },
                 "ml" => {
                     modloader = Some(Modloader::from(arg_split[1])?);
@@ -199,7 +212,7 @@ pub async fn get_input(config: Cfg, args: Vec<String>) -> MLE<Input> {
     
     match input.clone().command.unwrap() {
         Cmd::Mod => check_mod(input, config),
-        Cmd::List => check_list(input),
+        Cmd::List => check_list(input, config),
         _ => Ok(input),
     }
 }
@@ -223,7 +236,7 @@ fn check_mod(mut input: Input, config: Cfg) -> MLE<Input> {
     }
 }
 
-fn check_list(mut input: Input) -> MLE<Input> {
+fn check_list(mut input: Input, config: Cfg) -> MLE<Input> {
     if input.list_options.is_none() {
         return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_ARGUMENT"));
     };
@@ -240,12 +253,19 @@ fn check_list(mut input: Input) -> MLE<Input> {
             Ok(input)
         },
         ListOptions::Remove => {
-            if input.list_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "LISTS_NO_ID")); };
+            if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
             Ok(input)
         },
         ListOptions::Change => {
             //TODO check if no change
-            if input.list_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "LISTS_NO_ID")); };
+            if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
+            Ok(input)
+        },
+        ListOptions::Version => {
+            if input.list.is_none() {
+                println!("No list specified, using default");
+                input.list = Some(get_current_list(config)?);
+            };
             Ok(input)
         }
     }
diff --git a/src/main.rs b/src/main.rs
index 10980fb..2fca691 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,7 +19,7 @@ async fn main() {
         }
     };
 
-    dbg!(&input);
+    //dbg!(&input);
 
     match input.clone().command.unwrap() {
         Cmd::Mod => {
-- 
cgit v1.2.3