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_no_default_features", any(feature = "dbus", feature = "zbus"), unix, not(target_os = "macos")))]
10use crate::image::Image;
11
12#[cfg(all(feature = "images_no_default_features", 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_no_default_features", unix, not(target_os = "macos")))]
55 ImageData(Image),
56
57 ImagePath(String),
59
60 Resident(bool),
62
63 SoundFile(String),
65
66 SoundName(String),
69
70 SuppressSound(bool),
72
73 Transient(bool),
75
76 X(i32),
79
80 Y(i32),
83
84 Urgency(Urgency),
86
87 Custom(String, String),
89
90 CustomInt(String, i32),
92
93 Invalid }
96
97impl Hint {
98 pub fn as_bool(&self) -> Option<bool> {
100 match *self {
101 | Hint::ActionIcons(inner)
102 | Hint::Resident(inner)
103 | Hint::SuppressSound(inner)
104 | Hint::Transient(inner) => Some(inner),
105 _ => None
106 }
107 }
108
109 pub fn as_i32(&self) -> Option<i32> {
111 match *self {
112 Hint::X(inner) | Hint::Y(inner) => Some(inner),
113 _ => None
114 }
115 }
116
117 pub fn as_str(&self) -> Option<&str> {
119 match *self {
120 Hint::DesktopEntry(ref inner) |
121 Hint::ImagePath(ref inner) |
122 Hint::SoundFile(ref inner) |
123 Hint::SoundName(ref inner) => Some(inner),
124 _ => None
125 }
126 }
127
128 pub fn from_key_val(name: &str, value: &str) -> Result<Hint, String> {
130 match (name,value){
131 (constants::ACTION_ICONS,val) => val.parse::<bool>().map(Hint::ActionIcons).map_err(|e|e.to_string()),
132 (constants::CATEGORY, val) => Ok(Hint::Category(val.to_owned())),
133 (constants::DESKTOP_ENTRY, val) => Ok(Hint::DesktopEntry(val.to_owned())),
134 (constants::IMAGE_PATH, val) => Ok(Hint::ImagePath(val.to_owned())),
135 (constants::RESIDENT, val) => val.parse::<bool>().map(Hint::Resident).map_err(|e|e.to_string()),
136 (constants::SOUND_FILE, val) => Ok(Hint::SoundFile(val.to_owned())),
137 (constants::SOUND_NAME, val) => Ok(Hint::SoundName(val.to_owned())),
138 (constants::SUPPRESS_SOUND, val) => val.parse::<bool>().map(Hint::SuppressSound).map_err(|e|e.to_string()),
139 (constants::TRANSIENT, val) => val.parse::<bool>().map(Hint::Transient).map_err(|e|e.to_string()),
140 (constants::X, val) => val.parse::<i32>().map(Hint::X).map_err(|e|e.to_string()),
141 (constants::Y, val) => val.parse::<i32>().map(Hint::Y).map_err(|e|e.to_string()),
142 _ => Err(String::from("unknown name"))
143 }
144 }
145}
146
147#[cfg(all(unix, not(target_os = "macos")))]
148impl Hint {}
149
150#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
151#[test]
152fn test_hints_to_map() {
153
154 let n1 = Notification::new()
157 .hint(Hint::Custom("foo".into(), "bar1".into()))
158 .hint(Hint::Custom("foo".into(), "bar2".into()))
159 .hint(Hint::Custom("f00".into(), "bar3".into()))
160 .finalize();
161
162 assert_eq!(hints_to_map(&n1), maplit::hashmap!{
163 "foo" => zvariant::Value::Str("bar2".into()),
164 "f00" => zvariant::Value::Str("bar3".into())
165 });
166}
167
168#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
169pub(crate) fn hints_to_map(notification: &Notification) -> HashMap::<&str, zvariant::Value<'_>> {
170 notification
171 .get_hints()
172 .map(Into::into)
173 .collect()
174}
175
176#[cfg(all(feature = "zbus", unix, not(target_os = "macos")))]
177impl<'a> From<&'a Hint> for (&'a str, zvariant::Value<'a>) {
178 fn from(val: &'a Hint) -> Self {
179 use self::constants::*;
180 match val {
181 Hint::ActionIcons(value) => (ACTION_ICONS , zvariant::Value::Bool(*value)), Hint::Category(value) => (CATEGORY , zvariant::Value::Str(value.as_str().into())),
183 Hint::DesktopEntry(value) => (DESKTOP_ENTRY , zvariant::Value::Str(value.as_str().into())),
184
185 #[cfg(all(feature = "zbus", feature = "images_no_default_features", unix, not(target_os = "macos")))]
186 Hint::ImageData(image) => (
188 image_spec_str(*crate::SPEC_VERSION),
189 zvariant::Value::Structure(
190 image.to_tuple().into()
191 )
192 ),
193
194
195 Hint::ImagePath(value) => (IMAGE_PATH , zvariant::Value::Str(value.as_str().into())),
196 Hint::Resident(value) => (RESIDENT , zvariant::Value::Bool(*value)), Hint::SoundFile(value) => (SOUND_FILE , zvariant::Value::Str(value.as_str().into())),
198 Hint::SoundName(value) => (SOUND_NAME , zvariant::Value::Str(value.as_str().into())),
199 Hint::SuppressSound(value) => (SUPPRESS_SOUND , zvariant::Value::Bool(*value)),
200 Hint::Transient(value) => (TRANSIENT , zvariant::Value::Bool(*value)),
201 Hint::X(value) => (X , zvariant::Value::I32(*value)),
202 Hint::Y(value) => (Y , zvariant::Value::I32(*value)),
203 Hint::Urgency(value) => (URGENCY , zvariant::Value::U8(*value as u8)),
204 Hint::Custom(key, val) => (key.as_str() , zvariant::Value::Str(val.as_str().into())),
205 Hint::CustomInt(key, val) => (key.as_str() , zvariant::Value::I32(*val)),
206 Hint::Invalid => (INVALID , zvariant::Value::Str(INVALID.into()))
207 }
208 }
209}
210
211
212#[cfg(all(feature = "dbus", unix, not(target_os = "macos")))]
213impl<'a, A: dbus::arg::RefArg> From<(&'a String, &'a A)> for Hint {
214 fn from(pair: (&String, &A)) -> Self {
215
216 let (key, variant) = pair;
217 match (key.as_ref(), variant.as_u64(), variant.as_i64(), variant.as_str().map(String::from)) {
218
219 (constants::ACTION_ICONS, Some(1), _, _ ) => Hint::ActionIcons(true),
220 (constants::ACTION_ICONS, _, _, _ ) => Hint::ActionIcons(false),
221 (constants::URGENCY, level, _, _ ) => Hint::Urgency(level.into()),
222 (constants::CATEGORY, _, _, Some(name) ) => Hint::Category(name),
223
224 (constants::DESKTOP_ENTRY, _, _, Some(entry)) => Hint::DesktopEntry(entry),
225 (constants::IMAGE_PATH, _, _, Some(path) ) => Hint::ImagePath(path),
226 (constants::RESIDENT, Some(1), _, _ ) => Hint::Resident(true),
227 (constants::RESIDENT, _, _, _ ) => Hint::Resident(false),
228
229 (constants::SOUND_FILE, _, _, Some(path) ) => Hint::SoundFile(path),
230 (constants::SOUND_NAME, _, _, Some(name) ) => Hint::SoundName(name),
231 (constants::SUPPRESS_SOUND, Some(1), _, _ ) => Hint::SuppressSound(true),
232 (constants::SUPPRESS_SOUND, _, _, _ ) => Hint::SuppressSound(false),
233 (constants::TRANSIENT, Some(1), _, _ ) => Hint::Transient(true),
234 (constants::TRANSIENT, _, _, _ ) => Hint::Transient(false),
235 (constants::X, _, Some(x), _ ) => Hint::X(x as i32),
236 (constants::Y, _, Some(y), _ ) => Hint::Y(y as i32),
237
238 other => {
239 eprintln!("Invalid Hint {:#?} ", other);
240 Hint::Invalid
241 }
242 }
243 }
244}