Implement init command and hopfiles #22

Open
BlankParenthesis wants to merge 2 commits from BlankParenthesis/hopper:impl-hopfiles into main
5 changed files with 273 additions and 44 deletions

150
Cargo.lock generated
View File

@ -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",

View File

@ -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"] }

View File

@ -16,6 +16,21 @@ pub struct SearchArgs {
pub version: Option<Vec<String>>,
}
#[derive(clap::Args, Clone, Debug)]
pub struct HopfileArgs {
/// The directory in which the hopfile is being created
#[clap(short, long)]
pub dir: Option<PathBuf>,
/// The template file to base the hopfile on
#[clap(short, long)]
pub template: Option<String>,
/// The version of Minecraft packages are being managed
#[clap(short, long)]
pub version: Option<String>,
}
// 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)
}
}
}

119
src/hopfile.rs Normal file
View File

@ -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<String>,
// 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<String>, version: Option<String>) -> 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<Resource>,
pub resources: Vec<Resource>,
}
#[derive(Debug, Clone, Copy, Default)]
enum Provider {
#[default]
Modrinth,
}
impl FromStr for Provider {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
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<Self, Self::Err> {
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<E>(self, v: &str) -> Result<Self::Value, E>
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<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
deserializer.deserialize_str(V)
}
}

View File

@ -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"),
}
}