use super::error::ParseError;
use super::AppId;
use serde::ser::{SerializeMap, SerializeStruct};
use serde::{
de::{self, Deserializer, MapAccess, Visitor},
Deserialize, Serialize, Serializer,
};
use std::cmp::{Ord, Ordering};
use std::fmt;
use std::path::PathBuf;
use std::str::FromStr;
use strum_macros::{AsRefStr, Display, EnumString};
use url::Url;
#[derive(Clone, Copy, Debug, AsRefStr, EnumString, Display, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
#[non_exhaustive]
pub enum ArtifactKind {
Source,
Binary,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "lowercase")]
#[non_exhaustive]
pub enum Bundle {
Limba(String),
Flatpak {
#[serde(skip_serializing_if = "Option::is_none")]
runtime: Option<String>,
sdk: Option<String>,
reference: String,
},
AppImage(String),
Snap(String),
Tarball(String),
}
impl Serialize for Bundle {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut bundle_map = serializer.serialize_map(None)?;
match self {
Bundle::Limba(id) => {
bundle_map.serialize_entry("type", "limba")?;
bundle_map.serialize_entry("id", id)?;
}
Bundle::Flatpak {
sdk,
runtime,
reference,
} => {
bundle_map.serialize_entry("type", "flatpak")?;
bundle_map.serialize_entry("reference", reference)?;
if let Some(sdk) = sdk {
bundle_map.serialize_entry("sdk", sdk)?;
}
if let Some(runtime) = runtime {
bundle_map.serialize_entry("runtime", runtime)?;
}
}
Bundle::AppImage(id) => {
bundle_map.serialize_entry("type", "appimage")?;
bundle_map.serialize_entry("id", id)?;
}
Bundle::Snap(id) => {
bundle_map.serialize_entry("type", "snap")?;
bundle_map.serialize_entry("id", id)?;
}
Bundle::Tarball(id) => {
bundle_map.serialize_entry("type", "tarball")?;
bundle_map.serialize_entry("id", id)?;
}
}
bundle_map.end()
}
}
#[derive(Clone, Debug, AsRefStr, EnumString, Display, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
#[strum(serialize_all = "PascalCase")]
#[non_exhaustive]
pub enum Category {
AudioVideo,
Audio,
Video,
Development,
Education,
Game,
Graphics,
Network,
Office,
Science,
Settings,
System,
Utility,
Building,
Debugger,
IDE,
GUIDesigner,
Profiling,
RevisionControl,
Translation,
Calendar,
ContactManagement,
Database,
Dictionary,
Chart,
Email,
Finance,
FlowChart,
PDA,
ProjectManagement,
Presentation,
Spreadsheet,
WordProcessor,
TwoDGraphics,
VectorGraphics,
RasterGraphics,
ThreeDGraphics,
Scanning,
OCR,
Photography,
Publishing,
Viewer,
TextTools,
DesktopSettings,
HardwareSettings,
Printing,
PackageManager,
Dialup,
InstantMessaging,
Chat,
IRCClient,
Feed,
FileTransfer,
HamRadio,
News,
P2P,
RemoteAccess,
Telephony,
TelephonyTools,
VideoConference,
WebBrowser,
WebDevelopment,
Midi,
Mixer,
Sequencer,
Tuner,
TV,
AudioVideoEditing,
Player,
Recorder,
DiscBurning,
ActionGame,
AdventureGame,
ArcadeGame,
BoardGame,
BlocksGame,
CardGame,
KidsGame,
LogicGame,
RolePlaying,
Shooter,
Simulation,
SportsGame,
StrategyGame,
Art,
Construction,
Music,
Languages,
ArtificialIntelligence,
Astronomy,
Biology,
Chemistry,
ComputerScience,
DataVisualization,
Economy,
Electricity,
Geography,
Geology,
Geoscience,
History,
Humanities,
ImageProcessing,
Literature,
Maps,
Math,
NumericalAnalysis,
MedicalSoftware,
Physics,
Robotics,
Spirituality,
Sports,
ParallelComputing,
Amusement,
Archiving,
Compression,
Electronics,
Emulator,
Engineering,
FileTools,
FileManager,
TerminalEmulator,
Filesystem,
Monitor,
Security,
Accessibility,
Calculator,
Clock,
TextEditor,
Documentation,
Adult,
Core,
KDE,
GNOME,
XFCE,
GTK,
Qt,
Motif,
Java,
ConsoleOnly,
Screensaver,
TrayIcon,
Applet,
Shell,
#[strum(default)]
#[doc(hidden)]
Unknown(String),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[serde(tag = "type", content = "$value")]
#[non_exhaustive]
pub enum Checksum {
Sha1(String),
Sha256(String),
Blake2b(String),
Blake2s(String),
}
#[derive(Clone, Copy, Debug, AsRefStr, Serialize, Display, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
#[non_exhaustive]
pub enum ComponentKind {
Runtime,
#[serde(alias = "console")]
#[strum(serialize = "console")]
ConsoleApplication,
#[serde(alias = "desktop")]
#[strum(serialize = "desktop")]
DesktopApplication,
#[serde(alias = "webapp")]
#[strum(serialize = "web-application")]
WebApplication,
#[serde(rename = "inputmethod")]
#[strum(serialize = "inputmethod")]
InputMethod,
#[serde(alias = "operating-system")]
#[strum(serialize = "operating-system")]
OS,
Theme,
Firmware,
Addon,
Font,
Generic,
IconTheme,
Localization,
Driver,
Codec,
}
impl Default for ComponentKind {
fn default() -> Self {
ComponentKind::Generic
}
}
impl FromStr for ComponentKind {
type Err = ParseError;
fn from_str(c: &str) -> Result<Self, Self::Err> {
match c {
"runtime" => Ok(ComponentKind::Runtime),
"console" | "console-application" => Ok(ComponentKind::ConsoleApplication),
"desktop" | "desktop-application" => Ok(ComponentKind::DesktopApplication),
"webapp" => Ok(ComponentKind::WebApplication),
"inputmethod" => Ok(ComponentKind::InputMethod),
"operating-system" => Ok(ComponentKind::OS),
"theme" => Ok(ComponentKind::Theme),
"firmware" => Ok(ComponentKind::Firmware),
"addon" => Ok(ComponentKind::Addon),
"font" => Ok(ComponentKind::Font),
"icontheme" | "icon-theme" => Ok(ComponentKind::IconTheme),
"driver" => Ok(ComponentKind::Driver),
"codec" => Ok(ComponentKind::Codec),
"localization" => Ok(ComponentKind::Localization),
"" | "generic" => Ok(ComponentKind::default()),
_ => Err(ParseError::invalid_value(c, "type", "component")),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
#[serde(tag = "id", content = "$value")]
#[non_exhaustive]
pub enum ContentAttribute {
#[serde(rename = "violence-cartoon")]
ViolenceCartoon(ContentState),
#[serde(rename = "violence-fantasy")]
ViolenceFantasy(ContentState),
#[serde(rename = "violence-realistic")]
ViolenceRealistic(ContentState),
#[serde(rename = "violence-bloodshed")]
ViolenceBloodshed(ContentState),
#[serde(rename = "violence-sexual")]
ViolenceSexual(ContentState),
#[serde(rename = "violence-desecration")]
ViolenceDesecration(ContentState),
#[serde(rename = "violence-slavery")]
ViolenceSlavery(ContentState),
#[serde(rename = "violence-worship")]
ViolenceWorship(ContentState),
#[serde(rename = "drugs-alcohol")]
DrugsAlcohol(ContentState),
#[serde(rename = "drugs-narcotics")]
DrugsNarcotics(ContentState),
#[serde(rename = "drugs-tobacco")]
DrugsTobacco(ContentState),
#[serde(rename = "sex-nudity")]
SexNudity(ContentState),
#[serde(rename = "sex-themes")]
SexThemes(ContentState),
#[serde(rename = "sex-homosexuality")]
SexHomosexuality(ContentState),
#[serde(rename = "sex-prostitution")]
SexProstitution(ContentState),
#[serde(rename = "sex-adultery")]
SexAdultery(ContentState),
#[serde(rename = "sex-appearance")]
SexAppearance(ContentState),
#[serde(rename = "language-profanity")]
LanguageProfanity(ContentState),
#[serde(rename = "language-humor")]
LanguageHumor(ContentState),
#[serde(rename = "language-discrimination")]
LanguageDiscrimination(ContentState),
#[serde(rename = "social-chat")]
SocialChat(ContentState),
#[serde(rename = "social-info")]
SocialInfo(ContentState),
#[serde(rename = "social-audio")]
SocialAudio(ContentState),
#[serde(rename = "social-location")]
SocialLocation(ContentState),
#[serde(rename = "social-contacts")]
SocialContacts(ContentState),
#[serde(rename = "money-advertising")]
MoneyAdvertising(ContentState),
#[serde(rename = "money-purchasing")]
MoneyPurchasing(ContentState),
#[serde(rename = "money-gambling")]
MoneyGambling(ContentState),
}
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Debug)]
#[non_exhaustive]
pub enum ContentRatingVersion {
#[serde(rename = "oars-1.0")]
Oars1_0,
#[serde(rename = "oars-1.1")]
Oars1_1,
Unknown,
}
impl Default for ContentRatingVersion {
fn default() -> Self {
ContentRatingVersion::Unknown
}
}
impl Ord for ContentRatingVersion {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(ContentRatingVersion::Oars1_0, ContentRatingVersion::Oars1_1)
| (ContentRatingVersion::Unknown, _) => Ordering::Less,
(ContentRatingVersion::Oars1_1, ContentRatingVersion::Oars1_0)
| (_, ContentRatingVersion::Unknown) => Ordering::Greater,
(ContentRatingVersion::Oars1_0, ContentRatingVersion::Oars1_0)
| (ContentRatingVersion::Oars1_1, ContentRatingVersion::Oars1_1) => Ordering::Equal,
}
}
}
impl PartialOrd for ContentRatingVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Clone, Copy, Debug, AsRefStr, Display, EnumString, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum ContentState {
None,
Mild,
Moderate,
Intense,
}
impl Default for ContentState {
fn default() -> Self {
Self::None
}
}
#[derive(Clone, Copy, Debug, AsRefStr, EnumString, Display, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum FirmwareKind {
Flashed,
Runtime,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Icon {
Stock(String),
Cached {
path: PathBuf,
width: Option<u32>,
height: Option<u32>,
},
Remote {
url: Url,
width: Option<u32>,
height: Option<u32>,
},
Local {
path: PathBuf,
width: Option<u32>,
height: Option<u32>,
},
}
impl<'de> Deserialize<'de> for Icon {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct IconVisitor;
impl<'de> Visitor<'de> for IconVisitor {
type Value = Icon;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map representing an icon")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut kind = None;
let mut width = None;
let mut height = None;
let mut path = None;
while let Some(key) = access.next_key::<String>()? {
match &*key {
"type" => {
let value = access.next_value::<String>()?;
kind = Some(value.clone());
}
"width" => {
width = access.next_value::<u32>().ok();
}
"height" => {
height = access.next_value::<u32>().ok();
}
"path" | "name" | "url" => {
let value = access.next_value::<String>()?;
path = Some(value.clone());
}
_ => (),
}
}
let path = path.ok_or_else(|| de::Error::missing_field("path"))?;
let kind = kind.ok_or_else(|| de::Error::missing_field("type"))?;
match kind.as_ref() {
"remote" => Ok(Icon::Remote {
url: Url::parse(&path).map_err(|_| {
de::Error::invalid_value(
de::Unexpected::Str(&path),
&"expected a valid url",
)
})?,
width,
height,
}),
"stock" => Ok(Icon::Stock(path)),
"cached" => Ok(Icon::Cached {
path: path.into(),
width,
height,
}),
"local" => Ok(Icon::Local {
path: path.into(),
width,
height,
}),
e => Err(de::Error::invalid_value(
de::Unexpected::Str(e),
&"expected a type of local, remote, cached or stock",
)),
}
}
}
deserializer.deserialize_map(IconVisitor)
}
}
impl Serialize for Icon {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("icon", 4)?;
let mut w: &Option<u32> = &None;
let mut h: &Option<u32> = &None;
match self {
Icon::Stock(path) => {
s.serialize_field("type", "stock")?;
s.serialize_field("name", &path)?;
}
Icon::Remote { url, width, height } => {
s.serialize_field("type", "remote")?;
s.serialize_field("url", &url)?;
w = width;
h = height;
}
Icon::Cached {
path,
width,
height,
} => {
s.serialize_field("type", "cached")?;
s.serialize_field("path", &path)?;
w = width;
h = height;
}
Icon::Local {
path,
width,
height,
} => {
s.serialize_field("type", "local")?;
s.serialize_field("path", &path)?;
w = width;
h = height;
}
};
match w {
Some(v) => s.serialize_field("width", v)?,
None => s.skip_field("width")?,
};
match h {
Some(v) => s.serialize_field("height", v)?,
None => s.skip_field("height")?,
};
s.end()
}
}
#[derive(Clone, Copy, Debug, Display, AsRefStr, Serialize, Deserialize, PartialEq, EnumString)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum ImageKind {
Source,
Thumbnail,
}
impl Default for ImageKind {
fn default() -> Self {
Self::Source
}
}
#[derive(Clone, Debug, Deserialize, AsRefStr, Display, Serialize, PartialEq, EnumString)]
#[strum(serialize_all = "PascalCase")]
#[non_exhaustive]
pub enum Kudo {
#[doc(hidden)]
AppMenu,
HiDpiIcon,
HighContrast,
ModernToolkit,
Notifications,
SearchProvider,
UserDocs,
#[strum(default)]
#[doc(hidden)]
Unknown(String),
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase", tag = "type", content = "name")]
#[non_exhaustive]
pub enum Launchable {
#[serde(alias = "desktop_id")]
DesktopId(String),
Service(String),
Url(Url),
CockpitManifest(String),
#[doc(hidden)]
Unknown(String),
}
impl Serialize for Launchable {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("launchable", 2)?;
match self {
Launchable::DesktopId(app_id) => {
s.serialize_field("type", "desktop_id")?;
s.serialize_field("name", &app_id)?;
}
Launchable::Service(name) => {
s.serialize_field("type", "service")?;
s.serialize_field("name", &name)?;
}
Launchable::Url(url) => {
s.serialize_field("type", "url")?;
s.serialize_field("name", &url)?;
}
Launchable::CockpitManifest(manifest) => {
s.serialize_field("type", "cockpit_manifest")?;
s.serialize_field("name", &manifest)?;
}
Launchable::Unknown(name) => {
s.serialize_field("type", "unknown")?;
s.serialize_field("name", &name)?;
}
}
s.end()
}
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase", tag = "type", content = "url")]
#[non_exhaustive]
pub enum ProjectUrl {
Donation(Url),
Translate(Url),
Homepage(Url),
BugTracker(Url),
Help(Url),
Faq(Url),
Contact(Url),
#[doc(hidden)]
Unknown(Url),
}
impl Serialize for ProjectUrl {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("url", 2)?;
match self {
ProjectUrl::Donation(url) => {
s.serialize_field("type", "donation")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::Translate(url) => {
s.serialize_field("type", "translate")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::Homepage(url) => {
s.serialize_field("type", "homepage")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::BugTracker(url) => {
s.serialize_field("type", "bugtracker")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::Help(url) => {
s.serialize_field("type", "help")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::Faq(url) => {
s.serialize_field("type", "faq")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::Contact(url) => {
s.serialize_field("type", "contact")?;
s.serialize_field("url", &url)?;
}
ProjectUrl::Unknown(url) => {
s.serialize_field("type", "unknown")?;
s.serialize_field("url", &url)?;
}
}
s.end()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum Provide {
MediaType(String),
Library(PathBuf),
Binary(String),
Font(String),
Modalias(String),
Firmware {
#[serde(rename = "type")]
kind: FirmwareKind,
item: String,
},
Python2(String),
Python3(String),
DBus(String),
Id(AppId),
Codec(String),
}
#[derive(Clone, Copy, Debug, Display, EnumString, AsRefStr, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum ReleaseKind {
Stable,
Development,
}
impl Default for ReleaseKind {
fn default() -> Self {
ReleaseKind::Stable
}
}
#[derive(Clone, Copy, Debug, AsRefStr, EnumString, Display, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum ReleaseUrgency {
Low,
Medium,
High,
Critical,
}
impl Default for ReleaseUrgency {
fn default() -> Self {
ReleaseUrgency::Medium
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", content = "$value", rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Size {
Download(u64),
Installed(u64),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase", tag = "type", content = "name")]
#[non_exhaustive]
pub enum Translation {
Gettext(String),
Qt(String),
#[doc(hidden)]
Unknown,
}