orphanage 0.5.6

Random collection of stuff that is still searching for a home.
Documentation
//! ```
//! use serde::{Deserialize};
//!
//! use orphanage::serde_parsers::*;
//!
//! #[derive(Debug, Deserialize)]
//! struct Config {
//!   /// Support `count = 10000` and `count = "10K"`
//!   count: Count,
//!
//!   /// Support optional `count = 10000` and `count = "10K"`
//!   count_opt: Option<Count>,
//!
//!   /// Support `binsize = 65536` and `binsize = "64KB"`
//!   binsize: BinSize,
//!
//!   /// Support optional `binsize = 65536` and `binsize = "64KB"`
//!   binsize_opt: Option<BinSize>,
//!
//!   /// Support `decsize = 20000` and `decsize = "20KB"`
//!   decsize: DecSize,
//!
//!   /// Support optional `decsize = 20000` and `decsize = "20KB"`
//!   decsize_opt: Option<DecSize>,
//!
//!   /// Support optional `~/tmp`
//!   epth: Option<ExpandedPath>
//! }
//! ```

use std::path::PathBuf;

use serde::{Deserialize, de::Deserializer};


#[derive(Deserialize)]
#[serde(untagged)]
enum StrOrU64 {
  U64(u64),
  Str(String)
}


#[allow(clippy::missing_errors_doc)]
pub fn count<'de, D>(deserializer: D) -> Result<Count, D::Error>
where
  D: Deserializer<'de>
{
  match StrOrU64::deserialize(deserializer)? {
    StrOrU64::U64(v) => Ok(Count(v)),
    StrOrU64::Str(v) => {
      let cfg = parse_size::Config::new().with_decimal();
      cfg
        .parse_size(&v)
        .map(Count)
        .map_err(|_| serde::de::Error::custom("Can't parse count"))
    }
  }
}

impl<'de> Deserialize<'de> for Count {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: Deserializer<'de>
  {
    count(deserializer)
  }
}


#[allow(clippy::missing_errors_doc)]
pub fn binsize<'de, D>(deserializer: D) -> Result<BinSize, D::Error>
where
  D: Deserializer<'de>
{
  match StrOrU64::deserialize(deserializer)? {
    StrOrU64::U64(v) => Ok(BinSize(v)),
    StrOrU64::Str(v) => {
      let cfg = parse_size::Config::new().with_binary();
      cfg
        .parse_size(&v)
        .map(BinSize)
        .map_err(|_| serde::de::Error::custom("Can't parse size"))
    }
  }
}

impl<'de> Deserialize<'de> for BinSize {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: Deserializer<'de>
  {
    binsize(deserializer)
  }
}

#[allow(clippy::missing_errors_doc)]
pub fn decsize<'de, D>(deserializer: D) -> Result<DecSize, D::Error>
where
  D: Deserializer<'de>
{
  match StrOrU64::deserialize(deserializer)? {
    StrOrU64::U64(v) => Ok(DecSize(v)),
    StrOrU64::Str(v) => {
      let cfg = parse_size::Config::new().with_decimal();
      cfg
        .parse_size(&v)
        .map(DecSize)
        .map_err(|_| serde::de::Error::custom("Can't parse size"))
    }
  }
}

impl<'de> Deserialize<'de> for DecSize {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: Deserializer<'de>
  {
    decsize(deserializer)
  }
}

pub use crate::parsers::{BinSize, Count, DecSize, ExpandedPath};

#[allow(clippy::missing_errors_doc)]
pub fn expand_path<'de, D>(deserializer: D) -> Result<ExpandedPath, D::Error>
where
  D: Deserializer<'de>
{
  let s = String::deserialize(deserializer)?;
  match shellexpand::full(&s) {
    Ok(value) => Ok(ExpandedPath(PathBuf::from(value.into_owned()))),
    Err(e) => {
      let msg = format!("Unable to expand path; {e}");
      Err(serde::de::Error::custom(msg))
    }
  }
}

impl<'de> Deserialize<'de> for ExpandedPath {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: Deserializer<'de>
  {
    expand_path(deserializer)
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :