Skip to content

Commit

Permalink
feat(components): implement standard typed
Browse files Browse the repository at this point in the history
  • Loading branch information
krypt0nn committed Dec 31, 2024
1 parent 21acf8e commit 3cd1366
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/components/manifest/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use super::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ComponentCategory {
Translation,
Virtualisation,
Runtime,
General
}

impl std::fmt::Display for ComponentCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Translation => f.write_str("translation"),
Self::Virtualisation => f.write_str("virtualisation"),
Self::Runtime => f.write_str("runtime"),
Self::General => f.write_str("general")
}
}
}

impl std::str::FromStr for ComponentCategory {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"translation" => Ok(Self::Translation),
"virtualisation" => Ok(Self::Virtualisation),
"runtime" => Ok(Self::Runtime),
"general" => Ok(Self::General),

_ => anyhow::bail!("Unsupported component category: {s}")
}
}
}

impl AsHash for ComponentCategory {
#[inline]
fn hash(&self) -> Hash {
self.to_string().hash()
}
}
49 changes: 49 additions & 0 deletions src/components/manifest/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use serde_json::{json, Value as Json};

use crate::prelude::*;

pub mod variant;
pub mod package;
pub mod category;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ComponentsVariantManifest {
pub standard: u64,
pub variant: variant::Variant,
pub package: package::Package
}

impl AsJson for ComponentsVariantManifest {
fn to_json(&self) -> Result<Json, AsJsonError> {
Ok(json!({
"standard": self.standard,
"variant": self.variant.to_json()?,
"package": self.package.to_json()?
}))
}

fn from_json(json: &Json) -> Result<Self, AsJsonError> where Self: Sized {
Ok(Self {
standard: json.get("standard")
.ok_or_else(|| AsJsonError::FieldNotFound("standard"))?
.as_u64()
.ok_or_else(|| AsJsonError::InvalidFieldValue("standard"))?,

variant: json.get("variant")
.ok_or_else(|| AsJsonError::FieldNotFound("variant"))
.and_then(variant::Variant::from_json)?,

package: json.get("package")
.ok_or_else(|| AsJsonError::FieldNotFound("package"))
.and_then(package::Package::from_json)?
})
}
}

impl AsHash for ComponentsVariantManifest {
fn hash(&self) -> Hash {
self.standard.hash()
.chain(self.variant.hash())
.chain(self.package.hash())
}
}
110 changes: 110 additions & 0 deletions src/components/manifest/package.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::str::FromStr;

use serde::{Serialize, Deserialize};
use serde_json::{json, Value as Json};

use super::*;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Package {
pub url: String,
pub output: String,
pub runtime: PackageRuntime
}

impl AsJson for Package {
fn to_json(&self) -> Result<Json, AsJsonError> {
Ok(json!({
"url": self.url,
"output": self.output,
"runtime": self.runtime.to_json()?
}))
}

fn from_json(json: &Json) -> Result<Self, AsJsonError> where Self: Sized {
Ok(Self {
url: json.get("url")
.ok_or_else(|| AsJsonError::FieldNotFound("package.url"))?
.as_str()
.ok_or_else(|| AsJsonError::InvalidFieldValue("package.url"))?
.to_string(),

output: json.get("output")
.ok_or_else(|| AsJsonError::FieldNotFound("package.output"))?
.as_str()
.ok_or_else(|| AsJsonError::InvalidFieldValue("package.output"))?
.to_string(),

runtime: json.get("runtime")
.ok_or_else(|| AsJsonError::FieldNotFound("package.runtime"))
.and_then(PackageRuntime::from_json)?
})
}
}

