feat: up
This commit is contained in:
parent
800cd6d542
commit
90a1d15565
|
@ -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)]
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -6,16 +6,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn u16_size() {
|
||||
assert_eq!(size_of::<u16>(), todo!());
|
||||
assert_eq!(size_of::<u16>(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_size() {
|
||||
assert_eq!(size_of::<i32>(), todo!());
|
||||
assert_eq!(size_of::<i32>(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_size() {
|
||||
assert_eq!(size_of::<bool>(), todo!());
|
||||
assert_eq!(size_of::<bool>(), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn string_size() {
|
||||
assert_eq!(size_of::<String>(), todo!());
|
||||
assert_eq!(size_of::<String>(), 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::<Ticket>(), todo!());
|
||||
assert_eq!(size_of::<Ticket>(), 72);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
fn intro() -> &'static str {
|
||||
// TODO: fix me 👇
|
||||
"I'm ready to __!"
|
||||
"I'm ready to learn about traits!"
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// collections (e.g. BTreeMap).
|
||||
|
||||
/// Return the minimum of two values.
|
||||
pub fn min<T>(left: T, right: T) -> T {
|
||||
pub fn min<T: std::cmp::Ord>(left: T, right: T) -> T {
|
||||
if left <= right {
|
||||
left
|
||||
} else {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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::<str>();
|
||||
// std::mem::size_of::<str>();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,12 @@ pub struct WrappingU32 {
|
|||
value: u32,
|
||||
}
|
||||
|
||||
impl From<u32> for WrappingU32 {
|
||||
fn from(value: u32) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
fn example() {
|
||||
let wrapping: WrappingU32 = 42.into();
|
||||
let wrapping = WrappingU32::from(42);
|
||||
|
|
|
@ -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<T> {
|
||||
fn power(&self, t: T) -> Self;
|
||||
}
|
||||
|
||||
impl Power<u16> for u32 {
|
||||
fn power(&self, t: u16) -> Self {
|
||||
self.pow(t as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Power<u32> 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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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<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 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<SaturatingU16> 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<u16> for SaturatingU16 {
|
||||
type Output = u16;
|
||||
|
||||
fn add(self, rhs: u16) -> Self::Output {
|
||||
rhs.saturating_add(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<u16> for SaturatingU16 {
|
||||
fn eq(&self, other: &u16) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<SaturatingU16> for u16 {
|
||||
fn eq(&self, other: &SaturatingU16) -> bool {
|
||||
other.0 == *self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Self, &'static str> {
|
||||
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,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Ticket, String> {
|
||||
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,
|
||||
|
|
|
@ -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<Ticket, TicketNewError> {
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue