defaulted 0.1.3

Trait and derive macro for testing whether a value equals its default state, with per-field customization and optional serde integration
Documentation
use std::collections::{HashMap, HashSet};
use std::ffi::{OsStr, OsString};
use std::hash::BuildHasher;
use std::io::{Cursor, Empty, Sink};
use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock, RwLock};

use crate::Defaulted;

// Collections

impl<T, S: BuildHasher> Defaulted for HashSet<T, S>
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.is_empty()
    }
}

impl<K, V, S: BuildHasher> Defaulted for HashMap<K, V, S>
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.is_empty()
    }
}

// OS strings / paths

impl_defaulted_empty!(OsStr);
impl_defaulted_empty!(OsString);

impl Defaulted for Path
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.as_os_str().is_empty()
    }
}

impl Defaulted for PathBuf
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.as_os_str().is_empty()
    }
}

// Sync primitives

impl<T: Defaulted + ?Sized> Defaulted for Mutex<T>
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.try_lock().is_ok_and(|v| v.is_defaulted())
    }
}

impl<T: Defaulted + ?Sized> Defaulted for RwLock<T>
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.try_read().is_ok_and(|v| v.is_defaulted())
    }
}

impl<T> Defaulted for OnceLock<T>
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.get().is_none()
    }
}

// IO types

impl Defaulted for Empty
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        true
    }
}

impl Defaulted for Sink
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        true
    }
}

impl<T> Defaulted for Cursor<T>
{
    #[inline]
    fn is_defaulted(&self) -> bool
    {
        self.position() == 0
    }
}