use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use {Fmt, SingleFmtError, FormatTable};
pub struct Lazy<T, F>
where
T: Fmt,
F: FnMut() -> T
{
closure: RefCell<F>,
}
impl<T: Fmt, F: FnMut() -> T> Lazy<T, F> {
pub fn new(closure: F) -> Self {
Lazy {
closure: RefCell::new(closure),
}
}
}
impl<T: Fmt, F: FnMut() -> T> Fmt for Lazy<T, F> {
fn format(
&self,
full_name: &[String],
name: &[String],
args: &[String],
flags: &[char],
options: &HashMap<String, String>,
) -> Result<String, SingleFmtError> {
let mut r = self.closure.borrow_mut();
let fmt = (&mut *r)();
fmt.format(full_name, name, args, flags, options)
}
}
pub struct AdHocArgs<F>
where
F: FnMut(&[String], &[String], &[String]) -> Result<String, SingleFmtError>
{
closure: RefCell<F>,
}
impl<F> AdHocArgs<F>
where
F: FnMut(&[String], &[String], &[String]) -> Result<String, SingleFmtError>
{
pub fn new(closure: F) -> Self {
AdHocArgs {
closure: RefCell::new(closure),
}
}
}
impl<F> Fmt for AdHocArgs<F>
where
F: FnMut(&[String], &[String], &[String]) -> Result<String, SingleFmtError>
{
fn format(
&self,
full_name: &[String],
name: &[String],
args: &[String],
_flags: &[char],
_options: &HashMap<String, String>,
) -> Result<String, SingleFmtError> {
let mut closure = self.closure.borrow_mut();
(&mut *closure)(full_name, name, args)
}
}
pub struct AdHocOpts<F>
where
F: FnMut(&[String], &[String], &HashMap<String, String>) -> Result<String, SingleFmtError>
{
closure: RefCell<F>,
}
impl<F> AdHocOpts<F>
where
F: FnMut(&[String], &[String], &HashMap<String, String>) -> Result<String, SingleFmtError>
{
pub fn new(closure: F) -> Self {
AdHocOpts {
closure: RefCell::new(closure),
}
}
}
impl<F> Fmt for AdHocOpts<F>
where
F: FnMut(&[String], &[String], &HashMap<String, String>) -> Result<String, SingleFmtError>
{
fn format(
&self,
full_name: &[String],
name: &[String],
_args: &[String],
_flags: &[char],
options: &HashMap<String, String>,
) -> Result<String, SingleFmtError> {
let mut closure = self.closure.borrow_mut();
(&mut *closure)(full_name, name, options)
}
}
pub struct AdHocFlagsOpts<F>
where
F: FnMut(&[String], &[String], &[char], &HashMap<String, String>)
-> Result<String, SingleFmtError>
{
closure: RefCell<F>,
}
impl<F> AdHocFlagsOpts<F>
where
F: FnMut(&[String], &[String], &[char], &HashMap<String, String>)
-> Result<String, SingleFmtError>
{
pub fn new(closure: F) -> Self {
AdHocFlagsOpts {
closure: RefCell::new(closure),
}
}
}
impl<F> Fmt for AdHocFlagsOpts<F>
where
F: FnMut(&[String], &[String], &[char], &HashMap<String, String>)
-> Result<String, SingleFmtError>
{
fn format(
&self,
full_name: &[String],
name: &[String],
_args: &[String],
flags: &[char],
opts: &HashMap<String, String>,
) -> Result<String, SingleFmtError> {
let mut closure = self.closure.borrow_mut();
(&mut *closure)(full_name, name, flags, opts)
}
}
pub struct AdHocFull<F>
where
F: FnMut(&[String], &[String], &[String], &[char], &HashMap<String, String>)
-> Result<String, SingleFmtError>,
{
closure: RefCell<F>,
}
impl<F> AdHocFull<F>
where
F: FnMut(&[String], &[String], &[String], &[char], &HashMap<String, String>)
-> Result<String, SingleFmtError>,
{
pub fn new(closure: F) -> Self {
AdHocFull {
closure: RefCell::new(closure),
}
}
}
impl<F> Fmt for AdHocFull<F>
where
F: FnMut(&[String], &[String], &[String], &[char], &HashMap<String, String>)
-> Result<String, SingleFmtError>,
{
fn format(
&self,
full_name: &[String],
name: &[String],
args: &[String],
flags: &[char],
options: &HashMap<String, String>,
) -> Result<String, SingleFmtError> {
let mut closure = self.closure.borrow_mut();
(&mut *closure)(full_name, name, args, flags, options)
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Mono<S: Deref<Target = str>, T: Fmt>(pub S, pub T);
impl<'a, S: Deref<Target = str>, T: Fmt + 'a> FormatTable<'a> for Mono<S, T> {
type Item = &'a dyn Fmt;
fn get_fmt(&'a self, name: &str) -> Option<Self::Item> {
if name == self.0.deref() {
Some(&self.1)
} else {
None
}
}
}
#[cfg(test)]
mod extras_tests {
test_suite! {
name closures;
use std::cmp::Ordering;
use std::collections::HashMap;
use galvanic_assert::matchers::*;
use {FormatTable, Fmt, SingleFmtError};
use extras::*;
use util;
test lazy() {
let i = Lazy::new(|| 10_i32);
let mut table: HashMap<&str, &dyn Fmt> = HashMap::new();
table.insert("i", &i);
let s = table.format("{i}").expect("Failed to format");
assert_that!(&s.as_str(), eq("10"));
}
test mutable_lazy() {
let mut i = 0;
let c = Lazy::new(move || { i += 1; i });
let mut table: HashMap<&str, &dyn Fmt> = HashMap::new();
table.insert("i", &c);
let s = table.format("{i}, {i}, {i}").expect("Failed to format");
assert_that!(&s.as_str(), eq("1, 2, 3"));
}
test ad_hoc_args() {
let f = AdHocArgs::new(|_, _, args| {
if args.is_empty() {
Ok("!!!".to_string())
} else {
Ok(args[0].to_uppercase())
}
});
let mut table: HashMap<&str, &dyn Fmt> = HashMap::new();
table.insert("f", &f);
let s = table.format("{f{asdf}}, {f}").expect("Failed to format");
assert_that!(&s.as_str(), eq("ASDF, !!!"));
}
test ad_hoc_opts() {
let f = AdHocOpts::new(|_, _, opts| {
if opts.contains_key("opt") {
Ok("defined".to_string())
} else {
Ok("undefined".to_string())
}
});
let mut table: HashMap<&str, &dyn Fmt> = HashMap::new();
table.insert("f", &f);
let s = table.format("{f::opt=}, {f}").expect("Failed to format");
assert_that!(&s.as_str(), eq("defined, undefined"));
}
test ad_hoc_full() {
let f = AdHocFull::new(|full_name, _, args: &[String], _, _| {
if args.is_empty() {
Err(SingleFmtError::WrongNumberOfArguments(
util::join_name(full_name),
Ordering::Equal,
1))
} else {
let s = &args[0];
Ok(s.to_uppercase())
}
});
let mut table: HashMap<&str, &dyn Fmt> = HashMap::new();
table.insert("f", &f);
let s = table.format("{f{asdf}}").expect("Failed to format");
assert_that!(&s.as_str(), eq("ASDF"));
}
}
test_suite! {
name mono;
use std::collections::HashMap;
use galvanic_assert::matchers::*;
use {FormatTable, Fmt};
use extras::Mono;
test several_monos() {
let a = Mono("a", 5_i32);
let b = Mono("b", "foobar");
let s = (&a, &b).format("{a}, {b}").expect("Failed to format");
assert_that!(&s.as_str(), eq("5, foobar"));
}
test monos_and_hashmaps() {
let a = Mono("foobar", 10);
let t = {
let mut res: HashMap<&str, &dyn Fmt> = HashMap::new();
res.insert("a", &10);
res
};
let s = (&a, &t).format("{foobar}, {a}").expect("Failed to format");
assert_that!(&s.as_str(), eq("10, 10"));
}
}
}