From a41a167063bf2ad76789eaca07a60a77307e0471 Mon Sep 17 00:00:00 2001 From: TheAlan404 Date: Wed, 17 Jul 2024 06:07:45 +0000 Subject: [PATCH] mcman migrate --- src/api/app/actions/build/bootstrap.rs | 2 +- src/api/app/io.rs | 4 +- src/api/app/mod.rs | 4 +- src/api/models/addon/addon.rs | 1 + src/api/models/addon/addon_target.rs | 13 +-- src/api/models/addon/holders.rs | 9 +- src/api/models/hooks/mod.rs | 52 +++++++++ src/api/models/legacy/clientsidemod.rs | 12 +++ src/api/models/legacy/downloadable.rs | 80 ++++++++++++++ src/api/models/legacy/hook.rs | 0 src/api/models/legacy/markdown_options.rs | 23 ++++ src/api/models/legacy/mod.rs | 22 ++++ src/api/models/legacy/network.rs | 68 ++++++++++++ src/api/models/legacy/server.rs | 59 ++++++++++ src/api/models/legacy/server_launcher.rs | 60 +++++++++++ src/api/models/legacy/server_options.rs | 35 ++++++ src/api/models/legacy/server_type.rs | 100 +++++++++++++++++ src/api/models/legacy/world.rs | 11 ++ src/api/models/markdown/mod.rs | 7 ++ src/api/models/mod.rs | 2 + src/api/models/server/mod.rs | 12 ++- src/api/models/source.rs | 40 ++++--- src/api/utils/serde.rs | 8 ++ src/api/utils/toml.rs | 2 +- src/commands/migrate.rs | 124 ++++++++++++++++++++++ src/commands/mod.rs | 1 + src/main.rs | 2 + 27 files changed, 717 insertions(+), 36 deletions(-) create mode 100644 src/api/models/hooks/mod.rs create mode 100644 src/api/models/legacy/clientsidemod.rs create mode 100644 src/api/models/legacy/downloadable.rs create mode 100644 src/api/models/legacy/hook.rs create mode 100644 src/api/models/legacy/markdown_options.rs create mode 100644 src/api/models/legacy/mod.rs create mode 100644 src/api/models/legacy/network.rs create mode 100644 src/api/models/legacy/server.rs create mode 100644 src/api/models/legacy/server_launcher.rs create mode 100644 src/api/models/legacy/server_options.rs create mode 100644 src/api/models/legacy/server_type.rs create mode 100644 src/api/models/legacy/world.rs create mode 100644 src/commands/migrate.rs diff --git a/src/api/app/actions/build/bootstrap.rs b/src/api/app/actions/build/bootstrap.rs index af3b1f0..b45bc18 100644 --- a/src/api/app/actions/build/bootstrap.rs +++ b/src/api/app/actions/build/bootstrap.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, path::Path, sync::Arc}; +use std::{path::Path, sync::Arc}; use anyhow::{Context, Result}; use futures::{StreamExt, TryStreamExt}; diff --git a/src/api/app/io.rs b/src/api/app/io.rs index 8176af1..1ac4f88 100644 --- a/src/api/app/io.rs +++ b/src/api/app/io.rs @@ -20,11 +20,11 @@ impl App { pub async fn save_changes(&self) -> Result<()> { if let Some((path, server)) = &*self.server.read().await { - write_toml(&path, SERVER_TOML, &server)?; + write_toml(path.parent().unwrap(), SERVER_TOML, &server)?; } if let Some((path, network)) = &*self.network.read().await { - write_toml(&path, NETWORK_TOML, &network)?; + write_toml(path.parent().unwrap(), NETWORK_TOML, &network)?; } Ok(()) diff --git a/src/api/app/mod.rs b/src/api/app/mod.rs index cd8d497..78c7ca2 100644 --- a/src/api/app/mod.rs +++ b/src/api/app/mod.rs @@ -73,7 +73,9 @@ impl App { ci, }; - app.try_read_files()?; + if let Err(e) = app.try_read_files() { + println!("Error while reading files: {e:?}"); + } Ok(app) } diff --git a/src/api/models/addon/addon.rs b/src/api/models/addon/addon.rs index 27a2368..c80356d 100644 --- a/src/api/models/addon/addon.rs +++ b/src/api/models/addon/addon.rs @@ -7,6 +7,7 @@ use super::{AddonTarget, AddonType}; #[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq)] pub struct Addon { + #[serde(alias = "side")] pub environment: Option, #[serde(flatten)] pub addon_type: AddonType, diff --git a/src/api/models/addon/addon_target.rs b/src/api/models/addon/addon_target.rs index 34e4d2b..c646e4d 100644 --- a/src/api/models/addon/addon_target.rs +++ b/src/api/models/addon/addon_target.rs @@ -3,9 +3,10 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] pub enum AddonTarget { - Plugin, - Mod, + Plugins, + Mods, Custom(String), } @@ -18,16 +19,16 @@ impl Default for AddonTarget { impl AddonTarget { pub fn from_str(str: &str) -> Self { match str { - "mods" => AddonTarget::Mod, - "plugins" => AddonTarget::Plugin, + "mods" => AddonTarget::Mods, + "plugins" => AddonTarget::Plugins, other => AddonTarget::Custom(other.to_owned()), } } pub fn as_str(&self) -> &str { match self { - AddonTarget::Mod => "mods", - AddonTarget::Plugin => "plugins", + AddonTarget::Mods => "mods", + AddonTarget::Plugins => "plugins", AddonTarget::Custom(path) => path.as_str(), } } diff --git a/src/api/models/addon/holders.rs b/src/api/models/addon/holders.rs index 85c4398..7452a5a 100644 --- a/src/api/models/addon/holders.rs +++ b/src/api/models/addon/holders.rs @@ -2,15 +2,18 @@ use serde::{Deserialize, Serialize}; use super::{Addon, AddonTarget, AddonType}; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct AddonListFile { #[serde(default = "Vec::new")] + #[serde(skip_serializing_if = "Vec::is_empty")] pub addons: Vec, // backwards compatability #[serde(default = "Vec::new")] + #[serde(skip_serializing_if = "Vec::is_empty")] pub mods: Vec, #[serde(default = "Vec::new")] + #[serde(skip_serializing_if = "Vec::is_empty")] pub plugins: Vec, } @@ -24,7 +27,7 @@ impl AddonListFile { Addon { environment: None, addon_type, - target: AddonTarget::Mod, + target: AddonTarget::Mods, } }) .collect(), @@ -34,7 +37,7 @@ impl AddonListFile { Addon { environment: None, addon_type, - target: AddonTarget::Plugin, + target: AddonTarget::Plugins, } }).collect() ].concat() diff --git a/src/api/models/hooks/mod.rs b/src/api/models/hooks/mod.rs new file mode 100644 index 0000000..f773bb6 --- /dev/null +++ b/src/api/models/hooks/mod.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] +pub struct Hook { + pub when: HookEvent, + #[serde(default)] + pub onfail: HookFailBehavior, + #[serde(default = "bool_true")] + pub show_output: bool, + #[serde(default)] + pub description: String, + #[serde(default)] + pub disabled: bool, + #[serde(default)] + pub env: HashMap, + + pub windows: Option, + pub linux: Option, +} + +fn bool_true() -> bool { + true +} + +#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Default)] +#[serde(rename_all = "lowercase")] +pub enum HookEvent { + #[default] + None, + PreBuild, + PostBuild, + PreInstall, + PostInstall, + PreWorldUnpack, + PostWorldUnpack, + PreWorldPack, + PostWorldPack, + DevSessionStart, + DevSessionEnd, + TestSuccess, + TestFail, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Default)] +pub enum HookFailBehavior { + #[default] + Error, + Ignore, + Warn, +} diff --git a/src/api/models/legacy/clientsidemod.rs b/src/api/models/legacy/clientsidemod.rs new file mode 100644 index 0000000..66472be --- /dev/null +++ b/src/api/models/legacy/clientsidemod.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +use super::LegacyDownloadable; + +#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct LegacyClientSideMod { + #[serde(flatten)] + pub dl: LegacyDownloadable, + + pub optional: bool, + pub desc: String, +} diff --git a/src/api/models/legacy/downloadable.rs b/src/api/models/legacy/downloadable.rs new file mode 100644 index 0000000..d6c6761 --- /dev/null +++ b/src/api/models/legacy/downloadable.rs @@ -0,0 +1,80 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum LegacyDownloadable { + // sources + Url { + url: String, + #[serde(default)] + #[serde(skip_serializing_if = "crate::api::utils::serde::is_default")] + filename: Option, + #[serde(default)] + #[serde(skip_serializing_if = "crate::api::utils::serde::is_default")] + desc: Option, + }, + + #[serde(alias = "mr")] + Modrinth { + id: String, + #[serde(default = "latest")] + version: String, + }, + + #[serde(alias = "cr")] + CurseRinth { + id: String, + #[serde(default = "latest")] + version: String, + }, + + Spigot { + id: String, + #[serde(default = "latest")] + version: String, + }, + + Hangar { + id: String, + version: String, + }, + + #[serde(rename = "ghrel")] + GithubRelease { + repo: String, + tag: String, + asset: String, + }, + + // pain in the a- + Jenkins { + url: String, + job: String, + #[serde(default = "latest")] + build: String, + #[serde(default = "first")] + artifact: String, + }, + + Maven { + url: String, + group: String, + artifact: String, + #[serde(default = "latest")] + version: String, + #[serde(default = "artifact")] + filename: String, + }, +} + +pub fn latest() -> String { + "latest".to_owned() +} + +pub fn first() -> String { + "first".to_owned() +} + +pub fn artifact() -> String { + "artifact".to_owned() +} diff --git a/src/api/models/legacy/hook.rs b/src/api/models/legacy/hook.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/api/models/legacy/markdown_options.rs b/src/api/models/legacy/markdown_options.rs new file mode 100644 index 0000000..c0c7a0e --- /dev/null +++ b/src/api/models/legacy/markdown_options.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[serde(default)] +pub struct LegacyMarkdownOptions { + pub files: Vec, + pub auto_update: bool, +} + +impl Default for LegacyMarkdownOptions { + fn default() -> Self { + Self { + files: vec!["README.md".to_owned()], + auto_update: false, + } + } +} + +impl LegacyMarkdownOptions { + pub fn is_empty(&self) -> bool { + self.files.is_empty() + } +} diff --git a/src/api/models/legacy/mod.rs b/src/api/models/legacy/mod.rs new file mode 100644 index 0000000..26ed119 --- /dev/null +++ b/src/api/models/legacy/mod.rs @@ -0,0 +1,22 @@ +mod downloadable; +mod world; +mod clientsidemod; +mod markdown_options; +mod server; +mod server_options; +mod server_type; +mod server_launcher; +mod network; +mod hook; + +pub use downloadable::*; +pub use world::*; +pub use clientsidemod::*; +pub use markdown_options::*; +pub use server::*; +pub use server_options::*; +pub use server_type::*; +pub use server_launcher::*; +pub use network::*; +pub use hook::*; + diff --git a/src/api/models/legacy/network.rs b/src/api/models/legacy/network.rs new file mode 100644 index 0000000..6e9de36 --- /dev/null +++ b/src/api/models/legacy/network.rs @@ -0,0 +1,68 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::api::models::hooks::Hook; + +use super::{LegacyDownloadable, LegacyMarkdownOptions}; + +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(default)] +pub struct LegacyNetwork { + pub name: String, + pub proxy: String, + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub proxy_groups: Vec, + pub port: u16, + pub servers: HashMap, + pub variables: HashMap, + + #[serde(default)] + #[serde(skip_serializing_if = "LegacyMarkdownOptions::is_empty")] + pub markdown: LegacyMarkdownOptions, + + #[serde(default)] + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub hooks: HashMap, + + #[serde(default)] + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub groups: HashMap, +} + +#[derive(Debug, Deserialize, Serialize, Default, Clone)] +#[serde(default)] +pub struct LegacyServerEntry { + pub port: u16, + pub ip_address: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub groups: Vec, +} + +#[derive(Debug, Default, Deserialize, Serialize, Clone)] +#[serde(default)] +pub struct LegacyGroup { + #[serde(default)] + pub variables: HashMap, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub plugins: Vec, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub mods: Vec, +} + +impl Default for LegacyNetwork { + fn default() -> Self { + Self { + name: String::new(), + proxy: "proxy".to_owned(), + proxy_groups: vec![], + port: 25565, + servers: HashMap::new(), + variables: HashMap::new(), + markdown: LegacyMarkdownOptions::default(), + hooks: HashMap::new(), + groups: HashMap::new(), + } + } +} diff --git a/src/api/models/legacy/server.rs b/src/api/models/legacy/server.rs new file mode 100644 index 0000000..ae28c18 --- /dev/null +++ b/src/api/models/legacy/server.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::api::models::{hooks::Hook, launcher::ServerLauncher}; + +use super::{LegacyClientSideMod, LegacyDownloadable, LegacyMarkdownOptions, LegacyServerOptions, LegacyServerType, LegacyWorld}; + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[serde(default)] +pub struct LegacyServer { + pub name: String, + pub mc_version: String, + #[serde(with = "super::server_type")] + pub jar: LegacyServerType, + pub variables: HashMap, + pub launcher: ServerLauncher, + + #[serde(default)] + #[serde(skip_serializing_if = "LegacyMarkdownOptions::is_empty")] + pub markdown: LegacyMarkdownOptions, + + #[serde(default)] + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub hooks: HashMap, + + #[serde(default)] + pub options: LegacyServerOptions, + + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub worlds: HashMap, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub plugins: Vec, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub mods: Vec, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub clientsidemods: Vec, +} + +impl Default for LegacyServer { + fn default() -> Self { + let mut vars = HashMap::new(); + vars.insert("SERVER_PORT".to_owned(), "25565".to_owned()); + Self { + name: String::new(), + mc_version: "latest".to_owned(), + jar: LegacyServerType::Vanilla {}, + variables: vars, + launcher: ServerLauncher::default(), + markdown: LegacyMarkdownOptions::default(), + hooks: HashMap::new(), + options: LegacyServerOptions::default(), + worlds: HashMap::new(), + plugins: vec![], + mods: vec![], + clientsidemods: vec![], + } + } +} diff --git a/src/api/models/legacy/server_launcher.rs b/src/api/models/legacy/server_launcher.rs new file mode 100644 index 0000000..743f690 --- /dev/null +++ b/src/api/models/legacy/server_launcher.rs @@ -0,0 +1,60 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::api::utils::serde::*; + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)] +#[serde(rename_all = "lowercase")] +pub enum LegacyPresetFlags { + Aikars, + Proxy, + #[default] + None, +} + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +#[serde(default)] +pub struct LegacyServerLauncher { + pub eula_args: bool, + + #[serde(skip_serializing_if = "is_default")] + pub nogui: bool, + #[serde(skip_serializing_if = "is_default")] + pub preset_flags: LegacyPresetFlags, + #[serde(skip_serializing_if = "is_default")] + pub disable: bool, + #[serde(skip_serializing_if = "is_default")] + pub jvm_args: String, + #[serde(skip_serializing_if = "is_default")] + pub game_args: String, + #[serde(skip_serializing_if = "is_default")] + pub memory: String, + #[serde(skip_serializing_if = "is_default")] + pub properties: HashMap, + + #[serde(skip_serializing_if = "Vec::is_empty")] + pub prelaunch: Vec, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub postlaunch: Vec, + + pub java_version: Option, +} + +impl Default for LegacyServerLauncher { + fn default() -> Self { + Self { + preset_flags: LegacyPresetFlags::None, + nogui: true, + jvm_args: String::new(), + game_args: String::new(), + disable: false, + eula_args: true, + memory: String::new(), + properties: HashMap::default(), + prelaunch: vec![], + postlaunch: vec![], + java_version: None, + } + } +} diff --git a/src/api/models/legacy/server_options.rs b/src/api/models/legacy/server_options.rs new file mode 100644 index 0000000..6b1b0f9 --- /dev/null +++ b/src/api/models/legacy/server_options.rs @@ -0,0 +1,35 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)] +pub struct LegacyServerOptions { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub bootstrap_exts: Vec, + + #[serde( + default = "default_success_line", + skip_serializing_if = "is_default_success_line" + )] + pub success_line: String, + + #[serde( + default = "default_stop_command", + skip_serializing_if = "is_default_stop_command" + )] + pub stop_command: String, +} + +pub fn default_success_line() -> String { + String::from("]: Done") +} + +pub fn is_default_success_line(s: &str) -> bool { + s == default_success_line() +} + +pub fn default_stop_command() -> String { + String::from("stop") +} + +pub fn is_default_stop_command(s: &str) -> bool { + s == default_stop_command() +} diff --git a/src/api/models/legacy/server_type.rs b/src/api/models/legacy/server_type.rs new file mode 100644 index 0000000..7376f28 --- /dev/null +++ b/src/api/models/legacy/server_type.rs @@ -0,0 +1,100 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use crate::api::utils::serde::*; + +use super::LegacyDownloadable; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum LegacyServerType { + Vanilla {}, + + PaperMC { + project: String, + #[serde(default = "str_latest")] + build: String, + }, + + Purpur { + #[serde(default = "str_latest")] + build: String, + }, + + Fabric { + #[serde(default = "str_latest")] + loader: String, + + #[serde(default = "str_latest")] + installer: String, + }, + + Quilt { + #[serde(default = "str_latest")] + loader: String, + + #[serde(default = "str_latest")] + installer: String, + }, + + NeoForge { + #[serde(default = "str_latest")] + loader: String, + }, + + Forge { + #[serde(default = "str_latest")] + loader: String, + }, + + BuildTools { + #[serde(default = "str_spigot")] + software: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default = "Vec::new")] + args: Vec, + }, + + Paper {}, + Velocity {}, + Waterfall {}, + BungeeCord {}, + + Downloadable { + #[serde(flatten)] + inner: LegacyDownloadable, + }, +} + +pub fn str_spigot() -> String { + String::from("spigot") +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum Bridge { + ServerType(LegacyServerType), + Downloadable(LegacyDownloadable), +} + +impl From for LegacyServerType { + fn from(value: Bridge) -> Self { + match value { + Bridge::ServerType(ty) => ty, + Bridge::Downloadable(d) => Self::Downloadable { inner: d }, + } + } +} + +pub fn serialize(st: &LegacyServerType, serializer: S) -> Result +where + S: Serializer, +{ + st.serialize(serializer) +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(LegacyServerType::from(Bridge::deserialize(deserializer)?)) +} + diff --git a/src/api/models/legacy/world.rs b/src/api/models/legacy/world.rs new file mode 100644 index 0000000..fa9302c --- /dev/null +++ b/src/api/models/legacy/world.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +use super::LegacyDownloadable; + +#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq)] +#[serde(default)] +pub struct LegacyWorld { + #[serde(skip_serializing_if = "Vec::is_empty")] + pub datapacks: Vec, + pub download: Option, +} diff --git a/src/api/models/markdown/mod.rs b/src/api/models/markdown/mod.rs index a87408e..12f4d1c 100644 --- a/src/api/models/markdown/mod.rs +++ b/src/api/models/markdown/mod.rs @@ -2,6 +2,8 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use crate::api::utils::serde::*; + pub mod render; #[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq)] @@ -14,10 +16,15 @@ pub enum MarkdownOutput { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct MarkdownOptions { pub files: Vec, + #[serde(skip_serializing_if = "is_default")] pub columns: Vec, + #[serde(skip_serializing_if = "is_default")] pub titles: HashMap, + #[serde(skip_serializing_if = "is_true")] pub name_includes_link: bool, + #[serde(skip_serializing_if = "is_default")] pub sort_by: MdSort, + #[serde(skip_serializing_if = "is_default")] pub output_type: MarkdownOutput, } diff --git a/src/api/models/mod.rs b/src/api/models/mod.rs index f7cd9eb..d01ecc2 100644 --- a/src/api/models/mod.rs +++ b/src/api/models/mod.rs @@ -13,6 +13,8 @@ pub mod unsup; pub mod markdown; pub mod metadata; pub mod launcher; +pub mod legacy; +pub mod hooks; pub use addon::*; pub use env::*; diff --git a/src/api/models/server/mod.rs b/src/api/models/server/mod.rs index 9e2a799..cb3c0be 100644 --- a/src/api/models/server/mod.rs +++ b/src/api/models/server/mod.rs @@ -19,16 +19,18 @@ pub struct Server { pub port: Option, pub jar: Option, - #[serde(default)] - pub markdown: MarkdownOptions, - #[serde(default)] - pub launcher: ServerLauncher, #[serde(default = "Vec::new")] pub sources: Vec, - + #[serde(default = "HashMap::new")] pub variables: HashMap, + + #[serde(default)] + pub markdown: MarkdownOptions, + + #[serde(default)] + pub launcher: ServerLauncher, } impl Default for Server { diff --git a/src/api/models/source.rs b/src/api/models/source.rs index b4d4d7f..42385c3 100644 --- a/src/api/models/source.rs +++ b/src/api/models/source.rs @@ -4,11 +4,17 @@ 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::toml::read_toml}; +use crate::api::{app::App, models::ModpackSource, utils::{accessor::Accessor, toml::read_toml}}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct Source { + #[serde(flatten)] + pub source_type: SourceType, +} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(tag = "type", rename_all = "lowercase")] -pub enum Source { +pub enum SourceType { File { path: String, }, @@ -25,31 +31,31 @@ pub enum Source { impl Source { pub fn source_name(&self) -> &'static str { - match self { - Source::File { .. } => "File", - Source::Folder { .. } => "Folder", - Source::Modpack { .. } => "Modpack", + match self.source_type { + SourceType::File { .. } => "File", + SourceType::Folder { .. } => "Folder", + SourceType::Modpack { .. } => "Modpack", } } - pub fn source_accessor(&self) -> Result { - match self { - Source::File { path } => Ok(path.clone()), - Source::Folder { path } => Ok(path.clone()), - Source::Modpack { modpack } => Ok(modpack.accessor()?.to_string()), + pub fn accessor(&self) -> Result { + match &self.source_type { + SourceType::File { path } => Ok(Accessor::Local(path.into())), + SourceType::Folder { path } => Ok(Accessor::Local(path.into())), + SourceType::Modpack { modpack } => modpack.accessor(), } } pub fn modpack_type(&self) -> Option { - match self { - Source::Modpack { modpack } => Some(modpack.modpack_type()), + match &self.source_type { + SourceType::Modpack { modpack } => Some(modpack.modpack_type()), _ => None, } } pub async fn resolve_addons(&self, app: &App) -> Result> { - match self { - Source::File { path } => { + match &self.source_type { + SourceType::File { path } => { let file: AddonListFile = read_toml(&PathBuf::from(if path.ends_with(".toml") { path.clone() } else { @@ -58,9 +64,9 @@ impl Source { Ok(file.flatten()) }, - Source::Folder { .. } => Ok(vec![]), + SourceType::Folder { .. } => Ok(vec![]), - Source::Modpack { modpack } => { + SourceType::Modpack { modpack } => { let accessor = modpack.accessor()?; match modpack.modpack_type() { ModpackType::MRPack => resolve_mrpack_addons(app, accessor).await, diff --git a/src/api/utils/serde.rs b/src/api/utils/serde.rs index c0fc91a..3552973 100644 --- a/src/api/utils/serde.rs +++ b/src/api/utils/serde.rs @@ -34,6 +34,14 @@ pub fn str_default() -> String { "default".to_owned() } +pub fn bool_true() -> bool { + true +} + +pub fn is_true(b: &bool) -> bool { + *b +} + pub fn is_default(t: &T) -> bool { t == &T::default() } diff --git a/src/api/utils/toml.rs b/src/api/utils/toml.rs index 19503f0..5cf23c3 100644 --- a/src/api/utils/toml.rs +++ b/src/api/utils/toml.rs @@ -34,7 +34,7 @@ pub fn write_toml(path: &Path, filename: &str, value: &T) -> Resul let content = toml::to_string_pretty(value)?; - let mut file = std::fs::File::open(path.join(filename))?; + let mut file = std::fs::File::create(path.join(filename))?; file.write_all(content.as_bytes())?; Ok(()) diff --git a/src/commands/migrate.rs b/src/commands/migrate.rs new file mode 100644 index 0000000..60f4d73 --- /dev/null +++ b/src/commands/migrate.rs @@ -0,0 +1,124 @@ +use std::{path::PathBuf, sync::Arc}; + +use anyhow::{Context, Result}; + +use crate::api::{app::App, models::{legacy::{LegacyDownloadable, LegacyServer, LegacyServerType}, markdown::MarkdownOptions, server::{PaperMCProject, Server, ServerFlavor, ServerJar, ServerType}, Addon, AddonListFile, AddonTarget, AddonType, Environment, Source, SourceType}, utils::toml::{read_toml, write_toml}}; + +#[derive(clap::Args)] +pub struct Args { + +} + +pub async fn run(_app: Arc, _args: Args) -> Result<()> { + if PathBuf::from("./server.toml").exists() { + println!("Migrating server..."); + migrate_server().await?; + } else if PathBuf::from("./network.toml").exists() { + todo!(); + } else { + println!("No server.toml or network.toml found in current directory to migrate"); + } + + Ok(()) +} + +pub fn migrate_downloadable(downloadable: LegacyDownloadable) -> AddonType { + match downloadable { + LegacyDownloadable::Url { url, .. } => AddonType::Url { url }, + LegacyDownloadable::Modrinth { id, version } => AddonType::Modrinth { id, version }, + LegacyDownloadable::CurseRinth { id, version } => AddonType::Curseforge { id, version }, + LegacyDownloadable::Spigot { id, version } => AddonType::Spigot { id, version }, + LegacyDownloadable::Hangar { id, version } => AddonType::Hangar { id, version }, + LegacyDownloadable::GithubRelease { repo, tag, asset } => AddonType::GithubRelease { repo, version: tag, filename: asset }, + LegacyDownloadable::Jenkins { url, job, build, artifact } => AddonType::Jenkins { url, job, build, artifact }, + LegacyDownloadable::Maven { url, group, artifact, version, filename } => AddonType::MavenArtifact { url, group, artifact, version, filename }, + } +} + +pub async fn migrate_server() -> Result<()> { + let legacy_server = read_toml::(&PathBuf::from("./server.toml")) + .with_context(|| "Reading server.toml")?; + + let mut addons: Vec = vec![]; + + for plugin in legacy_server.plugins { + addons.push(Addon { + addon_type: migrate_downloadable(plugin), + environment: None, + target: AddonTarget::Plugins, + }); + } + for m in legacy_server.mods { + addons.push(Addon { + addon_type: migrate_downloadable(m), + environment: None, + target: AddonTarget::Mods, + }); + } + for cmod in legacy_server.clientsidemods { + addons.push(Addon { + addon_type: migrate_downloadable(cmod.dl), + environment: Some(Environment::Client), + target: AddonTarget::Mods, + }); + } + + let file = AddonListFile { + addons, + ..Default::default() + }; + + write_toml(&std::env::current_dir()?, "addons.toml", &file) + .with_context(|| format!("Writing addons.toml"))?; + + let source = Source { + source_type: SourceType::File { path: String::from("./addons.toml") } + }; + + let server = Server { + name: legacy_server.name, + port: None, + launcher: legacy_server.launcher, + markdown: MarkdownOptions { + files: legacy_server.markdown.files, + ..Default::default() + }, + variables: legacy_server.variables, + sources: vec![source], + jar: Some(ServerJar { + mc_version: legacy_server.mc_version, + server_type: match legacy_server.jar { + LegacyServerType::Vanilla { } => ServerType::Vanilla { }, + LegacyServerType::PaperMC { project, build } => ServerType::PaperMC { project: serde_json::from_value::(serde_json::Value::String(project))?, build }, + LegacyServerType::Purpur { build } => ServerType::Purpur { build }, + LegacyServerType::Fabric { loader, installer } => ServerType::Fabric { loader, installer }, + LegacyServerType::Quilt { loader, installer } => ServerType::Quilt { loader, installer }, + LegacyServerType::NeoForge { loader } => ServerType::NeoForge { loader }, + LegacyServerType::Forge { loader } => ServerType::Forge { loader }, + LegacyServerType::BuildTools { software, args } => ServerType::BuildTools { craftbukkit: software == "craftbukkit", args }, + LegacyServerType::Paper { } => ServerType::PaperMC { project: PaperMCProject::Paper, build: String::from("latest") }, + LegacyServerType::Velocity { } => ServerType::PaperMC { project: PaperMCProject::Velocity, build: String::from("latest") }, + LegacyServerType::Waterfall { } => ServerType::PaperMC { project: PaperMCProject::Waterfall, build: String::from("latest") }, + LegacyServerType::BungeeCord { } => todo!(), + LegacyServerType::Downloadable { inner } => { + let flavor = ServerFlavor::Patched; + + ServerType::Custom { + inner: migrate_downloadable(inner), + flavor, + exec: None + } + }, + }, + }), + }; + + std::fs::copy("./server.toml", "./__v1__server.toml")?; + + write_toml(&std::env::current_dir()?, "server.toml", &server) + .with_context(|| format!("Writing server.toml"))?; + + println!("Migrated! You may now delete the backup file if there are no issues."); + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index c3bf353..baf80a8 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -3,3 +3,4 @@ pub mod init; pub mod sources; pub mod java; pub mod markdown; +pub mod migrate; diff --git a/src/main.rs b/src/main.rs index 12bbf37..3bcc86c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,7 @@ enum Commands { Java(commands::java::Commands), #[command(alias = "md", subcommand)] Markdown(commands::markdown::Commands), + Migrate(commands::migrate::Args), } #[tokio::main] @@ -41,5 +42,6 @@ async fn main() -> Result<()> { Commands::Build(args) => commands::build::run(app, args).await, Commands::Java(args) => commands::java::run(app, args).await, Commands::Markdown(args) => commands::markdown::run(app, args).await, + Commands::Migrate(args) => commands::migrate::run(app, args).await, } }