SHARE
TWEET

Untitled

a guest Mar 18th, 2019 60 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top