size.rs 5.5 KB

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