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