use std::fmt::Write;
use std::string::String;
use types::*;
use formatter::Formatter;
fn write_char(f: &mut Formatter, c: char, n: usize) {
for _ in 0..n {
f.write_char(c).unwrap();
}
}
#[test]
fn test_write_char() {
let mut s = String::new();
s.write_str("h ").unwrap();
{
let mut f = Formatter::from_str("{}", &mut s).unwrap();
write_char(&mut f, 'f', 3);
}
assert!(s == "h fff");
}
fn write_from<I>(fmt: &mut Formatter, f: I, n: usize) -> usize
where I: Iterator<Item = char>
{
if n == 0 {
return 0;
}
let mut n_written: usize = 0;
for c in f {
fmt.write_char(c).unwrap();
n_written += 1;
if n_written == n {
return n_written;
}
}
n_written
}
#[test]
fn test_write_from() {
let mut s = String::new();
s.write_str("h ").unwrap();
{
let mut f = Formatter::from_str("{}", &mut s).unwrap();
write_from(&mut f, "fff".chars(), 5);
}
assert!(s == "h fff");
{
let mut f = Formatter::from_str("{}", &mut s).unwrap();
write_from(&mut f, "xxxx".chars(), 2);
}
assert!(s == "h fffxx");
{
let mut f = Formatter::from_str("{}", &mut s).unwrap();
write_from(&mut f, "333".chars(), 3);
}
assert!(s == "h fffxx333");
s.clear();
{
let mut f = Formatter::from_str("{}", &mut s).unwrap();
write!(f, "hello").unwrap();
}
assert!(s == "hello");
}
impl<'a, 'b> Formatter<'a, 'b> {
pub fn str(&mut self, s: &str) -> Result<()> {
if !(self.ty() == None || self.ty() == Some('s')) {
let mut msg = String::new();
write!(msg, "Unknown format code {:?} for object of type 'str'", self.ty()).unwrap();
return Err(FmtError::TypeError(msg));
} else if self.alternate() {
return Err(FmtError::TypeError("Alternate form (#) not allowed in string \
format specifier".to_string()));
} else if self.thousands() {
return Err(FmtError::TypeError("Cannot specify ',' with 's'".to_string()));
} else if self.sign().is_unspecified() {
return Err(FmtError::TypeError("Sign not allowed in string format specifier"
.to_string()));
}
self.str_unchecked(s)
}
pub fn str_unchecked(&mut self, s: &str) -> Result<()> {
let fill = self.fill();
let width = self.width();
let precision = self.precision();
let len = match precision {
Some(p) => if p < s.len() {
p
} else {
s.len()
},
None => s.len(),
};
let mut chars = s.chars();
let mut pad: usize = 0;
if let Some(mut width) = width {
if width > len {
let align = self.align();
match align {
Alignment::Left => pad = width - len,
Alignment::Center => {
width -= len;
pad = width / 2;
write_char(self, fill, pad);
pad += width % 2;
}
Alignment::Right => {
write_char(self, fill, width - len);
}
Alignment::Equal => return Err(FmtError::Invalid(
"sign aware zero padding and Align '=' not yet supported".to_string())),
}
}
}
write_from(self, &mut chars, len);
write_char(self, fill, pad);
Ok(())
}
}
pub fn strfmt_map<F>(fmtstr: &str, f: &F) -> Result<String>
where F: Fn(Formatter) -> Result<()>
{
let mut out = String::with_capacity(fmtstr.len() * 2);
let mut bytes_read: usize = 0;
let mut opening_brace: usize = 0;
let mut closing_brace: bool = false;
let mut reading_fmt = false;
let mut remaining = fmtstr;
for c in fmtstr.chars() {
bytes_read += c.len_utf8();
if c == '{' {
if reading_fmt && opening_brace == bytes_read - 2 {
out.push(c);
reading_fmt = false;
} else if !reading_fmt {
reading_fmt = true;
opening_brace = bytes_read - 1;
} else {
out.clear();
out.write_str("extra { found").unwrap();
return Err(FmtError::Invalid(out));
}
} else if c == '}' {
if !reading_fmt && !closing_brace {
closing_brace = true;
} else if closing_brace {
out.push(c);
closing_brace = false;
} else {
let (_, r) = remaining.split_at(opening_brace);
let (fmt_pattern, r) = r.split_at(bytes_read - opening_brace);
remaining = r;
let (_, fmt_pattern) = fmt_pattern.split_at(1);
let (fmt_pattern, _) = fmt_pattern.split_at(fmt_pattern.len() - 1);
let fmt = try!(Formatter::from_str(fmt_pattern, &mut out));
try!(f(fmt));
reading_fmt = false;
bytes_read = 0;
}
} else if closing_brace {
return Err(FmtError::Invalid("Single '}' encountered in format string".to_string()));
} else if !reading_fmt {
out.push(c)
} }
if closing_brace {
return Err(FmtError::Invalid("Single '}' encountered in format string".to_string()));
} else if reading_fmt {
return Err(FmtError::Invalid("Expected '}' before end of string".to_string()));
}
out.shrink_to_fit();
Ok(out)
}