use std::fmt;
use std::ops::Deref;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterPatternError {
Empty,
ContainsNul,
}
impl fmt::Display for FilterPatternError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => f.write_str("filter pattern must be a non-empty string"),
Self::ContainsNul => f.write_str("filter pattern must not contain NUL bytes"),
}
}
}
impl std::error::Error for FilterPatternError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FilterPattern(String);
impl FilterPattern {
pub fn try_new(value: impl Into<String>) -> Result<Self, FilterPatternError> {
let value = value.into();
if value.is_empty() || value.trim().is_empty() {
return Err(FilterPatternError::Empty);
}
if value.contains('\0') {
return Err(FilterPatternError::ContainsNul);
}
Ok(Self(value))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
}
impl Deref for FilterPattern {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for FilterPattern {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for FilterPattern {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl PartialEq<str> for FilterPattern {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl PartialEq<&str> for FilterPattern {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PickerPathError {
Empty,
ContainsNul,
}
impl fmt::Display for PickerPathError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => f.write_str("picker path must be a non-empty string"),
Self::ContainsNul => f.write_str("picker path must not contain NUL bytes"),
}
}
}
impl std::error::Error for PickerPathError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PickerPath(String);
impl PickerPath {
pub fn try_new(value: impl Into<String>) -> Result<Self, PickerPathError> {
let value = value.into();
if value.is_empty() {
return Err(PickerPathError::Empty);
}
if value.contains('\0') {
return Err(PickerPathError::ContainsNul);
}
Ok(Self(value))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
}
impl Deref for PickerPath {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for PickerPath {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for PickerPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl PartialEq<str> for PickerPath {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl PartialEq<&str> for PickerPath {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filter_pattern_try_new_rejects_empty_and_whitespace() {
assert_eq!(
FilterPattern::try_new("").unwrap_err(),
FilterPatternError::Empty
);
assert_eq!(
FilterPattern::try_new(" ").unwrap_err(),
FilterPatternError::Empty
);
assert_eq!(FilterPattern::try_new("*.txt").unwrap().as_str(), "*.txt");
}
#[test]
fn filter_pattern_try_new_rejects_nul() {
assert_eq!(
FilterPattern::try_new("*.t\0xt").unwrap_err(),
FilterPatternError::ContainsNul
);
}
#[test]
fn picker_path_try_new_rejects_empty_and_nul() {
assert_eq!(PickerPath::try_new("").unwrap_err(), PickerPathError::Empty);
assert_eq!(
PickerPath::try_new("/tmp\0").unwrap_err(),
PickerPathError::ContainsNul
);
assert_eq!(PickerPath::try_new("/tmp").unwrap().as_str(), "/tmp");
}
#[test]
fn picker_path_allows_whitespace_only() {
assert_eq!(PickerPath::try_new(" ").unwrap().as_str(), " ");
}
}