1use core::fmt::{Display, Write, Formatter, self};
2use std::io;
3use std::io::BufRead;
4use std::collections::VecDeque;
5
6pub fn trim_str(target: &impl AsRef<str>, maxlen: usize) -> impl Display + '_ {
8 TrimStr {
9 target: target.as_ref(),
10 maxlen,
11 }
12}
13
14struct TrimStr<'s> {
16 target: &'s str,
17 maxlen: usize,
18}
19
20impl Display for TrimStr<'_> {
22 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
23 if self.target.len() > self.maxlen {
24 write!(fmt, "{}...", &self.target[..(self.maxlen-3)])
25 } else {
26 fmt.write_str(self.target)
27 }
28 }
29}
30
31pub fn title_case(s: &impl AsRef<str>) -> impl Display + '_ {
32 TitleCase { target: s.as_ref() }
33}
34
35struct TitleCase<'s> {
36 target: &'s str,
37}
38
39impl Display for TitleCase<'_> {
40 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
41 let mut prev = None;
42 for next in self.target.chars() {
43 if next.is_alphabetic() && prev.map_or(true, char::is_whitespace) {
44 for c in next.to_uppercase() {
45 fmt.write_char(c)?;
46 }
47 } else {
48 fmt.write_char(next)?;
49 }
50
51 prev.replace(next);
52 }
53 Ok(())
54 }
55}
56
57pub fn fmt_join<'a>(sep: impl Display + 'a, items: &'a [impl Display]) -> impl Display + 'a {
58 DisplayJoin { sep, items }
59}
60
61struct DisplayJoin<'a, S, D> where S: Display, D: Display {
62 sep: S,
63 items: &'a [D],
64}
65
66impl<S, D> Display for DisplayJoin<'_, S, D> where S: Display, D: Display {
67 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
68 for item in self.items.iter().take(self.items.len() - 1) {
69 item.fmt(fmt)?;
70 self.sep.fmt(fmt)?;
71 }
72 if let Some(item) = self.items.last() {
73 item.fmt(fmt)?;
74 }
75 Ok(())
76 }
77}
78
79
80#[derive(Debug)]
84pub struct ReadChars<R> where R: BufRead {
85 read: R,
86 linebuf: String,
87 charbuf: VecDeque<char>,
88}
89
90impl<R> ReadChars<R> where R: BufRead {
91 pub fn new(read: R) -> Self {
92 ReadChars {
93 read,
94 linebuf: String::new(),
95 charbuf: VecDeque::new(),
96 }
97 }
98}
99
100impl<R> Iterator for ReadChars<R> where R: BufRead {
101 type Item = io::Result<char>;
102
103 fn next(&mut self) -> Option<io::Result<char>> {
104 let next = self.charbuf.pop_front().map(Ok);
105 if next.is_some() {
106 return next;
107 }
108
109 self.linebuf.clear();
112
113 let mut safety = 0;
114 while self.linebuf.is_empty() && safety < 0xFFFF {
115
116 match self.read.read_line(&mut self.linebuf) {
117 Err(error) => return Some(Err(error)),
118 Ok(0) => return None, _ => { safety += 1 },
120 }
121
122 }
123
124 self.charbuf.extend(self.linebuf.chars());
125 self.charbuf.pop_front().map(Ok)
126 }
127}
128
129
130
131pub fn make_display<F>(fmt_func: F) -> impl fmt::Display where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result {
135 FnFormatter { fmt_func }
136}
137
138struct FnFormatter<F> where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result {
139 fmt_func: F,
140}
141
142impl<F> fmt::Display for FnFormatter<F> where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result {
143 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
144 (self.fmt_func)(fmt)
145 }
146}
147
148
149pub fn format_error(fmt: &mut fmt::Formatter<'_>, title: &str, message: Option<&str>, source: Option<&dyn std::error::Error>) -> fmt::Result {
151 let message =
153 if let Some("") = message { None }
154 else { message };
155
156 match (message, source) {
157 (None, None) => fmt.write_str(title),
158 (None, Some(error)) => write!(fmt, "{}: {}", title, error),
159 (Some(message), None) => write!(fmt, "{}: {}", title, message),
160 (Some(message), Some(error)) => write!(fmt, "{}: {}: {}", title, message, error),
161 }
162}