Skip to main content

hyprshell_hyprland/data/
regular.rs

1use super::*;
2use crate::default_instance;
3use crate::error::hypr_err;
4use crate::instance::Instance;
5use derive_more::Display;
6use serde::{Deserialize, Serialize};
7use serde_repr::{Deserialize_repr, Serialize_repr};
8
9/// This pub(crate) enum holds every socket command that returns data
10#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)]
11pub(crate) enum DataCommands {
12    #[display("monitors all")]
13    Monitors,
14    #[display("workspaces")]
15    Workspaces,
16    #[display("activeworkspace")]
17    ActiveWorkspace,
18    #[display("clients")]
19    Clients,
20    #[display("activewindow")]
21    ActiveWindow,
22    #[display("layers")]
23    Layers,
24    #[display("devices")]
25    Devices,
26    #[display("version")]
27    Version,
28    #[display("cursorpos")]
29    CursorPosition,
30    #[display("binds")]
31    Binds,
32    #[display("animations")]
33    Animations,
34    #[display("workspacerules")]
35    WorkspaceRules,
36}
37
38/// This struct holds a basic identifier for a workspace often used in other structs
39#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
40pub struct WorkspaceBasic {
41    /// The workspace Id
42    pub id: WorkspaceId,
43    /// The workspace's name
44    pub name: String,
45}
46
47/// This enum provides the different monitor transforms
48#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, PartialEq, Eq, Copy)]
49#[repr(u8)]
50pub enum Transforms {
51    /// No transform
52    Normal = 0,
53    /// Rotated 90 degrees
54    Normal90 = 1,
55    /// Rotated 180 degrees
56    Normal180 = 2,
57    /// Rotated 270 degrees
58    Normal270 = 3,
59    /// Flipped
60    Flipped = 4,
61    /// Flipped and rotated 90 degrees
62    Flipped90 = 5,
63    /// Flipped and rotated 180 degrees
64    Flipped180 = 6,
65    /// Flipped and rotated 270 degrees
66    Flipped270 = 7,
67}
68
69/// This struct holds information for a monitor
70#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
71pub struct Monitor {
72    /// The monitor id
73    pub id: MonitorId,
74    /// The monitor's name
75    pub name: String,
76    /// The monitor's description
77    pub description: String,
78    /// The monitor width (in pixels)
79    pub width: u16,
80    /// The monitor height (in pixels)
81    pub height: u16,
82    /// The monitor's refresh rate (in hertz)
83    #[serde(rename = "refreshRate")]
84    pub refresh_rate: f32,
85    /// The monitor's position on the x axis (not irl ofc)
86    pub x: i32,
87    /// The monitor's position on the x axis (not irl ofc)
88    pub y: i32,
89    /// A basic identifier for the active workspace
90    #[serde(rename = "activeWorkspace")]
91    pub active_workspace: WorkspaceBasic,
92    /// A basic identifier for the special workspace
93    #[serde(rename = "specialWorkspace")]
94    pub special_workspace: WorkspaceBasic,
95    /// Reserved is the amount of space (in pre-scale pixels) that a layer surface has claimed
96    pub reserved: (u16, u16, u16, u16),
97    /// The display's scale
98    pub scale: f32,
99    /// I think like the rotation?
100    pub transform: Transforms,
101    /// a string that identifies if the display is active
102    pub focused: bool,
103    /// The dpms status of a monitor
104    #[serde(rename = "dpmsStatus")]
105    pub dpms_status: bool,
106    /// VRR state
107    pub vrr: bool,
108    /// Is the monitor disabled or not
109    pub disabled: bool,
110    /// The physical width of the monitor in mm
111    #[serde(rename = "physicalWidth", default)]
112    pub physical_width: u16,
113    /// The physical size of the monitor in mm
114    #[serde(rename = "physicalHeight", default)]
115    pub physical_height: u16,
116}
117
118impl HyprDataActive for Monitor {
119    fn get_active() -> crate::Result<Self> {
120        Self::instance_get_active(default_instance()?)
121    }
122    #[cfg(any(feature = "async-lite", feature = "tokio"))]
123    async fn get_active_async() -> crate::Result<Self> {
124        Self::instance_get_active_async(default_instance()?).await
125    }
126    fn instance_get_active(instance: &Instance) -> crate::Result<Self> {
127        let all = Monitors::instance_get(instance)?;
128        if let Some(it) = all.into_iter().find(|item| item.focused) {
129            Ok(it)
130        } else {
131            hypr_err!("No active Hyprland monitor detected!")
132        }
133    }
134    #[cfg(any(feature = "async-lite", feature = "tokio"))]
135    async fn instance_get_active_async(instance: &Instance) -> crate::Result<Self> {
136        let all = Monitors::instance_get_async(instance).await?;
137        if let Some(it) = all.into_iter().find(|item| item.focused) {
138            Ok(it)
139        } else {
140            hypr_err!("No active Hyprland monitor detected!")
141        }
142    }
143}
144
145create_data_struct!(
146    vector,
147    name: Monitors,
148    command: DataCommands::Monitors,
149    holding_type: Monitor,
150    doc: "This struct holds a vector of monitors"
151);
152
153/// This struct holds information for a workspace
154#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
155pub struct Workspace {
156    /// The workspace Id
157    pub id: WorkspaceId,
158    /// The workspace's name
159    pub name: String,
160    /// The monitor the workspace is on
161    pub monitor: String,
162    /// The monitor id the workspace is on, can be None in some cases
163    #[serde(rename = "monitorID")]
164    pub monitor_id: Option<MonitorId>,
165    /// The amount of windows in the workspace
166    pub windows: u16,
167    /// A bool that shows if there is a fullscreen window in the workspace
168    #[serde(rename = "hasfullscreen")]
169    pub fullscreen: bool,
170    /// The last window's [Address]
171    #[serde(rename = "lastwindow")]
172    pub last_window: Address,
173    /// The last window's title
174    #[serde(rename = "lastwindowtitle")]
175    pub last_window_title: String,
176}
177
178impl HyprDataActive for Workspace {
179    fn get_active() -> crate::Result<Self> {
180        Self::instance_get_active(default_instance()?)
181    }
182    #[cfg(any(feature = "async-lite", feature = "tokio"))]
183    async fn get_active_async() -> crate::Result<Self> {
184        Self::instance_get_active_async(default_instance()?).await
185    }
186    fn instance_get_active(instance: &Instance) -> crate::Result<Self> {
187        let data = instance.write_to_socket(command!(JSON, "{}", DataCommands::ActiveWorkspace))?;
188        let deserialized: Workspace = serde_json::from_str(&data)?;
189        Ok(deserialized)
190    }
191    #[cfg(any(feature = "async-lite", feature = "tokio"))]
192    async fn instance_get_active_async(instance: &Instance) -> crate::Result<Self> {
193        let data = instance
194            .write_to_socket_async(command!(JSON, "{}", DataCommands::ActiveWorkspace))
195            .await?;
196        let deserialized: Workspace = serde_json::from_str(&data)?;
197        Ok(deserialized)
198    }
199}
200
201create_data_struct!(
202    vector,
203    name: Workspaces,
204    command: DataCommands::Workspaces,
205    holding_type: Workspace,
206    doc: "This type provides a vector of workspaces"
207);
208
209/// This struct holds information for a client/window fullscreen mode
210#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, PartialEq, Eq, Copy)]
211#[repr(u8)]
212pub enum FullscreenMode {
213    /// Normal window
214    None = 0,
215    /// Maximized window
216    Maximized = 1,
217    /// Fullscreen window
218    Fullscreen = 2,
219    /// Maximized and fullscreen window
220    MaximizedFullscreen = 3,
221}
222
223/// This struct holds information for a client/window
224#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
225pub struct Client {
226    /// The client's [`Address`][crate::shared::Address]
227    pub address: Address,
228    /// The window location
229    pub at: (i16, i16),
230    /// The window size
231    pub size: (i16, i16),
232    /// The workspace its on
233    pub workspace: WorkspaceBasic,
234    /// Is this window floating?
235    pub floating: bool,
236    /// The internal fullscreen mode
237    pub fullscreen: FullscreenMode,
238    /// The client fullscreen mode
239    #[serde(rename = "fullscreenClient")]
240    pub fullscreen_client: FullscreenMode,
241    /// The monitor id the window is on, can be None in some cases
242    pub monitor: Option<MonitorId>,
243    /// The initial window class
244    #[serde(rename = "initialClass")]
245    pub initial_class: String,
246    /// The window class
247    pub class: String,
248    /// The initial window title
249    #[serde(rename = "initialTitle")]
250    pub initial_title: String,
251    /// The window title
252    pub title: String,
253    /// The process Id of the client
254    pub pid: i32,
255    /// Is this window running under XWayland?
256    pub xwayland: bool,
257    /// Is this window pinned?
258    pub pinned: bool,
259    /// Group members
260    pub grouped: Vec<Box<Address>>,
261    /// Is this window print on screen
262    pub mapped: bool,
263    /// The swallowed window
264    pub swallowing: Option<Box<Address>>,
265    /// When was this window last focused relatively to other windows? 0 for current, 1 previous, 2 previous before that, etc
266    #[serde(rename = "focusHistoryID")]
267    pub focus_history_id: i8,
268}
269
270#[derive(Deserialize, Debug)]
271#[serde(deny_unknown_fields)]
272struct Empty {}
273
274impl HyprDataActiveOptional for Client {
275    fn get_active() -> crate::Result<Option<Self>> {
276        Self::instance_get_active(default_instance()?)
277    }
278    #[cfg(any(feature = "async-lite", feature = "tokio"))]
279    async fn get_active_async() -> crate::Result<Option<Self>> {
280        Self::instance_get_active_async(default_instance()?).await
281    }
282    fn instance_get_active(instance: &Instance) -> crate::Result<Option<Self>> {
283        let data = instance.write_to_socket(command!(JSON, "{}", DataCommands::ActiveWindow))?;
284        let res = serde_json::from_str::<Empty>(&data);
285        if res.is_err() {
286            let t = serde_json::from_str::<Client>(&data)?;
287            Ok(Some(t))
288        } else {
289            Ok(None)
290        }
291    }
292    #[cfg(any(feature = "async-lite", feature = "tokio"))]
293    async fn instance_get_active_async(instance: &Instance) -> crate::Result<Option<Self>> {
294        let data = instance
295            .write_to_socket_async(command!(JSON, "{}", DataCommands::ActiveWindow))
296            .await?;
297        let res = serde_json::from_str::<Empty>(&data);
298        if res.is_err() {
299            let t = serde_json::from_str::<Client>(&data)?;
300            Ok(Some(t))
301        } else {
302            Ok(None)
303        }
304    }
305}
306
307create_data_struct!(
308    vector,
309    name: Clients,
310    command: DataCommands::Clients,
311    holding_type: Client,
312    doc: "This struct holds a vector of clients"
313);
314
315/// This struct holds information about a layer surface/client
316#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
317pub struct LayerClient {
318    /// The layer's [`Address`][crate::shared::Address]
319    pub address: Address,
320    /// The layer's x position
321    pub x: i32,
322    /// The layer's y position
323    pub y: i32,
324    /// The layer's width
325    pub w: i16,
326    /// The layer's height
327    pub h: i16,
328    /// The layer's namespace
329    pub namespace: String,
330}
331
332/// This struct holds all the layer surfaces for a display
333#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
334pub struct LayerDisplay {
335    /// The different levels of layers
336    pub levels: HashMap<String, Vec<LayerClient>>,
337}
338
339implement_iterators!(
340    table,
341    name: LayerDisplay,
342    iterated_field: levels,
343    key: String,
344    value: Vec<LayerClient>,
345);
346
347create_data_struct!(
348    table,
349    name: Layers,
350    command: DataCommands::Layers,
351    key: String,
352    value: LayerDisplay,
353    doc: "This struct holds a hashmap of all current displays, and their layer surfaces"
354);
355
356/// This struct holds information about a mouse device
357#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
358pub struct Mouse {
359    /// The mouse's address
360    pub address: Address,
361    /// The mouse's name
362    pub name: String,
363}
364
365/// This struct holds information about a keyboard device
366#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
367pub struct Keyboard {
368    /// The keyboard's address
369    pub address: Address,
370    /// The keyboard's name
371    pub name: String,
372    /// The keyboard rules
373    pub rules: String,
374    /// The keyboard model
375    pub model: String,
376    /// The layout of the keyboard
377    pub layout: String,
378    /// The keyboard variant
379    pub variant: String,
380    /// The keyboard options
381    pub options: String,
382    /// The keyboard's active keymap
383    pub active_keymap: String,
384    /// The keyboard's primary status
385    pub main: bool,
386}
387
388/// A enum that holds the types of tablets
389#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
390pub enum TabletType {
391    /// The TabletPad type of tablet
392    #[serde(rename = "tabletPad")]
393    TabletPad,
394    /// The TabletTool type of tablet
395    #[serde(rename = "tabletTool")]
396    TabletTool,
397}
398
399/// A enum to match what the tablet belongs to
400#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
401#[serde(untagged)]
402pub enum TabletBelongsTo {
403    /// The belongsTo data if the tablet is of type TabletPad
404    TabletPad {
405        /// The name of the parent
406        name: String,
407        /// The address of the parent
408        address: Address,
409    },
410    /// The belongsTo data if the tablet is of type TabletTool
411    Address(Address),
412}
413
414/// This struct holds information about a tablet device
415#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
416pub struct Tablet {
417    /// The tablet's address
418    pub address: Address,
419    /// The tablet type
420    #[serde(rename = "type")]
421    pub tablet_type: Option<TabletType>,
422    /// What the tablet belongs to
423    #[serde(rename = "belongsTo")]
424    pub belongs_to: Option<TabletBelongsTo>,
425    /// The name of the tablet
426    pub name: Option<String>,
427}
428
429/// This struct holds all current devices
430#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
431pub struct Devices {
432    /// All the mice
433    pub mice: Vec<Mouse>,
434    /// All the keyboards
435    pub keyboards: Vec<Keyboard>,
436    /// All the tablets
437    pub tablets: Vec<Tablet>,
438}
439impl_on!(Devices);
440
441/// This struct holds version information
442#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
443pub struct Version {
444    /// The git branch Hyprland was built on
445    pub branch: String,
446    /// The git commit Hyprland was built on
447    pub commit: String,
448    #[serde(default)]
449    /// The Hyprland version
450    pub version: Option<String>,
451    /// This is true if there were unstaged changed when Hyprland was built
452    pub dirty: bool,
453    /// The git commit message
454    pub commit_message: String,
455    /// The git commit date
456    pub commit_date: String,
457    /// The git tag hyprland was built on
458    pub tag: String,
459    /// The amount of commits to Hyprland at buildtime
460    pub commits: String,
461    /// Aquamarine version
462    #[serde(rename = "buildAquamarine")]
463    pub build_aquamarine: String,
464    /// The flags that Hyprland was built with
465    pub flags: Vec<String>,
466}
467impl_on!(Version);
468
469/// This struct holds information on the cursor position
470#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
471pub struct CursorPosition {
472    /// The x position of the cursor
473    pub x: i64,
474    /// The y position of the cursor
475    pub y: i64,
476}
477impl_on!(CursorPosition);
478
479/// A keybinding returned from the binds command
480#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
481pub struct Bind {
482    /// Is it locked?
483    pub locked: bool,
484    /// Is it a mouse bind?
485    pub mouse: bool,
486    /// Does it execute on release?
487    pub release: bool,
488    /// Can it be held?
489    pub repeat: bool,
490    /// It's modmask
491    pub modmask: u16,
492    /// The submap its apart of
493    pub submap: String,
494    /// The key
495    pub key: String,
496    /// The keycode
497    pub keycode: i16,
498    /// The dispatcher to be executed
499    pub dispatcher: String,
500    /// The dispatcher arg
501    pub arg: String,
502    /// description from bind[d]
503    pub description: String,
504}
505
506create_data_struct!(
507    vector,
508    name: Binds,
509    command: DataCommands::Binds,
510    holding_type: Bind,
511    doc: "This struct holds a vector of binds"
512);
513
514/// Animation styles
515#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
516pub enum AnimationStyle {
517    /// Slide animation
518    Slide,
519    /// Vertical slide animation
520    SlideVert,
521    /// Fading slide animation
522    SlideFade,
523    /// Fading slide animation in a vertical direction
524    SlideFadeVert,
525    /// Popin animation (with percentage)
526    PopIn(u8),
527    /// Fade animation
528    Fade,
529    /// Once animation used for gradient animation
530    Once,
531    /// Loop animation used for gradient animation
532    Loop,
533    /// No animation style
534    None,
535    /// Unknown style
536    Unknown(String),
537}
538
539impl From<String> for AnimationStyle {
540    fn from(value: String) -> Self {
541        if value.starts_with("popin") {
542            let mut iter = value.split(' ');
543            iter.next();
544            AnimationStyle::PopIn({
545                let mut str = iter.next().unwrap_or("100%").to_string();
546                str.remove(str.len() - 1);
547
548                str.parse().unwrap_or(100_u8)
549            })
550        } else {
551            match value.as_str() {
552                "slide" => AnimationStyle::Slide,
553                "slidevert" => AnimationStyle::SlideVert,
554                "fade" => AnimationStyle::Fade,
555                "slidefade" => AnimationStyle::SlideFade,
556                "slidefadevert" => AnimationStyle::SlideFadeVert,
557                "once" => AnimationStyle::Once,
558                "loop" => AnimationStyle::Loop,
559                "" => AnimationStyle::None,
560                _ => AnimationStyle::Unknown(value),
561            }
562        }
563    }
564}
565/// Bezier identifier
566#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
567pub enum BezierIdent {
568    /// No bezier specified
569    #[serde(rename = "")]
570    None,
571    /// The default bezier
572    #[serde(rename = "default")]
573    Default,
574    /// A specified bezier
575    #[serde(rename = "name")]
576    Specified(String),
577}
578
579impl From<String> for BezierIdent {
580    fn from(value: String) -> Self {
581        match value.as_str() {
582            "" => BezierIdent::None,
583            "default" => BezierIdent::Default,
584            _ => BezierIdent::Specified(value),
585        }
586    }
587}
588
589#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
590struct RawBezierIdent {
591    pub name: String,
592}
593
594/// A bezier curve
595#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
596pub struct Bezier {
597    ///. Name of the bezier
598    pub name: String,
599    /// X position of first point
600    pub x0: f32,
601    /// Y position of first point
602    pub y0: f32,
603    /// X position of second point
604    pub x1: f32,
605    /// Y position of second point
606    pub y1: f32,
607}
608
609/// A struct representing a animation
610#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
611struct AnimationRaw {
612    /// The name of the animation
613    pub name: String,
614    /// Is it overridden?
615    pub overridden: bool,
616    /// What bezier does it use?
617    pub bezier: String,
618    /// Is it enabled?
619    pub enabled: bool,
620    /// How fast is it?
621    pub speed: f32,
622    /// The style of animation
623    pub style: String,
624}
625
626/// A struct representing a animation
627#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
628pub struct Animation {
629    /// The name of the animation
630    pub name: String,
631    /// Is it overridden?
632    pub overridden: bool,
633    /// What bezier does it use?
634    pub bezier: BezierIdent,
635    /// Is it enabled?
636    pub enabled: bool,
637    /// How fast is it?
638    pub speed: f32,
639    /// The style of animation
640    pub style: AnimationStyle,
641}
642
643#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
644struct AnimationsRaw(Vec<AnimationRaw>, Vec<RawBezierIdent>);
645
646/// Struct that holds animations and beziers
647#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
648pub struct Animations(pub Vec<Animation>, pub Vec<BezierIdent>);
649
650impl HyprData for Animations {
651    fn get() -> crate::Result<Self> {
652        Self::instance_get(default_instance()?)
653    }
654
655    #[cfg(any(feature = "async-lite", feature = "tokio"))]
656    async fn get_async() -> crate::Result<Self> {
657        Self::instance_get_async(default_instance()?).await
658    }
659
660    fn instance_get(instance: &Instance) -> crate::Result<Self> {
661        let out = instance.write_to_socket(command!(JSON, "{}", DataCommands::Animations))?;
662        let des: AnimationsRaw = serde_json::from_str(&out)?;
663        let AnimationsRaw(anims, beziers) = des;
664        let new_anims: Vec<Animation> = anims
665            .into_iter()
666            .map(|item| Animation {
667                name: item.name,
668                overridden: item.overridden,
669                bezier: item.bezier.into(),
670                enabled: item.enabled,
671                speed: item.speed,
672                style: item.style.into(),
673            })
674            .collect();
675        let new_bezs: Vec<BezierIdent> = beziers.into_iter().map(|item| item.name.into()).collect();
676        Ok(Animations(new_anims, new_bezs))
677    }
678    #[cfg(any(feature = "async-lite", feature = "tokio"))]
679    async fn instance_get_async(instance: &Instance) -> crate::Result<Self> {
680        let out = instance
681            .write_to_socket_async(command!(JSON, "{}", DataCommands::Animations))
682            .await?;
683        let des: AnimationsRaw = serde_json::from_str(&out)?;
684        let AnimationsRaw(anims, beziers) = des;
685        let new_anims: Vec<Animation> = anims
686            .into_iter()
687            .map(|item| Animation {
688                name: item.name,
689                overridden: item.overridden,
690                bezier: item.bezier.into(),
691                enabled: item.enabled,
692                speed: item.speed,
693                style: item.style.into(),
694            })
695            .collect();
696        let new_bezs: Vec<BezierIdent> = beziers.into_iter().map(|item| item.name.into()).collect();
697        Ok(Animations(new_anims, new_bezs))
698    }
699}
700
701// HACK: shadow and decorate are actually missing from the hyprctl json output for some reason
702// HACK: gaps_in and gaps_out are returned as arrays with 4 integers, even though Hyprland doesn't support per-side gaps
703/// The rules of an individual workspace, as returned by hyprctl json.
704#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
705pub struct WorkspaceRuleset {
706    /// The name of the workspace
707    #[serde(rename = "workspaceString")]
708    pub workspace_string: String,
709    /// The monitor the workspace is on
710    pub monitor: Option<String>,
711    /// Is it default?
712    pub default: Option<bool>,
713    /// The gaps between windows
714    #[serde(rename = "gapsIn")]
715    pub gaps_in: Option<Vec<i64>>,
716    /// The gaps between windows and monitor edges
717    #[serde(rename = "gapsOut")]
718    pub gaps_out: Option<Vec<i64>>,
719    /// The size of window borders
720    #[serde(rename = "borderSize")]
721    pub border_size: Option<i64>,
722    /// Are borders enabled?
723    pub border: Option<bool>,
724    /// Are shadows enabled?
725    pub shadow: Option<bool>,
726    /// Is rounding enabled?
727    pub rounding: Option<bool>,
728    /// Are window decorations enabled?
729    pub decorate: Option<bool>,
730    /// Is it persistent?
731    pub persistent: Option<bool>,
732}
733
734create_data_struct!(
735    vector,
736    name: WorkspaceRules,
737    command: DataCommands::WorkspaceRules,
738    holding_type: WorkspaceRuleset,
739    doc: "This struct holds a vector of workspace rules per workspace"
740);