1use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
5#[serde(rename_all = "kebab-case")]
6pub enum PanelAnchor {
7 TopLeft,
8 TopCenter,
9 #[default]
10 TopRight,
11 LeftCenter,
12 Center,
13 RightCenter,
14 BottomLeft,
15 BottomCenter,
16 BottomRight,
17}
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
20#[serde(rename_all = "kebab-case")]
21pub enum PanelLayout {
22 #[default]
23 Overlay,
24 Sidebar,
25 Footer,
26}
27
28#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub struct ExtensionPanel {
31 pub version: u32,
32 #[serde(default)]
33 pub order: i32,
34 #[serde(default)]
35 pub layout: PanelLayout,
36 #[serde(default)]
37 pub anchor: PanelAnchor,
38 #[serde(default)]
39 pub offset_x: i32,
40 #[serde(default)]
41 pub offset_y: i32,
42 #[serde(default = "default_width")]
43 pub width: usize,
44 #[serde(default = "default_height")]
45 pub max_height: usize,
46 #[serde(default)]
47 pub min_screen_width: usize,
48 #[serde(default = "default_border")]
49 pub border: bool,
50 #[serde(default)]
51 pub title: String,
52 pub lines: Vec<String>,
53}
54fn default_width() -> usize {
55 44
56}
57fn default_height() -> usize {
58 16
59}
60fn default_border() -> bool {
61 true
62}
63impl ExtensionPanel {
64 pub fn validate(&self) -> Result<(), String> {
65 if self.version != 1 {
66 return Err("unsupported panel version (expected 1)".into());
67 }
68 if !(4..=240).contains(&self.width)
69 || !(1..=80).contains(&self.max_height)
70 || self.min_screen_width > 1000
71 || self.offset_x.unsigned_abs() > 1000
72 || self.offset_y.unsigned_abs() > 1000
73 {
74 return Err("panel geometry is out of range".into());
75 }
76 if self.lines.len() > 64
77 || self.lines.iter().any(|line| line.len() > 2048)
78 || self.title.len() > 256
79 {
80 return Err("panel content exceeds limits".into());
81 }
82 Ok(())
83 }
84 pub fn same_geometry(&self, other: &Self) -> bool {
86 self.layout == other.layout
87 && self.anchor == other.anchor
88 && self.offset_x == other.offset_x
89 && self.offset_y == other.offset_y
90 && self.width == other.width
91 && self.max_height == other.max_height
92 }
93}