termux_notification/
lib.rs1#[cfg(any(doc, feature = "callbacks"))]
22pub mod callbacks;
23mod options;
24pub mod remove_handle;
25
26use std::{
27 collections::HashMap,
28 io,
29 process::{Command, Output},
30};
31
32use remove_handle::RemoveHandle;
33
34#[derive(Debug, Default, Clone, PartialEq, Eq)]
36pub struct TermuxNotification {
37 args: HashMap<&'static str, Option<String>>,
38}
39
40impl TermuxNotification {
41 #[must_use]
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn id(&mut self, id: impl Into<String>) -> &mut Self {
48 self.args.insert(options::ID, Some(id.into()));
49 self
50 }
51
52 pub fn title(&mut self, title: impl Into<String>) -> &mut Self {
54 self.args.insert(options::TITLE, Some(title.into()));
55 self
56 }
57
58 pub fn content(&mut self, content: impl Into<String>) -> &mut Self {
60 self.args.insert(options::CONTENT, Some(content.into()));
61 self
62 }
63
64 pub fn icon(&mut self, icon: impl Into<String>) -> &mut Self {
68 self.args.insert(options::ICON, Some(icon.into()));
69 self
70 }
71
72 pub fn alert_once(&mut self, alert_once: bool) -> &mut Self {
74 let k = options::ALERT_ONCE;
75 if alert_once {
76 self.args.insert(k, None);
77 } else {
78 self.args.remove(k);
79 }
80 self
81 }
82
83 pub fn ongoing(&mut self, ongoing: bool) -> &mut Self {
85 let k = options::ONGOING;
86 if ongoing {
87 self.args.insert(k, None);
88 } else {
89 self.args.remove(k);
90 }
91 self
92 }
93
94 pub fn action(&mut self, action: impl Into<String>) -> &mut Self {
96 self.args.insert(options::ACTION, Some(action.into()));
97 self
98 }
99
100 pub fn on_delete(
102 &mut self,
103 on_delete: impl Into<String>,
104 ) -> &mut Self {
105 self.args.insert(options::ON_DELETE, Some(on_delete.into()));
106 self
107 }
108
109 pub fn button1(
111 &mut self,
112 label: impl Into<String>,
113 action: impl Into<String>,
114 ) -> &mut Self {
115 self.args.insert(options::BUTTON1, Some(label.into()));
116 self
117 .args
118 .insert(options::BUTTON1_ACTION, Some(action.into()));
119 self
120 }
121
122 pub fn button2(
124 &mut self,
125 label: impl Into<String>,
126 action: impl Into<String>,
127 ) -> &mut Self {
128 self.args.insert(options::BUTTON2, Some(label.into()));
129 self
130 .args
131 .insert(options::BUTTON2_ACTION, Some(action.into()));
132 self
133 }
134
135 pub fn button3(
137 &mut self,
138 label: impl Into<String>,
139 action: impl Into<String>,
140 ) -> &mut Self {
141 self.args.insert(options::BUTTON3, Some(label.into()));
142 self
143 .args
144 .insert(options::BUTTON3_ACTION, Some(action.into()));
145 self
146 }
147
148 pub fn show(&self) -> io::Result<RemoveHandle> {
154 ensure_success(&self.to_command().output()?)?;
155 let id = self.args.get(options::ID);
156 Ok(RemoveHandle::new(id.cloned().flatten()))
157 }
158
159 #[must_use]
161 pub fn to_command(&self) -> Command {
162 let mut cmd = Command::new("termux-notification");
163 for (key, val) in &self.args {
164 cmd.arg(key);
165 if let Some(val) = val {
166 cmd.arg(val);
167 }
168 }
169 cmd
170 }
171}
172
173fn ensure_success(output: &Output) -> io::Result<()> {
177 if output.status.success() {
178 Ok(())
179 } else {
180 Err(io::Error::other(stdout_stderr(output)))
181 }
182}
183
184fn stdout_stderr(output: &Output) -> String {
185 let stdout = String::from_utf8_lossy(&output.stdout);
186 let stderr = String::from_utf8_lossy(&output.stderr);
187 [stdout, stderr]
188 .into_iter()
189 .filter(|s| !s.is_empty())
190 .collect::<Vec<_>>()
191 .join("\n")
192}