1use std::{collections::HashMap, fs::File, io::Read, path::Path};
4
5use serde::Deserialize;
6use toml::Value;
7
8#[derive(Debug, Clone)]
9pub struct ProjectDescriptor {
10 pub package: PackageDescriptor,
11 pub app: AppDescriptor,
12 pub settings: Option<HashMap<String, Value>>,
13 pub actions: Option<HashMap<String, ActionDescriptor>>,
14}
15
16#[derive(Debug, Deserialize, Clone)]
17#[serde(rename(serialize = "component"))]
18pub struct CargoToml {
19 pub package: PackageDescriptor,
20}
21
22#[derive(Debug, Deserialize, Clone)]
23#[serde(rename(serialize = "component"))]
24pub struct AppToml {
25 pub app: AppDescriptor,
26 pub settings: Option<HashMap<String, Value>>,
27 pub actions: Option<HashMap<String, ActionDescriptor>>,
28}
29
30#[derive(Debug, Deserialize, Clone)]
31pub struct PackageDescriptor {
32 pub name: String,
33 pub version: String,
34 pub authors: Option<Vec<String>>,
35 pub homepage: Option<String>,
36 pub license: Option<String>,
37 pub repository: Option<String>,
38}
39
40#[derive(Debug, Deserialize, Clone)]
41#[serde(rename_all = "kebab-case")]
42pub struct AppDescriptor {
43 pub id: String,
45 pub name: Option<String>,
46 pub generic_name: Option<String>,
47 pub summary: String,
48 pub description: String,
49 pub categories: Vec<String>,
50 pub metadata_license: String,
51 pub screenshots: Option<Vec<Screenshot>>,
52 pub releases: Option<Vec<Release>>,
53 pub content_rating: Option<Vec<ContentRating>>,
54 pub requires: Vec<Recommend>,
55 pub recommends: Vec<Recommend>,
56 pub permissions: Vec<String>,
57 pub mimetype: Option<String>,
58
59 pub flatpak_runtime_version: Option<String>,
61 pub flatpak_modules: Option<Vec<String>>,
62
63 pub resources: Option<String>,
65}
66
67#[derive(Debug, Deserialize, Clone)]
68pub struct Release {
69 pub version: String,
70 pub date: String,
71 pub description: String,
72}
73
74#[derive(Debug, Deserialize, Clone)]
75pub struct Screenshot {
76 #[serde(rename = "type")]
77 pub type_: Option<String>,
78 pub url: String,
79}
80
81#[derive(Debug, Deserialize, Clone)]
82#[serde(rename_all = "kebab-case")]
83pub struct ContentRating {
84 pub id: String,
85 pub value: String,
86}
87
88#[derive(Debug, Deserialize, Clone)]
89#[serde(rename_all = "kebab-case")]
90pub enum Recommend {
91 Display(String),
92 DisplayLength(String),
93 Control(String),
94}
95
96#[derive(Debug, Deserialize, Clone)]
97pub struct ActionDescriptor {
98 pub state: Option<String>,
99 #[serde(rename = "type")]
100 pub type_: Option<String>,
101 pub accelerators: Option<Vec<String>>,
102}
103
104pub fn parse_project_descriptor(
105 cargo_toml_path: &Path,
106 app_toml_path: &Path,
107) -> std::io::Result<ProjectDescriptor> {
108 let mut cargo_file = match File::open(cargo_toml_path) {
109 Ok(f) => f,
110 Err(e) => {
111 eprintln!("[gra] Could not read {:?}: {}", cargo_toml_path, e);
112 return Err(e);
113 }
114 };
115 let mut app_file = match File::open(app_toml_path) {
116 Ok(f) => f,
117 Err(e) => {
118 eprintln!("[gra] Could not read {:?}: {}", app_toml_path, e);
119 return Err(e);
120 }
121 };
122 let mut cargo_toml_content = String::new();
123 cargo_file.read_to_string(&mut cargo_toml_content)?;
124 let mut app_toml_content = String::new();
125 app_file.read_to_string(&mut app_toml_content)?;
126 match parse_project_descriptor_str(&cargo_toml_content, &app_toml_content) {
127 Err(e) => {
128 eprintln!("[gra] Error: {}.\n{}", e, cargo_toml_content);
129 Err(e)
130 }
131 Ok(p) => Ok(p),
132 }
133}
134
135pub fn parse_project_descriptor_str(
136 cargo_toml: &str,
137 app_toml: &str,
138) -> std::io::Result<ProjectDescriptor> {
139 let cargo_toml: CargoToml = toml::from_str(cargo_toml)
140 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{}.", e)))?;
141 let app_toml: AppToml = toml::from_str(app_toml)
142 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{}.", e)))?;
143 Ok(ProjectDescriptor {
144 package: cargo_toml.package,
145 app: app_toml.app,
146 settings: app_toml.settings,
147 actions: app_toml.actions,
148 })
149}