From f49dc71e9d45a360fa4e82781a77941146e5850b Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Sat, 7 Dec 2024 15:00:38 +0100 Subject: [PATCH] feat: SkyClient Part 1 --- apps/frontend/src/assets/logos/skyclient.png | Bin 0 -> 751 bytes .../ui/components/content/ProviderIcon.tsx | 2 + .../ui/components/content/SearchResults.tsx | 18 +- .../src/ui/pages/browser/BrowserPackage.tsx | 62 ++-- .../src/ui/pages/browser/BrowserRoot.tsx | 34 ++- apps/frontend/src/utils/browser.ts | 1 + apps/frontend/src/utils/index.ts | 3 +- packages/client/src/bindings.ts | 6 +- .../src/api/package/content/curseforge.rs | 9 +- packages/core/src/api/package/content/mod.rs | 49 +-- .../core/src/api/package/content/modrinth.rs | 4 +- .../core/src/api/package/content/skyclient.rs | 288 ++++++++++++++++++ packages/core/src/constants.rs | 2 + packages/core/src/store/clusters.rs | 7 +- packages/core/src/store/package.rs | 10 +- 15 files changed, 410 insertions(+), 85 deletions(-) create mode 100644 apps/frontend/src/assets/logos/skyclient.png create mode 100644 packages/core/src/api/package/content/skyclient.rs diff --git a/apps/frontend/src/assets/logos/skyclient.png b/apps/frontend/src/assets/logos/skyclient.png new file mode 100644 index 0000000000000000000000000000000000000000..1879366c07be1bc7092dc6db23dd5afa0a84e3ea GIT binary patch literal 751 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!Ysfi#1%;Y|NnpD?M%5n z#mWZr-UD(t3p^r=85sBugD~Uq{1qucLG}_)Usv{LOib)N z?62ps{sZcn?CIhdVsW~5^214o6*yW%JEUB9R{x*Am(_Ru-{AB)Za*TXU0G5h7I^OE z=~G7(>;!+E*?dB6T~L`1-^AL$)K0afTZLTGR=xijb(Hgb()K9?l&x>g;d<`LAi>`PaI}+F5u%1ET z{Kbh$d~L~mj2hj{BA*z49Fun5%;4Z~@oy-fw@l@qPjy_J^{f&T54Y@I;OzQ_QRzYF z?(dG#bD5vFbO~_CCx5y3o_zv?Lxs7;=i--(Uw&cyHsi!{%jq4G+cr%x>w4AFu&Uz} z&jGQd%Q7$4-dT0(hE?cU#@S{cc@j2#HGH>H(7{Fb-frC~tKD|=8-<0qd|=Gcvu02c zJD0)jXK2t;WrRpye?_N7qw>55h)!LlP>NgqAg~yyL`&1`* zv5@(1OzoQI%s)E2c@Bx`usrzs^iI8f%L414wp>4(>=r$Ibj!s#{q@s#`SG=*7zg7M*OfVGIHO$->3XCz;64!{5l*E!$tK_0oAjM#0U}U6g074-K v=2nJ=R)%KU1_o9J2D)>(AE9W-%}>cptHiA#=D_ASpaup{S3j3^P6, string | Component> = { modrinth: ModrinthImage, curseforge: CurseforgeIcon, + skyclient: SkyClientImage, }; return mapping[providerName]; diff --git a/apps/frontend/src/ui/components/content/SearchResults.tsx b/apps/frontend/src/ui/components/content/SearchResults.tsx index a8c5417c..2ec4eb45 100644 --- a/apps/frontend/src/ui/components/content/SearchResults.tsx +++ b/apps/frontend/src/ui/components/content/SearchResults.tsx @@ -160,16 +160,18 @@ function PackageItem(props: SearchResult & { provider: Providers; row?: boolean

{props.description}

-
- - {abbreviateNumber(props.downloads)} -
- - 0}> +
- - {abbreviateNumber(props.follows)} + + {abbreviateNumber(props.downloads)}
+ + 0}> +
+ + {abbreviateNumber(props.follows)} +
+
diff --git a/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx b/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx index 9f0a4ae9..6e07fbfe 100644 --- a/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx +++ b/apps/frontend/src/ui/pages/browser/BrowserPackage.tsx @@ -137,8 +137,8 @@ export default BrowserPackage; function BrowserSidebar(props: { package: ManagedPackage }) { const [authors] = useCommand(() => props.package, () => bridge.commands.getProviderAuthors(props.package.provider, props.package.author)); - const createdAt = () => new Date(props.package.created); - const updatedAt = () => new Date(props.package.updated); + const createdAt = createMemo(() => props.package.created ? new Date(props.package.created) : null); + const updatedAt = createMemo(() => props.package.updated ? new Date(props.package.updated) : null); const promptOpen = usePromptOpener(); return ( @@ -166,16 +166,18 @@ function BrowserSidebar(props: { package: ManagedPackage }) {

{props.package.description}

-
- - {abbreviateNumber(props.package.downloads)} -
- - 0}> +
- - {abbreviateNumber(props.package.followers)} + + {abbreviateNumber(props.package.downloads)}
+ + 0}> +
+ + {abbreviateNumber(props.package.followers)} +
+
@@ -235,25 +237,29 @@ function BrowserSidebar(props: { package: ManagedPackage }) { - -
- - Created - - {formatAsRelative(createdAt().getTime(), 'en', 'long')} - -
-
+ + +
+ + Created + + {formatAsRelative(createdAt()!.getTime(), 'en', 'long')} + +
+
+
- -
- - Last Updated - - {formatAsRelative(updatedAt().getTime(), 'en', 'long')} - -
-
+ + +
+ + Last Updated + + {formatAsRelative(updatedAt()!.getTime(), 'en', 'long')} + +
+
+
diff --git a/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx b/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx index 58add3b2..e03fc5ae 100644 --- a/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx +++ b/apps/frontend/src/ui/pages/browser/BrowserRoot.tsx @@ -7,7 +7,7 @@ import ProviderIcon from '~ui/components/content/ProviderIcon'; import useBrowser from '~ui/hooks/useBrowser'; import { PROVIDERS } from '~utils'; import { browserCategories } from '~utils/browser'; -import { For, type JSX, type ParentProps } from 'solid-js'; +import { createMemo, For, type JSX, type ParentProps, Show } from 'solid-js'; import BrowserMain from './BrowserMain'; import BrowserPackage from './BrowserPackage'; import BrowserSearch from './BrowserSearch'; @@ -87,23 +87,29 @@ function BrowserCategories() { browser.search(); }; + const categories = createMemo(() => { + return browserCategories.byPackageType(browser.packageType(), browser.searchQuery().provider); + }); + return (
-
-
Categories
- - {category => ( -

toggleCategory(category.id)} - > - {category.display} -

- )} -
-
+ 0}> +
+
Categories
+ + {category => ( +

toggleCategory(category.id)} + > + {category.display} +

+ )} +
+
+
); diff --git a/apps/frontend/src/utils/browser.ts b/apps/frontend/src/utils/browser.ts index af4adf28..db4fbdae 100644 --- a/apps/frontend/src/utils/browser.ts +++ b/apps/frontend/src/utils/browser.ts @@ -93,4 +93,5 @@ const modMapping: Record = { { display: 'Utility', id: 'utility' }, { display: 'World Generation', id: 'worldgen' }, ], + SkyClient: [], }; diff --git a/apps/frontend/src/utils/index.ts b/apps/frontend/src/utils/index.ts index 186144de..f1775136 100644 --- a/apps/frontend/src/utils/index.ts +++ b/apps/frontend/src/utils/index.ts @@ -174,13 +174,14 @@ export function getPackageUrl(pkg: ManagedPackage): string { return `https://www.curseforge.com/minecraft/${packageTypeMapping[pkg.package_type]}/${pkg.main}`; }, + SkyClient: () => 'TODO', }; return mapping[pkg.provider](); } export const LOADERS: Loader[] = ['vanilla', 'fabric', 'forge', 'neoforge', 'quilt'] as const; -export const PROVIDERS: Providers[] = ['Modrinth', 'Curseforge'] as const; +export const PROVIDERS: Providers[] = ['Modrinth', 'Curseforge', 'SkyClient'] as const; export const PACKAGE_TYPES: PackageType[] = ['mod', 'resourcepack', 'datapack', 'shaderpack'] as const; export const LAUNCHER_IMPORT_TYPES: ImportType[] = [ 'PrismLauncher', diff --git a/packages/client/src/bindings.ts b/packages/client/src/bindings.ts index c04e5716..6ff12a38 100644 --- a/packages/client/src/bindings.ts +++ b/packages/client/src/bindings.ts @@ -857,7 +857,7 @@ export interface ManagedDependency { version_id: string | null; package_id: stri /** * Universal metadata for any managed package from a Mod distribution platform. */ -export interface ManagedPackage { provider: Providers; id: string; package_type: PackageType; title: string; description: string; body: PackageBody; main: string; versions: string[]; game_versions: string[]; loaders: Loader[]; icon_url: string | null; created: string; updated: string; client: PackageSide; server: PackageSide; downloads: bigint; followers: number; categories: string[]; optional_categories: string[] | null; license: License | null; author: Author; is_archived: boolean }; +export interface ManagedPackage { provider: Providers; id: string; package_type: PackageType; title: string; description: string; body: PackageBody; main: string; versions: string[]; game_versions: string[]; loaders: Loader[]; icon_url: string | null; created: string | null; updated: string | null; client: PackageSide; server: PackageSide; downloads: bigint; followers: number; categories: string[]; optional_categories: string[] | null; license: License | null; author: Author; is_archived: boolean }; /** * Universal metadata for any managed user from a Mod distribution platform. */ @@ -989,12 +989,12 @@ export interface ProviderSearchResults { provider: Providers; results: SearchRes /** * Providers for content packages */ -export type Providers = 'Modrinth' | 'Curseforge'; +export type Providers = 'Modrinth' | 'Curseforge' | 'SkyClient'; /** * Global Minecraft resolution. */ export type Resolution = [number, number]; -export interface SearchResult { slug: string; title: string; description: string; categories?: string[]; client_side: PackageSide; server_side: PackageSide; project_type: PackageType; downloads: bigint; icon_url?: string; project_id: string; author: string; display_categories?: string[]; versions: string[]; follows: number; date_created: string; date_modified: string }; +export interface SearchResult { slug: string; title: string; description: string; categories?: string[]; client_side: PackageSide; server_side: PackageSide; project_type: PackageType; downloads: bigint; icon_url?: string; project_id: string; author: string; versions: string[]; follows: number; date_created: string | null; date_modified: string | null }; /** * A global settings state for the launcher. */ diff --git a/packages/core/src/api/package/content/curseforge.rs b/packages/core/src/api/package/content/curseforge.rs index ceba5833..cd55b216 100644 --- a/packages/core/src/api/package/content/curseforge.rs +++ b/packages/core/src/api/package/content/curseforge.rs @@ -146,8 +146,8 @@ impl From for ManagedPackage { game_versions: vec![], loaders: vec![], icon_url: package.logo.and_then(|l| Some(l.url)), - created: package.date_created, - updated: package.date_modified, + created: Some(package.date_created), + updated: Some(package.date_modified), client: crate::store::PackageSide::Unknown, server: crate::store::PackageSide::Unknown, downloads: package.download_count, @@ -193,11 +193,10 @@ impl Into for CurseforgePackage { downloads: self.download_count, icon_url: self.logo.map_or(String::new(), |l| l.url), categories: vec![], // TODO - display_categories: vec![], versions: vec![], follows: self.thumbs_up_count, - date_created: self.date_created, - date_modified: self.date_modified, + date_created: Some(self.date_created), + date_modified: Some(self.date_modified), } } } diff --git a/packages/core/src/api/package/content/mod.rs b/packages/core/src/api/package/content/mod.rs index 7f8ef5d0..8b50d977 100644 --- a/packages/core/src/api/package/content/mod.rs +++ b/packages/core/src/api/package/content/mod.rs @@ -17,6 +17,7 @@ use crate::{Result, State}; mod curseforge; mod modrinth; +mod skyclient; /// Providers for content packages #[cfg_attr(feature = "specta", derive(specta::Type))] @@ -24,6 +25,7 @@ mod modrinth; pub enum Providers { Modrinth, Curseforge, + SkyClient, } impl std::fmt::Display for Providers { @@ -39,6 +41,7 @@ impl Providers { match self { Self::Modrinth => "Modrinth", Self::Curseforge => "Curseforge", + Self::SkyClient => "SkyClient", } } @@ -48,11 +51,12 @@ impl Providers { match self { Self::Modrinth => "https://modrinth.com", Self::Curseforge => "https://curseforge.com", + Self::SkyClient => "https://skyclient.co", } } pub const fn get_providers() -> &'static [Providers] { - &[Self::Modrinth, Self::Curseforge] + &[Self::Modrinth, Self::Curseforge, Self::SkyClient] } #[allow(clippy::too_many_arguments)] @@ -98,35 +102,37 @@ impl Providers { ) .await } + Self::SkyClient => { + skyclient::search( + query, + limit, + offset, + game_versions, + loaders + ).await + }, } } pub async fn get(&self, slug_or_id: &str) -> Result { Ok(match self { Self::Modrinth => modrinth::get(slug_or_id).await?.into(), - Self::Curseforge => curseforge::get( - slug_or_id - .parse::() - .map_err(|err| anyhow::anyhow!(err))?, - ) - .await? - .into(), + Self::Curseforge => curseforge::get(slug_or_id.parse::().map_err(|err| anyhow::anyhow!(err))?).await?.into(), + Self::SkyClient => skyclient::get(slug_or_id).await?.into(), }) } pub async fn get_multiple(&self, slug_or_ids: &[String]) -> Result> { - Ok(match self { - Self::Modrinth => { - if slug_or_ids.len() <= 0 { - return Ok(vec![]); - } + if slug_or_ids.len() <= 0 { + return Ok(vec![]); + } - modrinth::get_multiple(slug_or_ids) + Ok(match self { + Self::Modrinth => modrinth::get_multiple(slug_or_ids) .await? .into_iter() .map(Into::into) - .collect() - }, + .collect(), Self::Curseforge => { let parsed_ids = slug_or_ids .iter() @@ -145,6 +151,11 @@ impl Providers { .map(Into::into) .collect() }, + Self::SkyClient => skyclient::get_multiple(slug_or_ids) + .await? + .into_iter() + .map(Into::into) + .collect(), }) } @@ -166,6 +177,8 @@ impl Providers { let data = curseforge::get_all_versions(project_id, game_versions, loaders, page, page_size).await?; (data.0.into_iter().map(Into::into).collect(), data.1) } + + Self::SkyClient => todo!(), }) } @@ -181,6 +194,7 @@ impl Providers { .into_iter() .map(Into::into) .collect(), + Self::SkyClient => todo!(), }) } @@ -215,7 +229,8 @@ impl Providers { .await? .into_iter() .map(|(hash, version)| (hash, version.into())) - .collect() + .collect(), + Self::SkyClient => todo!(), }) } } diff --git a/packages/core/src/api/package/content/modrinth.rs b/packages/core/src/api/package/content/modrinth.rs index 67de79b4..19595e06 100644 --- a/packages/core/src/api/package/content/modrinth.rs +++ b/packages/core/src/api/package/content/modrinth.rs @@ -75,8 +75,8 @@ impl From for ManagedPackage { game_versions: value.game_versions, loaders: value.loaders, icon_url: value.icon_url, - created: value.published, - updated: value.updated, + created: Some(value.published), + updated: Some(value.updated), client: value.client_side, server: value.server_side, downloads: value.downloads as u64, diff --git a/packages/core/src/api/package/content/skyclient.rs b/packages/core/src/api/package/content/skyclient.rs new file mode 100644 index 00000000..adb54225 --- /dev/null +++ b/packages/core/src/api/package/content/skyclient.rs @@ -0,0 +1,288 @@ +use std::sync::Arc; + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use tokio::sync::{OnceCell, RwLock}; + +use crate::{data::{Loader, ManagedPackage, ManagedUser, ManagedVersion}, store::{ProviderSearchResults, SearchResult}, utils::{http, pagination::Pagination}, Result, State}; + +async fn fetch(url: &str) -> Result { + let state = State::get().await?; + Ok(serde_json::from_slice( + &http::fetch( + format!("{}/{}", crate::constants::SKYCLIENT_BASE_URL, url).as_str(), + None, + &state.fetch_semaphore + ).await? + )?) +} + +static SKYCLIENTSTORE_STATIC: OnceCell> = OnceCell::const_new(); + +struct SkyClientStore { + pub mods: Option> +} + +impl SkyClientStore { + pub async fn get() -> Result>> { + Ok(Arc::new( + SKYCLIENTSTORE_STATIC + .get_or_try_init(Self::initialize) + .await? + .read() + .await, + )) + } + + #[tracing::instrument] + #[onelauncher_macros::memory] + async fn initialize() -> Result> { + let mods: Vec = fetch("/mods/mods.json").await?; + + Ok(RwLock::new(SkyClientStore { + mods: Some(mods) + })) + } +} + + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct SkyClientMod { + pub id: String, + #[serde(default)] + pub nicknames: Vec, + pub display: String, + pub creator: String, + pub discord_code: Option, + pub description: Option, + pub icon: Option, + #[serde(default)] + pub links: Vec, + pub versions: Vec, + #[serde(default)] + pub commands: Vec, + pub icon_scaling: Option, + pub oneconfig: Option, +} + +impl Into for SkyClientMod { + fn into(self) -> SearchResult { + SearchResult { + slug: self.id.clone(), + project_id: self.id, + author: self.creator, + categories: vec![], + title: self.display, + client_side: crate::store::PackageSide::Required, + server_side: crate::store::PackageSide::Unknown, + date_created: None, + date_modified: None, + description: self.description.unwrap_or_default(), + project_type: crate::data::PackageType::Mod, + downloads: 0, + icon_url: self.icon.map(|i| format!("{}/icons/{}", crate::constants::SKYCLIENT_BASE_URL, i)).unwrap_or_default(), + versions: self.versions.iter().map(|v| v.version.clone()).collect(), + follows: 0, + } + } +} + +impl Into for SkyClientMod { + fn into(self) -> ManagedPackage { + ManagedPackage { + id: self.id.clone(), + main: self.id, + title: self.display, + body: crate::store::PackageBody::Markdown(self.description.clone().unwrap_or_default()), + categories: vec![], + client: crate::store::PackageSide::Required, + server: crate::store::PackageSide::Unknown, + created: None, + updated: None, + description: self.description.unwrap_or_default(), + downloads: 0, + followers: 0, + game_versions: self.versions.iter().flat_map(|v| v.game_versions.clone()).collect(), + icon_url: self.icon.map(|i| format!("{}/icons/{}", crate::constants::SKYCLIENT_BASE_URL, i)), + is_archived: false, + license: None, + loaders: self.versions.iter().flat_map(|v| v.loaders.clone().into_iter().map(Loader::from_string)).collect(), + versions: self.versions.iter().map(|v| v.version.clone()).collect(), + optional_categories: None, + author: crate::store::Author::Users(vec![ + ManagedUser { + id: self.creator.clone(), + username: self.creator, + is_organization_user: false, + avatar_url: None, + bio: None, + role: None, + url: self.discord_code.map(|code| format!("https://discord.gg/{}", code)), + } + ]), + package_type: crate::data::PackageType::Mod, + provider: super::Providers::SkyClient, + } + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct SkyClientModLink { + pub icon: String, + pub text: String, + pub link: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct SkyClientModVersion { + pub version: String, + pub game_versions: Vec, + pub loaders: Vec, + pub file: String, + pub url: String, + pub hash: String, + pub mod_id: Option, +} + +pub async fn get(id: &str) -> Result { + let store = SkyClientStore::get().await?; + + let mods = match &store.mods { + Some(mods) => mods, + None => return Err(anyhow::anyhow!("mod not found").into()) + }; + + for m in mods { + if m.id == id { + return Ok(m.clone()); + } + } + + Err(anyhow::anyhow!("mod not found").into()) +} + +pub async fn get_multiple(slug_or_ids: &[String]) -> Result> { + let store = SkyClientStore::get().await?; + + let mods = match &store.mods { + Some(mods) => mods, + None => return Ok(vec![]) + }; + + let mut results = vec![]; + + for id in slug_or_ids { + for m in mods { + if m.id == *id { + results.push(m.clone()); + } + } + } + + Ok(results) +} + +// pub async fn get_all_versions( +// project_id: &str, +// game_versions: Option>, +// loaders: Option>, +// page: Option, +// page_size: Option, +// ) -> Result<(Vec, Pagination)> { +// let store = SkyClientStore::get().await?; + +// let mods = match &store.mods { +// Some(mods) => mods, +// None => return Ok((vec![], Pagination::default())) +// }; + + +// let mut versions = vec![]; + +// for m in mods { +// if m.id == project_id { +// for v in m.versions { +// let mut can_add = true; + +// if let Some(game_versions) = &game_versions { +// can_add = v.game_versions.iter().any(|gv| game_versions.contains(gv)) +// } + +// if can_add { +// if let Some(loaders) = &loaders { +// can_add = v.loaders.iter().any(|l| loaders.contains(&Loader::from_string(l))) +// } +// } + +// if can_add { +// +// } +// } +// } +// } +// } + +pub async fn search( + query: Option, + limit: Option, + offset: Option, + game_versions: Option>, + // package_types: Option>, + loaders: Option>, +) -> Result { + let store = SkyClientStore::get().await?; + + let mut results = ProviderSearchResults { + provider: super::Providers::SkyClient, + results: vec![], + total: 0, + }; + + let mods = match &store.mods { + Some(mods) => mods, + None => return Ok(results), + }; + + let query = query.map(|q| q.trim().to_lowercase()); + let limit = limit.map(|l| l as usize); + let offset = offset.unwrap_or(0); + + for m in mods { + let mut can_add = true; + + if let Some(query) = &query { + if !query.is_empty() { + let display = m.display.to_lowercase(); + + can_add = display.contains(query) || m.id.contains(query) || m.nicknames.contains(query) + } + } + + if can_add { + if let Some(game_versions) = &game_versions { + if !game_versions.is_empty() { + can_add = m.versions.iter().any(|v| v.game_versions.iter().any(|gv| game_versions.contains(&gv))) + } + } + } + + if can_add { + if let Some(loaders) = &loaders { + if !loaders.is_empty() { + can_add = m.versions.iter().any(|v| v.loaders.iter().any(|l| loaders.contains(&Loader::from_string(l.to_owned())))) + } + } + } + + if can_add { + if limit.map(|l| results.results.len() < l).unwrap_or(true) && offset <= results.total { + let result: SearchResult = m.clone().into(); + results.results.push(result); + } + + results.total += 1; + } + + } + + Ok(results) +} \ No newline at end of file diff --git a/packages/core/src/constants.rs b/packages/core/src/constants.rs index 69dde14a..515cf5f5 100644 --- a/packages/core/src/constants.rs +++ b/packages/core/src/constants.rs @@ -48,6 +48,8 @@ pub const METADATA_API_URL: &str = "https://meta.polyfrost.org"; pub const FEATURED_PACKAGES_URL: &str = "https://polyfrost.org/meta/onelauncher/featured.json"; /// / API base url. pub const MCLOGS_API_URL: &str = "https://api.mclo.gs/1"; +/// https://skyclient.co/ metadata base url. +pub const SKYCLIENT_BASE_URL: &str = "https://raw.githubusercontent.com/SkyblockClient/SkyblockClient-REPO/refs/heads/main/v1"; // =========== Paths =========== /// The public `settings.json` file used to store the global [`Settings`] state. diff --git a/packages/core/src/store/clusters.rs b/packages/core/src/store/clusters.rs index a87e0f8b..9f6fb9d2 100644 --- a/packages/core/src/store/clusters.rs +++ b/packages/core/src/store/clusters.rs @@ -376,7 +376,12 @@ impl Loader { } #[must_use] - pub fn from_string(val: &str) -> Self { + pub fn from_string(val: String) -> Self { + Self::from_str(val.as_str()) + } + + #[must_use] + pub fn from_str(val: &str) -> Self { match val { "forge" => Self::Forge, "fabric" => Self::Fabric, diff --git a/packages/core/src/store/package.rs b/packages/core/src/store/package.rs index 862f2b02..09f82f5a 100644 --- a/packages/core/src/store/package.rs +++ b/packages/core/src/store/package.rs @@ -148,12 +148,10 @@ pub struct SearchResult { pub icon_url: String, pub project_id: String, pub author: String, - #[serde(default)] - pub display_categories: Vec, pub versions: Vec, pub follows: u32, - pub date_created: DateTime, - pub date_modified: DateTime, + pub date_created: Option>, + pub date_modified: Option>, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -719,8 +717,8 @@ pub struct ManagedPackage { pub loaders: Vec, pub icon_url: Option, - pub created: DateTime, - pub updated: DateTime, + pub created: Option>, + pub updated: Option>, pub client: PackageSide, pub server: PackageSide, pub downloads: u64,