-
Notifications
You must be signed in to change notification settings - Fork 0
Initial implementation #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c690965
afec592
dbd7155
49c3347
6226dca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,84 @@ | ||
| #[cfg(test)] | ||
| mod tests { | ||
| #[test] | ||
| fn it_works() { | ||
| let result = 2 + 2; | ||
| assert_eq!(result, 4); | ||
| //! # Dotium | ||
| //! | ||
| //! The uniform object provider for Minecraft Mod hosting platforms. | ||
| //! Implements traits for api wrappers Ferinth, Furse, and Octocrab to then be used platform independedly in GDLauncher | ||
|
|
||
| #[cfg(feature = "curseforge")] | ||
| mod curseforge; | ||
| #[cfg(feature = "github")] | ||
| mod github; | ||
| #[cfg(feature = "modrinth")] | ||
| mod modrinth; | ||
| pub mod project; | ||
| pub mod request; | ||
| pub mod version; | ||
|
|
||
| use async_trait::async_trait; | ||
| use project::{Author, Project}; | ||
| use serde::{Deserialize, Serialize}; | ||
| use version::Version; | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| pub struct Platform { | ||
| /// The name of the platform | ||
| pub name: String, | ||
| /// The two-letter nick name of the Platform | ||
| pub short_name: String, | ||
| /// Logo of the platform | ||
| pub logo: request::Asset, | ||
| } | ||
|
|
||
| #[derive(thiserror::Error, Debug)] | ||
| #[error("The trait is not implemented")] | ||
| pub struct UnimplementedError; | ||
|
|
||
| pub(crate) type Datetime = chrono::DateTime<chrono::Utc>; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UtcDateTime, it can be confused with the generic one it shadows otherwise |
||
|
|
||
| #[async_trait] | ||
| pub trait Container<E: From<UnimplementedError>, ID: Sync + Send> { | ||
| fn new() -> Self; | ||
| fn get_platform() -> Platform; | ||
|
|
||
| // GATs unstable | ||
| // type Result<T> = std::result::Result<T, E>; | ||
|
|
||
| async fn get_project(&self, id: &ID) -> Result<Project<ID>, E>; | ||
|
|
||
| async fn get_projects(&self, ids: Vec<&ID>) -> Result<Vec<Project<ID>>, E> { | ||
| let mut projects = Vec::new(); | ||
| for id in ids { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not totally sure about that, but wouldn't this mean that we are working a project at a time?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sometimes too many concurrent requests completely fails, I had to deal with this problem when implementing concurrent requests, and it was a rather huge pain. I ended up using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like we have the first design problem lol
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well the only platform that doesn't implement multiple requests is github (the others overrides this default implementation), and we won't be using the multiple requests on github afaik. |
||
| projects.push(self.get_project(id).await?); | ||
| } | ||
| Ok(projects) | ||
| } | ||
|
|
||
| async fn get_project_body(&self, project_id: &ID) -> Result<String, E>; | ||
|
|
||
| async fn get_project_authors(&self, project_id: &ID) -> Result<Vec<Author<ID>>, E>; | ||
|
|
||
| async fn get_project_dependencies( | ||
| &self, | ||
| project_id: &ID, | ||
| ) -> Result<(Vec<Project<ID>>, Vec<Version<ID>>), E>; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would converting this to a Vec<(Project, Version) cause issues somewhere?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I did think about doing this too
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes because this is not a sorted map. I specifically asked Modrinth on this:
That means, the two Vec's can have different lengths. Also they said that I shouldn't rely on their sorting. |
||
|
|
||
| async fn get_version(&self, project_id: &ID, id: &ID) -> Result<Version<ID>, E>; | ||
|
|
||
| async fn get_project_versions(&self, project_id: &ID) -> Result<Vec<Version<ID>>, E>; | ||
|
|
||
| async fn get_versions(&self, ids: Vec<(&ID, &ID)>) -> Result<Vec<Version<ID>>, E> { | ||
| let mut versions = Vec::new(); | ||
| for id in ids { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as for |
||
| versions.push(self.get_version(id.0, id.1).await?); | ||
| } | ||
| Ok(versions) | ||
| } | ||
|
|
||
| // async fn search( | ||
| // &self, | ||
| // query: &str, | ||
| // project_type: Option<&str>, | ||
| // mc_version: Vec<&str>, | ||
| // modloader: &str, | ||
| // category: &str, | ||
| // ) -> Result<Vec<project::Project>, E>; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| use super::ID; | ||
| use crate::{project, request, version}; | ||
| use ferinth::structures::*; | ||
|
|
||
| impl From<project_structs::Project> for project::Project<ID> { | ||
| fn from(from: project_structs::Project) -> Self { | ||
| project::Project { | ||
| id: from.id, | ||
| slug: from.slug, | ||
| project_type: from.project_type.into(), | ||
| name: from.title, | ||
| description: from.description, | ||
| links: project::Links { | ||
| github: from.source_url, | ||
| issues: from.issues_url, | ||
| wiki: from.wiki_url, | ||
| discord: from.discord_url, | ||
| donations: from.donation_urls.into_iter().map(Into::into).collect(), | ||
| }, | ||
| requirements: project::ProjectRequirements { | ||
| server: from.server_side.into(), | ||
| client: from.client_side.into(), | ||
| }, | ||
| categories: from.categories, | ||
| downloads: from.downloads, | ||
| followers: from.followers, | ||
| icon: from.icon_url.map(request::Asset::by_url), | ||
| status: from.status.into(), | ||
| published: from.published, | ||
| updated: from.updated, | ||
| created: from.published, | ||
| gallery: from.gallery.into_iter().map(Into::into).collect(), | ||
| allows_distribution: &from.license.id != "custom" && &from.license.id != "arr", | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<version_structs::Version> for version::Version<ID> { | ||
| fn from(from: version_structs::Version) -> Self { | ||
| Self { | ||
| id: from.id, | ||
| name: from.name, | ||
| identifier: from.version_number, | ||
| project_id: from.project_id, | ||
| files: from.files.into_iter().map(Into::into).collect(), | ||
| downloads: from.downloads, | ||
| loaders: from | ||
| .loaders | ||
| .into_iter() | ||
| .map(|loader| version::ModLoader::from(loader.as_ref())) | ||
| .collect(), | ||
| game_versions: from.game_versions, | ||
| published: from.date_published, | ||
| version_type: from.version_type.into(), | ||
| dependencies: from.dependencies.into_iter().map(Into::into).collect(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<version_structs::VersionFile> for request::Asset { | ||
| fn from(from: version_structs::VersionFile) -> Self { | ||
| Self { | ||
| url: from.url, | ||
| name: Some(from.filename), | ||
| description: None, | ||
| headers: std::collections::HashMap::new(), | ||
| request_type: request::RequestType::GET, | ||
| hash: Some(request::AssetHash { | ||
| hash: from.hashes.sha512, | ||
| algorithm: request::HashAlgorithm::SHA512, | ||
| }), | ||
| size: Some(from.size), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<version_structs::Dependency> for version::VersionDependency<ID> { | ||
| fn from(from: version_structs::Dependency) -> Self { | ||
| Self { | ||
| project_id: from.project_id, | ||
| version: from.version_id, | ||
| dependency_type: from.dependency_type.into(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<user_structs::User> for project::Author<ID> { | ||
| fn from(from: user_structs::User) -> Self { | ||
| Self { | ||
| username: from.username, | ||
| name: from.name, | ||
| id: from.id, | ||
| avatar_url: Some(request::Asset::by_url(from.avatar_url)), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<version_structs::DependencyType> for version::DependencyType { | ||
| fn from(from: version_structs::DependencyType) -> Self { | ||
| match from { | ||
| version_structs::DependencyType::Required => Self::RequiredDependency, | ||
| version_structs::DependencyType::Optional => Self::OptionalDependency, | ||
| version_structs::DependencyType::Incompatible => Self::Incompatible, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<project_structs::ProjectType> for project::ProjectType { | ||
| fn from(from: project_structs::ProjectType) -> Self { | ||
| match from { | ||
| project_structs::ProjectType::Mod => project::ProjectType::Mod, | ||
| project_structs::ProjectType::Modpack => project::ProjectType::Modpack, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<version_structs::VersionType> for version::VersionType { | ||
| fn from(from: version_structs::VersionType) -> Self { | ||
| match from { | ||
| version_structs::VersionType::Alpha => Self::Alpha, | ||
| version_structs::VersionType::Beta => Self::Beta, | ||
| version_structs::VersionType::Release => Self::Release, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<&str> for version::ModLoader { | ||
| fn from(from: &str) -> Self { | ||
| match from.to_lowercase().as_str() { | ||
| "modloader" => Self::Modloader, | ||
| "fabric" => Self::Fabric, | ||
| "quilt" => Self::Quilt, | ||
| "forge" => Self::Forge, | ||
| "rift" => Self::Rift, | ||
| _ => Self::Unknown, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<project_structs::ProjectSupportRange> for project::Requirement { | ||
| fn from(from: project_structs::ProjectSupportRange) -> Self { | ||
| match from { | ||
| project_structs::ProjectSupportRange::Required => Self::Required, | ||
| project_structs::ProjectSupportRange::Optional => Self::Optional, | ||
| project_structs::ProjectSupportRange::Unsupported => Self::Unsupported, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<project_structs::ProjectStatus> for project::Status { | ||
| fn from(from: project_structs::ProjectStatus) -> Self { | ||
| match from { | ||
| project_structs::ProjectStatus::Approved => Self::Approved, | ||
| project_structs::ProjectStatus::Rejected => Self::Rejected, | ||
| project_structs::ProjectStatus::Draft => Self::New, | ||
| project_structs::ProjectStatus::Unlisted => Self::Unlisted, | ||
| project_structs::ProjectStatus::Archived => Self::Abandoned, | ||
| project_structs::ProjectStatus::Processing => Self::UnderReview, | ||
| project_structs::ProjectStatus::Unknown => Self::Unknown, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<project_structs::DonationLink> for project::DonationLink { | ||
| fn from(from: project_structs::DonationLink) -> Self { | ||
| Self { | ||
| platform: from.platform, | ||
| url: from.url, | ||
| id: from.id, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<project_structs::GalleryItem> for request::Asset { | ||
| fn from(from: project_structs::GalleryItem) -> Self { | ||
| Self::by_description(from.url, from.title, from.description) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better if it was a trait that depends on std::error:Error?
So that we can fall back to more normal methods if the need arises, or if we need something like the step the error happened for example