1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
 * --------------------
 * THIS FILE IS LICENSED UNDER THE TERMS OF THE FOLLOWING LICENSE
 *
 * this code and any software derived from it may not be used for any purpose. be gay, do crime.
 *
 * THE FOLLOWING MESSAGE IS NOT A LICENSE
 *
 * <barrow@tilde.team> wrote this file.
 * by reading this text, you are reading "TRANS RIGHTS".
 * this file and the content within it is the gay agenda.
 * if we meet some day, and you think this stuff is worth it,
 * you can buy me a beer, tea, or something stronger.
 * -Ezra Barrow
 * --------------------
 */
//! Adds a trait NoneIfEmpty that converts a T to an Option<T> by turning an empty T into None.
//!
//! [![GitHub last commit](https://img.shields.io/github/last-commit/barrowsys/noneifempty)](https://github.com/barrowsys/noneifempty)
//! [![Crates.io](https://img.shields.io/crates/v/noneifempty)](https://crates.io/crates/noneifempty/)
//! [![Docs.rs](https://docs.rs/noneifempty/badge.svg)](https://docs.rs/noneifempty)
//! [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
//!
//! ```
//! // Bring the trait into scope
//! use noneifempty::NoneIfEmpty;
//!
//! // Converts empty strings to None
//! let empty_str = "";
//! assert_eq!(empty_str.none_if_empty(), None);
//!
//! // And full strings to Some
//! let full_str  = "hello, world!";
//! assert_eq!(full_str.none_if_empty(),  Some("hello, world!"));
//!
//! // Also works with vecs, hashmaps, hashsets, custom types...
//! let empty_vec: Vec<&str> = vec![];
//! let full_vec:  Vec<&str> = vec!["hi"];
//! assert_eq!(empty_vec.none_if_empty(), None);
//! assert_eq!(full_vec.none_if_empty(),  Some(vec!["hi"]));
//!
//! // Automatically implemented for Option<T: NoneIfEmpty>
//! let no_vec:    Option<Vec<&str>>  = None;
//! let empty_vec: Option<Vec<&str>>  = Some(vec![]);
//! let full_vec:  Option<Vec<&str>>  = Some(vec!["hi"]);
//! assert_eq!(no_vec.none_if_empty(),    None);
//! assert_eq!(empty_vec.none_if_empty(), None);
//! assert_eq!(full_vec.none_if_empty(),  Some(vec!["hi"]));
//! ```

use std::collections::{HashMap, HashSet};

/// Trait for converting an empty T to None
pub trait NoneIfEmpty
where
    Self: Sized,
{
    type Output;
    fn none_if_empty(self) -> Option<Self::Output>;
}
/// Automatically implement NoneIfEmpty for Option<T: NoneIfEmpty>
///
/// This is basically a flatten
impl<N: NoneIfEmpty> NoneIfEmpty for Option<N> {
    type Output = <N as NoneIfEmpty>::Output;
    fn none_if_empty(self) -> Option<Self::Output> {
        (self?).none_if_empty()
    }
}
/// Converts a &str to None if empty
impl<'s> NoneIfEmpty for &'s str {
    type Output = &'s str;
    fn none_if_empty(self) -> Option<&'s str> {
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }
}
/// Converts a String to None if empty
impl NoneIfEmpty for String {
    type Output = String;
    fn none_if_empty(self) -> Option<String> {
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }
}
/// Converts a Vec to None if empty
impl<T> NoneIfEmpty for Vec<T> {
    type Output = Self;
    fn none_if_empty(self) -> Option<Self::Output> {
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }
}
/// Converts a HashMap to None if empty
impl<K, V> NoneIfEmpty for HashMap<K, V> {
    type Output = Self;
    fn none_if_empty(self) -> Option<Self::Output> {
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }
}
/// Converts a HashSet to None if empty
impl<T> NoneIfEmpty for HashSet<T> {
    type Output = Self;
    fn none_if_empty(self) -> Option<Self::Output> {
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }
}