diff options
| author | 2025-02-02 01:54:47 -0600 | |
|---|---|---|
| committer | 2025-02-02 01:54:47 -0600 | |
| commit | 7d4330e1b7596249cc0c74ff498945bd13e68d28 (patch) | |
| tree | 8506165fbb2d31089a822690e5eccc804a89d4f3 | |
| parent | restructure project (diff) | |
random ahh changes before I make the actual thing
| -rw-r--r-- | Cargo.lock | 22 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | ozone-cli/src/main.rs | 2 | ||||
| -rw-r--r-- | ozone-helpers/src/lib.rs | 10 | ||||
| -rw-r--r-- | ozone/Cargo.toml | 2 | ||||
| -rw-r--r-- | ozone/src/auth.rs | 4 | ||||
| -rw-r--r-- | ozone/src/auth/msa.rs | 1 | ||||
| -rw-r--r-- | ozone/src/launcher.rs | 44 | ||||
| -rw-r--r-- | ozone/src/launcher/jre/manifest.rs | 8 | ||||
| -rw-r--r-- | ozone/src/launcher/rules.rs | 10 | ||||
| -rw-r--r-- | ozone/src/launcher/runner.rs | 34 | ||||
| -rw-r--r-- | ozone/src/launcher/settings.rs | 17 | ||||
| -rw-r--r-- | ozone/src/util.rs | 14 | ||||
| -rw-r--r-- | ozone/src/version.rs | 4 | ||||
| -rw-r--r-- | ozone/src/version/manifest.rs | 61 |
15 files changed, 76 insertions, 158 deletions
@@ -843,26 +843,6 @@ dependencies = [ ] [[package]] -name = "const_format" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] name = "copypasta" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3378,7 +3358,6 @@ version = "0.1.0" dependencies = [ "cfg-if", "chrono", - "const_format", "futures", "indexmap", "lazy_static", @@ -3386,6 +3365,7 @@ dependencies = [ "lzma-rs", "multimap", "oauth2", + "ozone-helpers", "regex", "reqwest", "serde", @@ -1,2 +1,3 @@ [workspace] +resolver = "2" members = [ "ozone", "ozone-cli", "ozone-helpers", "ozone-ui" ] diff --git a/ozone-cli/src/main.rs b/ozone-cli/src/main.rs index a27cc98..f94fbe6 100644 --- a/ozone-cli/src/main.rs +++ b/ozone-cli/src/main.rs @@ -12,6 +12,8 @@ async fn main() -> Result<(), Box<dyn Error>> { info!("stuff: {:?} {:?} {:?} {:?} {:?}", System::name(), System::os_version(), System::long_os_version(), System::kernel_version(), System::cpu_arch()); info!("stuff: {:?} {:?} {:?} {}", System::distribution_id(), OS, ARCH, size_of::<*const i32>()); + + let settings = Settings::load("./work/ozone.json").await?; settings.save().await?; diff --git a/ozone-helpers/src/lib.rs b/ozone-helpers/src/lib.rs index 4412282..9a19422 100644 --- a/ozone-helpers/src/lib.rs +++ b/ozone-helpers/src/lib.rs @@ -1,8 +1,8 @@ extern crate proc_macro; -use proc_macro::TokenStream; +use proc_macro::{Literal, TokenStream, TokenTree}; -#[proc_macro_attribute] -pub fn ozone_special(attr: TokenStream, item: TokenStream) -> TokenStream { - todo!() -}
\ No newline at end of file +#[proc_macro] +pub fn ozone_arch_bits(_input: TokenStream) -> TokenStream { + TokenTree::from(Literal::string(&usize::BITS.to_string())).into() +} diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index cd2f852..a6fbb23 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] +ozone-helpers = { path = "../ozone-helpers" } cfg-if = "1.0.0" chrono = { version = "0.4.39", default-features = false, features = ["std", "alloc", "clock", "now", "serde"] } -const_format = "0.2.34" futures = "0.3.31" indexmap = { version = "2.7.1", features = ["serde"] } lazy_static = "1.5.0" diff --git a/ozone/src/auth.rs b/ozone/src/auth.rs index 057cceb..712bd4d 100644 --- a/ozone/src/auth.rs +++ b/ozone/src/auth.rs @@ -290,7 +290,7 @@ mod test { async fn abc() { simple_logger::SimpleLogger::new().with_colors(true).with_level(log::LevelFilter::Trace).init().unwrap(); - let mut user = match tokio::fs::read_to_string("test_stuff/test.json").await { + let mut user = match tokio::fs::read_to_string("../test_stuff/test.json").await { Ok(s) => serde_json::from_str::<MsaUser>(&s).unwrap(), Err(e) if e.kind() == tokio::io::ErrorKind::NotFound => { MsaUser { @@ -331,6 +331,6 @@ mod test { debug!("User: {user:?}"); let user_str = serde_json::to_string_pretty(&user).unwrap(); - tokio::fs::write("test_stuff/test.json", user_str.as_str()).await.unwrap(); + tokio::fs::write("../test_stuff/test.json", user_str.as_str()).await.unwrap(); } } diff --git a/ozone/src/auth/msa.rs b/ozone/src/auth/msa.rs index add345c..ac4a75b 100644 --- a/ozone/src/auth/msa.rs +++ b/ozone/src/auth/msa.rs @@ -5,7 +5,6 @@ use log::debug; use oauth2::AccessToken; use reqwest::{Method}; use serde::{Deserialize, Serialize}; -use uuid::Uuid; use crate::auth::AuthError; use crate::auth::types::Token; diff --git a/ozone/src/launcher.rs b/ozone/src/launcher.rs index 2836db5..d1fbd04 100644 --- a/ozone/src/launcher.rs +++ b/ozone/src/launcher.rs @@ -21,7 +21,6 @@ use std::path::{Component, Path, PathBuf}; use std::{env, process}; use std::env::JoinPathsError; use std::time::{Instant, SystemTime, UNIX_EPOCH}; -use const_format::formatcp; use futures::{StreamExt, TryStreamExt}; use indexmap::IndexMap; use log::{debug, info, trace, warn}; @@ -35,6 +34,7 @@ use version::{VersionList, VersionResolveError, VersionResult}; use crate::version::{Library, OSRestriction, OperatingSystem, DownloadType, LibraryExtractRule, FeatureMatcher, ClientLogging}; use assets::{AssetError, AssetRepository}; +use ozone_helpers::ozone_arch_bits; use crate::util::{self, AsJavaPath}; pub use settings::*; @@ -208,7 +208,9 @@ struct LaunchInfo<'l, F: FeatureMatcher> { client_jar: Option<PathBuf>, version_id: String, version_type: Option<VersionType>, - asset_index: Option<AssetIndex> + asset_index: Option<AssetIndex>, + + resolution: Option<Resolution> } #[derive(Debug)] @@ -218,7 +220,7 @@ pub struct Launch { main_class: String, instance_path: PathBuf, runtime_path: PathBuf, - runtime_legacy_launch: bool + jni_launch: bool } struct ProfileFeatureMatcher<'prof> { @@ -499,7 +501,7 @@ impl Launcher { runtime_path = self.java_runtimes.ensure_jre(java_ver.component.as_str(), runtime).await.map_err(LaunchError::JavaRuntimeRepo)?; } - let Some(runtime_exe_path) = runner::find_java(runtime_path.as_path(), profile.is_legacy_launch()).await + let Some(runtime_exe_path) = runner::find_java(runtime_path.as_path(), profile.is_jni_launch()).await .map_err(|e| LaunchError::ResolveJavaRuntime {what: "finding java executable", error: e})? else { return Err(LaunchError::MissingJavaRuntime); }; @@ -520,7 +522,9 @@ impl Launcher { client_jar: client_jar_path, version_id: ver.id.to_string(), version_type: ver.version_type.clone(), - asset_index: asset_idx + asset_index: asset_idx, + + resolution: profile.get_resolution() }; let Some(ref main_class) = ver.main_class else { @@ -542,43 +546,17 @@ impl Launcher { main_class: main_class.to_string(), instance_path: inst_home, runtime_path: runtime_exe_path, - runtime_legacy_launch: profile.is_legacy_launch() + jni_launch: profile.is_jni_launch() }) } } #[derive(Debug)] -enum LibraryError { - InvalidName(String), - IO { what: &'static str, error: io::Error } -} - -impl Display for LibraryError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - LibraryError::InvalidName(name) => write!(f, "invalid name: {name}"), - LibraryError::IO { what, error } => write!(f, "library i/o error ({what}): {error}"), - } - } -} - -impl Error for LibraryError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - LibraryError::IO { error, .. } => Some(error), - _ => None - } - } -} - -#[derive(Debug)] struct LibraryExtractJob { source: PathBuf, rule: Option<LibraryExtractRule> } -const ARCH_BITS: &str = formatcp!("{}", usize::BITS); - impl LibraryRepository { fn get_artifact_base_dir(name: &str) -> Option<PathBuf> { let end_of_gid = name.find(':')?; @@ -593,7 +571,7 @@ impl LibraryRepository { impl SubFunc<'static> for LibReplace { fn substitute(&self, key: &str) -> Option<Cow<'static, str>> { match key { - "arch" => Some(Cow::Borrowed(ARCH_BITS)), + "arch" => Some(Cow::Borrowed(ozone_arch_bits!())), _ => None } } diff --git a/ozone/src/launcher/jre/manifest.rs b/ozone/src/launcher/jre/manifest.rs index 3fd6484..6848fcd 100644 --- a/ozone/src/launcher/jre/manifest.rs +++ b/ozone/src/launcher/jre/manifest.rs @@ -5,22 +5,14 @@ use crate::version::DownloadInfo; #[derive(Debug, Deserialize)] pub struct Availability { - pub group: u32, // unknown meaning pub progress: u32 // unknown meaning } #[derive(Debug, Deserialize)] -pub struct Version { - pub name: String, - pub version: String -} - -#[derive(Debug, Deserialize)] pub struct JavaRuntimeInfo { // I don't see how half of this information is useful with how the JRE system currently functions -figboot pub availability: Availability, pub manifest: DownloadInfo, - //pub version: Version } pub type JavaRuntimesManifest = HashMap<String, HashMap<String, Vec<JavaRuntimeInfo>>>; diff --git a/ozone/src/launcher/rules.rs b/ozone/src/launcher/rules.rs index 29a36d1..88bed09 100644 --- a/ozone/src/launcher/rules.rs +++ b/ozone/src/launcher/rules.rs @@ -35,14 +35,8 @@ pub trait CompatCheck: seal::CompatCheckInner { let Some(rules) = self.get_rules() else { return Ok(()) }; let mut action = RuleAction::Disallow; - fn match_os(os: &OSRestriction, system: &SystemInfo) -> bool { - os.os.is_none_or(|o| system.is_our_os(o)) - && os.version.as_ref().is_none_or(|v| v.is_match(system.os_version.as_str())) - && os.arch.as_ref().is_none_or(|a| a.is_match(system.arch.as_str())) - } - for rule in rules { - if rule.os.as_ref().is_none_or(|o| match_os(o, system)) + if rule.os.as_ref().is_none_or(|o| system.applies(o)) && rule.features_match(feature_matcher) { action = rule.action; } @@ -111,4 +105,4 @@ impl seal::CompatCheckInner for Argument { impl CompatCheck for CompatibilityRule {} impl CompatCheck for CompleteVersion {} impl CompatCheck for Library {} -impl CompatCheck for Argument {}
\ No newline at end of file +impl CompatCheck for Argument {} diff --git a/ozone/src/launcher/runner.rs b/ozone/src/launcher/runner.rs index afdfc7f..1dc0761 100644 --- a/ozone/src/launcher/runner.rs +++ b/ozone/src/launcher/runner.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use log::{debug, warn}; use tokio::{fs, io}; -use crate::util::AsJavaPath; +use crate::util::{self, AsJavaPath}; use crate::version::{CompleteVersion, FeatureMatcher, OperatingSystem}; use super::rules::CompatCheck; use super::strsub::{self, SubFunc}; @@ -30,15 +30,15 @@ impl<'rep, F: FeatureMatcher> SubFunc<'rep> for LaunchArgSub<'rep, '_, F> { "auth_player_name" => Some(Cow::Borrowed("Player")), // TODO "auth_session" => Some(Cow::Borrowed("-")), // TODO "auth_uuid" => Some(Cow::Borrowed("00000000-0000-0000-0000-000000000000")), // TODO - "auth_xuid" => Some(Cow::Borrowed("00000000-0000-0000-0000-000000000000")), // TODO + "auth_xuid" => Some(Cow::Borrowed("0000000000000000")), // TODO "classpath" => Some(Cow::Borrowed(self.0.classpath.as_str())), "classpath_separator" => Some(Cow::Borrowed(PATH_SEP)), "game_assets" => self.0.virtual_assets_path.as_ref() .map(|s| s.as_path().as_java_path().to_string_lossy()), "game_directory" => Some(self.0.instance_home.as_java_path().to_string_lossy()), "language" => Some(Cow::Borrowed("en-us")), // ??? - "launcher_name" => Some(Cow::Borrowed("ozone (olauncher 3)")), // TODO - "launcher_version" => Some(Cow::Borrowed("yeah")), // TODO + "launcher_name" => Some(Cow::Borrowed(util::NAME)), + "launcher_version" => Some(Cow::Borrowed(util::VERSION)), "library_directory" => Some(self.0.launcher.libraries.home.as_java_path().to_string_lossy()), "natives_directory" => Some(self.0.natives_path.as_java_path().to_string_lossy()), "primary_jar" => self.0.client_jar.as_ref().map(|p| p.as_path().as_java_path().to_string_lossy()), @@ -46,13 +46,13 @@ impl<'rep, F: FeatureMatcher> SubFunc<'rep> for LaunchArgSub<'rep, '_, F> { "quickPlayPath" => None, // TODO "quickPlayRealms" => None, // TODO "quickPlaySingleplayer" => None, // TODO - "resolution_height" => None, // TODO - "resolution_width" => None, // TODO + "resolution_height" => self.0.resolution.map(|r| Cow::Owned(r.height.to_string())), + "resolution_width" => self.0.resolution.map(|r| Cow::Owned(r.width.to_string())), "user_properties" => Some(Cow::Borrowed("{}")), // TODO "user_property_map" => Some(Cow::Borrowed("[]")), // TODO "user_type" => Some(Cow::Borrowed("legacy")), // TODO "version_name" => Some(Cow::Borrowed(self.0.version_id.as_ref())), - "version_type" => self.0.version_type.as_ref().map(|s| Cow::Borrowed(s.to_str())), + "version_type" => self.0.version_type.as_ref().map(|s| Cow::Borrowed(s.as_ref())), _ => { if let Some(asset_key) = key.strip_prefix("asset=") { return self.0.asset_index.as_ref().and_then(|idx| idx.objects.get(asset_key)) @@ -122,15 +122,15 @@ pub fn build_arguments<F: FeatureMatcher>(launch: &LaunchInfo<'_, F>, version: & } pub fn run_the_game(launch: &Launch) -> Result<(), Box<dyn std::error::Error>> { - if launch.runtime_legacy_launch { + if launch.jni_launch { + todo!("jni launch not supported :(") + } else { Command::new(launch.runtime_path.as_path().as_java_path()) .args(launch.jvm_args.iter() .map(|o| o.as_os_str()) .chain(iter::once(OsStr::new(launch.main_class.as_str()))) .chain(launch.game_args.iter().map(|o| o.as_os_str()))) .current_dir(launch.instance_path.as_path().as_java_path()).spawn()?.wait()?; - } else { - todo!("jni launch not supported :(") } Ok(()) @@ -173,11 +173,11 @@ use self::macos::*; #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] use self::unknown::*; -fn search_java_sync(base: impl AsRef<Path>, legacy: bool) -> Result<Option<PathBuf>, io::Error> { +fn search_java_sync(base: impl AsRef<Path>, jni: bool) -> Result<Option<PathBuf>, io::Error> { assert!(JRE_PLATFORM_KNOWN); - let search_path = Path::new(match legacy { - true => JAVA_SEARCH_PATH, - _ => JNI_SEARCH_PATH + let search_path = Path::new(match jni { + true => JNI_SEARCH_PATH, + _ => JAVA_SEARCH_PATH }.unwrap()); let walker = walkdir::WalkDir::new(base.as_ref()).into_iter() @@ -198,7 +198,7 @@ fn search_java_sync(base: impl AsRef<Path>, legacy: bool) -> Result<Option<PathB } //noinspection RsConstantConditionIf -pub async fn find_java(base: impl AsRef<Path>, legacy: bool) -> Result<Option<PathBuf>, io::Error> { +pub async fn find_java(base: impl AsRef<Path>, jni: bool) -> Result<Option<PathBuf>, io::Error> { let meta = fs::metadata(&base).await?; if meta.is_dir() { // do search if !JRE_PLATFORM_KNOWN { @@ -210,13 +210,13 @@ pub async fn find_java(base: impl AsRef<Path>, legacy: bool) -> Result<Option<Pa let base = base.as_ref().to_path_buf(); // idc tokio::task::spawn_blocking(move || { - let res = search_java_sync(base, legacy); + let res = search_java_sync(base, jni); let _ = tx.send(res); // I really don't care if the reader hung up }).await.expect("jre search panicked"); rx.await.expect("jre search didn't send us a result") } else { // we are pointed directly at a file. assume it's what we want - debug!("JRE path {} is a file ({}). Assuming it's what we want.", base.as_ref().display(), legacy); + debug!("JRE path {} is a file ({}). Assuming it's what we want.", base.as_ref().display(), jni); Ok(Some(base.as_ref().to_path_buf())) } } diff --git a/ozone/src/launcher/settings.rs b/ozone/src/launcher/settings.rs index 8453653..6295433 100644 --- a/ozone/src/launcher/settings.rs +++ b/ozone/src/launcher/settings.rs @@ -142,8 +142,8 @@ pub enum ProfileVersion { #[derive(Deserialize, Serialize, Debug, Clone, Copy)] pub struct Resolution { - width: u32, - height: u32 + pub width: u32, + pub height: u32 } impl Default for Resolution { @@ -158,11 +158,12 @@ pub struct Profile { java_runtime: Option<String>, instance: String, - #[serde(default)] + #[serde(default, skip_serializing_if = "Vec::is_empty")] jvm_arguments: Vec<String>, - #[serde(default)] - legacy_launch: bool, + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + jni_launch: bool, + #[serde(skip_serializing_if = "Option::is_none")] resolution: Option<Resolution> } @@ -201,7 +202,7 @@ impl Profile { java_runtime: None, instance: instance_name.into(), jvm_arguments: DEF_JVM_ARGUMENTS.iter().map(|s| String::from(*s)).collect(), - legacy_launch: false, + jni_launch: false, resolution: None } } @@ -226,7 +227,7 @@ impl Profile { self.java_runtime.as_ref() } - pub fn is_legacy_launch(&self) -> bool { - self.legacy_launch + pub fn is_jni_launch(&self) -> bool { + self.jni_launch } } diff --git a/ozone/src/util.rs b/ozone/src/util.rs index 7510a33..0444454 100644 --- a/ozone/src/util.rs +++ b/ozone/src/util.rs @@ -4,18 +4,15 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use std::io::ErrorKind; use std::path::{Component, Path, PathBuf}; -use const_format::formatcp; use log::{debug, info, warn}; use sha1_smol::{Digest, Sha1}; use tokio::fs::File; use tokio::{fs, io}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -const PKG_NAME: &str = env!("CARGO_PKG_NAME"); -const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -const CRATE_NAME: &str = env!("CARGO_CRATE_NAME"); - -pub const USER_AGENT: &str = formatcp!("{PKG_NAME}/{PKG_VERSION} (in {CRATE_NAME})"); +pub const NAME: &str = env!("CARGO_PKG_NAME"); +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); #[derive(Debug)] pub enum IntegrityError { @@ -331,4 +328,9 @@ mod tests { _ => panic!("path does not begin with prefix") } } + + #[test] + fn show_ua() { + dbg!(USER_AGENT); + } } diff --git a/ozone/src/version.rs b/ozone/src/version.rs index ed5f95f..27c2a6d 100644 --- a/ozone/src/version.rs +++ b/ozone/src/version.rs @@ -463,7 +463,7 @@ mod tests { #[test] fn test_it() { - let s = fs::read_to_string("test_stuff/versions/1.7.10.json"); + let s = fs::read_to_string("../test_stuff/versions/1.7.10.json"); let arg: CompleteVersion = serde_json::from_str(s.unwrap().as_str()).unwrap(); dbg!(arg); @@ -471,7 +471,7 @@ mod tests { #[test] fn test_it2() { - let s = fs::read_to_string("test_stuff/version_manifest_v2.json"); + let s = fs::read_to_string("../test_stuff/version_manifest_v2.json"); let arg: VersionManifest = serde_json::from_str(s.unwrap().as_str()).unwrap(); dbg!(arg); diff --git a/ozone/src/version/manifest.rs b/ozone/src/version/manifest.rs index b2b8524..28d4444 100644 --- a/ozone/src/version/manifest.rs +++ b/ozone/src/version/manifest.rs @@ -1,8 +1,7 @@ use core::fmt; -use std::convert::Infallible; -use std::str::FromStr; +use std::fmt::{Display, Formatter}; use chrono::{DateTime, Utc}; -use serde::{de::Visitor, Deserialize}; +use serde::Deserialize; use sha1_smol::Digest; #[derive(Deserialize, Debug)] @@ -11,62 +10,32 @@ pub struct LatestVersions { pub snapshot: String } -#[derive(Debug, Clone)] +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] pub enum VersionType { Snapshot, Release, OldBeta, OldAlpha, + #[serde(untagged)] Other(String) } -impl FromStr for VersionType { - type Err = Infallible; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "snapshot" => Ok(Self::Snapshot), - "release" => Ok(Self::Release), - "old_beta" => Ok(Self::OldBeta), - "old_alpha" => Ok(Self::OldAlpha), - _ => Ok(Self::Other(s.to_owned())) - } - } -} - -impl VersionType { - pub fn to_str(&self) -> &str { +impl AsRef<str> for VersionType { + fn as_ref(&self) -> &str { match self { - Self::Snapshot => "snapshot", - Self::Release => "release", - Self::OldBeta => "old_beta", - Self::OldAlpha => "old_alpha", - Self::Other(s) => s + VersionType::Snapshot => "snapshot", + VersionType::Release => "release", + VersionType::OldBeta => "old_beta", + VersionType::OldAlpha => "old_alpha", + VersionType::Other(s) => s } } } -struct VersionTypeVisitor; - -impl Visitor<'_> for VersionTypeVisitor { - type Value = VersionType; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a Minecraft release type") - } - - fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> - where - E: serde::de::Error, { - Ok(VersionType::from_str(v).unwrap(/* infallible */)) - } -} - -impl<'de> Deserialize<'de> for VersionType { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: serde::Deserializer<'de> { - deserializer.deserialize_string(VersionTypeVisitor) +impl Display for VersionType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) } } |
