initial commit
This commit is contained in:
10
exercises/01_intro/00_welcome/Cargo.toml
Normal file
10
exercises/01_intro/00_welcome/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "welcome_00"
|
||||
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"
|
||||
46
exercises/01_intro/00_welcome/src/lib.rs
Normal file
46
exercises/01_intro/00_welcome/src/lib.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
// This is a Rust file. It is a plain text file with a `.rs` extension.
|
||||
//
|
||||
// Like most modern programming languages, Rust supports comments. You're looking at one right now!
|
||||
// Comments are ignored by the compiler; you can leverage them to annotate code with notes and
|
||||
// explanations.
|
||||
// There are various ways to write comments in Rust, each with its own purpose.
|
||||
// For now we'll stick to the most common one: the line comment.
|
||||
// Everything from `//` to the end of the line is considered a comment.
|
||||
|
||||
// Exercises will include `TODO`, `todo!()` or `__` markers to draw your attention to the lines
|
||||
// where you need to write code.
|
||||
// You'll need to replace these markers with your own code to complete the exercise.
|
||||
// Sometimes it'll be enough to write a single line of code, other times you'll have to write
|
||||
// longer sections.
|
||||
//
|
||||
// If you get stuck for more than 10 minutes on an exercise, grab a trainer! We're here to help!
|
||||
// You can also find solutions to all exercises in the `solutions` git branch.
|
||||
fn greeting() -> &'static str {
|
||||
// TODO: fix me 👇
|
||||
"I'm ready to learn Rust!"
|
||||
}
|
||||
|
||||
// Your solutions will be automatically verified by a set of tests.
|
||||
// You can run these tests directly by invoking the `cargo test` command in your terminal,
|
||||
// from the root of this exercise's directory. That's what the `wr` command does for you
|
||||
// under the hood.
|
||||
//
|
||||
// Rust lets you write tests alongside your code.
|
||||
// The `#[cfg(test)]` attribute tells the compiler to only compile the code below when
|
||||
// running tests (i.e. when you run `cargo test`).
|
||||
// You'll learn more about attributes and testing later in the course.
|
||||
// For now, just know that you need to look for the `#[cfg(test)]` attribute to find the tests
|
||||
// that will be verifying the correctness of your solutions!
|
||||
//
|
||||
// ⚠️ **DO NOT MODIFY THE TESTS** ⚠️
|
||||
// They are there to help you validate your solutions. You should only change the code that's being
|
||||
// tested, not the tests themselves.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::greeting;
|
||||
|
||||
#[test]
|
||||
fn test_welcome() {
|
||||
assert_eq!(greeting(), "I'm ready to learn Rust!");
|
||||
}
|
||||
}
|
||||
10
exercises/01_intro/01_syntax/Cargo.toml
Normal file
10
exercises/01_intro/01_syntax/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "syntax"
|
||||
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"
|
||||
19
exercises/01_intro/01_syntax/src/lib.rs
Normal file
19
exercises/01_intro/01_syntax/src/lib.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
// TODO: fix the function signature below to make the tests pass.
|
||||
// Make sure to read the compiler error message—the Rust compiler is your pair programming
|
||||
// partner in this course and it'll often guide you in the right direction!
|
||||
//
|
||||
// The input parameters should have the same type of the return type.
|
||||
fn compute(a: u32, b: u32) -> u32 {
|
||||
// Don't touch the function body.
|
||||
a + b * 2
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::compute;
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
assert_eq!(compute(1, 2), 5);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/00_intro/Cargo.toml
Normal file
10
exercises/02_basic_calculator/00_intro/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "intro_01"
|
||||
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/02_basic_calculator/00_intro/src/lib.rs
Normal file
14
exercises/02_basic_calculator/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 build a calculator in Rust!");
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/01_integers/Cargo.toml
Normal file
10
exercises/02_basic_calculator/01_integers/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "integers"
|
||||
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"
|
||||
15
exercises/02_basic_calculator/01_integers/src/lib.rs
Normal file
15
exercises/02_basic_calculator/01_integers/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
fn compute(a: u32, b: u32) -> u32 {
|
||||
// TODO: change the line below to fix the compiler error and make the tests pass.
|
||||
let multiplier: u8 = 4;
|
||||
a + b * multiplier
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::compute;
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
assert_eq!(compute(1, 2), 9);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/02_variables/Cargo.toml
Normal file
10
exercises/02_basic_calculator/02_variables/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "variables"
|
||||
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"
|
||||
34
exercises/02_basic_calculator/02_variables/src/lib.rs
Normal file
34
exercises/02_basic_calculator/02_variables/src/lib.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
// 👇 The lines below, starting with `///`, are called **documentation comments**.
|
||||
// They attach documentation to the item that follows them. In this case, the `speed` function.
|
||||
// If you run `cargo doc --open` from this exercise's directory, Rust will generate
|
||||
// HTML documentation from these comments and open it in your browser.
|
||||
|
||||
/// Given the start and end points of a journey, and the time it took to complete it,
|
||||
/// calculate the average speed.
|
||||
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
|
||||
// TODO: define a variable named `distance` with the right value to get tests to pass
|
||||
// Do you need to annotate the type of `distance`? Why or why not?
|
||||
|
||||
// Don't change the line below
|
||||
distance / time_elapsed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::speed;
|
||||
|
||||
#[test]
|
||||
fn case1() {
|
||||
assert_eq!(speed(0, 10, 10), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case2() {
|
||||
assert_eq!(speed(10, 30, 10), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case3() {
|
||||
assert_eq!(speed(10, 31, 10), 2);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/03_if_else/Cargo.toml
Normal file
10
exercises/02_basic_calculator/03_if_else/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "if_else"
|
||||
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"
|
||||
36
exercises/02_basic_calculator/03_if_else/src/lib.rs
Normal file
36
exercises/02_basic_calculator/03_if_else/src/lib.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
/// Return `12` if `n` is even,
|
||||
/// `13` if `n` is divisible by `3`,
|
||||
/// `17` otherwise.
|
||||
fn magic_number(n: u32) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::magic_number;
|
||||
|
||||
#[test]
|
||||
fn one() {
|
||||
assert_eq!(magic_number(1), 17);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two() {
|
||||
assert_eq!(magic_number(2), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn six() {
|
||||
assert_eq!(magic_number(6), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nine() {
|
||||
assert_eq!(magic_number(9), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn high() {
|
||||
assert_eq!(magic_number(233), 17);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/04_panics/Cargo.toml
Normal file
10
exercises/02_basic_calculator/04_panics/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "panics"
|
||||
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"
|
||||
26
exercises/02_basic_calculator/04_panics/src/lib.rs
Normal file
26
exercises/02_basic_calculator/04_panics/src/lib.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
/// Given the start and end points of a journey, and the time it took to complete the journey,
|
||||
/// calculate the average speed of the journey.
|
||||
fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
|
||||
// TODO: Panic with a custom message if `time_elapsed` is 0
|
||||
|
||||
(end - start) / time_elapsed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::speed;
|
||||
|
||||
#[test]
|
||||
fn case1() {
|
||||
assert_eq!(speed(0, 10, 10), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// 👇 With the `#[should_panic]` annotation we can assert that we expect the code
|
||||
// under test to panic. We can also check the panic message by using `expected`.
|
||||
// This is all part of Rust's built-in test framework!
|
||||
#[should_panic(expected = "The journey took no time at all. That's impossible!")]
|
||||
fn by_zero() {
|
||||
speed(0, 10, 0);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/05_factorial/Cargo.toml
Normal file
10
exercises/02_basic_calculator/05_factorial/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "factorial"
|
||||
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"
|
||||
36
exercises/02_basic_calculator/05_factorial/src/lib.rs
Normal file
36
exercises/02_basic_calculator/05_factorial/src/lib.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Define a function named `factorial` that, given a non-negative integer `n`,
|
||||
// returns `n!`, the factorial of `n`.
|
||||
//
|
||||
// The factorial of `n` is defined as the product of all positive integers up to `n`.
|
||||
// For example, `5!` (read "five factorial") is `5 * 4 * 3 * 2 * 1`, which is `120`.
|
||||
// `0!` is defined to be `1`.
|
||||
//
|
||||
// We expect `factorial(0)` to return `1`, `factorial(1)` to return `1`,
|
||||
// `factorial(2)` to return `2`, and so on.
|
||||
//
|
||||
// Use only what you learned! No loops yet, so you'll have to use recursion!
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::factorial;
|
||||
|
||||
#[test]
|
||||
fn first() {
|
||||
assert_eq!(factorial(0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second() {
|
||||
assert_eq!(factorial(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third() {
|
||||
assert_eq!(factorial(2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fifth() {
|
||||
assert_eq!(factorial(5), 120);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/06_while/Cargo.toml
Normal file
10
exercises/02_basic_calculator/06_while/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "while_"
|
||||
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"
|
||||
33
exercises/02_basic_calculator/06_while/src/lib.rs
Normal file
33
exercises/02_basic_calculator/06_while/src/lib.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
// Rewrite the factorial function using a `while` loop.
|
||||
pub fn factorial(n: u32) -> u32 {
|
||||
// The `todo!()` macro is a placeholder that the compiler
|
||||
// interprets as "I'll get back to this later", thus
|
||||
// suppressing type errors.
|
||||
// It panics at runtime.
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::factorial;
|
||||
|
||||
#[test]
|
||||
fn first() {
|
||||
assert_eq!(factorial(0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second() {
|
||||
assert_eq!(factorial(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third() {
|
||||
assert_eq!(factorial(2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fifth() {
|
||||
assert_eq!(factorial(5), 120);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/07_for/Cargo.toml
Normal file
10
exercises/02_basic_calculator/07_for/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "for_"
|
||||
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/02_basic_calculator/07_for/src/lib.rs
Normal file
29
exercises/02_basic_calculator/07_for/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Rewrite the factorial function using a `for` loop.
|
||||
pub fn factorial(n: u32) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::factorial;
|
||||
|
||||
#[test]
|
||||
fn first() {
|
||||
assert_eq!(factorial(0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second() {
|
||||
assert_eq!(factorial(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third() {
|
||||
assert_eq!(factorial(2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fifth() {
|
||||
assert_eq!(factorial(5), 120);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/08_overflow/Cargo.toml
Normal file
10
exercises/02_basic_calculator/08_overflow/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "overflow"
|
||||
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"
|
||||
49
exercises/02_basic_calculator/08_overflow/src/lib.rs
Normal file
49
exercises/02_basic_calculator/08_overflow/src/lib.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
// Customize the `dev` profile to wrap around on overflow.
|
||||
// Check Cargo's documentation to find out the right syntax:
|
||||
// https://doc.rust-lang.org/cargo/reference/profiles.html
|
||||
//
|
||||
// For reasons that we'll explain later, the customization needs to be done in the `Cargo.toml`
|
||||
// at the root of the repository, not in the `Cargo.toml` of the exercise.
|
||||
|
||||
pub fn factorial(n: u32) -> u32 {
|
||||
let mut result = 1;
|
||||
for i in 1..=n {
|
||||
result *= i;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::factorial;
|
||||
|
||||
#[test]
|
||||
fn twentieth() {
|
||||
// 20! is 2432902008176640000, which is too large to fit in a u32
|
||||
// With the default dev profile, this will panic when you run `cargo test`
|
||||
// We want it to wrap around instead
|
||||
assert_eq!(factorial(20), 2_192_834_560);
|
||||
// ☝️
|
||||
// A large number literal using underscores to improve readability!
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first() {
|
||||
assert_eq!(factorial(0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second() {
|
||||
assert_eq!(factorial(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third() {
|
||||
assert_eq!(factorial(2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fifth() {
|
||||
assert_eq!(factorial(5), 120);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/09_saturating/Cargo.toml
Normal file
10
exercises/02_basic_calculator/09_saturating/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "saturating"
|
||||
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"
|
||||
39
exercises/02_basic_calculator/09_saturating/src/lib.rs
Normal file
39
exercises/02_basic_calculator/09_saturating/src/lib.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
pub fn factorial(n: u32) -> u32 {
|
||||
let mut result = 1;
|
||||
for i in 1..=n {
|
||||
// Use saturating multiplication to stop at the maximum value of u32
|
||||
// rather than overflowing and wrapping around
|
||||
result *= i;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::factorial;
|
||||
|
||||
#[test]
|
||||
fn twentieth() {
|
||||
assert_eq!(factorial(20), u32::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first() {
|
||||
assert_eq!(factorial(0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second() {
|
||||
assert_eq!(factorial(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third() {
|
||||
assert_eq!(factorial(2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fifth() {
|
||||
assert_eq!(factorial(5), 120);
|
||||
}
|
||||
}
|
||||
10
exercises/02_basic_calculator/10_as_casting/Cargo.toml
Normal file
10
exercises/02_basic_calculator/10_as_casting/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "as_cast"
|
||||
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"
|
||||
37
exercises/02_basic_calculator/10_as_casting/src/lib.rs
Normal file
37
exercises/02_basic_calculator/10_as_casting/src/lib.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
// TODO: based on what you learned in this section, replace `todo!()` with
|
||||
// the correct value after the conversion.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn u16_to_u32() {
|
||||
let v: u32 = todo!();
|
||||
assert_eq!(47u16 as u32, v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u8_to_i8() {
|
||||
// The compiler is smart enough to know that the value 255 cannot fit
|
||||
// inside an i8, so it'll emit a hard error. We intentionally disable
|
||||
// this guardrail to make this (bad) conversion possible.
|
||||
// The compiler is only able to pick on this because the value is a
|
||||
// literal. If we were to use a variable, the compiler wouldn't be able to
|
||||
// catch this at compile time.
|
||||
#[allow(overflowing_literals)]
|
||||
let x = { 255 as i8 };
|
||||
|
||||
// You could solve this by using exactly the same expression as above,
|
||||
// but that would defeat the purpose of the exercise. Instead, use a genuine
|
||||
// `i8` value that is equivalent to `255` when converted to `u8`.
|
||||
let y: i8 = todo!();
|
||||
|
||||
assert_eq!(x, y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_to_u8() {
|
||||
let v: u8 = todo!();
|
||||
assert_eq!(true as u8, v);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
4
exercises/04_traits/00_intro/Cargo.toml
Normal file
4
exercises/04_traits/00_intro/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "intro_03"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
14
exercises/04_traits/00_intro/src/lib.rs
Normal file
14
exercises/04_traits/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 learn about traits!");
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/01_trait/Cargo.toml
Normal file
4
exercises/04_traits/01_trait/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "trait_"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
23
exercises/04_traits/01_trait/src/lib.rs
Normal file
23
exercises/04_traits/01_trait/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Define a trait named `IsEven` that has a method `is_even` that returns a `true` if `self` is
|
||||
// even, otherwise `false`.
|
||||
//
|
||||
// Then implement the trait for `u32` and `i32`.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_u32_is_even() {
|
||||
assert!(42u32.is_even());
|
||||
assert!(!43u32.is_even());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i32_is_even() {
|
||||
assert!(42i32.is_even());
|
||||
assert!(!43i32.is_even());
|
||||
assert!(0i32.is_even());
|
||||
assert!(!(-1i32).is_even());
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/02_orphan_rule/Cargo.toml
Normal file
4
exercises/04_traits/02_orphan_rule/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "orphan"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
11
exercises/04_traits/02_orphan_rule/src/lib.rs
Normal file
11
exercises/04_traits/02_orphan_rule/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
// TODO: this is an example of an orphan rule violation.
|
||||
// We're implementing a foreign trait (`PartialEq`, from `std`) on
|
||||
// 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!()
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/03_operator_overloading/Cargo.toml
Normal file
4
exercises/04_traits/03_operator_overloading/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "overloading"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
85
exercises/04_traits/03_operator_overloading/src/lib.rs
Normal file
85
exercises/04_traits/03_operator_overloading/src/lib.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use std::cmp::PartialEq;
|
||||
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
// TODO: Implement the `PartialEq` trait for `Ticket`.
|
||||
|
||||
impl PartialEq for Ticket {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq() {
|
||||
let title = "title";
|
||||
let description = "description";
|
||||
let status = "To-Do";
|
||||
let ticket1 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
assert!(ticket1 == ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_description_not_matching() {
|
||||
let title = "title";
|
||||
let status = "To-Do";
|
||||
let ticket1 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: "description".to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: "description2".to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
assert!(ticket1 != ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_title_not_matching() {
|
||||
let status = "To-Do";
|
||||
let description = "description";
|
||||
let ticket1 = Ticket {
|
||||
title: "title".to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: "title2".to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
assert!(ticket1 != ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_not_matching() {
|
||||
let title = "title";
|
||||
let description = "description";
|
||||
let ticket1 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: "status".to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: "status2".to_string(),
|
||||
};
|
||||
assert!(ticket1 != ticket2);
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/04_derive/Cargo.toml
Normal file
4
exercises/04_traits/04_derive/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "derives"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
90
exercises/04_traits/04_derive/src/lib.rs
Normal file
90
exercises/04_traits/04_derive/src/lib.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully.
|
||||
// Fix it!
|
||||
//
|
||||
// # `Debug` primer
|
||||
//
|
||||
// `Debug` returns a representation of a Rust type that's suitable for debugging (hence the name).
|
||||
// `assert_eq!` requires `Ticket` to implement `Debug` because, when the assertion fails, it tries to
|
||||
// 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)]
|
||||
struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq() {
|
||||
let title = "title";
|
||||
let description = "description";
|
||||
let status = "To-Do";
|
||||
let ticket1 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
assert_eq!(ticket1, ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_description_not_matching() {
|
||||
let title = "title";
|
||||
let status = "To-Do";
|
||||
let ticket1 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: "description".to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: "description2".to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
assert_ne!(ticket1, ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_title_not_matching() {
|
||||
let status = "To-Do";
|
||||
let description = "description";
|
||||
let ticket1 = Ticket {
|
||||
title: "title".to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: "title2".to_string(),
|
||||
description: description.to_string(),
|
||||
status: status.to_string(),
|
||||
};
|
||||
assert_ne!(ticket1, ticket2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_not_matching() {
|
||||
let title = "title";
|
||||
let description = "description";
|
||||
let ticket1 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: "status".to_string(),
|
||||
};
|
||||
let ticket2 = Ticket {
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
status: "status2".to_string(),
|
||||
};
|
||||
assert_ne!(ticket1, ticket2);
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/05_trait_bounds/Cargo.toml
Normal file
4
exercises/04_traits/05_trait_bounds/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "trait_bounds"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
15
exercises/04_traits/05_trait_bounds/src/lib.rs
Normal file
15
exercises/04_traits/05_trait_bounds/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
// TODO: Add the necessary trait bounds to `min` so that it compiles successfully.
|
||||
// Refer to the documentation of the `std::cmp` module for more information on the traits you might need.
|
||||
//
|
||||
// Note: there are different trait bounds that'll make the compiler happy, but they come with
|
||||
// different _semantics_. We'll cover those differences later in the course when we talk about ordered
|
||||
// collections (e.g. BTreeMap).
|
||||
|
||||
/// Return the minimum of two values.
|
||||
pub fn min<T>(left: T, right: T) -> T {
|
||||
if left <= right {
|
||||
left
|
||||
} else {
|
||||
right
|
||||
}
|
||||
}
|
||||
7
exercises/04_traits/06_str_slice/Cargo.toml
Normal file
7
exercises/04_traits/06_str_slice/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "str_slice"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
61
exercises/04_traits/06_str_slice/src/lib.rs
Normal file
61
exercises/04_traits/06_str_slice/src/lib.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
// TODO: Re-implement `Ticket`'s accessor methods. This time return a `&str` rather than a `&String`.
|
||||
|
||||
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::*;
|
||||
use common::{valid_description, valid_title};
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
#[test]
|
||||
fn test_type() {
|
||||
let ticket = Ticket::new(valid_title(), valid_description(), "To-Do".to_string());
|
||||
// Some dark magic to verify that you used the expected return types
|
||||
assert_eq!(TypeId::of::<str>(), ticket.title().type_id());
|
||||
assert_eq!(TypeId::of::<str>(), ticket.description().type_id());
|
||||
assert_eq!(TypeId::of::<str>(), ticket.status().type_id());
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/07_deref/Cargo.toml
Normal file
4
exercises/04_traits/07_deref/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "deref"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
38
exercises/04_traits/07_deref/src/lib.rs
Normal file
38
exercises/04_traits/07_deref/src/lib.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
// TODO: whenever `title` and `description` are returned via their accessor methods, they
|
||||
// should be normalized—i.e. leading and trailing whitespace should be removed.
|
||||
// There is a method in Rust's standard library that can help with this, but you won't
|
||||
// find it in the documentation for `String`.
|
||||
// Can you figure out where it is defined and how to use it?
|
||||
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: String,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn title(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_normalization() {
|
||||
let ticket = Ticket {
|
||||
title: " A title ".to_string(),
|
||||
description: " A description ".to_string(),
|
||||
status: "To-Do".to_string(),
|
||||
};
|
||||
|
||||
assert_eq!("A title", ticket.title());
|
||||
assert_eq!("A description", ticket.description());
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/08_sized/Cargo.toml
Normal file
4
exercises/04_traits/08_sized/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "sized"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
7
exercises/04_traits/08_sized/src/lib.rs
Normal file
7
exercises/04_traits/08_sized/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub fn example() {
|
||||
// Trying to get the size of a str (or any other DST)
|
||||
// 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>();
|
||||
}
|
||||
4
exercises/04_traits/09_from/Cargo.toml
Normal file
4
exercises/04_traits/09_from/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "from"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
10
exercises/04_traits/09_from/src/lib.rs
Normal file
10
exercises/04_traits/09_from/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
// TODO: Implement the `From` trait for the `WrappingU32` type to make `example` compile.
|
||||
|
||||
pub struct WrappingU32 {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
fn example() {
|
||||
let wrapping: WrappingU32 = 42.into();
|
||||
let wrapping = WrappingU32::from(42);
|
||||
}
|
||||
4
exercises/04_traits/10_assoc_vs_generic/Cargo.toml
Normal file
4
exercises/04_traits/10_assoc_vs_generic/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "assoc_vs_generic"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
37
exercises/04_traits/10_assoc_vs_generic/src/lib.rs
Normal file
37
exercises/04_traits/10_assoc_vs_generic/src/lib.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
// TODO: Define a new trait, `Power`, that has a method `power` that raises `self`
|
||||
// to the power of `n`.
|
||||
// The trait definition and its implementations should be enough to get
|
||||
// the tests to compile and pass.
|
||||
//
|
||||
// Recommendation: you may be tempted to write a generic implementation to handle
|
||||
// all cases at once. However, this is fairly complicated and requires the use of
|
||||
// additional crates (i.e. `num-traits`).
|
||||
// Even then, it might be preferable to use a simple macro instead to avoid
|
||||
// the complexity of a highly generic implementation. Check out the
|
||||
// "Little book of Rust macros" (https://veykril.github.io/tlborm/) if you're
|
||||
// interested in learning more about it.
|
||||
// You don't have to though: it's perfectly okay to write three separate
|
||||
// implementations manually. Venture further only if you're curious.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Power;
|
||||
|
||||
#[test]
|
||||
fn test_power_u16() {
|
||||
let x: u32 = 2_u32.power(3u16);
|
||||
assert_eq!(x, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_power_u32() {
|
||||
let x: u32 = 2_u32.power(3u32);
|
||||
assert_eq!(x, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_power_ref_u32() {
|
||||
let x: u32 = 2_u32.power(&3u32);
|
||||
assert_eq!(x, 8);
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/11_clone/Cargo.toml
Normal file
4
exercises/04_traits/11_clone/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "clone"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
26
exercises/04_traits/11_clone/src/lib.rs
Normal file
26
exercises/04_traits/11_clone/src/lib.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
// TODO: add the necessary `Clone` implementations (and invocations)
|
||||
// to get the code to compile.
|
||||
|
||||
pub fn summary(ticket: Ticket) -> (Ticket, Summary) {
|
||||
(ticket, ticket.summary())
|
||||
}
|
||||
|
||||
pub struct Ticket {
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn summary(self) -> Summary {
|
||||
Summary {
|
||||
title: self.title,
|
||||
status: self.status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Summary {
|
||||
pub title: String,
|
||||
pub status: String,
|
||||
}
|
||||
4
exercises/04_traits/12_copy/Cargo.toml
Normal file
4
exercises/04_traits/12_copy/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "copy"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
25
exercises/04_traits/12_copy/src/lib.rs
Normal file
25
exercises/04_traits/12_copy/src/lib.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// TODO: implement the necessary traits to make the test compile and pass.
|
||||
// You *can't* modify the test.
|
||||
|
||||
pub struct WrappingU32 {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl WrappingU32 {
|
||||
pub fn new(value: u32) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_ops() {
|
||||
let x = WrappingU32::new(42);
|
||||
let y = WrappingU32::new(31);
|
||||
let z = WrappingU32::new(u32::MAX);
|
||||
assert_eq!(x + y + y + z, WrappingU32::new(103));
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/13_drop/Cargo.toml
Normal file
4
exercises/04_traits/13_drop/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "drop"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
23
exercises/04_traits/13_drop/src/lib.rs
Normal file
23
exercises/04_traits/13_drop/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// TODO: implement a so-called "Drop bomb": a type that panics when dropped
|
||||
// unless a certain operation has been performed on it.
|
||||
// You can see the expected API in the tests below.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_drop_bomb() {
|
||||
let bomb = DropBomb::new();
|
||||
// The bomb should panic when dropped
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_defused_drop_bomb() {
|
||||
let mut bomb = DropBomb::new();
|
||||
bomb.defuse();
|
||||
// The bomb should not panic when dropped
|
||||
// since it has been defused
|
||||
}
|
||||
}
|
||||
4
exercises/04_traits/14_outro/Cargo.toml
Normal file
4
exercises/04_traits/14_outro/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "outro_03"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
10
exercises/04_traits/14_outro/src/lib.rs
Normal file
10
exercises/04_traits/14_outro/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
// TODO: Define a new `SaturatingU16` type.
|
||||
// It should hold a `u16` value.
|
||||
// It should provide conversions from `u16`, `u8`, `&u16` and `&u8`.
|
||||
// It should support addition with a right-hand side of type
|
||||
// SaturatingU16, u16, &u16, and &SaturatingU16. Addition should saturate at the
|
||||
// maximum value for `u16`.
|
||||
// It should be possible to compare it with another `SaturatingU16` or a `u16`.
|
||||
// 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.
|
||||
17
exercises/04_traits/14_outro/tests/integration.rs
Normal file
17
exercises/04_traits/14_outro/tests/integration.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use outro_03::SaturatingU16;
|
||||
|
||||
#[test]
|
||||
fn test_saturating_u16() {
|
||||
let a: SaturatingU16 = (&10u8).into();
|
||||
let b: SaturatingU16 = 5u8.into();
|
||||
let c: SaturatingU16 = u16::MAX.into();
|
||||
let d: SaturatingU16 = (&1u16).into();
|
||||
let e = &c;
|
||||
|
||||
assert_eq!(a + b, SaturatingU16::from(15u16));
|
||||
assert_eq!(a + c, SaturatingU16::from(u16::MAX));
|
||||
assert_eq!(a + d, SaturatingU16::from(11u16));
|
||||
assert_eq!(a + a, 20u16);
|
||||
assert_eq!(a + 5u16, 15u16);
|
||||
assert_eq!(a + e, SaturatingU16::from(u16::MAX));
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user