impl AsHash for Package {
fn hash(&self) -> Hash {
self.url.hash()
.chain(self.output.hash())
.chain(self.runtime.hash())
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PackageRuntime {
pub source_platforms: Vec<TargetPlatform>,
pub target_platforms: Vec<TargetPlatform>
}

impl AsJson for PackageRuntime {
fn to_json(&self) -> Result<Json, AsJsonError> {
Ok(json!({
"source_platforms": self.source_platforms.iter()
.map(TargetPlatform::to_string)
.collect::<Vec<String>>(),

"target_platforms": self.target_platforms.iter()
.map(TargetPlatform::to_string)
.collect::<Vec<String>>()
}))
}

fn from_json(json: &Json) -> Result<Self, AsJsonError> where Self: Sized {
Ok(Self {
source_platforms: json.get("source_platforms")
.and_then(Json::as_array)
.ok_or_else(|| AsJsonError::FieldNotFound("package.runtime.source_platforms"))?
.iter()
.map(|platform| {
platform.as_str()
.ok_or_else(|| AsJsonError::InvalidFieldValue("package.runtime.source_platforms[]"))
.and_then(|platform| {
TargetPlatform::from_str(platform)
.map_err(|err| AsJsonError::Other(err.into()))
})
})
.collect::<Result<Vec<_>, _>>()?,

target_platforms: json.get("target_platforms")
.and_then(Json::as_array)
.ok_or_else(|| AsJsonError::FieldNotFound("package.runtime.target_platforms"))?
.iter()
.map(|platform| {
platform.as_str()
.ok_or_else(|| AsJsonError::InvalidFieldValue("package.runtime.target_platforms[]"))
.and_then(|platform| {
TargetPlatform::from_str(platform)
.map_err(|err| AsJsonError::Other(err.into()))
})
})
.collect::<Result<Vec<_>, _>>()?
})
}
}

impl AsHash for PackageRuntime {
fn hash(&self) -> Hash {
self.source_platforms.hash()
.chain(self.target_platforms.hash())
}
}
58 changes: 58 additions & 0 deletions src/components/manifest/variant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::str::FromStr;

use serde_json::{json, Value as Json};

use super::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Variant {
pub name: String,
pub title: LocalizableString,
pub description: LocalizableString,
pub category: ComponentCategory
}

impl AsJson for Variant {
fn to_json(&self) -> Result<Json, AsJsonError> {
Ok(json!({
"name": self.name,
"title": self.title.to_json()?,
"description": self.description.to_json()?,
"category": self.category.to_string()
}))
}

fn from_json(json: &Json) -> Result<Self, AsJsonError> where Self: Sized {
Ok(Self {
name: json.get("name")
.ok_or_else(|| AsJsonError::FieldNotFound("variant.name"))?
.as_str()
.ok_or_else(|| AsJsonError::InvalidFieldValue("variant.name"))?
.to_string(),

title: json.get("title")
.ok_or_else(|| AsJsonError::FieldNotFound("variant.title"))
.and_then(LocalizableString::from_json)?,

description: json.get("description")
.ok_or_else(|| AsJsonError::FieldNotFound("variant.description"))
.and_then(LocalizableString::from_json)?,

category: json.get("category")
.ok_or_else(|| AsJsonError::FieldNotFound("variant.category"))?
.as_str()
.ok_or_else(|| AsJsonError::InvalidFieldValue("variant.category"))
.map(ComponentCategory::from_str)?
.map_err(|err| AsJsonError::Other(err.into()))?
})
}
}

impl AsHash for Variant {
fn hash(&self) -> Hash {
self.name.hash()
.chain(self.title.hash())
.chain(self.description.hash())
.chain(self.category.hash())
}
}
9 changes: 9 additions & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub mod manifest;
pub mod registry;

pub mod prelude {
pub use super::manifest::ComponentsVariantManifest;
pub use super::manifest::category::ComponentCategory;

pub use super::registry::Manifest as ComponentsRegistryManifest;
}
91 changes: 91 additions & 0 deletions src/components/registry/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use serde_json::{json, Value as Json};

use crate::prelude::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Manifest {
pub format: u64,
pub title: LocalizableString,
pub translation_components: Vec<String>,
pub virtualisation_components: Vec<String>,
pub runtime_components: Vec<String>,
pub general_components: Vec<String>
}

impl AsJson for Manifest {
fn to_json(&self) -> Result<Json, AsJsonError> {
Ok(json!({
"format": self.format,
"title": self.title.to_json()?,
"components": {
"translation": self.translation_components,
"virtualisation": self.virtualisation_components,
"runtime": self.runtime_components,
"general": self.general_components
}
}))
}

fn from_json(json: &Json) -> Result<Self, AsJsonError> where Self: Sized {
let components = json.get("components")
.ok_or_else(|| AsJsonError::FieldNotFound("components"))?;

Ok(Self {
format: json.get("format")
.ok_or_else(|| AsJsonError::FieldNotFound("format"))?
.as_u64()
.ok_or_else(|| AsJsonError::InvalidFieldValue("format"))?,

title: json.get("title")
.ok_or_else(|| AsJsonError::FieldNotFound("title"))
.and_then(LocalizableString::from_json)?,

translation_components: components.get("translation")
.ok_or_else(|| AsJsonError::FieldNotFound("components.translation"))?
.as_array()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.translation"))?
.iter()
.map(|url| url.as_str().map(String::from))
.collect::<Option<Vec<String>>>()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.translation[]"))?,

virtualisation_components: components.get("virtualisation")
.ok_or_else(|| AsJsonError::FieldNotFound("components.virtualisation"))?
.as_array()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.virtualisation"))?
.iter()
.map(|url| url.as_str().map(String::from))
.collect::<Option<Vec<String>>>()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.virtualisation[]"))?,

runtime_components: components.get("runtime")
.ok_or_else(|| AsJsonError::FieldNotFound("components.runtime"))?
.as_array()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.runtime"))?
.iter()
.map(|url| url.as_str().map(String::from))
.collect::<Option<Vec<String>>>()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.runtime[]"))?,

general_components: components.get("general")
.ok_or_else(|| AsJsonError::FieldNotFound("components.general"))?
.as_array()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.general"))?
.iter()
.map(|url| url.as_str().map(String::from))
.collect::<Option<Vec<String>>>()
.ok_or_else(|| AsJsonError::InvalidFieldValue("components.general[]"))?
})
}
}

impl AsHash for Manifest {
fn hash(&self) -> Hash {
self.format.hash()
.chain(self.title.hash())
.chain(self.translation_components.hash())
.chain(self.virtualisation_components.hash())
.chain(self.runtime_components.hash())
.chain(self.general_components.hash())
}
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod cache;
pub mod packages;
pub mod generations;
pub mod games;
pub mod components;
pub mod profiles;

pub mod i18n;
Expand All @@ -34,6 +35,7 @@ pub mod prelude {
pub use super::packages::prelude::*;
pub use super::generations::prelude::*;
pub use super::games::prelude::*;
pub use super::components::prelude::*;
pub use super::profiles::prelude::*;
pub use super::ui::prelude::*;

Expand Down

0 comments on commit 3cd1366

Please sign in to comment.