wip papermc cli stuff
This commit is contained in:
parent
f2c4b97626
commit
63e1d170bc
5 changed files with 221 additions and 0 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -16,8 +16,10 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"figment",
|
"figment",
|
||||||
|
"papermc-api",
|
||||||
"serde",
|
"serde",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
|
"ureq",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
backup = { path = "../backup" }
|
backup = { path = "../backup" }
|
||||||
|
papermc-api = { path = "../papermc-api" }
|
||||||
|
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
@ -13,3 +14,4 @@ serde.workspace = true
|
||||||
clap = { version = "4.5.37", features = ["derive", "env"] }
|
clap = { version = "4.5.37", features = ["derive", "env"] }
|
||||||
signal-hook = "0.3.15"
|
signal-hook = "0.3.15"
|
||||||
figment = { version = "0.10.10", features = ["env", "toml"] }
|
figment = { version = "0.10.10", features = ["env", "toml"] }
|
||||||
|
ureq = "3.3.0"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod backup;
|
mod backup;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod papermc;
|
||||||
mod run;
|
mod run;
|
||||||
|
|
||||||
use std::{path::PathBuf, str::FromStr};
|
use std::{path::PathBuf, str::FromStr};
|
||||||
|
|
@ -72,6 +73,9 @@ pub enum Commands {
|
||||||
Run(RunCli),
|
Run(RunCli),
|
||||||
/// Interact with the backup system without starting a server
|
/// Interact with the backup system without starting a server
|
||||||
Backup(BackupArgs),
|
Backup(BackupArgs),
|
||||||
|
/// Interact with the PaperMC API and download new JARs
|
||||||
|
#[command(name = "papermc")]
|
||||||
|
PaperMC(papermc::Cli),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cli {
|
impl Cli {
|
||||||
|
|
@ -81,6 +85,7 @@ impl Cli {
|
||||||
match &self.command {
|
match &self.command {
|
||||||
Commands::Run(args) => args.run(self, &config),
|
Commands::Run(args) => args.run(self, &config),
|
||||||
Commands::Backup(args) => Ok(args.run(&config)?),
|
Commands::Backup(args) => Ok(args.run(&config)?),
|
||||||
|
Commands::PaperMC(cli) => cli.run(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
210
alex/src/cli/papermc.rs
Normal file
210
alex/src/cli/papermc.rs
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use chrono::Local;
|
||||||
|
use clap::{Args, Subcommand};
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands {
|
||||||
|
/// Show information or a specific version or build
|
||||||
|
Show(ShowArgs),
|
||||||
|
/// List all versions PaperMC jars are available for
|
||||||
|
ListVersions,
|
||||||
|
/// List all available builds for a given version
|
||||||
|
ListBuilds(ListBuildsArgs),
|
||||||
|
/// Download the jar for a specific build
|
||||||
|
Download(DownloadBuildArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct ShowArgs {
|
||||||
|
/// Version to show information for
|
||||||
|
version: String,
|
||||||
|
|
||||||
|
/// Build within version to show information for
|
||||||
|
build: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct ListBuildsArgs {
|
||||||
|
/// Version to list builds for
|
||||||
|
version: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct DownloadBuildArgs {
|
||||||
|
/// Version of build to download
|
||||||
|
version: String,
|
||||||
|
/// Build number for build to download
|
||||||
|
build: String,
|
||||||
|
|
||||||
|
/// Path to store the new JAR file in; stores JAR in the local directory if not specified
|
||||||
|
#[arg(short, long, value_name = "OUT_PATH")]
|
||||||
|
out: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cli {
|
||||||
|
pub fn run(&self) -> crate::Result<()> {
|
||||||
|
match &self.command {
|
||||||
|
Commands::Show(ShowArgs {
|
||||||
|
version,
|
||||||
|
build: None,
|
||||||
|
}) => show_version(&version),
|
||||||
|
Commands::Show(ShowArgs {
|
||||||
|
version,
|
||||||
|
build: Some(build),
|
||||||
|
}) => show_build(&version, &build),
|
||||||
|
Commands::ListVersions => print_versions(),
|
||||||
|
Commands::ListBuilds(args) => print_builds(&args.version),
|
||||||
|
Commands::Download(args) => {
|
||||||
|
download_build(&args.version, &args.build, args.out.as_deref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_version(version_str: &str) {
|
||||||
|
let client = papermc_api::Client::new();
|
||||||
|
|
||||||
|
let version = match client.project("paper").version(version_str).info() {
|
||||||
|
Ok(version) => version,
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to query API: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("id: {}", version.id);
|
||||||
|
println!("status: {}", version.support_status);
|
||||||
|
println!("Min. Java: {}", version.java.minimum_version);
|
||||||
|
println!("builds: {}", version.builds.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_build(version_str: &str, build_str: &str) {
|
||||||
|
let client = papermc_api::Client::new();
|
||||||
|
|
||||||
|
let build = match client
|
||||||
|
.project("paper")
|
||||||
|
.version(version_str)
|
||||||
|
.build(build_str)
|
||||||
|
{
|
||||||
|
Ok(build) => build,
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to query API: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("id: {}", build.id);
|
||||||
|
println!("time: {}", build.time.with_timezone(&Local));
|
||||||
|
println!("channel: {}", build.channel);
|
||||||
|
println!("commits:");
|
||||||
|
|
||||||
|
for commit in build.commits {
|
||||||
|
println!("- SHA: {}", commit.sha);
|
||||||
|
println!(" time: {}", commit.time.with_timezone(&Local));
|
||||||
|
println!(" message: {}", commit.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("downloads:");
|
||||||
|
|
||||||
|
for (name, download) in build.downloads.iter() {
|
||||||
|
println!(" {name}:");
|
||||||
|
println!(" name: {}", download.name);
|
||||||
|
println!(" size: {}", download.size);
|
||||||
|
println!(" URL: {}", download.url);
|
||||||
|
println!(" checksums:");
|
||||||
|
|
||||||
|
for (name, value) in download.checksums.iter() {
|
||||||
|
println!(" - {}: {}", name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_versions() {
|
||||||
|
let client = papermc_api::Client::new();
|
||||||
|
|
||||||
|
match client.project("paper").versions() {
|
||||||
|
Ok(versions) => {
|
||||||
|
for version in versions {
|
||||||
|
println!("{}", version.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to query API: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_builds(version: &str) {
|
||||||
|
let client = papermc_api::Client::new();
|
||||||
|
|
||||||
|
match client.project("paper").version(version).builds() {
|
||||||
|
Ok(builds) => {
|
||||||
|
for build in builds {
|
||||||
|
println!("{} ({})", build.id, build.channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to query API: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_build(version: &str, build: &str, out_path: Option<&Path>) {
|
||||||
|
let client = papermc_api::Client::new();
|
||||||
|
let build = match client.project("paper").version(version).build(build) {
|
||||||
|
Ok(build) => build,
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to query API: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let filename = format!("paper-{}-{}.jar", version, build.id);
|
||||||
|
let dest_path = match out_path {
|
||||||
|
Some(path) if path.is_dir() => path.join(filename),
|
||||||
|
Some(path) => path.to_path_buf(),
|
||||||
|
None => PathBuf::from(filename),
|
||||||
|
};
|
||||||
|
|
||||||
|
let download_url = match build
|
||||||
|
.downloads
|
||||||
|
.get("server:default")
|
||||||
|
.or(build.downloads.values().next())
|
||||||
|
{
|
||||||
|
Some(download) => &download.url,
|
||||||
|
None => {
|
||||||
|
println!("no download URLs found for build.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut f = match std::fs::File::create(dest_path) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to create destination file: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = match ureq::get(download_url).call() {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
println!("failed to download file: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut reader = res.body_mut().as_reader();
|
||||||
|
if let Err(err) = std::io::copy(&mut reader, &mut f) {
|
||||||
|
println!("failed to download file: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IO(io::Error),
|
IO(io::Error),
|
||||||
Figment(figment::Error),
|
Figment(figment::Error),
|
||||||
|
Other(Box<dyn std::error::Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
|
@ -13,6 +14,7 @@ impl fmt::Display for Error {
|
||||||
match self {
|
match self {
|
||||||
Error::IO(err) => write!(fmt, "{}", err),
|
Error::IO(err) => write!(fmt, "{}", err),
|
||||||
Error::Figment(err) => write!(fmt, "{}", err),
|
Error::Figment(err) => write!(fmt, "{}", err),
|
||||||
|
Error::Other(err) => write!(fmt, "{}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue