diff --git a/Cargo.lock b/Cargo.lock index fc39bd6..425f82f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,12 +64,6 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -117,12 +111,13 @@ dependencies = [ [[package]] name = "confy" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2913470204e9e8498a0f31f17f90a0de801ae92c8c5ac18c49af4819e6786697" +checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" dependencies = [ "directories", "serde", + "thiserror", "toml", ] @@ -171,11 +166,10 @@ dependencies = [ [[package]] name = "directories" -version = "2.0.2" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ - "cfg-if 0.1.10", "dirs-sys", ] @@ -190,6 +184,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -202,7 +202,7 @@ version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -307,7 +307,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -369,6 +369,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "toml_edit", ] [[package]] @@ -461,9 +462,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.7.0" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -487,7 +488,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -496,6 +497,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -538,7 +548,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -559,6 +569,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "0.7.14" @@ -599,6 +615,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom8" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d908f0297c3526d34e478d438b07eefe3d7b0416494d7ffccb17f1c7f7262c" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -637,7 +663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -686,7 +712,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall", @@ -750,11 +776,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -933,18 +959,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.130" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" dependencies = [ "proc-macro2", "quote", @@ -1013,13 +1039,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.81" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1028,7 +1054,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "rand", "redox_syscall", @@ -1061,6 +1087,26 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.5.1" @@ -1140,6 +1186,28 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcd65b83c7473af53e3fd994eb2888dfddfeb28cac9a82825ec5803c233c882c" +dependencies = [ + "indexmap", + "itertools", + "nom8", + "serde", + "toml_datetime", +] + [[package]] name = "tower-service" version = "0.3.1" @@ -1152,7 +1220,7 @@ version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "pin-project-lite", "tracing-core", ] @@ -1178,6 +1246,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -1193,12 +1267,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - [[package]] name = "url" version = "2.2.2" @@ -1245,7 +1313,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -1270,7 +1338,7 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", diff --git a/Cargo.toml b/Cargo.toml index be435aa..fc1a1fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" -confy = "0.4" +confy = "0.5" console = "0.15.0" dialoguer = "0.9.0" env_logger = "0.9.0" @@ -18,3 +18,4 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" clap = { version = "3.2.20", features = ["derive"] } tokio = { version = "1", features = ["full"] } +toml_edit = { version = "0.16.0", features = ["easy"] } diff --git a/src/config.rs b/src/config.rs index 8ba1f47..500e8ab 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,21 @@ pub struct SearchArgs { pub version: Option>, } +#[derive(clap::Args, Clone, Debug)] +pub struct HopfileArgs { + /// The directory in which the hopfile is being created + #[clap(short, long)] + pub dir: Option, + + /// The template file to base the hopfile on + #[clap(short, long)] + pub template: Option, + + /// The version of Minecraft packages are being managed + #[clap(short, long)] + pub version: Option, +} + // TODO use ColoredHelp by default? #[derive(Subcommand, Clone, Debug)] pub enum Command { @@ -27,6 +42,7 @@ pub enum Command { }, Get(SearchArgs), Update, + Init(HopfileArgs), Clean, } @@ -71,7 +87,7 @@ impl Args { if let Some(config_path) = &self.config { confy::load_path(config_path) } else { - confy::load("hopper") + confy::load("hopper", None) } } } diff --git a/src/hopfile.rs b/src/hopfile.rs new file mode 100644 index 0000000..01b5dea --- /dev/null +++ b/src/hopfile.rs @@ -0,0 +1,119 @@ +use std::str::FromStr; + +use serde::{Deserialize, Serialize, de::Visitor}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Hopfile { + pub template: Option, + // TODO: possibly parse this into a more specific format (enum maybe?) + pub mc_version: String, + pub packages: Packages, +} + +impl Hopfile { + pub fn new(template: Option, version: Option) -> Self { + Self { + template, + mc_version: version.unwrap_or_else(|| String::from("1.19.1")), + packages: Packages::default(), + } + } +} + + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct Packages { + pub mods: Vec, + pub resources: Vec, +} + + +#[derive(Debug, Clone, Copy, Default)] +enum Provider { + #[default] + Modrinth, +} + +impl FromStr for Provider { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "modrinth" => Ok(Self::Modrinth), + _ => Err(()), + } + } +} + +impl ToString for Provider { + fn to_string(&self) -> String { + String::from(match self { + Self::Modrinth => "modrinth", + }) + } +} + +#[derive(Debug, Clone)] +pub struct Resource { + provider: Provider, + name: String, +} + +impl FromStr for Resource { + type Err = (); + + fn from_str(s: &str) -> Result { + if let Some((provider, name)) = s.split_once(':') { + Ok(Resource { + provider: Provider::from_str(provider)?, + name: name.to_string(), + }) + } else if !s.is_empty() { + Ok(Resource { + provider: Provider::default(), + name: s.to_string(), + }) + } else { + Err(()) + } + } +} + +impl ToString for Resource { + fn to_string(&self) -> String { + [self.provider.to_string().as_str(), self.name.as_str()].join(":") + } +} + +impl Serialize for Resource { + fn serialize(&self, serializer: S) -> Result + where S: serde::Serializer { + serializer.serialize_str(&self.to_string()) + } +} + +struct V; + +impl<'de> Visitor<'de> for V { + type Value = Resource; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a string") + } + + fn visit_str(self, v: &str) -> Result + where E: serde::de::Error, { + Resource::from_str(v).map_err(|_| serde::de::Error::custom( + format!("Failed to parse mod/resource: '{}'", v) + )) + } +} + +impl<'de> Deserialize<'de> for Resource { + + fn deserialize(deserializer: D) -> Result + where D: serde::Deserializer<'de> { + deserializer.deserialize_str(V) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ec7553e..f9cb29c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,16 @@ mod api; mod client; mod config; +mod hopfile; + +use clap::Parser; +use tokio::fs::File; +use tokio::io::AsyncWriteExt; use api::*; -use clap::Parser; use client::*; use config::*; +use hopfile::*; fn display_search_results(ctx: &AppContext, response: &SearchResponse) { let iter = response.hits.iter().enumerate(); @@ -108,6 +113,25 @@ async fn cmd_get(ctx: &AppContext, search_args: SearchArgs) -> anyhow::Result<() Ok(()) } +async fn cmd_init(args: HopfileArgs) -> anyhow::Result<()> { + let mut path = args.dir.unwrap_or_default(); + path.push("info.hop"); + + if path.try_exists().expect("Invalid dir") { + let message = format!("hopfile already exists: {}", path.to_str().unwrap()); + Err(anyhow::Error::msg(message)) + } else { + let mut file = File::create(&path).await?; + let doc = Hopfile::new(args.template, args.version); + let output = toml_edit::easy::to_string_pretty(&doc).unwrap(); + + file.write_all(output.as_bytes()).await?; + + println!("Saved {}", path.to_str().unwrap()); + Ok(()) + } +} + #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::init(); @@ -116,6 +140,7 @@ async fn main() -> anyhow::Result<()> { let ctx = AppContext { args, config }; match ctx.args.to_owned().command { Command::Get(search_args) => cmd_get(&ctx, search_args).await, + Command::Init(hopfile_args) => cmd_init(hopfile_args).await, _ => unimplemented!("unimplemented subcommand"), } }