initial commit
This commit is contained in:
10
exercises/03_ticket_v1/00_intro/Cargo.toml
Normal file
10
exercises/03_ticket_v1/00_intro/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "intro_02"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints.rust]
|
||||
# We silence dead code warnings for the time being in order to reduce
|
||||
# compiler noise.
|
||||
# We'll re-enable them again once we explain how visibility works in Rust.
|
||||
dead_code = "allow"
|
||||
14
exercises/03_ticket_v1/00_intro/src/lib.rs
Normal file
14
exercises/03_ticket_v1/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 start modelling a software ticket!");
|
||||
}
|
||||
}
|
||||
10
exercises/03_ticket_v1/01_struct/Cargo.toml
Normal file
10
exercises/03_ticket_v1/01_struct/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "struct_"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints.rust]
|
||||
# We silence dead code warnings for the time being in order to reduce
|
||||
# compiler noise.
|
||||
# We'll re-enable them again once we explain how visibility works in Rust.
|
||||
dead_code = "allow"
|
||||
29
exercises/03_ticket_v1/01_struct/src/lib.rs
Normal file
29
exercises/03_ticket_v1/01_struct/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Define a struct named `Order` with the following fields:
|
||||
// - `price`, an unsigned integer
|
||||
// - `quantity`, an unsigned integer
|
||||
//
|
||||
// It should also have a method named `is_available` that returns a `true` if the quantity is
|
||||
// greater than 0, otherwise `false`.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_order_is_available() {
|
||||
let order = Order {
|
||||
price: 100,
|
||||
quantity: 10,
|
||||
};
|
||||
assert!(order.is_available());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order_is_not_available() {
|
||||
let order = Order {
|
||||
price: 100,
|
||||
quantity: 0,
|
||||
};
|
||||
assert!(!order.is_available());
|
||||
}
|
||||
}
|
||||
13
exercises/03_ticket_v1/02_validation/Cargo.toml
Normal file
13
exercises/03_ticket_v1/02_validation/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "validation"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
|
||||
[lints.rust]
|
||||
# We silence dead code warnings for the time being in order to reduce
|
||||
# compiler noise.
|
||||
# We'll re-enable them again once we explain how visibility works in Rust.
|
||||
dead_code = "allow"
|
||||
74
exercises/03_ticket_v1/02_validation/src/lib.rs
Normal file
74
exercises/03_ticket_v1/02_validation/src/lib.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
// TODO: implement the `new` function.
|
||||
// The following requirements should be met:
|
||||
// - Only `To-Do`, `In Progress`, and `Done` statuses are allowed.
|
||||
// - The `title` and `description` fields should not be empty.
|
||||
// - the `title` should be at most 50 bytes long.
|
||||
// - the `description` should be at most 500 bytes long.
|
||||
// The method should panic if any of the requirements are not met.
|
||||
// You can find the needed panic messages in the tests.
|
||||
//
|
||||
// You'll have to use what you learned in the previous exercises,
|
||||
// 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!();
|
||||
Self {
|
||||
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() {
|
||||
Ticket::new("".into(), valid_description(), "To-Do".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Description cannot be empty")]
|
||||
fn description_cannot_be_empty() {
|
||||
Ticket::new(valid_title(), "".into(), "To-Do".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
Ticket::new(overly_long_title(), valid_description(), "To-Do".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Description cannot be longer than 500 bytes")]
|
||||
fn description_cannot_be_longer_than_500_chars() {
|
||||
Ticket::new(valid_title(), overly_long_description(), "To-Do".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Only `To-Do`, `In Progress`, and `Done` statuses are allowed")]
|
||||
fn status_must_be_valid() {
|
||||
Ticket::new(valid_title(), valid_description(), "Funny".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn done_is_allowed() {
|
||||
Ticket::new(valid_title(), valid_description(), "Done".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_progress_is_allowed() {
|
||||
Ticket::new(valid_title(), valid_description(), "In Progress".into());
|
||||
}
|
||||
}
|
||||
10
exercises/03_ticket_v1/03_modules/Cargo.toml
Normal file
10
exercises/03_ticket_v1/03_modules/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "modules"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints.rust]
|
||||
# We silence dead code warnings for the time being in order to reduce
|
||||
# compiler noise.
|
||||
# We'll re-enable them again once we explain how visibility works in Rust.
|
||||
dead_code = "allow"
|
||||
40
exercises/03_ticket_v1/03_modules/src/lib.rs
Normal file
40
exercises/03_ticket_v1/03_modules/src/lib.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
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.
|
||||
|
||||
fn create_todo_ticket(title: String, description: String) -> Ticket {
|
||||
Ticket::new(title, description, "To-Do".into())
|
||||
}
|
||||
}
|
||||
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/04_visibility/Cargo.toml
Normal file
4
exercises/03_ticket_v1/04_visibility/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "visibility"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
77
exercises/03_ticket_v1/04_visibility/src/lib.rs
Normal file
77
exercises/03_ticket_v1/04_visibility/src/lib.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
mod ticket {
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: **Exceptionally**, you'll be modifying both the `ticket` module and the `tests` module
|
||||
// in this exercise.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// TODO: Add the necessary `pub` modifiers in the parent module to remove the compiler
|
||||
// errors about the use statement below.
|
||||
use super::ticket::Ticket;
|
||||
|
||||
// Be careful though! We don't want this function to compile after you have changed
|
||||
// visibility to make the use statement compile!
|
||||
// Once you have verified that it indeed doesn't compile, comment it out.
|
||||
fn should_not_be_possible() {
|
||||
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
|
||||
// You should be seeing this error when trying to run this exercise:
|
||||
//
|
||||
// error[E0616]: field `description` of struct `Ticket` is private
|
||||
// |
|
||||
// | assert_eq!(ticket.description, "A description");
|
||||
// | ^^^^^^^^^^^^^^^^^^
|
||||
//
|
||||
// TODO: Once you have verified that the below does not compile,
|
||||
// comment the line out to move on to the next exercise!
|
||||
assert_eq!(ticket.description, "A description");
|
||||
}
|
||||
|
||||
fn encapsulation_cannot_be_violated() {
|
||||
// This should be impossible as well, with a similar error as the one encountered above.
|
||||
// (It will throw a compilation error only after you have commented the faulty line
|
||||
// in the previous test - next compilation stage!)
|
||||
//
|
||||
// This proves that `Ticket::new` is now the only way to get a `Ticket` instance.
|
||||
// It's impossible to create a ticket with an illegal title or description!
|
||||
//
|
||||
// TODO: Once you have verified that the below does not compile,
|
||||
// comment the lines out to move on to the next exercise!
|
||||
let ticket = Ticket {
|
||||
title: "A title".into(),
|
||||
description: "A description".into(),
|
||||
status: "To-Do".into(),
|
||||
};
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/05_encapsulation/Cargo.toml
Normal file
4
exercises/03_ticket_v1/05_encapsulation/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "encapsulation"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
61
exercises/03_ticket_v1/05_encapsulation/src/lib.rs
Normal file
61
exercises/03_ticket_v1/05_encapsulation/src/lib.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
pub mod ticket {
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add three public methods to the `Ticket` struct:
|
||||
// - `title` that returns the `title` field.
|
||||
// - `description` that returns the `description` field.
|
||||
// - `status` that returns the `status` field.
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ticket::Ticket;
|
||||
|
||||
#[test]
|
||||
fn description() {
|
||||
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
assert_eq!(ticket.description(), "A description");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn title() {
|
||||
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
assert_eq!(ticket.title(), "A title");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status() {
|
||||
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
assert_eq!(ticket.status(), "To-Do");
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/06_ownership/Cargo.toml
Normal file
4
exercises/03_ticket_v1/06_ownership/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "ownership"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
64
exercises/03_ticket_v1/06_ownership/src/lib.rs
Normal file
64
exercises/03_ticket_v1/06_ownership/src/lib.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
// TODO: based on what we just learned about ownership, it sounds like immutable references
|
||||
// are a good fit for our accessor methods.
|
||||
// Change the existing implementation of `Ticket`'s accessor methods to take a reference
|
||||
// to `self` as an argument, rather than taking ownership of it.
|
||||
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
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::Ticket;
|
||||
|
||||
#[test]
|
||||
fn works() {
|
||||
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
// If you change the signatures as requested, this should compile:
|
||||
// we can call these methods one after the other because they borrow `self`
|
||||
// rather than taking ownership of it.
|
||||
assert_eq!(ticket.title(), "A title");
|
||||
assert_eq!(ticket.description(), "A description");
|
||||
assert_eq!(ticket.status(), "To-Do");
|
||||
}
|
||||
}
|
||||
7
exercises/03_ticket_v1/07_setters/Cargo.toml
Normal file
7
exercises/03_ticket_v1/07_setters/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "setters"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
98
exercises/03_ticket_v1/07_setters/src/lib.rs
Normal file
98
exercises/03_ticket_v1/07_setters/src/lib.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
// TODO: Add &mut-setters to the `Ticket` struct for each of its fields.
|
||||
// Make sure to enforce the same validation rules you have in `Ticket::new`!
|
||||
// Even better, extract that logic and reuse it in both places. You can use
|
||||
// private functions or private static methods for that.
|
||||
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
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::Ticket;
|
||||
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
|
||||
|
||||
#[test]
|
||||
fn works() {
|
||||
let mut ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
|
||||
ticket.set_title("A new title".into());
|
||||
ticket.set_description("A new description".into());
|
||||
ticket.set_status("Done".into());
|
||||
|
||||
assert_eq!(ticket.title(), "A new title");
|
||||
assert_eq!(ticket.description(), "A new description");
|
||||
assert_eq!(ticket.status(), "Done");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be empty")]
|
||||
fn title_cannot_be_empty() {
|
||||
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_title("".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Description cannot be empty")]
|
||||
fn description_cannot_be_empty() {
|
||||
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_description("".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
|
||||
fn title_cannot_be_longer_than_fifty_chars() {
|
||||
Ticket::new(valid_title(), valid_description(), "To-Do".into())
|
||||
.set_title(overly_long_title())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Description cannot be longer than 500 bytes")]
|
||||
fn description_cannot_be_longer_than_500_chars() {
|
||||
Ticket::new(valid_title(), valid_description(), "To-Do".into())
|
||||
.set_description(overly_long_description())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Only `To-Do`, `In Progress`, and `Done` statuses are allowed")]
|
||||
fn status_must_be_valid() {
|
||||
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_status("Funny".into());
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/08_stack/Cargo.toml
Normal file
4
exercises/03_ticket_v1/08_stack/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "stack"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
21
exercises/03_ticket_v1/08_stack/src/lib.rs
Normal file
21
exercises/03_ticket_v1/08_stack/src/lib.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// TODO: based on what you learned in this section, replace `todo!()` with
|
||||
// the correct **stack size** for the respective type.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn u16_size() {
|
||||
assert_eq!(size_of::<u16>(), todo!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_size() {
|
||||
assert_eq!(size_of::<i32>(), todo!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_size() {
|
||||
assert_eq!(size_of::<bool>(), todo!());
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/09_heap/Cargo.toml
Normal file
4
exercises/03_ticket_v1/09_heap/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "heap"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
28
exercises/03_ticket_v1/09_heap/src/lib.rs
Normal file
28
exercises/03_ticket_v1/09_heap/src/lib.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
// TODO: based on what you learned in this section, replace `todo!()` with
|
||||
// the correct **stack size** for the respective type.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ticket;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn string_size() {
|
||||
assert_eq!(size_of::<String>(), todo!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ticket_size() {
|
||||
// This is a tricky question!
|
||||
// The "intuitive" answer happens to be the correct answer this time,
|
||||
// but, in general, the memory layout of structs is a more complex topic.
|
||||
// If you're curious, check out the "Type layout" section of The Rust Reference
|
||||
// https://doc.rust-lang.org/reference/type-layout.html for more information.
|
||||
assert_eq!(size_of::<Ticket>(), todo!());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "references_in_memory"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
28
exercises/03_ticket_v1/10_references_in_memory/src/lib.rs
Normal file
28
exercises/03_ticket_v1/10_references_in_memory/src/lib.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
// TODO: based on what you learned in this section, replace `todo!()` with
|
||||
// the correct **stack size** for the respective type.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ticket;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn u16_ref_size() {
|
||||
assert_eq!(size_of::<&u16>(), todo!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u64_mut_ref_size() {
|
||||
assert_eq!(size_of::<&mut u64>(), todo!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ticket_ref_size() {
|
||||
assert_eq!(size_of::<&Ticket>(), todo!());
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/11_destructor/Cargo.toml
Normal file
4
exercises/03_ticket_v1/11_destructor/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "destructor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
16
exercises/03_ticket_v1/11_destructor/src/lib.rs
Normal file
16
exercises/03_ticket_v1/11_destructor/src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
// We need some more machinery to write a proper exercise for destructors.
|
||||
// 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 __!"
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::outro;
|
||||
|
||||
#[test]
|
||||
fn test_outro() {
|
||||
assert_eq!(outro(), "I have a basic understanding of destructors!");
|
||||
}
|
||||
}
|
||||
4
exercises/03_ticket_v1/12_outro/Cargo.toml
Normal file
4
exercises/03_ticket_v1/12_outro/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "outro_02"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
13
exercises/03_ticket_v1/12_outro/src/lib.rs
Normal file
13
exercises/03_ticket_v1/12_outro/src/lib.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
// TODO: Define a new `Order` type.
|
||||
// It should keep track of three pieces of information: `product_name`, `quantity`, and `unit_price`.
|
||||
// The product name can't be empty and it can't be longer than 300 bytes.
|
||||
// The quantity must be strictly greater than zero.
|
||||
// The unit price is in cents and must be strictly greater than zero.
|
||||
// Order must include a method named `total` that returns the total price of the order.
|
||||
// Order must provide setters and getters for each field.
|
||||
//
|
||||
// Tests are located in a different place this time—in the `tests` folder.
|
||||
// The `tests` folder is a special location for `cargo`. It's where it looks for **integration tests**.
|
||||
// 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.
|
||||
49
exercises/03_ticket_v1/12_outro/tests/integration.rs
Normal file
49
exercises/03_ticket_v1/12_outro/tests/integration.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use outro_02::Order;
|
||||
|
||||
// Files inside the `tests` directory are only compiled when you run tests.
|
||||
// As a consequence, we don't need the `#[cfg(test)]` attribute for conditional compilation—it's
|
||||
// implied.
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let mut order = Order::new("Rusty Book".to_string(), 3, 2999);
|
||||
|
||||
assert_eq!(order.product_name(), "Rusty Book");
|
||||
assert_eq!(order.quantity(), &3);
|
||||
assert_eq!(order.unit_price(), &2999);
|
||||
assert_eq!(order.total(), 8997);
|
||||
|
||||
order.set_product_name("Rust Book".to_string());
|
||||
order.set_quantity(2);
|
||||
order.set_unit_price(3999);
|
||||
|
||||
assert_eq!(order.product_name(), "Rust Book");
|
||||
assert_eq!(order.quantity(), &2);
|
||||
assert_eq!(order.unit_price(), &3999);
|
||||
assert_eq!(order.total(), 7998);
|
||||
}
|
||||
|
||||
// Validation tests
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_empty_product_name() {
|
||||
Order::new("".to_string(), 3, 2999);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_long_product_name() {
|
||||
Order::new("a".repeat(301), 3, 2999);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_zero_quantity() {
|
||||
Order::new("Rust Book".to_string(), 0, 2999);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_zero_unit_price() {
|
||||
Order::new("Rust Book".to_string(), 3, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user