1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct PluginManifest {
6 pub id: String,
7 pub name: String,
8 pub version: String,
9 pub description: String,
10 pub author: String,
11 #[serde(default)]
12 pub min_mangofetch_version: Option<String>,
13 #[serde(default)]
14 pub license: Option<String>,
15 #[serde(default)]
16 pub homepage: Option<String>,
17 #[serde(default)]
18 pub icon: Option<String>,
19 #[serde(default)]
20 pub nav: Vec<PluginNavItem>,
21 #[serde(default)]
22 pub events: PluginEvents,
23 #[serde(default)]
24 pub capabilities: Vec<String>,
25 #[serde(default)]
26 pub settings_schema: Option<serde_json::Value>,
27 #[serde(default)]
28 pub rust_crate: Option<String>,
29 #[serde(default)]
30 pub frontend_dir: Option<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct PluginNavItem {
35 pub route: String,
36 pub label: HashMap<String, String>,
37 #[serde(default)]
38 pub icon_svg: Option<String>,
39 #[serde(default = "default_nav_group")]
40 pub group: NavGroup,
41 #[serde(default = "default_nav_order")]
42 pub order: u32,
43}
44
45#[derive(Debug, Clone, Default, Serialize, Deserialize)]
46pub struct PluginEvents {
47 #[serde(default)]
48 pub progress: Vec<String>,
49 #[serde(default)]
50 pub complete: Vec<String>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
54#[serde(rename_all = "lowercase")]
55pub enum NavGroup {
56 Primary,
57 #[default]
58 Secondary,
59}
60
61fn default_nav_group() -> NavGroup {
62 NavGroup::Secondary
63}
64
65fn default_nav_order() -> u32 {
66 50
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct InstalledPlugin {
71 pub id: String,
72 pub version: String,
73 pub installed_at: String,
74 pub updated_at: String,
75 pub enabled: bool,
76 pub repo: Option<String>,
77 pub source_release: Option<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct RegistryEntry {
82 pub id: String,
83 pub name: String,
84 pub description: String,
85 pub author: String,
86 pub repo: String,
87 #[serde(default)]
88 pub homepage: Option<String>,
89 #[serde(default)]
90 pub tags: Vec<String>,
91 #[serde(default)]
92 pub official: bool,
93 #[serde(default)]
94 pub capabilities: Vec<String>,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct PluginRegistry {
99 #[serde(default)]
100 pub schema_version: u32,
101 pub plugins: Vec<RegistryEntry>,
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_plugin_manifest_default_deserialization() {
110 let json = r#"{
111 "id": "test-plugin",
112 "name": "Test Plugin",
113 "version": "1.0.0",
114 "description": "A test plugin",
115 "author": "Test Author"
116 }"#;
117
118 let manifest: PluginManifest = serde_json::from_str(json).unwrap();
119
120 assert_eq!(manifest.id, "test-plugin");
121 assert_eq!(manifest.min_mangofetch_version, None);
122 assert_eq!(manifest.license, None);
123 assert_eq!(manifest.homepage, None);
124 assert_eq!(manifest.icon, None);
125 assert!(manifest.nav.is_empty());
126 assert!(manifest.events.progress.is_empty());
127 assert!(manifest.events.complete.is_empty());
128 assert!(manifest.capabilities.is_empty());
129 assert_eq!(manifest.settings_schema, None);
130 assert_eq!(manifest.rust_crate, None);
131 assert_eq!(manifest.frontend_dir, None);
132 }
133
134 #[test]
135 fn test_plugin_nav_item_default_deserialization() {
136 let json = r#"{
137 "route": "/test",
138 "label": {
139 "en": "Test"
140 }
141 }"#;
142
143 let nav_item: PluginNavItem = serde_json::from_str(json).unwrap();
144
145 assert_eq!(nav_item.route, "/test");
146 assert_eq!(nav_item.icon_svg, None);
147 assert_eq!(nav_item.group, NavGroup::Secondary);
148 assert_eq!(nav_item.order, 50);
149 }
150
151 #[test]
152 fn test_registry_entry_default_deserialization() {
153 let json = r#"{
154 "id": "test-plugin",
155 "name": "Test Plugin",
156 "description": "A test plugin",
157 "author": "Test Author",
158 "repo": "https://github.com/test/repo"
159 }"#;
160
161 let entry: RegistryEntry = serde_json::from_str(json).unwrap();
162
163 assert_eq!(entry.id, "test-plugin");
164 assert_eq!(entry.homepage, None);
165 assert!(entry.tags.is_empty());
166 assert_eq!(entry.official, false);
167 assert!(entry.capabilities.is_empty());
168 }
169}