cdns_rs/sync/log.rs
1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 *
4 * Copyright (C) 2020 Aleksandr Morozov
5 *
6 * Copyright 2025 Aleksandr Morozov
7 *
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 *
11 * You may not use this work except in compliance with the Licence.
12 *
13 * You may obtain a copy of the Licence at:
14 *
15 * https://joinup.ec.europa.eu/software/page/eupl
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24pub mod cdns_custom_output
25{
26 use std::{fmt::{self, Write}, str, sync::{LazyLock, Mutex, OnceLock}};
27
28 struct DummyOutputAdapter{}
29
30 unsafe impl Send for DummyOutputAdapter {}
31
32 impl Write for DummyOutputAdapter
33 {
34 fn write_str(&mut self, _s: &str) -> fmt::Result
35 {
36 return Ok(());
37 }
38 }
39
40 impl DummyOutputAdapter
41 {
42 fn new() -> Self
43 {
44 return Self{};
45 }
46 }
47
48 static IS_INITIALIZED: OnceLock<bool> = OnceLock::new();
49
50 /// An instance which implements the io::Write + Send
51 static WRITABLE_BUFFER: LazyLock<Mutex<Box<dyn Write + Send>>> = LazyLock::new(|| { Mutex::new(Box::new(DummyOutputAdapter::new())) });
52 static LAST_ERROR: LazyLock<Mutex<Option<String>>> = LazyLock::new(|| { Mutex::new(None) });
53
54 /// Initializes the output to the custom buffer.
55 /// This function does not check if you set the output twice and panics!
56 ///
57 /// # Arguments
58 ///
59 /// * `w` - a dynamic instance in [Box] which implements [Write] and [Send]
60 ///
61 /// # Panics
62 ///
63 /// If the mutex was poisoned, may panic. Also will panic, if it will be
64 /// attempted to reinitialize the instance. If this is a problem, use
65 /// [initialize_safe]
66 pub
67 fn initialize(w: Box<dyn Write + Send>)
68 {
69 // occure the lock
70 let mut lock = WRITABLE_BUFFER.lock().unwrap();
71
72 // set new instance
73 *lock = w;
74
75 // lock the status
76 IS_INITIALIZED.get_or_init(|| true);
77
78 return;
79 }
80
81 /// Initializes the output to the custom buffer.
82 ///
83 /// # Arguments
84 ///
85 /// * `w` - a dynamic instance in [Box] which implements [Write] and [Send]
86 ///
87 /// # Returns
88 ///
89 /// * [bool] true if mutex is not poisoned
90 ///
91 /// * [bool] false if mutex was poisoned
92 ///
93 /// # Panics
94 ///
95 /// Does not panic!
96 pub
97 fn initialize_safe(w: Box<dyn Write + Send>) -> bool
98 {
99 // check if instance is initialized
100 if let None = IS_INITIALIZED.get()
101 {
102 // lock
103 match WRITABLE_BUFFER.lock()
104 {
105 Ok(mut r) =>
106 {
107 // set instance
108 *r = w;
109
110 // lock
111 IS_INITIALIZED.get_or_init(|| true);
112
113 return true;
114 },
115 Err(_e) =>
116 return false,
117 }
118 }
119 else
120 {
121 return false;
122 }
123 }
124
125 /// Returns the status. Once initialized, can not be deinitialized.
126 ///
127 /// # Returns
128 ///
129 /// * [bool] true, if initialized
130 ///
131 /// * [bool] false, if not initialized
132 ///
133 /// # Panics
134 ///
135 /// May panic is mutex is poisoned!
136 pub
137 fn is_initialized() -> bool
138 {
139 return IS_INITIALIZED.get().is_some();
140 }
141
142 /// Returns the status of the mutex answering the quiestion if
143 /// the mutex which guards the access is poisoned.
144 ///
145 /// # Returns
146 ///
147 /// * [bool] true, if poisoned
148 ///
149 /// * [bool] false, if not poisoned
150 ///
151 /// # Panics
152 ///
153 /// Never panics!
154 pub
155 fn is_poisoned() -> bool
156 {
157 return WRITABLE_BUFFER.is_poisoned();
158 }
159
160 /// Moves the last error from storage.
161 pub
162 fn last_error() -> Option<String>
163 {
164 match LAST_ERROR.lock()
165 {
166 Ok(mut r) => return (*r).take(),
167 Err(_e) => return None,
168 }
169 }
170
171 #[inline]
172 pub
173 fn cdns_custom_output_write(s: &str)
174 {
175 if IS_INITIALIZED.get().is_some() == false
176 {
177 // just ignore
178 return;
179 }
180
181 match WRITABLE_BUFFER.lock()
182 {
183 Ok(mut r) =>
184 {
185 let err = (*r).write_str(s);
186
187 if let Err(e) = err
188 {
189 match LAST_ERROR.lock()
190 {
191 Ok(mut r) => *r = Some(e.to_string()),
192 Err(_e) => {}
193 }
194 }
195 }
196 Err(_e) => {}
197 }
198
199 return;
200 }
201}
202
203use crate::error::Writer;
204
205/*
206n = force_none
207c = force_custom
208d = debug_assertions
209t = test
210
211V - set
212X - not allowed
213O - does not care, ignored
214 n c d t
215none V X O O
216std X X O O
217cus X V O O
218*/
219
220#[cfg(
221 all(
222 feature = "no_error_output",
223 feature = "custom_error_output",
224 )
225)]
226compile_error!("It is not allowed to use features
227 'no_error_output' and 'custom_error_output' simultaniously");
228
229
230// ------ OUTPUT to stdout and stderr ------ FEAUTE: no + any(debug_assertions, test)
231
232/// A macro for printing error to stderror all error messages generated by
233/// the library.
234#[cfg(
235 all(
236 not(feature = "no_error_output"),
237 not(feature = "custom_error_output"),
238 any( debug_assertions, test)
239 )
240)]
241#[macro_export]
242macro_rules! write_error
243{
244 ($($arg:tt)*) => (
245 eprintln!($($arg)*)
246 )
247}
248
249#[cfg(
250 all(
251 not(feature = "no_error_output"),
252 not(feature = "custom_error_output"),
253 any( debug_assertions, test)
254 )
255)]
256pub
257fn sync_log_writer(w: Writer)
258{
259 eprintln!("{}", s);
260}
261
262
263// ------ OUTPUT to custom ----- FEATURE: custom_error_output
264
265#[cfg(
266 all(
267 not(feature = "no_error_output"),
268 feature = "custom_error_output",
269 any(debug_assertions, test)
270 )
271)]
272pub use cdns_custom_output::*;
273
274
275/// A macro for printing to progrmas buffer all error messages generated by
276/// the library.
277#[cfg(
278 all(
279 not(feature = "no_error_output"),
280 feature = "custom_error_output",
281 any(debug_assertions, test)
282 )
283)]
284#[macro_export]
285macro_rules! write_error
286{
287 ($($arg:tt)*) => (
288 $crate::sync::log::cdns_custom_output::cdns_custom_output_write(format!($($arg)*).as_str())
289 )
290}
291
292#[cfg(
293 all(
294 not(feature = "no_error_output"),
295 feature = "custom_error_output",
296 any(debug_assertions, test)
297 )
298)]
299pub
300fn sync_log_writer(w: Writer)
301{
302 if w.is_some() == true
303 {
304 cdns_custom_output_write(unsafe { w.get_str() });
305 }
306}
307
308
309// ----- OUTPUT to null ----- FEATURE: no_error_output
310
311/*#[cfg(
312 all(
313 not(feature = "custom_error_output"),
314 feature = "no_error_output",
315 )
316)]
317#[inline]
318pub
319fn empty_func() { return; }*/
320
321/// A macro for printing to nowhere all error messages generated by
322/// the library.
323#[cfg(
324 all(
325 not(feature = "custom_error_output"),
326 feature = "no_error_output",
327 )
328)]
329#[macro_export]
330macro_rules! write_error
331{
332 ($($arg:tt)*) => (
333 {}
334 )
335}
336
337#[cfg(
338 all(
339 not(feature = "custom_error_output"),
340 feature = "no_error_output",
341 )
342)]
343#[inline]
344pub
345fn sync_log_writer(_w: Writer)
346{
347 return;
348}