From 90a1d155650abf081001c96d9e7df47f9abfa20b Mon Sep 17 00:00:00 2001 From: "yangrui.0" Date: Tue, 22 Oct 2024 20:37:18 +0800 Subject: [PATCH] feat: up --- exercises/03_ticket_v1/00_intro/src/lib.rs | 2 +- exercises/03_ticket_v1/01_struct/src/lib.rs | 11 ++++ .../03_ticket_v1/02_validation/src/lib.rs | 16 ++++- exercises/03_ticket_v1/03_modules/src/lib.rs | 2 + .../03_ticket_v1/04_visibility/src/lib.rs | 10 +-- .../03_ticket_v1/05_encapsulation/src/lib.rs | 11 ++++ .../03_ticket_v1/06_ownership/src/lib.rs | 12 ++-- exercises/03_ticket_v1/07_setters/src/lib.rs | 52 +++++++++------ exercises/03_ticket_v1/08_stack/src/lib.rs | 6 +- exercises/03_ticket_v1/09_heap/src/lib.rs | 4 +- .../10_references_in_memory/src/lib.rs | 6 +- .../03_ticket_v1/11_destructor/src/lib.rs | 2 +- exercises/03_ticket_v1/12_outro/src/lib.rs | 58 +++++++++++++++++ exercises/04_traits/00_intro/src/lib.rs | 2 +- exercises/04_traits/01_trait/src/lib.rs | 16 +++++ exercises/04_traits/02_orphan_rule/src/lib.rs | 6 -- .../03_operator_overloading/src/lib.rs | 8 ++- exercises/04_traits/04_derive/src/lib.rs | 2 +- .../04_traits/05_trait_bounds/src/lib.rs | 2 +- exercises/04_traits/06_str_slice/src/lib.rs | 6 +- exercises/04_traits/07_deref/src/lib.rs | 4 +- exercises/04_traits/08_sized/src/lib.rs | 2 +- exercises/04_traits/09_from/src/lib.rs | 6 ++ .../04_traits/10_assoc_vs_generic/src/lib.rs | 22 +++++++ exercises/04_traits/11_clone/src/lib.rs | 3 +- exercises/04_traits/12_copy/src/lib.rs | 11 ++++ exercises/04_traits/13_drop/src/lib.rs | 20 ++++++ exercises/04_traits/14_outro/src/lib.rs | 64 +++++++++++++++++++ exercises/05_ticket_v2/00_intro/src/lib.rs | 2 +- exercises/05_ticket_v2/01_enum/src/lib.rs | 16 +++-- exercises/05_ticket_v2/02_match/src/lib.rs | 8 ++- .../03_variants_with_data/src/lib.rs | 6 +- exercises/05_ticket_v2/04_if_let/src/lib.rs | 6 +- .../05_ticket_v2/05_nullability/src/lib.rs | 6 +- .../05_ticket_v2/06_fallibility/src/lib.rs | 14 ++-- exercises/05_ticket_v2/07_unwrap/src/lib.rs | 19 +++--- .../05_ticket_v2/08_error_enums/src/lib.rs | 27 ++++++-- 37 files changed, 378 insertions(+), 92 deletions(-) diff --git a/exercises/03_ticket_v1/00_intro/src/lib.rs b/exercises/03_ticket_v1/00_intro/src/lib.rs index f7afa47..c52e3ac 100644 --- a/exercises/03_ticket_v1/00_intro/src/lib.rs +++ b/exercises/03_ticket_v1/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me ๐Ÿ‘‡ - "I'm ready to __!" + "I'm ready to start modelling a software ticket!" } #[cfg(test)] diff --git a/exercises/03_ticket_v1/01_struct/src/lib.rs b/exercises/03_ticket_v1/01_struct/src/lib.rs index 1119e33..6118e68 100644 --- a/exercises/03_ticket_v1/01_struct/src/lib.rs +++ b/exercises/03_ticket_v1/01_struct/src/lib.rs @@ -5,6 +5,17 @@ // It should also have a method named `is_available` that returns a `true` if the quantity is // greater than 0, otherwise `false`. +struct Order { + price: u8, + quantity: u8, +} + +impl Order { + fn is_available(&self) -> bool { + self.quantity > 0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/03_ticket_v1/02_validation/src/lib.rs b/exercises/03_ticket_v1/02_validation/src/lib.rs index 7eaa5e5..ddb7805 100644 --- a/exercises/03_ticket_v1/02_validation/src/lib.rs +++ b/exercises/03_ticket_v1/02_validation/src/lib.rs @@ -18,7 +18,21 @@ impl Ticket { // as well as some `String` methods. Use the documentation of Rust's standard library // to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html fn new(title: String, description: String, status: String) -> Self { - todo!(); + if title.is_empty() { + panic!("Title cannot be empty") + } + if description.is_empty() { + panic!("Description cannot be empty") + } + if title.len() > 50 { + panic!("Title cannot be longer than 50 bytes") + } + if description.len() > 500 { + panic!("Description cannot be longer than 500 bytes") + } + if !["To-Do", "In Progress", "Done"].contains(&status.as_str()) { + panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed") + } Self { title, description, diff --git a/exercises/03_ticket_v1/03_modules/src/lib.rs b/exercises/03_ticket_v1/03_modules/src/lib.rs index df07620..0dc5c36 100644 --- a/exercises/03_ticket_v1/03_modules/src/lib.rs +++ b/exercises/03_ticket_v1/03_modules/src/lib.rs @@ -2,6 +2,8 @@ mod helpers { // TODO: Make this code compile, either by adding a `use` statement or by using // the appropriate path to refer to the `Ticket` struct. + use crate::Ticket; + fn create_todo_ticket(title: String, description: String) -> Ticket { Ticket::new(title, description, "To-Do".into()) } diff --git a/exercises/03_ticket_v1/04_visibility/src/lib.rs b/exercises/03_ticket_v1/04_visibility/src/lib.rs index b494fc9..d7842f9 100644 --- a/exercises/03_ticket_v1/04_visibility/src/lib.rs +++ b/exercises/03_ticket_v1/04_visibility/src/lib.rs @@ -1,12 +1,12 @@ mod ticket { - struct Ticket { - title: String, - description: String, - status: String, + pub struct Ticket { + pub title: String, + pub description: String, + pub status: String, } impl Ticket { - fn new(title: String, description: String, status: String) -> Ticket { + pub fn new(title: String, description: String, status: String) -> Ticket { if title.is_empty() { panic!("Title cannot be empty"); } diff --git a/exercises/03_ticket_v1/05_encapsulation/src/lib.rs b/exercises/03_ticket_v1/05_encapsulation/src/lib.rs index 91e06eb..2cb3e58 100644 --- a/exercises/03_ticket_v1/05_encapsulation/src/lib.rs +++ b/exercises/03_ticket_v1/05_encapsulation/src/lib.rs @@ -34,6 +34,17 @@ pub mod ticket { // - `title` that returns the `title` field. // - `description` that returns the `description` field. // - `status` that returns the `status` field. + pub fn description(&self) -> &str { + &self.description + } + + pub fn title(&self) -> &str { + &self.title + } + + pub fn status(&self) -> &str { + &self.status + } } } diff --git a/exercises/03_ticket_v1/06_ownership/src/lib.rs b/exercises/03_ticket_v1/06_ownership/src/lib.rs index ded2ad8..5c056fb 100644 --- a/exercises/03_ticket_v1/06_ownership/src/lib.rs +++ b/exercises/03_ticket_v1/06_ownership/src/lib.rs @@ -34,16 +34,16 @@ impl Ticket { } } - pub fn title(self) -> String { - self.title + pub fn title(&self) -> &str { + &self.title } - pub fn description(self) -> String { - self.description + pub fn description(&self) -> &str { + &self.description } - pub fn status(self) -> String { - self.status + pub fn status(&self) -> &str { + &self.status } } diff --git a/exercises/03_ticket_v1/07_setters/src/lib.rs b/exercises/03_ticket_v1/07_setters/src/lib.rs index e13ec87..6f550af 100644 --- a/exercises/03_ticket_v1/07_setters/src/lib.rs +++ b/exercises/03_ticket_v1/07_setters/src/lib.rs @@ -3,6 +3,7 @@ // Even better, extract that logic and reuse it in both places. You can use // private functions or private static methods for that. +#[derive(Default)] pub struct Ticket { title: String, description: String, @@ -11,27 +12,12 @@ pub struct Ticket { impl Ticket { pub fn new(title: String, description: String, status: String) -> Ticket { - if title.is_empty() { - panic!("Title cannot be empty"); - } - if title.len() > 50 { - panic!("Title cannot be longer than 50 bytes"); - } - if description.is_empty() { - panic!("Description cannot be empty"); - } - if description.len() > 500 { - panic!("Description cannot be longer than 500 bytes"); - } - if status != "To-Do" && status != "In Progress" && status != "Done" { - panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); - } + let mut t = Self::default(); + t.set_title(title); + t.set_description(description); + t.set_status(status); - Ticket { - title, - description, - status, - } + t } pub fn title(&self) -> &String { @@ -45,6 +31,32 @@ impl Ticket { pub fn status(&self) -> &String { &self.status } + + pub fn set_title(&mut self, title: String) { + if title.is_empty() { + panic!("Title cannot be empty"); + } + if title.len() > 50 { + panic!("Title cannot be longer than 50 bytes"); + } + self.title = title; + } + + pub fn set_status(&mut self, status: String) { + if status != "To-Do" && status != "In Progress" && status != "Done" { + panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); + } + self.status = status; + } + pub fn set_description(&mut self, description: String) { + if description.is_empty() { + panic!("Description cannot be empty"); + } + if description.len() > 500 { + panic!("Description cannot be longer than 500 bytes"); + } + self.description = description; + } } #[cfg(test)] diff --git a/exercises/03_ticket_v1/08_stack/src/lib.rs b/exercises/03_ticket_v1/08_stack/src/lib.rs index e97518c..ad7d2a4 100644 --- a/exercises/03_ticket_v1/08_stack/src/lib.rs +++ b/exercises/03_ticket_v1/08_stack/src/lib.rs @@ -6,16 +6,16 @@ mod tests { #[test] fn u16_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 2); } #[test] fn i32_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 4); } #[test] fn bool_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 1); } } diff --git a/exercises/03_ticket_v1/09_heap/src/lib.rs b/exercises/03_ticket_v1/09_heap/src/lib.rs index 24d0c6e..5e24f6b 100644 --- a/exercises/03_ticket_v1/09_heap/src/lib.rs +++ b/exercises/03_ticket_v1/09_heap/src/lib.rs @@ -13,7 +13,7 @@ mod tests { #[test] fn string_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 24); } #[test] @@ -23,6 +23,6 @@ mod tests { // but, in general, the memory layout of structs is a more complex topic. // If you're curious, check out the "Data layout" section of the Rustonomicon // https://doc.rust-lang.org/nomicon/data.html for more information. - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 72); } } diff --git a/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs b/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs index d580758..d184a19 100644 --- a/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs +++ b/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs @@ -13,16 +13,16 @@ mod tests { #[test] fn u16_ref_size() { - assert_eq!(size_of::<&u16>(), todo!()); + assert_eq!(size_of::<&u16>(), 8); } #[test] fn u64_mut_ref_size() { - assert_eq!(size_of::<&mut u64>(), todo!()); + assert_eq!(size_of::<&mut u64>(), 8); } #[test] fn ticket_ref_size() { - assert_eq!(size_of::<&Ticket>(), todo!()); + assert_eq!(size_of::<&Ticket>(), 8); } } diff --git a/exercises/03_ticket_v1/11_destructor/src/lib.rs b/exercises/03_ticket_v1/11_destructor/src/lib.rs index 45bc89c..6555fbb 100644 --- a/exercises/03_ticket_v1/11_destructor/src/lib.rs +++ b/exercises/03_ticket_v1/11_destructor/src/lib.rs @@ -2,7 +2,7 @@ // We'll pick the concept up again in a later chapter after covering traits and // interior mutability. fn outro() -> &'static str { - "I have a basic understanding of __!" + "I have a basic understanding of destructors!" } #[cfg(test)] diff --git a/exercises/03_ticket_v1/12_outro/src/lib.rs b/exercises/03_ticket_v1/12_outro/src/lib.rs index 15c99b8..b076daf 100644 --- a/exercises/03_ticket_v1/12_outro/src/lib.rs +++ b/exercises/03_ticket_v1/12_outro/src/lib.rs @@ -11,3 +11,61 @@ // Integration here has a very specific meaning: they test **the public API** of your project. // You'll need to pay attention to the visibility of your types and methods; integration // tests can't access private or `pub(crate)` items. + +use core::panic; + +#[derive(Default)] +pub struct Order { + product_name: String, + quantity: usize, + unit_price: usize, +} + +impl Order { + pub fn new(product_name: String, quantity: usize, unit_price: usize) -> Self { + let mut s = Order::default(); + s.set_product_name(product_name); + s.set_quantity(quantity); + s.set_unit_price(unit_price); + s + } + pub fn product_name(&self) -> &str { + &self.product_name + } + + pub fn quantity(&self) -> &usize { + &self.quantity + } + + pub fn unit_price(&self) -> &usize { + &self.unit_price + } + + pub fn total(&self) -> usize { + self.quantity * self.unit_price + } + + pub fn set_product_name(&mut self, name: String) { + if name.is_empty() { + panic!("product_name is empty") + } + if name.len() > 300 { + panic!("product_name is too long") + } + self.product_name = name; + } + + pub fn set_quantity(&mut self, quantity: usize) { + if quantity == 0 { + panic!("quantity is zero") + } + self.quantity = quantity; + } + + pub fn set_unit_price(&mut self, unit_price: usize) { + if unit_price == 0 { + panic!("unit_price is zero") + } + self.unit_price = unit_price; + } +} diff --git a/exercises/04_traits/00_intro/src/lib.rs b/exercises/04_traits/00_intro/src/lib.rs index 9513649..1b0f8ee 100644 --- a/exercises/04_traits/00_intro/src/lib.rs +++ b/exercises/04_traits/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me ๐Ÿ‘‡ - "I'm ready to __!" + "I'm ready to learn about traits!" } #[cfg(test)] diff --git a/exercises/04_traits/01_trait/src/lib.rs b/exercises/04_traits/01_trait/src/lib.rs index 258eac5..fbb82a7 100644 --- a/exercises/04_traits/01_trait/src/lib.rs +++ b/exercises/04_traits/01_trait/src/lib.rs @@ -3,6 +3,22 @@ // // Then implement the trait for `u32` and `i32`. +trait IsEven { + fn is_even(&self) -> bool; +} + +impl IsEven for u32 { + fn is_even(&self) -> bool { + self % 2 == 0 + } +} + +impl IsEven for i32 { + fn is_even(&self) -> bool { + self % 2 == 0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/04_traits/02_orphan_rule/src/lib.rs b/exercises/04_traits/02_orphan_rule/src/lib.rs index b707c10..7927ba2 100644 --- a/exercises/04_traits/02_orphan_rule/src/lib.rs +++ b/exercises/04_traits/02_orphan_rule/src/lib.rs @@ -3,9 +3,3 @@ // a foreign type (`u32`, from `std`). // Look at the compiler error to get familiar with what it looks like. // Then delete the code below and move on to the next exercise. - -impl PartialEq for u32 { - fn eq(&self, _other: &Self) -> bool { - todo!() - } -} diff --git a/exercises/04_traits/03_operator_overloading/src/lib.rs b/exercises/04_traits/03_operator_overloading/src/lib.rs index b75c0f1..4a091d8 100644 --- a/exercises/04_traits/03_operator_overloading/src/lib.rs +++ b/exercises/04_traits/03_operator_overloading/src/lib.rs @@ -8,7 +8,13 @@ struct Ticket { // TODO: Implement the `PartialEq` trait for `Ticket`. -impl PartialEq for Ticket {} +impl PartialEq for Ticket { + fn eq(&self, other: &Self) -> bool { + self.title == other.title + && self.description == other.description + && self.status == other.status + } +} #[cfg(test)] mod tests { diff --git a/exercises/04_traits/04_derive/src/lib.rs b/exercises/04_traits/04_derive/src/lib.rs index 0a74a26..d39d6c5 100644 --- a/exercises/04_traits/04_derive/src/lib.rs +++ b/exercises/04_traits/04_derive/src/lib.rs @@ -8,7 +8,7 @@ // print both sides of the comparison to the terminal. // If the compared type doesn't implement `Debug`, it doesn't know how to represent them! -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] struct Ticket { title: String, description: String, diff --git a/exercises/04_traits/05_trait_bounds/src/lib.rs b/exercises/04_traits/05_trait_bounds/src/lib.rs index 10f0eb4..7187a1d 100644 --- a/exercises/04_traits/05_trait_bounds/src/lib.rs +++ b/exercises/04_traits/05_trait_bounds/src/lib.rs @@ -6,7 +6,7 @@ // collections (e.g. BTreeMap). /// Return the minimum of two values. -pub fn min(left: T, right: T) -> T { +pub fn min(left: T, right: T) -> T { if left <= right { left } else { diff --git a/exercises/04_traits/06_str_slice/src/lib.rs b/exercises/04_traits/06_str_slice/src/lib.rs index 5bf6614..a37c0d4 100644 --- a/exercises/04_traits/06_str_slice/src/lib.rs +++ b/exercises/04_traits/06_str_slice/src/lib.rs @@ -31,15 +31,15 @@ impl Ticket { } } - pub fn title(&self) -> &String { + pub fn title(&self) -> &str { &self.title } - pub fn description(&self) -> &String { + pub fn description(&self) -> &str { &self.description } - pub fn status(&self) -> &String { + pub fn status(&self) -> &str { &self.status } } diff --git a/exercises/04_traits/07_deref/src/lib.rs b/exercises/04_traits/07_deref/src/lib.rs index c7a5c35..a21f9ef 100644 --- a/exercises/04_traits/07_deref/src/lib.rs +++ b/exercises/04_traits/07_deref/src/lib.rs @@ -12,11 +12,11 @@ pub struct Ticket { impl Ticket { pub fn title(&self) -> &str { - todo!() + self.title.trim() } pub fn description(&self) -> &str { - todo!() + self.description.trim() } } diff --git a/exercises/04_traits/08_sized/src/lib.rs b/exercises/04_traits/08_sized/src/lib.rs index a406fc5..0ebca5a 100644 --- a/exercises/04_traits/08_sized/src/lib.rs +++ b/exercises/04_traits/08_sized/src/lib.rs @@ -3,5 +3,5 @@ pub fn example() { // via `std::mem::size_of` will result in a compile-time error. // // TODO: Comment out the following line and move on to the next exercise. - std::mem::size_of::(); + // std::mem::size_of::(); } diff --git a/exercises/04_traits/09_from/src/lib.rs b/exercises/04_traits/09_from/src/lib.rs index cc6f5b1..b528bea 100644 --- a/exercises/04_traits/09_from/src/lib.rs +++ b/exercises/04_traits/09_from/src/lib.rs @@ -4,6 +4,12 @@ pub struct WrappingU32 { value: u32, } +impl From for WrappingU32 { + fn from(value: u32) -> Self { + Self { value } + } +} + fn example() { let wrapping: WrappingU32 = 42.into(); let wrapping = WrappingU32::from(42); diff --git a/exercises/04_traits/10_assoc_vs_generic/src/lib.rs b/exercises/04_traits/10_assoc_vs_generic/src/lib.rs index 84f3e7b..178a078 100644 --- a/exercises/04_traits/10_assoc_vs_generic/src/lib.rs +++ b/exercises/04_traits/10_assoc_vs_generic/src/lib.rs @@ -13,6 +13,28 @@ // You don't have to though: it's perfectly okay to write three separate // implementations manually. Venture further only if you're curious. +trait Power { + fn power(&self, t: T) -> Self; +} + +impl Power for u32 { + fn power(&self, t: u16) -> Self { + self.pow(t as u32) + } +} + +impl Power for u32 { + fn power(&self, t: u32) -> Self { + self.pow(t) + } +} + +impl Power<&u32> for u32 { + fn power(&self, t: &u32) -> Self { + self.pow(*t) + } +} + #[cfg(test)] mod tests { use super::Power; diff --git a/exercises/04_traits/11_clone/src/lib.rs b/exercises/04_traits/11_clone/src/lib.rs index 4fbbe47..8d93806 100644 --- a/exercises/04_traits/11_clone/src/lib.rs +++ b/exercises/04_traits/11_clone/src/lib.rs @@ -2,9 +2,10 @@ // to get the code to compile. pub fn summary(ticket: Ticket) -> (Ticket, Summary) { - (ticket, ticket.summary()) + (ticket.clone(), ticket.summary()) } +#[derive(Clone)] pub struct Ticket { pub title: String, pub description: String, diff --git a/exercises/04_traits/12_copy/src/lib.rs b/exercises/04_traits/12_copy/src/lib.rs index d74720b..9210b87 100644 --- a/exercises/04_traits/12_copy/src/lib.rs +++ b/exercises/04_traits/12_copy/src/lib.rs @@ -1,10 +1,21 @@ // TODO: implement the necessary traits to make the test compile and pass. // You *can't* modify the test. +use std::ops::Add; + +#[derive(Copy, Clone, Debug, PartialEq)] pub struct WrappingU32 { value: u32, } +impl Add for WrappingU32 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::new(self.value.wrapping_add(rhs.value)) + } +} + impl WrappingU32 { pub fn new(value: u32) -> Self { Self { value } diff --git a/exercises/04_traits/13_drop/src/lib.rs b/exercises/04_traits/13_drop/src/lib.rs index 2124d34..86f2cf2 100644 --- a/exercises/04_traits/13_drop/src/lib.rs +++ b/exercises/04_traits/13_drop/src/lib.rs @@ -2,6 +2,26 @@ // unless a certain operation has been performed on it. // You can see the expected API in the tests below. +struct DropBomb(bool); + +impl DropBomb { + pub fn new() -> Self { + Self(false) + } + + pub fn defuse(&mut self) { + self.0 = true + } +} + +impl Drop for DropBomb { + fn drop(&mut self) { + if !self.0 { + panic!("not defuse") + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/04_traits/14_outro/src/lib.rs b/exercises/04_traits/14_outro/src/lib.rs index 547fd94..889459e 100644 --- a/exercises/04_traits/14_outro/src/lib.rs +++ b/exercises/04_traits/14_outro/src/lib.rs @@ -8,3 +8,67 @@ // It should be possible to print its debug representation. // // Tests are located in the `tests` folderโ€”pay attention to the visibility of your types and methods. + +use std::ops::Add; + +#[derive(Debug, Copy, Clone)] +pub struct SaturatingU16(u16); + +impl From for SaturatingU16 { + fn from(value: u16) -> Self { + Self(value) + } +} + +impl From for SaturatingU16 { + fn from(value: u8) -> Self { + Self(value as u16) + } +} + +impl From<&u16> for SaturatingU16 { + fn from(value: &u16) -> Self { + Self(*value) + } +} +impl From<&u8> for SaturatingU16 { + fn from(value: &u8) -> Self { + Self(*value as u16) + } +} + +impl Add for SaturatingU16 { + type Output = u16; + + fn add(self, rhs: SaturatingU16) -> Self::Output { + self.0.saturating_add(rhs.0) + } +} + +impl Add<&SaturatingU16> for SaturatingU16 { + type Output = u16; + + fn add(self, rhs: &SaturatingU16) -> Self::Output { + self.0.saturating_add(rhs.0) + } +} + +impl Add for SaturatingU16 { + type Output = u16; + + fn add(self, rhs: u16) -> Self::Output { + rhs.saturating_add(self.0) + } +} + +impl PartialEq for SaturatingU16 { + fn eq(&self, other: &u16) -> bool { + self.0 == *other + } +} + +impl PartialEq for u16 { + fn eq(&self, other: &SaturatingU16) -> bool { + other.0 == *self + } +} diff --git a/exercises/05_ticket_v2/00_intro/src/lib.rs b/exercises/05_ticket_v2/00_intro/src/lib.rs index ce1f75f..ff614d1 100644 --- a/exercises/05_ticket_v2/00_intro/src/lib.rs +++ b/exercises/05_ticket_v2/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me ๐Ÿ‘‡ - "I'm ready to __!" + "I'm ready to refine the `Ticket` type!" } #[cfg(test)] diff --git a/exercises/05_ticket_v2/01_enum/src/lib.rs b/exercises/05_ticket_v2/01_enum/src/lib.rs index a3d9592..90a0496 100644 --- a/exercises/05_ticket_v2/01_enum/src/lib.rs +++ b/exercises/05_ticket_v2/01_enum/src/lib.rs @@ -7,15 +7,19 @@ struct Ticket { title: String, description: String, - status: String, + status: Status, } +#[derive(Debug, PartialEq, Clone)] enum Status { // TODO: add the missing variants + ToDo, + InProgress, + Done, } impl Ticket { - pub fn new(title: String, description: String, status: String) -> Ticket { + pub fn new(title: String, description: String, status: Status) -> Ticket { if title.is_empty() { panic!("Title cannot be empty"); } @@ -28,7 +32,7 @@ impl Ticket { if description.len() > 500 { panic!("Description cannot be longer than 500 bytes"); } - if status != "To-Do" && status != "In Progress" && status != "Done" { + if status != Status::ToDo && status != Status::InProgress && status != Status::Done { panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); } @@ -47,7 +51,7 @@ impl Ticket { &self.description } - pub fn status(&self) -> &String { + pub fn status(&self) -> &Status { &self.status } } @@ -81,7 +85,7 @@ mod tests { let ticket1 = Ticket { title: title.clone(), description: "description".to_string(), - status, + status: status.clone(), }; let ticket2 = Ticket { title: title.clone(), @@ -98,7 +102,7 @@ mod tests { let ticket1 = Ticket { title: "title".to_string(), description: description.clone(), - status, + status: status.clone(), }; let ticket2 = Ticket { title: "title2".to_string(), diff --git a/exercises/05_ticket_v2/02_match/src/lib.rs b/exercises/05_ticket_v2/02_match/src/lib.rs index d30c569..2b4dc90 100644 --- a/exercises/05_ticket_v2/02_match/src/lib.rs +++ b/exercises/05_ticket_v2/02_match/src/lib.rs @@ -9,7 +9,13 @@ enum Shape { impl Shape { // TODO: Implement the `n_sides` method using a `match`. pub fn n_sides(&self) -> u8 { - todo!() + match *self { + Shape::Circle => 0, + Shape::Square => 4, + Shape::Rectangle => 4, + Shape::Triangle => 3, + Shape::Pentagon => 5, + } } } diff --git a/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs b/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs index 03faf0c..ec880cf 100644 --- a/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs +++ b/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs @@ -38,7 +38,11 @@ impl Ticket { } } pub fn assigned_to(&self) -> &str { - todo!() + if let Status::InProgress { assigned_to } = &self.status { + assigned_to + } else { + panic!("Only `In-Progress` tickets can be assigned to someone") + } } } diff --git a/exercises/05_ticket_v2/04_if_let/src/lib.rs b/exercises/05_ticket_v2/04_if_let/src/lib.rs index 0884fb5..31d0838 100644 --- a/exercises/05_ticket_v2/04_if_let/src/lib.rs +++ b/exercises/05_ticket_v2/04_if_let/src/lib.rs @@ -8,7 +8,11 @@ impl Shape { // TODO: Implement the `radius` method using // either an `if let` or a `let/else`. pub fn radius(&self) -> f64 { - todo!() + if let Shape::Circle { radius } = &self { + *radius + } else { + panic!("wrong"); + } } } diff --git a/exercises/05_ticket_v2/05_nullability/src/lib.rs b/exercises/05_ticket_v2/05_nullability/src/lib.rs index f4e5cb2..7c8c0f9 100644 --- a/exercises/05_ticket_v2/05_nullability/src/lib.rs +++ b/exercises/05_ticket_v2/05_nullability/src/lib.rs @@ -36,7 +36,11 @@ impl Ticket { } } pub fn assigned_to(&self) -> Option<&String> { - todo!() + if let Status::InProgress { assigned_to } = &self.status { + Some(assigned_to) + } else { + None + } } } diff --git a/exercises/05_ticket_v2/06_fallibility/src/lib.rs b/exercises/05_ticket_v2/06_fallibility/src/lib.rs index 3144bee..e44949d 100644 --- a/exercises/05_ticket_v2/06_fallibility/src/lib.rs +++ b/exercises/05_ticket_v2/06_fallibility/src/lib.rs @@ -16,25 +16,25 @@ enum Status { } impl Ticket { - pub fn new(title: String, description: String, status: Status) -> Ticket { + pub fn new(title: String, description: String, status: Status) -> Result { if title.is_empty() { - panic!("Title cannot be empty"); + Err("Title cannot be empty")?; } if title.len() > 50 { - panic!("Title cannot be longer than 50 bytes"); + Err("Title cannot be longer than 50 bytes")?; } if description.is_empty() { - panic!("Description cannot be empty"); + Err("Description cannot be empty")?; } if description.len() > 500 { - panic!("Description cannot be longer than 500 bytes"); + Err("Description cannot be longer than 500 bytes")?; } - Ticket { + Ok(Ticket { title, description, status, - } + }) } } diff --git a/exercises/05_ticket_v2/07_unwrap/src/lib.rs b/exercises/05_ticket_v2/07_unwrap/src/lib.rs index 4b6419b..63e93e1 100644 --- a/exercises/05_ticket_v2/07_unwrap/src/lib.rs +++ b/exercises/05_ticket_v2/07_unwrap/src/lib.rs @@ -2,7 +2,7 @@ // When the description is invalid, instead, it should use a default description: // "Description not provided". fn easy_ticket(title: String, description: String, status: Status) -> Ticket { - todo!() + Ticket::new(title, description, status).unwrap() } #[derive(Debug, PartialEq, Clone)] @@ -22,17 +22,18 @@ enum Status { impl Ticket { pub fn new(title: String, description: String, status: Status) -> Result { if title.is_empty() { - return Err("Title cannot be empty".to_string()); + panic!("Title cannot be empty"); } if title.len() > 50 { - return Err("Title cannot be longer than 50 bytes".to_string()); - } - if description.is_empty() { - return Err("Description cannot be empty".to_string()); - } - if description.len() > 500 { - return Err("Description cannot be longer than 500 bytes".to_string()); + panic!("Title cannot be longer than 50 bytes"); } + let description = { + if description.is_empty() || description.len() > 500 { + "Description not provided".to_string() + } else { + description + } + }; Ok(Ticket { title, diff --git a/exercises/05_ticket_v2/08_error_enums/src/lib.rs b/exercises/05_ticket_v2/08_error_enums/src/lib.rs index c74fcc9..300096b 100644 --- a/exercises/05_ticket_v2/08_error_enums/src/lib.rs +++ b/exercises/05_ticket_v2/08_error_enums/src/lib.rs @@ -1,14 +1,23 @@ // TODO: Use two variants, one for a title error and one for a description error. // Each variant should contain a string with the explanation of what went wrong exactly. // You'll have to update the implementation of `Ticket::new` as well. -enum TicketNewError {} +enum TicketNewError { + TitleError(&'static str), + DescriptionError(&'static str), +} // TODO: `easy_ticket` should panic when the title is invalid, using the error message // stored inside the relevant variant of the `TicketNewError` enum. // When the description is invalid, instead, it should use a default description: // "Description not provided". fn easy_ticket(title: String, description: String, status: Status) -> Ticket { - todo!() + Ticket::new(title.clone(), description, status.clone()).unwrap_or_else(|t| match t { + TicketNewError::TitleError(e) => panic!("{}", e), + TicketNewError::DescriptionError(_) => { + Ticket::new(title, "Description not provided".to_string(), status) + .unwrap_or_else(|_| panic!("")) + } + }) } #[derive(Debug, PartialEq)] @@ -32,16 +41,22 @@ impl Ticket { status: Status, ) -> Result { if title.is_empty() { - return Err("Title cannot be empty".to_string()); + Err(TicketNewError::TitleError("Title cannot be empty"))?; } if title.len() > 50 { - return Err("Title cannot be longer than 50 bytes".to_string()); + Err(TicketNewError::TitleError( + "Title cannot be longer than 50 bytes", + ))?; } if description.is_empty() { - return Err("Description cannot be empty".to_string()); + Err(TicketNewError::DescriptionError( + "Description cannot be empty", + ))?; } if description.len() > 500 { - return Err("Description cannot be longer than 500 bytes".to_string()); + Err(TicketNewError::DescriptionError( + "Description cannot be longer than 500 bytes", + ))?; } Ok(Ticket {