syslog-rs 0.2.1

A native Rust implementation of the glibc/libc syslog.
Documentation
/*-
* syslog-rs - a syslog client translated from libc to rust
* Copyright (C) 2021  Aleksandr Morozov
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*#![feature(const_mut_refs)]
#![feature(const_fn)]*/

/*
    This is unsafe realization of the Cell. Uses unsafe code.
 */

use std::sync::atomic::{AtomicBool, Ordering};
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::ops;

pub struct UnsafeReadOnlyCell<T: Send> 
{
    title: &'static str,
    inner: UnsafeCell<MaybeUninit<T>>,
    blocked: AtomicBool,
}

unsafe impl<T: Send> Sync for UnsafeReadOnlyCell<T> {}

impl<T: Send> ops::Deref for UnsafeReadOnlyCell<T> 
{
    type Target = T;
    fn deref(&self) -> &T 
    {
        if self.blocked.load(Ordering::SeqCst) == false
        {
            panic!("Assertion trap: Dereference attempt of UnsafeReadOnlyCell \
                    '{}' which was not inited.", self.title);
        }

        return unsafe 
        { 
            &*(*self.inner.get()).as_ptr()
        };
    }
}

impl<T: Send> UnsafeReadOnlyCell<T>
{
    /// Creates new unititialized Cell.
    /// A contained type must implement [Send] and maybe [Sync]
    ///
    /// # Arguments
    ///
    /// * `title` - is a label which used to help in panic diagnostic.
    ///
    /// # Returns
    ///
    /// * A [UnsafeReadOnlyCell] of type <T>. Never panics
    ///
    /// # Example
    ///
    /// ```
    /// #[macro_use] extern crate lazy_static;
    /// use syslog_rs::UnsafeReadOnlyCell;
    /// struct Syslog{}
    /// lazy_static! {
    ///     static ref SYSLOG: UnsafeReadOnlyCell<Syslog> = 
    ///         unsafe { UnsafeReadOnlyCell::new_uninitialized("a_label") };
    /// }
    /// ```
    pub unsafe fn new_uninitialized(
        title: &'static str
    ) -> UnsafeReadOnlyCell<T> 
    {
        return
            UnsafeReadOnlyCell 
            {
                title: title,
                inner: UnsafeCell::new(MaybeUninit::uninit()),
                blocked: AtomicBool::new(false),
            };
    }

    /// Initializes the intance with provided data in `init` and locks the
    /// instance from modification. This function is unsafe and involves
    /// atomic operation.
    ///
    /// # Return
    /// 
    /// Nothing, but will panic on attempt to call this function again.
    pub unsafe fn init(&self, init: T) 
    {
        if self.blocked.load(Ordering::SeqCst) == false
        {
            (*self.inner.get()) = MaybeUninit::new(init);
            self.blocked.store(true, Ordering::SeqCst);
        }
        else
        {
            panic!("Assertion trap: attempt to re-initialize \
                    the ReadOnlySharedCell: {}", self.title);
        }
    }

    /// Returns the status of instance.
    ///
    /// # Return
    ///
    /// True - is locked or false.
    pub fn is_init(&self) -> bool
    {
        return self.blocked.load(Ordering::SeqCst);
    }
}

#[test]
fn test1()
{
    struct TestStruct1
    {
        field1: i64,
        field2: i64
    }
    unsafe impl Send for TestStruct1 {}
    unsafe impl Sync for TestStruct1 {}

    impl TestStruct1
    {
        pub fn get1(&self) -> i64
        {
            return self.field1;
        }

        pub fn get2(&self) -> i64
        {
            return self.field2;
        }
    }

    lazy_static! {
        static ref SYNC_SYSLOG: UnsafeReadOnlyCell<TestStruct1> = 
            unsafe { UnsafeReadOnlyCell::new_uninitialized("test_1") };
    }

    unsafe { SYNC_SYSLOG.init(TestStruct1{field1: 100, field2: 200}) };

    assert_eq!((*SYNC_SYSLOG).get1(), 100);
    assert_eq!((*SYNC_SYSLOG).get2(), 200);

    return;
}

#[test]
#[should_panic]
fn test2()
{
    struct TestStruct1
    {
        #[allow(dead_code)]
        field1: i64,
        #[allow(dead_code)]
        field2: i64
    }
    unsafe impl Send for TestStruct1 {}
    unsafe impl Sync for TestStruct1 {}

    lazy_static! {
        static ref SYNC_SYSLOG: UnsafeReadOnlyCell<TestStruct1> = 
            unsafe { UnsafeReadOnlyCell::new_uninitialized("test_1") };
    }

    unsafe { SYNC_SYSLOG.init(TestStruct1{field1: 100, field2: 200}) };

    let s1 = TestStruct1{field1: 100, field2: 200};

    unsafe { SYNC_SYSLOG.init(s1) };

    return;
}

#[test]
#[should_panic]
fn test3()
{
    struct TestStruct1
    {
        field1: i64,
        #[allow(dead_code)]
        field2: i64
    }
    unsafe impl Send for TestStruct1 {}
    unsafe impl Sync for TestStruct1 {}

    impl TestStruct1
    {
        pub fn get1(&self) -> i64
        {
            return self.field1;
        }

        #[allow(dead_code)]
        pub fn get2(&self) -> i64
        {
            return self.field2;
        }
    }

    lazy_static! {
        static ref SYNC_SYSLOG: UnsafeReadOnlyCell<TestStruct1> = 
            unsafe { UnsafeReadOnlyCell::new_uninitialized("test_1") };
    }

    assert_eq!((*SYNC_SYSLOG).get1(), 100);

    return;
}