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}