From 34cdad469b9ea258f96637605c865ec0d43ffd09 Mon Sep 17 00:00:00 2001 From: TheAlan404 Date: Thu, 11 Jul 2024 09:27:50 +0000 Subject: [PATCH] utils::{toml, zip}, scriptgen --- src/api/app/actions/build/mod.rs | 6 +-- src/api/app/actions/build/worlds.rs | 0 src/api/app/actions/script/mod.rs | 71 +++++++++------------------- src/api/app/io.rs | 2 +- src/api/models/server/mod.rs | 36 ++++++++++++++ src/api/models/server/server_type.rs | 2 +- src/api/models/source.rs | 2 +- src/api/utils/mod.rs | 45 +----------------- src/api/utils/pathdiff.rs | 22 ++++++++- src/api/utils/script.rs | 7 +++ src/api/utils/toml.rs | 39 +++++++++++++++ src/api/utils/zip.rs | 66 ++++++++++++++++++++++++++ 12 files changed, 198 insertions(+), 100 deletions(-) create mode 100644 src/api/app/actions/build/worlds.rs create mode 100644 src/api/utils/toml.rs create mode 100644 src/api/utils/zip.rs diff --git a/src/api/app/actions/build/mod.rs b/src/api/app/actions/build/mod.rs index 8772b53..f2d0055 100644 --- a/src/api/app/actions/build/mod.rs +++ b/src/api/app/actions/build/mod.rs @@ -1,19 +1,19 @@ use std::{path::Path, sync::Arc}; -use futures::stream::{self, StreamExt, TryStreamExt}; use anyhow::Result; -use crate::api::{app::App, models::{Addon, Environment}}; +use crate::api::app::App; pub mod addons; pub mod server_jar; +pub mod worlds; impl App { pub async fn action_build(self: Arc, base: &Path) -> Result<()> { self.action_install_jar(base).await?; self.clone().action_install_addons(base).await?; - self.action_generate_script().await?; + self.action_generate_scripts(base).await?; Ok(()) } diff --git a/src/api/app/actions/build/worlds.rs b/src/api/app/actions/build/worlds.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/api/app/actions/script/mod.rs b/src/api/app/actions/script/mod.rs index 53b3bfa..858abb7 100644 --- a/src/api/app/actions/script/mod.rs +++ b/src/api/app/actions/script/mod.rs @@ -1,66 +1,39 @@ +use std::path::Path; + use anyhow::Result; use crate::api::{app::App, models::server::Server, utils::script::Shell}; impl App { - pub fn get_args(&self, server: &Server) -> Vec { - let (prefix, suffix) = self.get_args_prefix_suffix(server); - let exec = self.get_args_exec(server); - - vec![prefix, exec, suffix].concat() - } - - pub fn get_args_exec(&self, server: &Server) -> Vec { - server.jar.as_ref().map(|s| s.get_exec_arguments()).unwrap_or_default() - } - - pub fn get_args_prefix_suffix(&self, server: &Server) -> (Vec, Vec) { - let mut prefix = vec![]; - - prefix.extend(server.launcher.jvm_args.split_whitespace().map(ToOwned::to_owned)); - - // TODO: -Xmx -Xms - - prefix.extend(server.launcher.preset_flags.get_flags()); - - if server.launcher.eula_args && server.jar.as_ref().is_some_and(|x| x.flavor().supports_eula_args()) { - prefix.push(String::from("-Dcom.mojang.eula.agree=true")); - } - - for (key, value) in &server.launcher.properties { - let value = serde_json::to_string(value).unwrap(); - - prefix.push(format!("-D{key}={value}")); - } - - let mut suffix = vec![]; - - if server.launcher.nogui && server.jar.as_ref().is_some_and(|x| x.flavor().supports_nogui()) { - suffix.push(String::from("--nogui")); - } - - suffix.extend(server.launcher.game_args.split_whitespace().map(ToOwned::to_owned)); - - (prefix, suffix) - } - - pub fn get_script_lines_for(&self, shell: Shell, server: &Server) -> Vec { + fn get_script_lines_for(&self, shell: &Shell, server: &Server) -> Vec { let mut lines = vec![]; - - if shell == Shell::Bat { + + if *shell == Shell::Bat { lines.push(format!("title {}", server.name)); } lines.extend(server.launcher.prelaunch.clone()); + let mut args = server.get_arguments(); + args.push(shell.script_args().to_owned()); + lines.push(args.join(" ")); + lines.extend(server.launcher.postlaunch.clone()); + lines + } - lines.push(self.get_args(server).join(" ")); + pub fn action_generate_script(&self, shell: Shell, server: &Server, base: &Path) -> Result<()> { + let script = shell.generate_script(self.get_script_lines_for(&shell, server)); - lines.extend(server.launcher.postlaunch.clone()); + std::fs::write(base.join(format!("start.{}", shell.file_ext())), script)?; - lines + Ok(()) } - pub async fn action_generate_script(&self) -> Result<()> { - todo!() + pub async fn action_generate_scripts(&self, base: &Path) -> Result<()> { + if let Some((_, server)) = &*self.server.read().await { + self.action_generate_script(Shell::Bat, server, base)?; + self.action_generate_script(Shell::Sh, server, base)?; + } + + Ok(()) } } diff --git a/src/api/app/io.rs b/src/api/app/io.rs index e99945c..8176af1 100644 --- a/src/api/app/io.rs +++ b/src/api/app/io.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::Result; use tokio::sync::RwLock; -use crate::api::{models::{network::{Network, NETWORK_TOML}, server::{Server, SERVER_TOML}}, utils::{try_find_toml_upwards, write_toml}}; +use crate::api::{models::{network::{Network, NETWORK_TOML}, server::{Server, SERVER_TOML}}, utils::toml::{try_find_toml_upwards, write_toml}}; use super::App; diff --git a/src/api/models/server/mod.rs b/src/api/models/server/mod.rs index d523e0d..14db549 100644 --- a/src/api/models/server/mod.rs +++ b/src/api/models/server/mod.rs @@ -44,3 +44,39 @@ impl Default for Server { } } } + +impl Server { + pub fn get_execution_arguments(&self) -> Vec { + self.jar.as_ref().map(|s| s.get_execution_arguments()).unwrap_or_default() + } + + pub fn get_arguments(&self) -> Vec { + let mut args = vec![]; + + args.extend(self.launcher.jvm_args.split_whitespace().map(ToOwned::to_owned)); + + // TODO: -Xmx -Xms + + args.extend(self.launcher.preset_flags.get_flags()); + + if self.launcher.eula_args && self.jar.as_ref().is_some_and(|x| x.flavor().supports_eula_args()) { + args.push(String::from("-Dcom.mojang.eula.agree=true")); + } + + for (key, value) in &self.launcher.properties { + let value = serde_json::to_string(value).unwrap(); + + args.push(format!("-D{key}={value}")); + } + + args.extend(self.get_execution_arguments()); + + if self.launcher.nogui && self.jar.as_ref().is_some_and(|x| x.flavor().supports_nogui()) { + args.push(String::from("--nogui")); + } + + args.extend(self.launcher.game_args.split_whitespace().map(ToOwned::to_owned)); + + args + } +} diff --git a/src/api/models/server/server_type.rs b/src/api/models/server/server_type.rs index 278a9cb..163ebb3 100644 --- a/src/api/models/server/server_type.rs +++ b/src/api/models/server/server_type.rs @@ -134,7 +134,7 @@ impl ServerJar { } } - pub fn get_exec_arguments(&self) -> Vec { + pub fn get_execution_arguments(&self) -> Vec { match &self.server_type { ServerType::Forge { .. } => todo!(), ServerType::Custom { exec, .. } => exec.clone() diff --git a/src/api/models/source.rs b/src/api/models/source.rs index be3452a..b4d4d7f 100644 --- a/src/api/models/source.rs +++ b/src/api/models/source.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use super::{mrpack::resolve_mrpack_addons, packwiz::resolve_packwiz_addons, Addon, AddonListFile, ModpackType}; -use crate::api::{app::App, models::ModpackSource, utils::read_toml}; +use crate::api::{app::App, models::ModpackSource, utils::toml::read_toml}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(tag = "type", rename_all = "lowercase")] diff --git a/src/api/utils/mod.rs b/src/api/utils/mod.rs index 2b662e6..1b43dca 100644 --- a/src/api/utils/mod.rs +++ b/src/api/utils/mod.rs @@ -1,11 +1,3 @@ -use std::{ - io::Write, - path::{Path, PathBuf}, -}; - -use ::serde::{de::DeserializeOwned, Serialize}; -use anyhow::Result; - pub mod accessor; pub mod hashing; pub mod pathdiff; @@ -14,38 +6,5 @@ pub mod url; pub mod console; pub mod markdown; pub mod script; - -pub fn try_find_toml_upwards(filename: &str) -> Result> { - let mut path = std::env::current_dir()?; - - let found_path = loop { - path.push(filename); - - if path.is_file() { - break path; - } - - if !(path.pop() && path.pop()) { - return Ok(None); - } - }; - - read_toml(&found_path).map(|data| Some((found_path, data))) -} - -pub fn read_toml(path: &Path) -> Result { - let data: T = toml::from_str(&std::fs::read_to_string(&path)?)?; - - Ok(data) -} - -pub fn write_toml(path: &Path, filename: &str, value: &T) -> Result<()> { - std::fs::create_dir_all(path)?; - - let content = toml::to_string_pretty(value)?; - - let mut file = std::fs::File::open(path.join(filename))?; - file.write_all(content.as_bytes())?; - - Ok(()) -} +pub mod zip; +pub mod toml; diff --git a/src/api/utils/pathdiff.rs b/src/api/utils/pathdiff.rs index 6b10804..b1ea7a6 100644 --- a/src/api/utils/pathdiff.rs +++ b/src/api/utils/pathdiff.rs @@ -1,10 +1,19 @@ //! https://github.com/Manishearth/pathdiff +use std::path::*; + +use anyhow::{anyhow, Result}; + pub trait DiffTo { fn diff_to

(&self, path: P) -> Option where P: AsRef, Self: AsRef; + + fn try_diff_to

(&self, path: P) -> Result + where + P: AsRef, + Self: AsRef; } impl DiffTo for B @@ -18,9 +27,18 @@ where { diff_paths(path, self) } -} -use std::path::*; + fn try_diff_to

(&self, path: P) -> anyhow::Result + where + P: AsRef, + Self: AsRef + { + diff_paths(&path, self) + .ok_or(anyhow!("Can't diff paths +Base: {} +Path: {}", self.as_ref().display(), path.as_ref().display())) + } +} /// Construct a relative path from a provided base directory path to the provided path. /// diff --git a/src/api/utils/script.rs b/src/api/utils/script.rs index 06b02a4..41dec09 100644 --- a/src/api/utils/script.rs +++ b/src/api/utils/script.rs @@ -28,6 +28,13 @@ impl Shell { } } + pub fn file_ext(&self) -> &'static str { + match self { + Shell::Sh => "sh", + Shell::Bat => "bat", + } + } + pub fn line_sep(&self) -> &'static str { match self { Shell::Sh => "\n", diff --git a/src/api/utils/toml.rs b/src/api/utils/toml.rs new file mode 100644 index 0000000..d4270fc --- /dev/null +++ b/src/api/utils/toml.rs @@ -0,0 +1,39 @@ +use std::{io::Write, path::{Path, PathBuf}}; + +use anyhow::Result; +use serde::{de::DeserializeOwned, Serialize}; + +pub fn try_find_toml_upwards(filename: &str) -> Result> { + let mut path = std::env::current_dir()?; + + let found_path = loop { + path.push(filename); + + if path.is_file() { + break path; + } + + if !(path.pop() && path.pop()) { + return Ok(None); + } + }; + + read_toml(&found_path).map(|data| Some((found_path, data))) +} + +pub fn read_toml(path: &Path) -> Result { + let data: T = toml::from_str(&std::fs::read_to_string(&path)?)?; + + Ok(data) +} + +pub fn write_toml(path: &Path, filename: &str, value: &T) -> Result<()> { + std::fs::create_dir_all(path)?; + + let content = toml::to_string_pretty(value)?; + + let mut file = std::fs::File::open(path.join(filename))?; + file.write_all(content.as_bytes())?; + + Ok(()) +} diff --git a/src/api/utils/zip.rs b/src/api/utils/zip.rs new file mode 100644 index 0000000..7909530 --- /dev/null +++ b/src/api/utils/zip.rs @@ -0,0 +1,66 @@ +use std::{io::{Read, Seek, Write}, path::Path}; + +use anyhow::{anyhow, Context, Result}; +use walkdir::WalkDir; +use zip::{write::FileOptions, ZipArchive, ZipWriter}; + +use crate::api::app::APP_VERSION; + +use super::pathdiff::DiffTo; + +pub async fn unzip(reader: T, to: &Path, prefix: Option) -> Result<()> { + let mut archive = ZipArchive::new(reader)?; + + let mut files = archive.file_names().map(ToOwned::to_owned).collect::>(); + + if let Some(prefix) = prefix.map(|p| format!("{p}/")) { + if files.iter().all(|f| f.starts_with(&prefix)) { + files = files.into_iter() + .map(|f| f.replacen(&prefix, "", 1)) + .collect() + } + } + + + for filename in files { + if filename.ends_with('/') { + // directory + continue; + } + + let mut file = archive.by_name(&filename)?; + let target_path = to.join(&filename); + + std::fs::create_dir_all(target_path.parent().unwrap())?; + let mut target_file = std::fs::File::create(&target_path)?; + + std::io::copy(&mut file, &mut target_file)?; + } + + Ok(()) +} + +pub async fn zip(writer: T, folder: &Path) -> Result<()> { + let mut archive = ZipWriter::new(writer); + + archive.set_comment(format!("generated by mcman/{APP_VERSION}")); + + for entry in WalkDir::new(folder) { + let entry = entry.with_context(|| "WalkDir")?; + + let path = folder.try_diff_to(entry.path())?; + let path = path.to_string_lossy(); + + if entry.file_type().is_dir() { + archive.add_directory(path, FileOptions::default())?; + continue; + } + + archive.start_file(path, FileOptions::default())?; + + let mut file = std::fs::File::open(entry.path())?; + std::io::copy(&mut file, &mut archive)?; + } + + Ok(()) +}