size.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. use ansiterm::Style;
  2. use locale::Numeric as NumericLocale;
  3. use number_prefix::Prefix;
  4. use crate::fs::fields as f;
  5. use crate::output::cell::{DisplayWidth, TextCell};
  6. use crate::output::table::SizeFormat;
  7. impl f::Size {
  8. pub fn render<C: Colours>(
  9. self,
  10. colours: &C,
  11. size_format: SizeFormat,
  12. numerics: &NumericLocale,
  13. ) -> TextCell {
  14. use number_prefix::NumberPrefix;
  15. let size = match self {
  16. Self::Some(s) => s,
  17. Self::None => return TextCell::blank(colours.no_size()),
  18. Self::DeviceIDs(ref ids) => return ids.render(colours),
  19. };
  20. #[rustfmt::skip]
  21. let result = match size_format {
  22. SizeFormat::DecimalBytes => NumberPrefix::decimal(size as f64),
  23. SizeFormat::BinaryBytes => NumberPrefix::binary(size as f64),
  24. SizeFormat::JustBytes => {
  25. // Use the binary prefix to select a style.
  26. let prefix = match NumberPrefix::binary(size as f64) {
  27. NumberPrefix::Standalone(_) => None,
  28. NumberPrefix::Prefixed(p, _) => Some(p),
  29. };
  30. // But format the number directly using the locale.
  31. let string = numerics.format_int(size);
  32. return TextCell::paint(colours.size(prefix), string);
  33. }
  34. };
  35. #[rustfmt::skip]
  36. let (prefix, n) = match result {
  37. NumberPrefix::Standalone(b) => return TextCell::paint(colours.size(None), numerics.format_int(b)),
  38. NumberPrefix::Prefixed(p, n) => (p, n),
  39. };
  40. let symbol = prefix.symbol();
  41. let number = if n < 10_f64 {
  42. numerics.format_float(n, 1)
  43. } else {
  44. numerics.format_int(n.round() as isize)
  45. };
  46. TextCell {
  47. // symbol is guaranteed to be ASCII since unit prefixes are hardcoded.
  48. width: DisplayWidth::from(&*number) + symbol.len(),
  49. contents: vec![
  50. colours.size(Some(prefix)).paint(number),
  51. colours.unit(Some(prefix)).paint(symbol),
  52. ]
  53. .into(),
  54. }
  55. }
  56. }
  57. impl f::DeviceIDs {
  58. fn render<C: Colours>(self, colours: &C) -> TextCell {
  59. let major = self.major.to_string();
  60. let minor = self.minor.to_string();
  61. TextCell {
  62. width: DisplayWidth::from(major.len() + 1 + minor.len()),
  63. contents: vec![
  64. colours.major().paint(major),
  65. colours.comma().paint(","),
  66. colours.minor().paint(minor),
  67. ]
  68. .into(),
  69. }
  70. }
  71. }
  72. pub trait Colours {
  73. fn size(&self, prefix: Option<Prefix>) -> Style;
  74. fn unit(&self, prefix: Option<Prefix>) -> Style;
  75. fn no_size(&self) -> Style;
  76. fn major(&self) -> Style;
  77. fn comma(&self) -> Style;
  78. fn minor(&self) -> Style;
  79. }
  80. #[cfg(test)]
  81. pub mod test {
  82. use super::Colours;
  83. use crate::fs::fields as f;
  84. use crate::output::cell::{DisplayWidth, TextCell};
  85. use crate::output::table::SizeFormat;
  86. use ansiterm::Colour::*;
  87. use ansiterm::Style;
  88. use locale::Numeric as NumericLocale;
  89. use number_prefix::Prefix;
  90. struct TestColours;
  91. #[rustfmt::skip]
  92. impl Colours for TestColours {
  93. fn size(&self, _prefix: Option<Prefix>) -> Style { Fixed(66).normal() }
  94. fn unit(&self, _prefix: Option<Prefix>) -> Style { Fixed(77).bold() }
  95. fn no_size(&self) -> Style { Black.italic() }
  96. fn major(&self) -> Style { Blue.on(Red) }
  97. fn comma(&self) -> Style { Green.italic() }
  98. fn minor(&self) -> Style { Cyan.on(Yellow) }
  99. }
  100. #[test]
  101. fn directory() {
  102. let directory = f::Size::None;
  103. let expected = TextCell::blank(Black.italic());
  104. assert_eq!(
  105. expected,
  106. directory.render(
  107. &TestColours,
  108. SizeFormat::JustBytes,
  109. &NumericLocale::english()
  110. )
  111. )
  112. }
  113. #[test]
  114. fn file_decimal() {
  115. let directory = f::Size::Some(2_100_000);
  116. let expected = TextCell {
  117. width: DisplayWidth::from(4),
  118. contents: vec![Fixed(66).paint("2.1"), Fixed(77).bold().paint("M")].into(),
  119. };
  120. assert_eq!(
  121. expected,
  122. directory.render(
  123. &TestColours,
  124. SizeFormat::DecimalBytes,
  125. &NumericLocale::english()
  126. )
  127. )
  128. }
  129. #[test]
  130. fn file_binary() {
  131. let directory = f::Size::Some(1_048_576);
  132. let expected = TextCell {
  133. width: DisplayWidth::from(5),
  134. contents: vec![Fixed(66).paint("1.0"), Fixed(77).bold().paint("Mi")].into(),
  135. };
  136. assert_eq!(
  137. expected,
  138. directory.render(
  139. &TestColours,
  140. SizeFormat::BinaryBytes,
  141. &NumericLocale::english()
  142. )
  143. )
  144. }
  145. #[test]
  146. fn file_bytes() {
  147. let directory = f::Size::Some(1_048_576);
  148. let expected = TextCell {
  149. width: DisplayWidth::from(9),
  150. contents: vec![Fixed(66).paint("1,048,576")].into(),
  151. };
  152. assert_eq!(
  153. expected,
  154. directory.render(
  155. &TestColours,
  156. SizeFormat::JustBytes,
  157. &NumericLocale::english()
  158. )
  159. )
  160. }
  161. #[test]
  162. fn device_ids() {
  163. let directory = f::Size::DeviceIDs(f::DeviceIDs {
  164. major: 10,
  165. minor: 80,
  166. });
  167. let expected = TextCell {
  168. width: DisplayWidth::from(5),
  169. contents: vec![
  170. Blue.on(Red).paint("10"),
  171. Green.italic().paint(","),
  172. Cyan.on(Yellow).paint("80"),
  173. ]
  174. .into(),
  175. };
  176. assert_eq!(
  177. expected,
  178. directory.render(
  179. &TestColours,
  180. SizeFormat::JustBytes,
  181. &NumericLocale::english()
  182. )
  183. )
  184. }
  185. }