1#![cfg_attr(rustfmt, rustfmt_skip)]
2
3#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
4use zbus::zvariant;
5
6#[cfg(all(unix, not(target_os = "macos")))]
7pub(crate) mod message;
8
9#[cfg(all(feature = "images", any(feature = "dbus", feature = "zbus"), unix, not(target_os = "macos")))]
10use crate::image::Image;
11
12#[cfg(all(feature = "images", feature = "zbus", unix, not(target_os = "macos")))]
13use crate::image::image_spec_str;
14use crate::Urgency;
15
16#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))] use crate::notification::Notification;
17#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))] use std::collections::HashMap;
18
19mod constants;
20
21#[cfg(all(unix, not(target_os = "macos")))]
22#[derive(Eq, PartialEq, Hash, Clone, Debug)]
23pub(crate) enum CustomHintType {
24 Int,
25 String,
26}
27
28#[derive(Eq, PartialEq, Hash, Clone, Debug)]
40pub enum Hint {
41 ActionIcons(bool),
43
44 Category(String),
48
49 DesktopEntry(String),
52
53 #[cfg(all(feature = "images", unix, not(target_os = "macos")))]
55 ImageData(Image),
56
57 ImagePath(String),
59
60 Resident(bool),
62
63 SoundFile(String),
65
66 SoundName(String),
68
69 SuppressSound(bool),
71
72 Transient(bool),
74
75 X(i32),
78
79 Y(i32),
82
83 Urgency(Urgency),
85
86 Custom(String, String),
88
89 CustomInt(String, i32),
91
92 Invalid }
95
96impl Hint {
97 pub fn as_bool(&self) -> Option<bool> {
99 match *self {
100 | Hint::ActionIcons(inner)
101 | Hint::Resident(inner)
102 | Hint::SuppressSound(inner)
103 | Hint::Transient(inner) => Some(inner),
104 _ => None
105 }
106 }
107
108 pub fn as_i32(&self) -> Option<i32> {
110 match *self {
111 Hint::X(inner) | Hint::Y(inner) => Some(inner),
112 _ => None
113 }
114 }
115
116 pub fn as_str(&self) -> Option<&str> {
118 match *self {
119 Hint::DesktopEntry(ref inner) |
120 Hint::ImagePath(ref inner) |
121 Hint::SoundFile(ref inner) |
122 Hint::SoundName(ref inner) => Some(inner),
123 _ => None
124 }
125 }
126
127 pub fn from_key_val(name: &str, value: &str) -> Result<Hint, String> {
129 match (name,value){
130 (constants::ACTION_ICONS,val) => val.parse::<bool>().map(Hint::ActionIcons).map_err(|e|e.to_string()),
131 (constants::CATEGORY, val) => Ok(Hint::Category(val.to_owned())),
132 (constants::DESKTOP_ENTRY, val) => Ok(Hint::DesktopEntry(val.to_owned())),
133 (constants::IMAGE_PATH, val) => Ok(Hint::ImagePath(val.to_owned())),
134 (constants::RESIDENT, val) => val.parse::<bool>().map(Hint::Resident).map_err(|e|e.to_string()),
135 (constants::SOUND_FILE, val) => Ok(Hint::SoundFile(val.to_owned())),
136 (constants::SOUND_NAME, val) => Ok(Hint::SoundName(val.to_owned())),
137 (constants::SUPPRESS_SOUND, val) => val.parse::<bool>().map(Hint::SuppressSound).map_err(|e|e.to_string()),
138 (constants::TRANSIENT, val) => val.parse::<bool>().map(Hint::Transient).map_err(|e|e.to_string()),
139 (constants::X, val) => val.parse::<i32>().map(Hint::X).map_err(|e|e.to_string()),
140 (constants::Y, val) => val.parse::<i32>().map(Hint::Y).map_err(|e|e.to_string()),
141 _ => Err(String::from("unknown name"))
142 }
143 }
144}
145
146#[cfg(all(unix, not(target_os = "macos")))]
147impl Hint {}
148
149#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
150#[test]
151fn test_hints_to_map() {
152
153 let n1 = Notification::new()
156 .hint(Hint::Custom("foo".into(), "bar1".into()))
157 .hint(Hint::Custom("foo".into(), "bar2".into()))
158 .hint(Hint::Custom("f00".into(), "bar3".into()))
159 .finalize();
160
161 assert_eq!(hints_to_map(&n1), maplit::hashmap!{
162 "foo" => zvariant::Value::Str("bar2".into()),
163 "f00" => zvariant::Value::Str("bar3".into())
164 });
165}
166
167#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
168pub(crate) fn hints_to_map(notification: &Notification) -> HashMap::<&str, zvariant::Value<'_>> {
169 notification
170 .get_hints()
171 .map(Into::into)
172 .collect()
173}
174
175#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
176impl<'a> From<&'a Hint> for (&'a str, zvariant::Value<'a>) {
177 fn from(val: &'a Hint) -> Self {
178 use self::constants::*;
179 match val {
180 Hint::ActionIcons(value) => (ACTION_ICONS , zvariant::Value::Bool(*value)), Hint::Category(value) => (CATEGORY , zvariant::Value::Str(value.as_str().into())),
182 Hint::DesktopEntry(value) => (DESKTOP_ENTRY , zvariant::Value::Str(value.as_str().into())),
183
184 #[cfg(all(feature = "zbus", feature = "images", unix, not(target_os = "macos")))]
185 Hint::ImageData(image) => (
187 image_spec_str(*crate::SPEC_VERSION),
188 zvariant::Value::Structure(
189 image.to_tuple().into()
190 )
191 ),
192
193
194 Hint::ImagePath(value) => (IMAGE_PATH , zvariant::Value::Str(value.as_str().into())),
195 Hint::Resident(value) => (RESIDENT , zvariant::Value::Bool(*value)), Hint::SoundFile(value) => (SOUND_FILE , zvariant::Value::Str(value.as_str().into())),
197 Hint::SoundName(value) => (SOUND_NAME , zvariant::Value::Str(value.as_str().into())),
198 Hint::SuppressSound(value) => (SUPPRESS_SOUND , zvariant::Value::Bool(*value)),
199 Hint::Transient(value) => (TRANSIENT , zvariant::Value::Bool(*value)),
200 Hint::X(value) => (X , zvariant::Value::I32(*value)),
201 Hint::Y(value) => (Y , zvariant::Value::I32(*value)),
202 Hint::Urgency(value) => (URGENCY , zvariant::Value::U8(*value as u8)),
203 Hint::Custom(key, val) => (key.as_str() , zvariant::Value::Str(val.as_str().into())),
204 Hint::CustomInt(key, val) => (key.as_str() , zvariant::Value::I32(*val)),
205 Hint::Invalid => (INVALID , zvariant::Value::Str(INVALID.into()))
206 }
207 }
208}
209
210
211#[cfg(all(feature = "dbus", unix, not(target_os = "macos")))]
212impl<'a, A: dbus::arg::RefArg> From<(&'a String, &'a A)> for Hint {
213 fn from(pair: (&String, &A)) -> Self {
214
215 let (key, variant) = pair;
216 match (key.as_ref(), variant.as_u64(), variant.as_i64(), variant.as_str().map(String::from)) {
217
218 (constants::ACTION_ICONS, Some(1), _, _ ) => Hint::ActionIcons(true),
219 (constants::ACTION_ICONS, _, _, _ ) => Hint::ActionIcons(false),
220 (constants::URGENCY, level, _, _ ) => Hint::Urgency(level.into()),
221 (constants::CATEGORY, _, _, Some(name) ) => Hint::Category(name),
222
223 (constants::DESKTOP_ENTRY, _, _, Some(entry)) => Hint::DesktopEntry(entry),
224 (constants::IMAGE_PATH, _, _, Some(path) ) => Hint::ImagePath(path),
225 (constants::RESIDENT, Some(1), _, _ ) => Hint::Resident(true),
226 (constants::RESIDENT, _, _, _ ) => Hint::Resident(false),
227
228 (constants::SOUND_FILE, _, _, Some(path) ) => Hint::SoundFile(path),
229 (constants::SOUND_NAME, _, _, Some(name) ) => Hint::SoundName(name),
230 (constants::SUPPRESS_SOUND, Some(1), _, _ ) => Hint::SuppressSound(true),
231 (constants::SUPPRESS_SOUND, _, _, _ ) => Hint::SuppressSound(false),
232 (constants::TRANSIENT, Some(1), _, _ ) => Hint::Transient(true),
233 (constants::TRANSIENT, _, _, _ ) => Hint::Transient(false),
234 (constants::X, _, Some(x), _ ) => Hint::X(x as i32),
235 (constants::Y, _, Some(y), _ ) => Hint::Y(y as i32),
236
237 other => {
238 eprintln!("Invalid Hint {:#?} ", other);
239 Hint::Invalid
240 }
241 }
242 }
243}