Advertisement
Guest User

Untitled

a guest
Mar 18th, 2019
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.70 KB | None | 0 0
  1. use std::collections::HashMap;
  2. use std::error::Error;
  3. use std::fmt::{Display, self};
  4. use std::iter::FromIterator;
  5.  
  6. #[derive(Debug)]
  7. pub struct Terminal {
  8. prices: HashMap<String, PriceScale>,
  9. }
  10.  
  11. #[derive(Debug)]
  12. pub struct PriceScale {
  13. prices: Vec<PricePoint>,
  14. }
  15.  
  16. impl FromIterator<(u32, f64)> for PriceScale {
  17. fn from_iter<T>(it: T) -> Self
  18. where
  19. T: IntoIterator<Item = (u32, f64)>,
  20. {
  21. let mut prices = it.into_iter()
  22. .map(|(quantity, price)| PricePoint {quantity, price})
  23. .collect::<Vec<_>>();
  24. prices.sort_by_key(|point| std::cmp::Reverse(point.quantity));
  25. PriceScale {prices}
  26. }
  27. }
  28.  
  29. #[derive(Debug)]
  30. pub struct PricePoint {
  31. quantity: u32,
  32. price: f64,
  33. }
  34.  
  35. impl Terminal {
  36. pub fn new<P, S, Q>(prices: P) -> Self
  37. where
  38. P: IntoIterator<Item = (S, Q)>,
  39. S: Into<String>,
  40. Q: IntoIterator<Item = (u32, f64)>,
  41. {
  42. let prices = prices.into_iter()
  43. .map(|(s, p)| (s.into(), p.into_iter().collect()))
  44. .collect();
  45. Terminal {prices}
  46. }
  47.  
  48. pub fn scan<I>(&self, cart: I) -> Result<f64, TerminalError>
  49. where
  50. I: IntoIterator,
  51. I::Item: Into<String>,
  52. {
  53. cart
  54. .into_iter()
  55. .map(Into::into)
  56. .fold(HashMap::new(), |mut map, product| {
  57. *map.entry(product).or_insert(0) += 1;
  58. map
  59. })
  60. .into_iter()
  61. .try_fold(0.0, |total, (product, quantity)| {
  62. let (price, quantity) = self.prices.get(&product)
  63. .ok_or_else(|| TerminalError::UnknownProduct(
  64. product.clone()))?
  65. .prices.iter()
  66. .fold((0.0, quantity), |(mut cost, mut q), price_point| {
  67. cost += price_point.price *
  68. (q / price_point.quantity) as f64;
  69. q %= price_point.quantity;
  70. (cost, q)
  71. });
  72. if quantity == 0 {
  73. Ok(total + price)
  74. } else {
  75. Err(TerminalError::NoUnitPrice(product))
  76. }
  77. })
  78. }
  79. }
  80.  
  81. #[derive(Debug)]
  82. pub enum TerminalError {
  83. NoUnitPrice(String),
  84. UnknownProduct(String),
  85. }
  86.  
  87. impl Display for TerminalError {
  88. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  89. match self {
  90. TerminalError::NoUnitPrice(id) =>
  91. write!(f, r#"No unit price for product "{}""#, id),
  92. TerminalError::UnknownProduct(id) =>
  93. write!(f, r#"Unknown product "{}""#, id),
  94. }
  95. }
  96. }
  97.  
  98. impl Error for TerminalError {}
  99.  
  100. #[cfg(test)]
  101. mod tests {
  102. use super::Terminal;
  103.  
  104. fn new_terminal() -> Terminal {
  105. Terminal::new([
  106. (
  107. "A",
  108. [(4, 7.0), (1, 2.0)][..].iter().cloned(),
  109. ),
  110. (
  111. "B",
  112. [(1, 12.0)][..].iter().cloned(),
  113. ),
  114. (
  115. "C",
  116. [(6, 6.0), (1, 1.25)][..].iter().cloned(),
  117. ),
  118. (
  119. "D",
  120. [(1, 0.15)][..].iter().cloned(),
  121. ),
  122. ].iter().cloned())
  123. }
  124.  
  125. #[test]
  126. fn it_works() {
  127. let expectations = [
  128. ("ABCDABAA", 32.40),
  129. ("CCCCCCC", 7.25),
  130. ("ABCD", 15.40),
  131. ];
  132. for &(purchase, expected_price) in &expectations {
  133. assert_eq!(
  134. new_terminal()
  135. .scan(purchase.chars().map(|c| c.to_string()))
  136. .unwrap(),
  137. expected_price,
  138. "Purchase {}",
  139. purchase,
  140. );
  141. }
  142. }
  143. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement