glade/lib.rs
1// SPDX-FileCopyrightText: 2021 Agathe Porte <microjoe@microjoe.org>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Easily import Glade-generated UI files into Rust code.
6//!
7//! ```
8//! use gtk::prelude::*;
9//! use gladis::Gladis;
10//!
11//! const GLADE_SRC: &str = r#"
12//! <?xml version="1.0" encoding="UTF-8"?>
13//! <!-- Generated with glade 3.22.2 -->
14//! <interface>
15//! <requires lib="gtk+" version="3.20"/>
16//! <object class="GtkApplicationWindow" id="window">
17//! <property name="can_focus">False</property>
18//! <child type="titlebar">
19//! <placeholder/>
20//! </child>
21//! <child>
22//! <object class="GtkLabel" id="label">
23//! <property name="visible">True</property>
24//! <property name="can_focus">False</property>
25//! <property name="label" translatable="yes">label</property>
26//! </object>
27//! </child>
28//! </object>
29//! </interface>"#;
30//!
31//! #[derive(Gladis, Clone)]
32//! pub struct Window {
33//! pub window: gtk::ApplicationWindow,
34//! pub label: gtk::Label,
35//! }
36//!
37//! gtk::init().unwrap();
38//! let _ui = Window::from_string(GLADE_SRC).unwrap();
39//! ```
40
41use std::{error::Error, fmt::Display};
42
43#[derive(Debug, Clone)]
44pub struct NotFoundError {
45 pub identifier: String,
46 pub typ: String,
47}
48
49impl Display for NotFoundError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(
52 f,
53 "identifier {} of type {} was not found",
54 self.identifier, self.typ
55 )
56 }
57}
58
59impl Error for NotFoundError {}
60
61#[derive(Debug, Clone)]
62pub enum GladisError {
63 NotFound(NotFoundError),
64}
65
66impl Display for GladisError {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 GladisError::NotFound(e) => write!(f, "not found error: {}", e),
70 }
71 }
72}
73
74impl Error for GladisError {
75 fn source(&self) -> Option<&(dyn Error + 'static)> {
76 match self {
77 GladisError::NotFound(e) => Some(e),
78 }
79 }
80}
81
82impl GladisError {
83 pub fn not_found<T>(identifier: T, typ: T) -> Self
84 where
85 T: ToString,
86 {
87 let identifier = identifier.to_string();
88 let typ = typ.to_string();
89 GladisError::NotFound(NotFoundError { identifier, typ })
90 }
91}
92
93pub type Result<T> = std::result::Result<T, GladisError>;
94
95pub trait Gladis {
96 //! A trait to load a struct from a builder.
97 //!
98 //! # Automatic implementation
99 //!
100 //! This trait wakes little sense alone, but truly show its power when used
101 //! with the [glade_derive](https://docs.rs/gladis_proc_macro) crate
102 //! and its `#[derive(Gladis)]` macro.
103 //!
104 //! ```
105 //! use gtk::prelude::*;
106 //! use gladis::Gladis;
107 //!
108 //! #[derive(Gladis, Clone)]
109 //! pub struct Window {
110 //! pub window: gtk::ApplicationWindow,
111 //! pub label: gtk::Label,
112 //! }
113 //! ```
114 //!
115 //! # Manual implementation
116 //!
117 //! Below is an example of manual implementation of the trait.
118 //!
119 //! ```
120 //! use gtk::prelude::*;
121 //! use gladis::{Gladis, Result, GladisError};
122 //!
123 //! pub struct Window {
124 //! pub window: gtk::ApplicationWindow,
125 //! pub label: gtk::Label,
126 //! }
127 //!
128 //! impl Gladis for Window {
129 //! fn from_builder(builder: gtk::Builder) -> Result<Self> {
130 //! let window: gtk::ApplicationWindow = builder
131 //! .object("window")
132 //! .ok_or(GladisError::not_found("window", "gtk::ApplicationWindow"))?;
133 //!
134 //! let label: gtk::Label = builder
135 //! .object("label")
136 //! .ok_or(GladisError::not_found("label", "gtk::Label"))?;
137 //!
138 //! Ok(Self { window, label })
139 //! }
140 //! }
141 //! ```
142
143 /// Populate struct from a builder.
144 ///
145 /// This method should not be called directly but is used as a common
146 /// function for the `from_string` and `from_resource` functions to
147 /// share the same code.
148 fn from_builder(builder: gtk::Builder) -> Result<Self>
149 where
150 Self: std::marker::Sized;
151
152 /// Populate struct from a Glade document.
153 fn from_string(src: &str) -> Result<Self>
154 where
155 Self: std::marker::Sized,
156 {
157 let builder = gtk::Builder::from_string(src);
158 Gladis::from_builder(builder)
159 }
160
161 /// Populate struct from a Glade document as a resource.
162 fn from_resource(resource_path: &str) -> Result<Self>
163 where
164 Self: std::marker::Sized,
165 {
166 let builder = gtk::Builder::from_resource(resource_path);
167 Gladis::from_builder(builder)
168 }
169}
170
171// Re-export #[derive(Gladis)].
172#[cfg(feature = "derive")]
173#[doc(hidden)]
174pub use glade_derive::Gladis;
175
176#[cfg(test)]
177mod tests {
178 use crate::{GladisError, NotFoundError};
179
180 #[test]
181 fn fmt_not_found_error() {
182 let err = NotFoundError {
183 identifier: "foo".to_string(),
184 typ: "bar".to_string(),
185 };
186 assert_eq!(err.to_string(), "identifier foo of type bar was not found");
187 }
188
189 #[test]
190 fn fmt_gladis_error() {
191 let err = GladisError::NotFound(NotFoundError {
192 identifier: "foo".to_string(),
193 typ: "bar".to_string(),
194 });
195 assert_eq!(
196 err.to_string(),
197 "not found error: identifier foo of type bar was not found"
198 );
199 }
200}