terminal_cli/
utils.rs

1use prelude::v1::*;
2
3
4/// A naive implementation. Can be implemented with a trie, but it's overkill here.
5/// http://en.wikipedia.org/wiki/LCP_array
6pub fn longest_common_prefix<'a>(strings: &'a [&'a str]) -> Option<&'a str> {
7	if strings.len() == 0 { return None; }
8	if strings.len() == 1 { 
9		let s = strings[0];
10		if s.len() > 0 {
11			return Some(s);
12		} else {
13			return None;
14		}
15	}
16
17	let ref first = strings[0].as_bytes();
18
19	for i in 0..first.len() {
20		let c = first[i];
21		for other in 1..strings.len() {
22			let other = strings[other].as_bytes();
23			if i >= other.len() || (c != other[i]) {
24				if i == 0 {					
25					return None;
26				} else {
27					let b = &first[0..i];
28					return match str::from_utf8(b) {
29						Ok(s) => Some(s),
30						Err(_) => None
31					};
32				}
33			}
34		}
35	}
36
37	if strings[0].len() > 0 {
38		Some(strings[0])
39	} else {
40		None
41	}
42}
43
44/// Formats the strings in autocomplete-style column notation. Fills the width of
45/// the entire line with a string plus the desired spacing characters. Preserves 
46/// the ordering in columns.
47///
48/// # Examples
49///
50/// ```
51/// # use terminal_cli::*;
52/// let s = vec!["A1", "A2", "A3", "B1", "B2", "C1", "C2"];
53/// let mut out = String::new();
54/// format_in_columns(s.as_slice(), 26, 10, "\r\n", &mut out).unwrap();
55/// println!("{}", out);
56/// # assert_eq!("A1          B1          C2\r\nA2          B2          \r\nA3          C1          \r\n", out);
57/// ```
58/// ```text
59/// A1          B1          C2
60/// A2          B2
61/// A3          C1
62/// ```
63pub fn format_in_columns<W: FmtWrite>(strings: &[&str], width: u16, spacing: u16, new_line: &str, write: &mut W) -> Result<(), FmtError> {
64	if strings.len() == 0 { return Ok(()); }
65
66	let max_len = strings.iter().max_by_key(|s| { s.len() }).unwrap().len() as u16;
67
68	let columns = {
69		let c = ((width as f32 / (max_len + spacing) as f32)).floor() as u16;
70		let plus_one_width = ((max_len + spacing) * c) + max_len;
71		if plus_one_width <= width {
72			c + 1
73		} else {
74			c
75		}
76	};
77
78	let rows = (strings.len() as f32 / columns as f32).ceil() as u16;
79
80	for i in 0..rows {
81		for j in 0..columns {			
82			let pos = (j as usize * rows as usize) + i as usize;
83			if let Some(s) = strings.get(pos) {
84				try!(write.write_str(&s));
85
86				if j < columns-1 {
87					let spaces = (max_len - s.len() as u16) + spacing;
88					for i in 0..spaces {
89						try!(write.write_str(" "));
90					}
91				}
92			}
93		}
94		write.write_str(new_line)?;
95	}
96
97	Ok(())
98}
99
100#[cfg(test)]
101mod tests {
102	use super::*;
103	use prelude::v1::*;
104
105	#[test]
106	fn test_lcp() {
107		{
108			let strings = vec!["p1", "p1/hello"];
109			let m = longest_common_prefix(&strings);
110			assert_eq!(Some("p1"), m)
111		}
112
113		{
114			let strings = vec!["abcx", "ab1", "abb", "aaa"];
115			let m = longest_common_prefix(&strings);
116			assert_eq!(Some("a"), m)
117		}
118
119		{
120			let strings = vec!["abcx", "ab1", "abb", "aaa", ""];
121			let m = longest_common_prefix(&strings);
122			assert_eq!(None, m)
123		}
124
125		{
126			let strings = vec!["abcx", "ab1", "abb"];
127			let m = longest_common_prefix(&strings);
128			assert_eq!(Some("ab"), m)
129		}
130
131		{
132			let strings = vec![];
133			let m = longest_common_prefix(&strings);
134			assert_eq!(None, m)	
135		}
136
137		{
138			let strings = vec![""];
139			let m = longest_common_prefix(&strings);
140			assert_eq!(None, m)	
141		}		
142
143		{
144			let strings = vec!["", "a"];
145			let m = longest_common_prefix(&strings);
146			assert_eq!(None, m)	
147		}
148	}
149
150	#[test]
151	fn test_column_format() {
152		let s = vec!["A1", "A2", "A3", "B1", "B2", "C1", "C2"];
153		let mut out = String::new();
154		format_in_columns(&s, 26, 10, "\r\n", &mut out).unwrap();
155		assert_eq!("A1          B1          C2\r\nA2          B2          \r\nA3          C1          \r\n", out);
156	}
157
158}