initial commit
This commit is contained in:
4
exercises/05_ticket_v2/00_intro/Cargo.toml
Normal file
4
exercises/05_ticket_v2/00_intro/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "intro_04"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
14
exercises/05_ticket_v2/00_intro/src/lib.rs
Normal file
14
exercises/05_ticket_v2/00_intro/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
fn intro() -> &'static str {
|
||||
// TODO: fix me 👇
|
||||
"I'm ready to __!"
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::intro;
|
||||
|
||||
#[test]
|
||||
fn test_intro() {
|
||||
assert_eq!(intro(), "I'm ready to refine the `Ticket` type!");
|
||||
}
|
||||
}
|
||||
7
exercises/05_ticket_v2/01_enum/Cargo.toml
Normal file
7
exercises/05_ticket_v2/01_enum/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "enum_"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
127
exercises/05_ticket_v2/01_enum/src/lib.rs
Normal file
127
exercises/05_ticket_v2/01_enum/src/lib.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
// TODO: use `Status` as type for `Ticket::status`
|
||||
// Adjust the signature and implementation of all other methods as necessary.
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
// `derive`s are recursive: it can only derive `PartialEq` if all fields also implement `PartialEq`.
|
||||
// Same holds for `Debug`. Do what you must with `Status` to make this work.
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
enum Status {
|
||||
// TODO: add the missing variants
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &String {
|
||||
&self.title
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &String {
|
||||
&self.description
|
||||
}
|
||||
|
||||
pub fn status(&self) -> &String {
|
||||
&self.status
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq() {
|
||||
let title = valid_title();
|
||||
let description = valid_description();
|
||||
let ticket1 = Ticket {
|
||||
title: title.clone(),
|
||||
description: description.clone(),
|
||||
status: Status::ToDo,
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.clone(),
|
||||
description: description.clone(),
|
||||
status: Status::ToDo,
|
||||
};
|
||||
assert_eq!(ticket1, ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_description_not_matching() {
|
||||
let title = valid_title();
|
||||
let status = Status::ToDo;
|
||||
let ticket1 = Ticket {
|
||||
title: title.clone(),
|
||||
description: "description".to_string(),
|
||||
status,
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.clone(),
|
||||
description: "description2".to_string(),
|
||||
status,
|
||||
};
|
||||
assert_ne!(ticket1, ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_title_not_matching() {
|
||||
let description = valid_description();
|
||||
let status = Status::InProgress;
|
||||
let ticket1 = Ticket {
|
||||
title: "title".to_string(),
|
||||
description: description.clone(),
|
||||
status,
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: "title2".to_string(),
|
||||
description: description.clone(),
|
||||
status,
|
||||
};
|
||||
assert_ne!(ticket1, ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_not_matching() {
|
||||
let title = valid_title();
|
||||
let description = valid_description();
|
||||
let ticket1 = Ticket {
|
||||
title: title.clone(),
|
||||
description: description.clone(),
|
||||
status: Status::InProgress,
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.clone(),
|
||||
description: description.clone(),
|
||||
status: Status::Done,
|
||||
};
|
||||
assert_ne!(ticket1, ticket2);
|
||||
}
|
||||
}
|
||||
4
exercises/05_ticket_v2/02_match/Cargo.toml
Normal file
4
exercises/05_ticket_v2/02_match/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "match_"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
44
exercises/05_ticket_v2/02_match/src/lib.rs
Normal file
44
exercises/05_ticket_v2/02_match/src/lib.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
enum Shape {
|
||||
Circle,
|
||||
Square,
|
||||
Rectangle,
|
||||
Triangle,
|
||||
Pentagon,
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
// TODO: Implement the `n_sides` method using a `match`.
|
||||
pub fn n_sides(&self) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_circle() {
|
||||
assert_eq!(Shape::Circle.n_sides(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_square() {
|
||||
assert_eq!(Shape::Square.n_sides(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rectangle() {
|
||||
assert_eq!(Shape::Rectangle.n_sides(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_triangle() {
|
||||
assert_eq!(Shape::Triangle.n_sides(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pentagon() {
|
||||
assert_eq!(Shape::Pentagon.n_sides(), 5);
|
||||
}
|
||||
}
|
||||
7
exercises/05_ticket_v2/03_variants_with_data/Cargo.toml
Normal file
7
exercises/05_ticket_v2/03_variants_with_data/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "variants_with_data"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
75
exercises/05_ticket_v2/03_variants_with_data/src/lib.rs
Normal file
75
exercises/05_ticket_v2/03_variants_with_data/src/lib.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
// TODO: Implement `Ticket::assigned_to`.
|
||||
// Return the name of the person assigned to the ticket, if the ticket is in progress.
|
||||
// Panic otherwise.
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: Status) -> 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");
|
||||
}
|
||||
|
||||
Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
}
|
||||
}
|
||||
pub fn assigned_to(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Only `In-Progress` tickets can be assigned to someone")]
|
||||
fn test_todo() {
|
||||
let ticket = Ticket::new(valid_title(), valid_description(), Status::ToDo);
|
||||
ticket.assigned_to();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Only `In-Progress` tickets can be assigned to someone")]
|
||||
fn test_done() {
|
||||
let ticket = Ticket::new(valid_title(), valid_description(), Status::Done);
|
||||
ticket.assigned_to();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_in_progress() {
|
||||
let ticket = Ticket::new(
|
||||
valid_title(),
|
||||
valid_description(),
|
||||
Status::InProgress {
|
||||
assigned_to: "Alice".to_string(),
|
||||
},
|
||||
);
|
||||
assert_eq!(ticket.assigned_to(), "Alice");
|
||||
}
|
||||
}
|
||||
4
exercises/05_ticket_v2/04_if_let/Cargo.toml
Normal file
4
exercises/05_ticket_v2/04_if_let/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "if_let"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
39
exercises/05_ticket_v2/04_if_let/src/lib.rs
Normal file
39
exercises/05_ticket_v2/04_if_let/src/lib.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
enum Shape {
|
||||
Circle { radius: f64 },
|
||||
Square { border: f64 },
|
||||
Rectangle { width: f64, height: f64 },
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
// TODO: Implement the `radius` method using
|
||||
// either an `if let` or a `let/else`.
|
||||
pub fn radius(&self) -> f64 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_circle() {
|
||||
let _ = Shape::Circle { radius: 1.0 }.radius();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_square() {
|
||||
let _ = Shape::Square { border: 1.0 }.radius();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_rectangle() {
|
||||
let _ = Shape::Rectangle {
|
||||
width: 1.0,
|
||||
height: 2.0,
|
||||
}
|
||||
.radius();
|
||||
}
|
||||
}
|
||||
7
exercises/05_ticket_v2/05_nullability/Cargo.toml
Normal file
7
exercises/05_ticket_v2/05_nullability/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "nullability"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
71
exercises/05_ticket_v2/05_nullability/src/lib.rs
Normal file
71
exercises/05_ticket_v2/05_nullability/src/lib.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
// TODO: Implement `Ticket::assigned_to` using `Option` as the return type.
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: Status) -> 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");
|
||||
}
|
||||
|
||||
Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
}
|
||||
}
|
||||
pub fn assigned_to(&self) -> Option<&String> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
fn test_todo() {
|
||||
let ticket = Ticket::new(valid_title(), valid_description(), Status::ToDo);
|
||||
assert!(ticket.assigned_to().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_done() {
|
||||
let ticket = Ticket::new(valid_title(), valid_description(), Status::Done);
|
||||
assert!(ticket.assigned_to().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_in_progress() {
|
||||
let ticket = Ticket::new(
|
||||
valid_title(),
|
||||
valid_description(),
|
||||
Status::InProgress {
|
||||
assigned_to: "Alice".to_string(),
|
||||
},
|
||||
);
|
||||
assert_eq!(ticket.assigned_to(), Some(&"Alice".to_string()));
|
||||
}
|
||||
}
|
||||
7
exercises/05_ticket_v2/06_fallibility/Cargo.toml
Normal file
7
exercises/05_ticket_v2/06_fallibility/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "fallibility"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
71
exercises/05_ticket_v2/06_fallibility/src/lib.rs
Normal file
71
exercises/05_ticket_v2/06_fallibility/src/lib.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
// TODO: Convert the `Ticket::new` method to return a `Result` instead of panicking.
|
||||
// Use `String` as the error type.
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: Status) -> 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");
|
||||
}
|
||||
|
||||
Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
fn title_cannot_be_empty() {
|
||||
let error = Ticket::new("".into(), valid_description(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(error, "Title cannot be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn description_cannot_be_empty() {
|
||||
let error = Ticket::new(valid_title(), "".into(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(error, "Description cannot be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
let error =
|
||||
Ticket::new(overly_long_title(), valid_description(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(error, "Title cannot be longer than 50 bytes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn description_cannot_be_longer_than_500_chars() {
|
||||
let error =
|
||||
Ticket::new(valid_title(), overly_long_description(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(error, "Description cannot be longer than 500 bytes");
|
||||
}
|
||||
}
|
||||
7
exercises/05_ticket_v2/07_unwrap/Cargo.toml
Normal file
7
exercises/05_ticket_v2/07_unwrap/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "unwrap"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
73
exercises/05_ticket_v2/07_unwrap/src/lib.rs
Normal file
73
exercises/05_ticket_v2/07_unwrap/src/lib.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// TODO: `easy_ticket` should panic when the title is invalid.
|
||||
// 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!()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
Ok(Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be empty")]
|
||||
fn title_cannot_be_empty() {
|
||||
easy_ticket("".into(), valid_description(), Status::ToDo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_description_is_used_if_empty() {
|
||||
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
|
||||
assert_eq!(ticket.description, "Description not provided");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_description_is_used_if_too_long() {
|
||||
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
|
||||
assert_eq!(ticket.description, "Description not provided");
|
||||
}
|
||||
}
|
||||
7
exercises/05_ticket_v2/08_error_enums/Cargo.toml
Normal file
7
exercises/05_ticket_v2/08_error_enums/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "error_enums"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
83
exercises/05_ticket_v2/08_error_enums/src/lib.rs
Normal file
83
exercises/05_ticket_v2/08_error_enums/src/lib.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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 {}
|
||||
|
||||
// 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!()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
) -> Result<Ticket, TicketNewError> {
|
||||
if title.is_empty() {
|
||||
return Err("Title cannot be empty".to_string());
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
Ok(Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be empty")]
|
||||
fn title_cannot_be_empty() {
|
||||
easy_ticket("".into(), valid_description(), Status::ToDo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_description_is_used_if_empty() {
|
||||
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
|
||||
assert_eq!(ticket.description, "Description not provided");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_description_is_used_if_too_long() {
|
||||
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
|
||||
assert_eq!(ticket.description, "Description not provided");
|
||||
}
|
||||
}
|
||||
8
exercises/05_ticket_v2/09_error_trait/Cargo.toml
Normal file
8
exercises/05_ticket_v2/09_error_trait/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "error_trait"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
static_assertions = "1.1.0"
|
||||
105
exercises/05_ticket_v2/09_error_trait/src/lib.rs
Normal file
105
exercises/05_ticket_v2/09_error_trait/src/lib.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
// TODO: Implement `Debug`, `Display` and `Error` for the `TicketNewError` enum.
|
||||
// When implementing `Display`, you may want to use the `write!` macro from Rust's standard library.
|
||||
// The docs for the `std::fmt` module are a good place to start and look for examples:
|
||||
// https://doc.rust-lang.org/std/fmt/index.html#write
|
||||
|
||||
enum TicketNewError {
|
||||
TitleError(String),
|
||||
DescriptionError(String),
|
||||
}
|
||||
|
||||
// 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!()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
) -> Result<Ticket, TicketNewError> {
|
||||
if title.is_empty() {
|
||||
return Err(TicketNewError::TitleError(
|
||||
"Title cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
if title.len() > 50 {
|
||||
return Err(TicketNewError::TitleError(
|
||||
"Title cannot be longer than 50 bytes".to_string(),
|
||||
));
|
||||
}
|
||||
if description.is_empty() {
|
||||
return Err(TicketNewError::DescriptionError(
|
||||
"Description cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
if description.len() > 500 {
|
||||
return Err(TicketNewError::DescriptionError(
|
||||
"Description cannot be longer than 500 bytes".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
|
||||
use static_assertions::assert_impl_one;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be empty")]
|
||||
fn title_cannot_be_empty() {
|
||||
easy_ticket("".into(), valid_description(), Status::ToDo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_description_is_used_if_empty() {
|
||||
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
|
||||
assert_eq!(ticket.description, "Description not provided");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_description_is_used_if_too_long() {
|
||||
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
|
||||
assert_eq!(ticket.description, "Description not provided");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_is_correctly_implemented() {
|
||||
let ticket = Ticket::new("".into(), valid_description(), Status::ToDo);
|
||||
assert_eq!(format!("{}", ticket.unwrap_err()), "Title cannot be empty");
|
||||
}
|
||||
|
||||
assert_impl_one!(TicketNewError: std::error::Error);
|
||||
}
|
||||
4
exercises/05_ticket_v2/10_packages/Cargo.toml
Normal file
4
exercises/05_ticket_v2/10_packages/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "packages"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
11
exercises/05_ticket_v2/10_packages/src/main.rs
Normal file
11
exercises/05_ticket_v2/10_packages/src/main.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
// This is a `main.rs` file, therefore `cargo` interprets this as the root of a binary target.
|
||||
|
||||
// TODO: fix this broken import. Create a new library target in the `src` directory.
|
||||
// The library target should expose a public function named `hello_world` that takes no arguments
|
||||
// and returns nothing.
|
||||
use packages::hello_world;
|
||||
|
||||
// This is the entrypoint of the binary.
|
||||
fn main() {
|
||||
hello_world();
|
||||
}
|
||||
4
exercises/05_ticket_v2/11_dependencies/Cargo.toml
Normal file
4
exercises/05_ticket_v2/11_dependencies/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "deps"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
6
exercises/05_ticket_v2/11_dependencies/src/lib.rs
Normal file
6
exercises/05_ticket_v2/11_dependencies/src/lib.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
// TODO: Add `anyhow` as a dependency of this project.
|
||||
// Don't touch this import!
|
||||
|
||||
// When you import a type (`Error`) from a dependency, the import path must start
|
||||
// with the crate name (`anyhow`, in this case).
|
||||
use anyhow::Error;
|
||||
9
exercises/05_ticket_v2/12_thiserror/Cargo.toml
Normal file
9
exercises/05_ticket_v2/12_thiserror/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "thiserror_"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
85
exercises/05_ticket_v2/12_thiserror/src/lib.rs
Normal file
85
exercises/05_ticket_v2/12_thiserror/src/lib.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
// TODO: Implement the `Error` trait for `TicketNewError` using `thiserror`.
|
||||
// We've changed the enum variants to be more specific, thus removing the need for storing
|
||||
// a `String` field into each variant.
|
||||
// You'll also have to add `thiserror` as a dependency in the `Cargo.toml` file.
|
||||
|
||||
enum TicketNewError {
|
||||
TitleCannotBeEmpty,
|
||||
TitleTooLong,
|
||||
DescriptionCannotBeEmpty,
|
||||
DescriptionTooLong,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress { assigned_to: String },
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
) -> Result<Ticket, TicketNewError> {
|
||||
if title.is_empty() {
|
||||
return Err(TicketNewError::TitleCannotBeEmpty);
|
||||
}
|
||||
if title.len() > 50 {
|
||||
return Err(TicketNewError::TitleTooLong);
|
||||
}
|
||||
if description.is_empty() {
|
||||
return Err(TicketNewError::DescriptionCannotBeEmpty);
|
||||
}
|
||||
if description.len() > 500 {
|
||||
return Err(TicketNewError::DescriptionTooLong);
|
||||
}
|
||||
|
||||
Ok(Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
fn title_cannot_be_empty() {
|
||||
let err = Ticket::new("".into(), valid_description(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(err.to_string(), "Title cannot be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn description_cannot_be_empty() {
|
||||
let err = Ticket::new(valid_title(), "".into(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(err.to_string(), "Description cannot be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
let err = Ticket::new(overly_long_title(), valid_description(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(err.to_string(), "Title cannot be longer than 50 bytes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn description_cannot_be_too_long() {
|
||||
let err = Ticket::new(valid_title(), overly_long_description(), Status::ToDo).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"Description cannot be longer than 500 bytes"
|
||||
);
|
||||
}
|
||||
}
|
||||
6
exercises/05_ticket_v2/13_try_from/Cargo.toml
Normal file
6
exercises/05_ticket_v2/13_try_from/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "tryfrom"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
39
exercises/05_ticket_v2/13_try_from/src/lib.rs
Normal file
39
exercises/05_ticket_v2/13_try_from/src/lib.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for `Status`.
|
||||
// The parsing should be case-insensitive.
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Status {
|
||||
ToDo,
|
||||
InProgress,
|
||||
Done,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_try_from_string() {
|
||||
let status = Status::try_from("ToDO".to_string()).unwrap();
|
||||
assert_eq!(status, Status::ToDo);
|
||||
|
||||
let status = Status::try_from("inproGress".to_string()).unwrap();
|
||||
assert_eq!(status, Status::InProgress);
|
||||
|
||||
let status = Status::try_from("Done".to_string()).unwrap();
|
||||
assert_eq!(status, Status::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_str() {
|
||||
let status = Status::try_from("todo").unwrap();
|
||||
assert_eq!(status, Status::ToDo);
|
||||
|
||||
let status = Status::try_from("inprogress").unwrap();
|
||||
assert_eq!(status, Status::InProgress);
|
||||
|
||||
let status = Status::try_from("done").unwrap();
|
||||
assert_eq!(status, Status::Done);
|
||||
}
|
||||
}
|
||||
10
exercises/05_ticket_v2/14_source/Cargo.toml
Normal file
10
exercises/05_ticket_v2/14_source/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "source"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.59"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
76
exercises/05_ticket_v2/14_source/src/lib.rs
Normal file
76
exercises/05_ticket_v2/14_source/src/lib.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use crate::status::Status;
|
||||
|
||||
// We've seen how to declare modules in one of the earliest exercises, but
|
||||
// we haven't seen how to extract them into separate files.
|
||||
// Let's fix that now!
|
||||
//
|
||||
// In the simplest case, when the extracted module is a single file, it is enough to
|
||||
// create a new file with the same name as the module and move the module content there.
|
||||
// The module file should be placed in the same directory as the file that declares the module.
|
||||
// In this case, `src/lib.rs`, thus `status.rs` should be placed in the `src` directory.
|
||||
mod status;
|
||||
|
||||
// TODO: Add a new error variant to `TicketNewError` for when the status string is invalid.
|
||||
// When calling `source` on an error of that variant, it should return a `ParseStatusError` rather than `None`.
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TicketNewError {
|
||||
#[error("Title cannot be empty")]
|
||||
TitleCannotBeEmpty,
|
||||
#[error("Title cannot be longer than 50 bytes")]
|
||||
TitleTooLong,
|
||||
#[error("Description cannot be empty")]
|
||||
DescriptionCannotBeEmpty,
|
||||
#[error("Description cannot be longer than 500 bytes")]
|
||||
DescriptionTooLong,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: String) -> Result<Self, TicketNewError> {
|
||||
if title.is_empty() {
|
||||
return Err(TicketNewError::TitleCannotBeEmpty);
|
||||
}
|
||||
if title.len() > 50 {
|
||||
return Err(TicketNewError::TitleTooLong);
|
||||
}
|
||||
if description.is_empty() {
|
||||
return Err(TicketNewError::DescriptionCannotBeEmpty);
|
||||
}
|
||||
if description.len() > 500 {
|
||||
return Err(TicketNewError::DescriptionTooLong);
|
||||
}
|
||||
|
||||
// TODO: Parse the status string into a `Status` enum.
|
||||
|
||||
Ok(Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::{valid_description, valid_title};
|
||||
use std::error::Error;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn invalid_status() {
|
||||
let err = Ticket::new(valid_title(), valid_description(), "invalid".into()).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"`invalid` is not a valid status. Use one of: ToDo, InProgress, Done"
|
||||
);
|
||||
assert!(err.source().is_some());
|
||||
}
|
||||
}
|
||||
46
exercises/05_ticket_v2/14_source/src/status.rs
Normal file
46
exercises/05_ticket_v2/14_source/src/status.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Status {
|
||||
ToDo,
|
||||
InProgress,
|
||||
Done,
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Status {
|
||||
type Error = ParseStatusError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
let value = value.to_lowercase();
|
||||
match value.as_str() {
|
||||
"todo" => Ok(Status::ToDo),
|
||||
"inprogress" => Ok(Status::InProgress),
|
||||
"done" => Ok(Status::Done),
|
||||
_ => Err(ParseStatusError {
|
||||
invalid_status: value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("`{invalid_status}` is not a valid status. Use one of: ToDo, InProgress, Done")]
|
||||
pub struct ParseStatusError {
|
||||
invalid_status: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_try_from_string() {
|
||||
let status = Status::try_from("ToDO".to_string()).unwrap();
|
||||
assert_eq!(status, Status::ToDo);
|
||||
|
||||
let status = Status::try_from("inproGress".to_string()).unwrap();
|
||||
assert_eq!(status, Status::InProgress);
|
||||
|
||||
let status = Status::try_from("Done".to_string()).unwrap();
|
||||
assert_eq!(status, Status::Done);
|
||||
}
|
||||
}
|
||||
4
exercises/05_ticket_v2/15_outro/Cargo.toml
Normal file
4
exercises/05_ticket_v2/15_outro/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "outro_04"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
39
exercises/05_ticket_v2/15_outro/src/description.rs
Normal file
39
exercises/05_ticket_v2/15_outro/src/description.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketDescription` type,
|
||||
// enforcing that the description is not empty and is not longer than 500 bytes.
|
||||
// Implement the traits required to make the tests pass too.
|
||||
|
||||
pub struct TicketDescription(String);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_try_from_string() {
|
||||
let description = TicketDescription::try_from("A description".to_string()).unwrap();
|
||||
assert_eq!(description.0, "A description");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_empty_string() {
|
||||
let err = TicketDescription::try_from("".to_string()).unwrap_err();
|
||||
assert_eq!(err.to_string(), "The description cannot be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_long_string() {
|
||||
let description = "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.".to_string();
|
||||
let err = TicketDescription::try_from(description).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"The description cannot be longer than 500 bytes"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_str() {
|
||||
let description = TicketDescription::try_from("A description").unwrap();
|
||||
assert_eq!(description.0, "A description");
|
||||
}
|
||||
}
|
||||
28
exercises/05_ticket_v2/15_outro/src/lib.rs
Normal file
28
exercises/05_ticket_v2/15_outro/src/lib.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
// TODO: you have something to do in each of the modules in this crate!
|
||||
mod description;
|
||||
mod status;
|
||||
mod title;
|
||||
|
||||
// A common pattern in Rust is to split code into multiple (private) modules
|
||||
// and then re-export the public parts of those modules at the root of the crate.
|
||||
//
|
||||
// This hides the internal structure of the crate from your users, while still
|
||||
// allowing you to organize your code however you like.
|
||||
pub use description::TicketDescription;
|
||||
pub use status::Status;
|
||||
pub use title::TicketTitle;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
// We no longer need to make the fields private!
|
||||
// Since each field encapsulates its own validation logic, there is no risk of
|
||||
// a user of `Ticket` modifying the fields in a way that would break the
|
||||
// invariants of the struct.
|
||||
//
|
||||
// Careful though: if you had any invariants that spanned multiple fields, you
|
||||
// would need to ensure that those invariants are still maintained and go back
|
||||
// to making the fields private.
|
||||
pub struct Ticket {
|
||||
pub title: TicketTitle,
|
||||
pub description: TicketDescription,
|
||||
pub status: Status,
|
||||
}
|
||||
44
exercises/05_ticket_v2/15_outro/src/status.rs
Normal file
44
exercises/05_ticket_v2/15_outro/src/status.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `Status` enum.
|
||||
// The parsing should be case-insensitive.
|
||||
|
||||
pub enum Status {
|
||||
ToDo,
|
||||
InProgress,
|
||||
Done,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_try_from_string() {
|
||||
let status = Status::try_from("ToDO".to_string()).unwrap();
|
||||
assert_eq!(status, Status::ToDo);
|
||||
|
||||
let status = Status::try_from("inproGress".to_string()).unwrap();
|
||||
assert_eq!(status, Status::InProgress);
|
||||
|
||||
let status = Status::try_from("Done".to_string()).unwrap();
|
||||
assert_eq!(status, Status::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_str() {
|
||||
let status = Status::try_from("ToDO").unwrap();
|
||||
assert_eq!(status, Status::ToDo);
|
||||
|
||||
let status = Status::try_from("inproGress").unwrap();
|
||||
assert_eq!(status, Status::InProgress);
|
||||
|
||||
let status = Status::try_from("Done").unwrap();
|
||||
assert_eq!(status, Status::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_invalid() {
|
||||
let status = Status::try_from("Invalid");
|
||||
assert!(status.is_err());
|
||||
}
|
||||
}
|
||||
38
exercises/05_ticket_v2/15_outro/src/title.rs
Normal file
38
exercises/05_ticket_v2/15_outro/src/title.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketTitle` type,
|
||||
// enforcing that the title is not empty and is not longer than 50 bytes.
|
||||
// Implement the traits required to make the tests pass too.
|
||||
|
||||
pub struct TicketTitle(String);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_try_from_string() {
|
||||
let title = TicketTitle::try_from("A title".to_string()).unwrap();
|
||||
assert_eq!(title.0, "A title");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_empty_string() {
|
||||
let err = TicketTitle::try_from("".to_string()).unwrap_err();
|
||||
assert_eq!(err.to_string(), "The title cannot be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_long_string() {
|
||||
let title =
|
||||
"A title that's definitely longer than what should be allowed in a development ticket"
|
||||
.to_string();
|
||||
let err = TicketTitle::try_from(title).unwrap_err();
|
||||
assert_eq!(err.to_string(), "The title cannot be longer than 50 bytes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_str() {
|
||||
let title = TicketTitle::try_from("A title").unwrap();
|
||||
assert_eq!(title.0, "A title");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user