├── .gitignore ├── 01_hello_world ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── prints-hello-world.rs ├── 02_bin_files ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── bin_files.rs ├── 03_arg_parse ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── parses_args.rs ├── 04_env_reader ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── env_reader.rs ├── 05_table_of_chairs ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── table_of_chairs.rs ├── 06_fibonacci ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── table_of_chairs.rs ├── 07_apology_letter ├── Cargo.toml ├── Hints.md ├── README.md ├── expected │ └── expected_output.txt ├── input │ └── example_input.txt ├── output │ └── example_output.txt ├── src │ └── main.rs └── tests │ └── apology_letter.rs ├── 08_image_resizer ├── Cargo.toml ├── Hints.md ├── README.md ├── expected_output_images │ ├── spacy_jim_1-2x.png │ ├── spacy_jim_1-4x.png │ ├── spacy_jim_1-8x.png │ ├── spacy_jim_2x.png │ ├── spacy_jim_4x.png │ └── spacy_jim_8x.png ├── input_image │ └── spacy_jim.png ├── output_images │ ├── spacy_jim_1-2x.png │ ├── spacy_jim_1-4x.png │ ├── spacy_jim_1-8x.png │ ├── spacy_jim_2x.png │ ├── spacy_jim_4x.png │ └── spacy_jim_8x.png ├── src │ └── main.rs └── tests │ └── resize_image.rs ├── 09_github_scaffold ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── github_scaffold.rs ├── 10_yes_or_no ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── yes_or_no.rs ├── 11_get_json ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── get_json.rs ├── 12_happy_birthday ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── happy_birthday.rs ├── 13_agile_standup ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── happy_birthday.rs ├── 14_pizza_order ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── pizza_order.rs ├── 15_higher_or_lower ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── higher_or_lower.rs ├── 16_wordle_game ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── rand_num_gen.rs ├── 17_s3_read_write ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── s3_read_write.rs ├── 18_database_crud ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── mongo_crud.rs ├── 19_audio_stutter ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs └── tests │ └── audio_stutter.rs ├── 20_traveling_salesman ├── Cargo.toml ├── Hints.md ├── README.md ├── src │ └── main.rs ├── tests │ └── traveling_salesman.rs └── traveling_salesman_problem.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /01_hello_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_world" 3 | version = "0.1.0" 4 | edition = "2021" 5 | -------------------------------------------------------------------------------- /01_hello_world/Hints.md: -------------------------------------------------------------------------------- 1 | - Edit the _main_ function _main.rs_ to return "Hello, World!" instead of the todo! 2 | 3 | - Try using the [assert_cmd](https://docs.rs/assert_cmd/latest/assert_cmd/) library for the integration test! 4 | 5 | - Try using [rust-to-npm](https://github.com/a11ywatch/rust-to-npm) for deploying this to cargo and npm! 6 | -------------------------------------------------------------------------------- /01_hello_world/README.md: -------------------------------------------------------------------------------- 1 | # Hello, World! 2 | A barebones project that just prints the text "Hello, World"! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to make the simplest Rust project possible- just the stuff needed to make a Rust project run. 8 | 9 | The idea here is that if you can write a Rust project that compiles, runs, and does _something_ then you can build off this to create all the things you could ever imagine! 10 | 11 |
12 | 13 | ## The Exercise 14 | Write the simplest Rust project possible that prints some form of, "Hello, World!" to the console. 15 | 16 |
17 | 18 | ## Tests 19 | The general pattern for where to put tests is Rust is that unit tests should 20 | 21 | It's up to you to decide if / how you would unit test this, but it would be nice to at least have one integration test that asserts that the correct output was printed to the console. 22 | 23 |
24 | 25 | ## Skills Practiced 26 | 27 | - Installing and using `cargo` 28 | 29 | - Creating a `main` function 30 | 31 | - Creating an integration test 32 | 33 | - Running a Rust project and tests 34 | 35 |
36 | 37 | ## Bonus 38 | 39 | - Add some documentation to your own README file describing the different commands people who run when developing and testing your project. 40 | 41 | - Deploy your cli tool to cargo and/or npm so others can install it! 42 | -------------------------------------------------------------------------------- /01_hello_world/src/main.rs: -------------------------------------------------------------------------------- 1 | //! The simplest of simple for Rust programs! 2 | fn main() { 3 | todo!("replace this line with your code!") 4 | } 5 | -------------------------------------------------------------------------------- /01_hello_world/tests/prints-hello-world.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn prints_hello_world() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /02_bin_files/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bin_files" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /02_bin_files/Hints.md: -------------------------------------------------------------------------------- 1 | - Try following the [package layout recommended in the Cargo Book](https://doc.rust-lang.org/cargo/guide/project-layout.html). -------------------------------------------------------------------------------- /02_bin_files/README.md: -------------------------------------------------------------------------------- 1 | # Bin Files 2 | Create a cli tool where the logic is split out nicely into separate functions and files. 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to make a cli tool that ultimately builds into a single binary executable file but depends on lots of different functions. 8 | 9 | The idea is that this code can serve as a proof-of-concept for a medium to large-scale codebase all contained within a single binary project. 10 | 11 |
12 | 13 | ## The Exercise 14 | The exercise is to create three functions in three separate files, all called from `main` which should print the total sum of the three numbers returned by `fn1`, `fn2`, and `fn3`. 15 | 16 | 1) The first function should be named `fn1` and should live inside a module named `mod1`. This function should just return the number 1. 17 | 18 | 2) The second function should be named `fn2` and should live inside a module named `mod2`. This module should import `f1` from `mod1`, and `fn2` should return 2 times the result of calling `fn1`. 19 | 20 | 3) The third function should be named `fn3` and should live inside a module named `mod3` (which itself may or may not live inside of other modules). This file should be inside of a subfolder and not next to the main.rs file, and it should return the number 3 when called. 21 | 22 |
23 | 24 | ## Tests 25 | The unit tests for fn1 and fn3 should check that they return the proper values. Experiment with returning a mocked value of fn1 when calling the fn2 unit test. 26 | 27 | As an integration test, verify that the grand total of 6 is printed to the console. 28 | 29 |
30 | 31 | ## Skills Practiced 32 | 33 | - Creating helper functions and modules 34 | 35 | - Importing a helper function and module in main.rs 36 | 37 | - Importing a helper function and module into another helper function and module 38 | 39 | - Importing a helper function and module from a subfolder 40 | 41 |
42 | -------------------------------------------------------------------------------- /02_bin_files/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /02_bin_files/tests/bin_files.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn adds_numbers_to_6() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /03_arg_parse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arg_parse" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /03_arg_parse/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the popular Rust cli arg parsing library [clap](https://github.com/clap-rs/clap)! 2 | -------------------------------------------------------------------------------- /03_arg_parse/README.md: -------------------------------------------------------------------------------- 1 | # Arg Parse 2 | Build a cli tool that reads and prints arguments! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to build a cli tool. Well, if you want the user to be able to send some parameter or input as they run it... those are what we call _arguments._ 8 | 9 |
10 | 11 | ## The Exercise 12 | Write a cli tool that takes two string arguments. 13 | 14 | The first argument should be required (the program errors or panics if it is not supplied with `cargo run`). 15 | 16 | The second argument is optional- if it is not supplied the program continues successfully, and arg2 is set to the `None` Option variant. 17 | 18 | Also, the cli tool should accept an optional flag. The flag can be passed as the long version `--flag` or the short version `-f`, and the flag take a positive integer as a parameter. The cli tool should print the number to the console, or just "None" if called without the flag. 19 | 20 | If both arguments are passed with `cargo run` then both are printed to the console. 21 | 22 | Additional arguments are ignored (or cause an error, up to you). 23 | 24 |
25 | 26 | ## Tests 27 | It's up to you to decide if / how you would unit test this. 28 | 29 | There are a few integration tests you can write, where the test code more or less calls `cargo run`, passing in a different number of args for each test, and then expecting the proper text to be printed to the console or panicking to occur. 30 | 31 |
32 | 33 | ## Skills Practiced 34 | 35 | - Reading positional cli arguments 36 | 37 | - creating required and optional args 38 | 39 | - Flags with a short and long name 40 | 41 | - Flags that take a parameter 42 | 43 |
44 | 45 | ## Bonus 46 | 47 | - Bonus points for finding multiple different ways of implementing this! 48 | -------------------------------------------------------------------------------- /03_arg_parse/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /03_arg_parse/tests/parses_args.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn parses_args() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /04_env_reader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "env_reader" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /04_env_reader/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the crate [dotenv](https://crates.io/crates/dotenv)! 2 | -------------------------------------------------------------------------------- /04_env_reader/README.md: -------------------------------------------------------------------------------- 1 | # Env Reader 2 | Build a cli tool that reads and prints environment variables! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to build a cli tool that uses some secret keys or credentials. It's a _huge_ no-no to hardcode them right into your project and save them into source control. 8 | 9 | Instead, you should probably load in these secrets as _environment variables._ 10 | adsads 11 | The first variable should be named `MY_VAR_1=hello!!` and should be read from a .env file. 12 | 13 | The second variable should be named `MY_VAR_2` and should _not_ be declared in the .env file and instead should be exported in the same shell that the program is run. If MY_VAR_2 is not exported, use the string `"default val"` as the default value. 14 | 15 | The values of both variables should be printed to the console. 16 | 17 |
18 | 19 | ## Tests 20 | It's up to you to decide if / how you would unit test this. 21 | 22 | Write at least one integration test that verifies the correct values were printed to the console. 23 | 24 |
25 | 26 | ## Skills Practiced 27 | 28 | - Reading environment variables from a .env file 29 | 30 | - Reading environment variables from the current execution environment 31 | 32 |
33 | -------------------------------------------------------------------------------- /04_env_reader/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /04_env_reader/tests/env_reader.rs: -------------------------------------------------------------------------------- 1 | fn reads_env_vars() -> Result<(), Box> { 2 | todo!("put your integration test code here!"); 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /05_table_of_chairs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "table_of_chairs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /05_table_of_chairs/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the [cli-table](https://crates.io/crates/clii-talbe) crate! -------------------------------------------------------------------------------- /05_table_of_chairs/README.md: -------------------------------------------------------------------------------- 1 | # Table of Chairs 2 | Create a cli tool that prints data in a nice tabular format! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you are the officer manager for, and have a to buy lots of chairs. You would like to view the data in a nice spreadsheet-like table rather than just a blob of json. 8 | 9 | This code can serve as a proof-of-concept for any cli-tool that needs to output a lot of data in a visually appealing format with nice rows, columns, and column headers. 10 | 11 |
12 | 13 | ## The Exercise 14 | The exercise is to visually display chair data in a nice table so that it looks something like this: 15 | 16 | ``` 17 | +--------------------------------+---------+-----------+----------+ 18 | | Name | Price | Color | Quantity | 19 | +--------------------------------+---------+-----------+----------+ 20 | | Ergonomic Office Chair | $199.99 | Black | 20 | 21 | +--------------------------------+---------+-----------+----------+ 22 | | Bucket Seat Gaming Chair | $249.99 | Turquoise | 3 | 23 | +--------------------------------+---------+-----------+----------+ 24 | | Curl Swivel Accent Chair | $407.96 | Orange | 2 | 25 | +--------------------------------+---------+-----------+----------+ 26 | | Velvet High Back Rocking Chair | $113.99 | Blue | 1 | 27 | +--------------------------------+---------+-----------+----------+ 28 | | Velvet High Back Rocking Chair | $27.99 | Grey | 5 | 29 | +--------------------------------+---------+-----------+----------+" 30 | ``` 31 | 32 | Feel free to hardcode the chair data as structs, arrays, or vectors. 33 | 34 |
35 | 36 | ## Tests 37 | It's up to you to decide how you would unit test this code. 38 | 39 | Write some integration tests that verify that the console output contains the correct data, nicely displayed in a tabular format. 40 |
41 | 42 | ## Skills Practiced 43 | 44 | - Displaying data in the terminal console as a table 45 | 46 |
47 | -------------------------------------------------------------------------------- /05_table_of_chairs/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /05_table_of_chairs/tests/table_of_chairs.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn prints_data_in_a_table() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /06_fibonacci/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fibonacci" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /06_fibonacci/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the [cli-table](https://crates.io/crates/clii-talbe) crate! -------------------------------------------------------------------------------- /06_fibonacci/README.md: -------------------------------------------------------------------------------- 1 | # Fibonacci 2 | Create a cli tool that prints a number from the fibonacci sequence! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to know what the 100th element in the fibonacci sequence. What about the 1000th? You decide to craete a cli tool that will print this information. 8 | 9 | _This project can serve as a proof-of-concept for any Rust code that needs to recursively call a function!_ 10 | 11 |
12 | 13 | ## The Exercise 14 | The exercise is to write a cli tool that accepts a positive integer as an argument and returns the number at that index of the [fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_sequence). 15 | 16 |
17 | 18 | ## Tests 19 | It's up to you to decide how you would unit test this code. 20 | 21 | Write some integration tests that verify that the console output contains the correct number from the fibonacci sequence based on the input integer. 22 |
23 | 24 | ## Skills Practiced 25 | 26 | - Recursive function calls 27 | 28 |
29 | -------------------------------------------------------------------------------- /06_fibonacci/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /06_fibonacci/tests/table_of_chairs.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn prints_data_in_a_table() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /07_apology_letter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "apology_letter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /07_apology_letter/Hints.md: -------------------------------------------------------------------------------- 1 | - You really just need to do some String manipulation here! 2 | 3 | - Try using `std::fs` to read and write the text files. 4 | 5 | - You might find these functions on the String trait to be helpful: `find`, `split_at`, `join` 6 | -------------------------------------------------------------------------------- /07_apology_letter/README.md: -------------------------------------------------------------------------------- 1 | # Arg Parse 2 | Build a cli tool that generates apology letters! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you are the PR person for some loose cannon celebrity. You often have to write letters apologizing to those in the wake of your client's craziness so you decide to write a little cli tool that will generate apology letters. 8 | 9 |
10 | 11 | ## The Exercise 12 | This cli tool should take two positional arguments: 13 | 14 | 1) recipient name - the name of the person you are apologizing to. 15 | 16 | 2) thing you did - the act that we are apologizing for. 17 | 18 | The program should read in the file "example_input.txt", replace the placeholders with the supplied args, and then write the output to a text file in the "output" folder. 19 | 20 |
21 | 22 | ## Tests 23 | Try to extract the string replacing logic into pure functions with unit tests. 24 | 25 | Create an integration test that actually recreates and saves the output file and asserts that is has the exact same contents as the expected output file in the "expected" directory. 26 |
27 | 28 | ## Skills Practiced 29 | 30 | - Reading and writing text files 31 | 32 | - Splitting and joining strings 33 | 34 | - Reading positional args 35 | 36 |
37 | -------------------------------------------------------------------------------- /07_apology_letter/expected/expected_output.txt: -------------------------------------------------------------------------------- 1 | 2 | Fancy Letterhead 3 | Very Fancy, Indeed 4 | Very, Very Fancy 5 | 6 | 7 | Dear foo, 8 | 9 | I would like to express how very deeply sorry I am for things. 10 | 11 | Sincerely, 12 | Your friend James -------------------------------------------------------------------------------- /07_apology_letter/input/example_input.txt: -------------------------------------------------------------------------------- 1 | 2 | Fancy Letterhead 3 | Very Fancy, Indeed 4 | Very, Very Fancy 5 | 6 | 7 | Dear *[__RECIPIENT_NAME__]*, 8 | 9 | I would like to express how very deeply sorry I am for *[__THING_YOU_DID__]*. 10 | 11 | Sincerely, 12 | Your friend James -------------------------------------------------------------------------------- /07_apology_letter/output/example_output.txt: -------------------------------------------------------------------------------- 1 | 2 | Fancy Letterhead 3 | Very Fancy, Indeed 4 | Very, Very Fancy 5 | 6 | 7 | Dear foo, 8 | 9 | I would like to express how very deeply sorry I am for things. 10 | 11 | Sincerely, 12 | Your friend James -------------------------------------------------------------------------------- /07_apology_letter/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /07_apology_letter/tests/apology_letter.rs: -------------------------------------------------------------------------------- 1 | fn generates_apology_letter() -> Result<(), Box> { 2 | todo!("put your integration test code here!"); 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /08_image_resizer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "apology_letter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /08_image_resizer/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the [image](https://crates.io/crates/image) crate and the `resize` method! -------------------------------------------------------------------------------- /08_image_resizer/README.md: -------------------------------------------------------------------------------- 1 | # Image Resizer 2 | Build a cli tool that resizes images! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to have super optimized images for different screen sizes- large, crisp images for wide desktop screens; tiny, compact images for small screens, and various sizes in between. 8 | 9 | Your designer finds it annoting to have to generate lots of different size images (and lets say the auto-generating function of his graphic design program doesn't give the quality or control we would like). So, it's up to you to build a cli tool that will take in an image and spit out that same image but in but different sizes. 10 | 11 |
12 | 13 | ## The Exercise 14 | This cli tool should read in the image from the `input_image` folder. 15 | 16 | The cli tool should then output 6 images in the `output_images` folder. 17 | 18 | Three should be larger than the original. The first should be 2x the size of the original image, the second 4x, and the third 8x. 19 | 20 | The other three images should be smaller than the original. The first should be half the size of the original, the second one fourth, an the third one eigth. 21 | 22 |
23 | 24 | ## Tests 25 | It's up to you to decide how to unit test this code. 26 | 27 | Create an integration test that compares the generated images to those in the `expected_output` directory. 28 |
29 | 30 | ## Skills Practiced 31 | 32 | - Reading and writing image files 33 | 34 | - Resizing images 35 | 36 |
37 | -------------------------------------------------------------------------------- /08_image_resizer/expected_output_images/spacy_jim_1-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/expected_output_images/spacy_jim_1-2x.png -------------------------------------------------------------------------------- /08_image_resizer/expected_output_images/spacy_jim_1-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/expected_output_images/spacy_jim_1-4x.png -------------------------------------------------------------------------------- /08_image_resizer/expected_output_images/spacy_jim_1-8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/expected_output_images/spacy_jim_1-8x.png -------------------------------------------------------------------------------- /08_image_resizer/expected_output_images/spacy_jim_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/expected_output_images/spacy_jim_2x.png -------------------------------------------------------------------------------- /08_image_resizer/expected_output_images/spacy_jim_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/expected_output_images/spacy_jim_4x.png -------------------------------------------------------------------------------- /08_image_resizer/expected_output_images/spacy_jim_8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/expected_output_images/spacy_jim_8x.png -------------------------------------------------------------------------------- /08_image_resizer/input_image/spacy_jim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/input_image/spacy_jim.png -------------------------------------------------------------------------------- /08_image_resizer/output_images/spacy_jim_1-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/output_images/spacy_jim_1-2x.png -------------------------------------------------------------------------------- /08_image_resizer/output_images/spacy_jim_1-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/output_images/spacy_jim_1-4x.png -------------------------------------------------------------------------------- /08_image_resizer/output_images/spacy_jim_1-8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/output_images/spacy_jim_1-8x.png -------------------------------------------------------------------------------- /08_image_resizer/output_images/spacy_jim_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/output_images/spacy_jim_2x.png -------------------------------------------------------------------------------- /08_image_resizer/output_images/spacy_jim_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/output_images/spacy_jim_4x.png -------------------------------------------------------------------------------- /08_image_resizer/output_images/spacy_jim_8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/08_image_resizer/output_images/spacy_jim_8x.png -------------------------------------------------------------------------------- /08_image_resizer/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /08_image_resizer/tests/resize_image.rs: -------------------------------------------------------------------------------- 1 | fn resizes_image() -> Result<(), Box> { 2 | todo!("put your integration test code here!"); 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /09_github_scaffold/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "github_scaffold" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /09_github_scaffold/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the [git2](https://crates.io/crates/git2) crate and the `clone` method! -------------------------------------------------------------------------------- /09_github_scaffold/README.md: -------------------------------------------------------------------------------- 1 | # Github Scaffold 2 | Build a cli tool that scaffolds a new project from files in a Github repository! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you have some awesome new framework or starter project, and you want to make it super easy for other developers to scaffold your project in their local directory (something like create-react-app). 8 | 9 | You decide to make a cli tool so users can easily scaffold out a new project with a single command! 10 | 11 |
12 | 13 | ## The Exercise 14 | This cli tool should clone a repository from Github into the "cloned_repos" directory (it's ok to hardcode the repository url into the code). 15 | 16 | The cli tool should then delete the ".git" folder that is created when the repo is cloned. 17 | 18 |
19 | 20 | ## Tests 21 | It's up to you to decide how you would unit test this code. 22 | 23 | Create an integration test that verifies that the repo was cloned and that the .git folder was deleted. 24 |
25 | 26 | ## Skills Practiced 27 | 28 | - Cloning a github repository 29 | 30 | - Working with the local filesystem 31 | 32 | - Deleting folders 33 | 34 | - Working with hardcoded string constants 35 | 36 |
37 | -------------------------------------------------------------------------------- /09_github_scaffold/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /09_github_scaffold/tests/github_scaffold.rs: -------------------------------------------------------------------------------- 1 | fn scaffolds_from_github_repo() -> Result<(), Box> { 2 | todo!("put your integration test code here!"); 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /10_yes_or_no/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yes_or_no" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /10_yes_or_no/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using a `Confirm` prompt from the [inquire](https://crates.io/crates/inquire) crate! -------------------------------------------------------------------------------- /10_yes_or_no/README.md: -------------------------------------------------------------------------------- 1 | # Yes or No 2 | Create a cli tool that asks the user a yes or no question! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to make a cli tool that can ask the user questions and stop, waiting for the user to enter some input. 8 | 9 | Sometimes the user doesn't always want to pass in _everything_ as args when running the cli tool. Prompting the user for input is a key skill to have in your toolbag for creating polished cli tools with a nice user experience! 10 | 11 |
12 | 13 | ## The Exercise 14 | Create a cli tool that simply asks the user the question, "yes or no". 15 | 16 | After the question is printed the console should hang and wait for a user input. 17 | 18 | The question should end by displaying [y/N] with the N capitalized to signify that it is the default input if the user simply presses the enter key without typing any text. 19 | 20 | The cli tool should read these inputs as a "yes": y, Y, yes, Yes, YES... 21 | 22 | The cli tool should read these inputs as a "no": n, N, no, No, NO... 23 | 24 | After the user submits a choice either "You said yes!" or "You say no..." should be printed to the console. 25 | 26 |
27 | 28 | ## Tests 29 | It's up to you to decide how to unit test this code. 30 | 31 | Write an integration test that simulates sending various inputs as a response to the yes or no question and verifies that the correct output is printed to the console. 32 |
33 | 34 | ## Skills Practiced 35 | 36 | - Prompting the user for input 37 | 38 | - Simulating keystroke events in integration tests 39 | 40 |
41 | -------------------------------------------------------------------------------- /10_yes_or_no/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /10_yes_or_no/tests/yes_or_no.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn asks_yes_or_no() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /11_get_json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "get_json" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /11_get_json/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using a crate for making GET requests, like [reqwest](https://crates.io/crates/reqwest), along with a json parsing crate like [serde_json](https://crates.io/crates/serde_json). -------------------------------------------------------------------------------- /11_get_json/README.md: -------------------------------------------------------------------------------- 1 | # Get Json 2 | Create a cli tool that gets some json! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to make a cli tool that brings in some outside data! 8 | 9 | This cli tool is a proof-of-concept for a whole class of cli tools that can make all the REST api calls you could ever imagine! 10 | 11 |
12 | 13 | ## The Exercise 14 | The exercise is to write a cli tool that calls out to and endpoint, gets some json, and parses it into data that Rust understands. 15 | 16 | Use the endpoint here as the source for your json data: https://jsonplaceholder.typicode.com/users/1 17 | 18 | Then print the "name" field from the json data to the console. 19 | 20 |
21 | 22 | ## Tests 23 | It's up to you to decide how you would unit test this code. 24 | 25 | Write a unit test that calls out the endpoint and expects the correct text to be printed to the console. 26 | 27 |
28 | 29 | ## Skills Practiced 30 | 31 | - Making a GET request 32 | 33 | - Parsing JSON data 34 | 35 |
36 | 37 | ## Bonus 38 | 39 | Try two different ways of parsing the json: one by deserializing it to a defined struct and one where you work with it as a dynamic value blob of data! -------------------------------------------------------------------------------- /11_get_json/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /11_get_json/tests/get_json.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn gets_json() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /12_happy_birthday/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "happy_birthday" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /12_happy_birthday/Hints.md: -------------------------------------------------------------------------------- 1 | - Try using the [chrono](https://crates.io/crates/chrono) crate for working with dates! -------------------------------------------------------------------------------- /12_happy_birthday/README.md: -------------------------------------------------------------------------------- 1 | # Happy Birthday 2 | Create a cli tool that wishes you happy birthday _and_ tells you how many days are left until your birthday! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you love celebrating your birthday and planning ahead for it. You often want to know how many days are left until your birthday, but it's annoying always having to do the math of calculating how many days are left. 8 | 9 | In this project we'll create a cli tool that will accept a birthday date and responds with the number of days to wait. 10 | 11 | as the input and respond with the number of days it is from the current date. 12 | 13 |
14 | 15 | ## The Exercise 16 | The exercise is to create a cli tool that accepts a date and prints to the console the number of days between that input date and the current date. 17 | 18 | The number of days should be calculated as a positive integer. 19 | 20 | If the number of days is zero, print "Happy Birthday!! 🥳" to the console. 21 | 22 |
23 | 24 | ## Tests 25 | It's up to you to decide how you would unit test this code. 26 | 27 | Create some integration tests that correctly responds with the number of days away for the user's birthday. 28 | 29 | Create at least one integration test where the cli tool correctly prints "Happy Birthday". 30 | 31 |
32 | 33 | ## Skills Practiced 34 | 35 | - Working with data and time data 36 | 37 | - Doing date math to find number of days between two dates 38 | 39 |
40 | 41 | ## Bonus 42 | 43 | Use a fancy command-line date picker component for collecting the user's birthday! -------------------------------------------------------------------------------- /12_happy_birthday/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /12_happy_birthday/tests/happy_birthday.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn says_happy_birthday() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /13_agile_standup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "happy_birthday" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /13_agile_standup/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/13_agile_standup/Hints.md -------------------------------------------------------------------------------- /13_agile_standup/README.md: -------------------------------------------------------------------------------- 1 | # Agile Standup 2 | Create a cli tool that collects each person's status for a dev team's daily standup meeting! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to automate the morning standup meeting so everyone can input their contribution asynchronously. 8 | 9 |
10 | 11 | ## The Exercise 12 | The exercise is to create a cli tool that accepts a date and prints to the console the number of days between that input date and the current date. 13 | 14 | 1) what you worked on yesterday 15 | 16 | 2) what you are working on today 17 | 18 | 3) if you have any blockers 19 | 20 | The text input can be collected either via cli args, flags, or through interactive q/a components. 21 | 22 | For this example, just print the final user submission to the console. 23 | 24 |
25 | 26 | ## Tests 27 | It's up to you to decide how you would unit test this code. 28 | 29 | Create at least one integration test that sends the necessary inputs and asserts the proper output is printed to the console. 30 | 31 |
32 | 33 | ## Skills Practiced 34 | 35 | - Accepting large text blocks as input 36 | 37 | - Building a cli tool that could actually be useful! 38 | 39 |
40 | 41 | ## Bonus 42 | 43 | - Store this information into a database 44 | 45 | - Allow everyone to see all submissions for a team! 46 | 47 | - Support _both_ args / flags inputs _and_ q/a components- prompt use to interactively enter required information not provided via args or flags. 48 | 49 | - Allow input to optionally be read from a text file to make things even easier for the user 50 | -------------------------------------------------------------------------------- /13_agile_standup/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /13_agile_standup/tests/happy_birthday.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn says_happy_birthday() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /14_pizza_order/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pizza_order" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /14_pizza_order/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/14_pizza_order/Hints.md -------------------------------------------------------------------------------- /14_pizza_order/README.md: -------------------------------------------------------------------------------- 1 | # Pizza Order 2 | Create a cli tool that orders pizzas! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you are in charge ordering for a massive organization that has no pizza oven. 8 | 9 | You decide to make a cli tool to make ordering the pizzas faster and easier! 10 | 11 |
12 | 13 | ## The Exercise 14 | Because we sometimes order things other than pizza, let's make the literal string `pizza` the first positional argument for these pizza ordering situations, and this should be a required argument. 15 | 16 | Then we can take the other information flags for our cli tool: 17 | 18 | - Size, --size or -s flags, possible values are: "small (s), medium (m), large (l), or xtra large (xl)" 19 | 20 | -> If the user does not pass the size flag, prompt for a size with a "select" input. 21 | 22 | - Toppings, --toppings or -t flag, possible values are: "Pepperoni, Sausage, Mushrooms, Bacon, Onions, Extra Cheese, Peppers, Chicken, Pineapple" 23 | 24 | -> If the user does not pass the size flag, prompt for a size with a "multiselect" input. Up to two toppings can be chosen, and no toppings selected should also be a valid choice. 25 | 26 | - Room Number, --room or -r flag, possible values are strings of the form "XYYY" where "X" is the department letter ("A", "B", "C", "D", or "E") and the X is the room number, which can be any 1, 2, or three digit positive number (excluding zero). 27 | 28 | - Quantity, --quantity or -q flag, possible values are integers between 1 and 25. If this flag is not present, do not prompt user for it. Instead, just assume quantity of 1. 29 | 30 | - Confirm, --confirm or -c flag, doesn't take any value argument. If this flag is NOT passed, read the order back after collecting other information and prompt user for Y/n confirmation. If the flag is present, skip this prompt. 31 | 32 | 33 | After the cli tool collects all information and confirms it should print out, "Your order for [quantity] [size] [toppings] pizza(s), delivered to [room], has been placed!" 34 | 35 |
36 | 37 | ## Skills Practiced 38 | 39 | - Parsing command line args and flags 40 | 41 | - Prompting the user with text, y/n confirm, select, and multiselect inputs. 42 | 43 | - Handling default argument values. 44 | 45 | - String and number input validation. 46 | 47 | - Conditionally prompting the user _only if_ flag is not present (a common thing in real world cli tools!) 48 | 49 |
50 | 51 | ## Bonus 52 | 53 | - Support _both_ args / flags inputs _and_ q/a components- prompt use to interactively enter required information not provided via args or flags. 54 | 55 | - Allow input to optionally be read from a text file to make things even easier for the user 56 | 57 | - Connect to an email or texting sending service and _actually_ place an order with a pizza shop! 58 | -------------------------------------------------------------------------------- /14_pizza_order/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /14_pizza_order/tests/pizza_order.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn orders_pizzas() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /15_higher_or_lower/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "higher_or_lower" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /15_higher_or_lower/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/15_higher_or_lower/Hints.md -------------------------------------------------------------------------------- /15_higher_or_lower/README.md: -------------------------------------------------------------------------------- 1 | # Higher Or Lower Game 2 | Create a cli tool that plays the higher or lower game! 3 | 4 |
5 | 6 | ## Backstory 7 | Imagine you want to build a fun number guessing game for your friends to play in the command line. 8 | 9 | Well, for this challenge you'll do just that... 10 | 11 |
12 | 13 | ## The Exercise 14 | Write a cli tool that allows the user to play a number guessing game. 15 | 16 | First, the game should think of a random integer between 0 and 100. 17 | 18 | Prompt the user to make a guess. 19 | 20 | Only allow guesses that are valid integers with the possible range. 21 | 22 | After a guess is made, it is "scored" and response is given: 23 | 24 | - If the number is equal to the secret number, the user wins! Print the number of guesses it took. 25 | 26 | - If the secret number is higher than the guessed number, respond something like, "your guess was too low". 27 | 28 | - If the secret number is lower than the guessed number, respond something like, "your guess was too high". 29 | 30 | You can optionally add a maximum number of guesses. If the remaining guesses reaches zero and correct number hasn't been guessed, the user loses! Display what the secret number was along with some kind of "better luck next time!" message. 31 | 32 |
33 | 34 | ## Tests 35 | Add unit tests for the pure logic functions here. 36 | 37 | Writing integration tests can be tricky for this one, but feel free to give it a go! 38 | 39 |
40 | 41 | ## Skills Practiced 42 | 43 | - Validating integer input 44 | 45 | - Generating a random uint 46 | 47 | - Recursive function calls 48 | 49 | - Validating if a given string is a word 50 | 51 | - Keeping track of an overall game state 52 | 53 |
54 | 55 | ## Bonus 56 | 57 | - Add in a maximum number of guess or the user loses. 58 | 59 | - Accept arguments or flags for changing the maximum secret number and number of guesses the user is allowed. 60 | 61 | -------------------------------------------------------------------------------- /15_higher_or_lower/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /15_higher_or_lower/tests/higher_or_lower.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn plays_higher_or_lower_game() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /16_wordle_game/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rand_num_gen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /16_wordle_game/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/16_wordle_game/Hints.md -------------------------------------------------------------------------------- /16_wordle_game/README.md: -------------------------------------------------------------------------------- 1 | # Wordle Game 2 | Create a cli tool that plays a letter guessing game like [Wordle](https://www.nytimes.com/games/wordle/index.html)! 3 | 4 | ___Disclaimer: We are not affiliated at all with Wordle or New York Times. This is simply just a toy project to practice building cli tools and coding in Rust.___ 5 | 6 |
7 | 8 | ## Backstory 9 | Imagine you want to build a fun word guessing game for your friends to play in the command line- like wordle, but _better!_ 10 | 11 | Well, for this challenge you'll do just that... 12 | 13 |
14 | 15 | ## The Exercise 16 | Write a cli tool that allows the user to play a wordle like game. 17 | 18 | First, the game should think of a random word. It should be a valid engligh word! 19 | 20 | Prompt the user to make a guess. 21 | 22 | Only allow guesses that are the correct number of characters and are valid english words. 23 | 24 | After a guess is made, it is "scored" and the letters are highlighted in a specific way: 25 | 26 | - if the letter is in the correct position, it is highlighted GREEN. 27 | 28 | - if the letter is in the word but incorrect position, it is highlighted YELLOW. 29 | 30 | - if the letter is not in the word, it is highlighted YELLOW. 31 | 32 | If the user guess all correct letters then they win! Display a nice winning messge here. 33 | 34 | If the number of guesses reaches zero and correct word hasn't been guessed, the user loses! Display what the secret word was along with some kind of "better luck next time!" message. 35 | 36 |
37 | 38 | ## Tests 39 | It's up to you to decide if / how you would unit test this. Definitely add unit tests for the pure logic functions! 40 | 41 | Writing integration tests can be tricky for this one, but give it a shot! 42 | 43 |
44 | 45 | ## Skills Practiced 46 | 47 | - Validating text input 48 | 49 | - Generating random words 50 | 51 | - Color highlighting text in the console 52 | 53 | - Validating if a given string is a word 54 | 55 | - Recursive function calls 56 | 57 | - Keeping track of an overall game state 58 | 59 |
60 | 61 | ## Bonus 62 | 63 | - Display a "keyboard" of already guessed and unguessed letters 64 | 65 | - Allow the user to change the number of letters in the word and number of guesses in a game 66 | 67 | - Allow the game to be played in languages other than english 68 | 69 | - Write automated tests that play the game and assert there are no rendering mistakes 70 | -------------------------------------------------------------------------------- /16_wordle_game/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /16_wordle_game/tests/rand_num_gen.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn generates_random_numbers() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /17_s3_read_write/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "s3_read_write" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /17_s3_read_write/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/17_s3_read_write/Hints.md -------------------------------------------------------------------------------- /17_s3_read_write/README.md: -------------------------------------------------------------------------------- 1 | # S3 Read Write 2 | Create a cli tool that reads data from and writes data to an AWS S3 bucket! 3 | 4 |
5 | 6 | ## Backstory 7 | You have data! 8 | 9 | But, where to put it? 🤔 10 | 11 | Let's suppose you (or your boss / company) has decided they want to store it in an AWS s3 bucket since it's a very cheap and convenient way to store data. 12 | 13 | So, you want to interact with s3 buckets via your command line tools! 14 | 15 |
16 | 17 | ## The Exercise 18 | Write a cli tool that allows you to read and write s3 records. 19 | 20 | It's up to you to decide if you would like to accept a "command" as the first a cli tool argument, use an inquire "select" input for the user to choose, or something else! 21 | 22 | Suppose we have a single mongo collection named _MyBucket_ with documents that look something like this: 23 | 24 | We want to be able to do these s3 interactions: 25 | 26 |
27 | 28 | ### Write Operations 29 | 30 | - create(newDoc) - inserts a blob of content into the s3 bucket 31 | 32 |
33 | 34 | - replace(_id, newDoc) - takes an _id input and completely replaces the existing content at that location 35 | 36 |
37 | 38 | - delete(_id) - takes an _id input, deletes the content with that _id 39 | 40 |
41 | 42 | ### Read Operations 43 | 44 | - read(_id) - takes an _id input and returns the content blob 45 | 46 |
47 | 48 | ## Tests 49 | Ensure that your unit tests do NOT actually call to AWS. 50 | 51 | For your integration tests, run a local instance of s3, run your tests that call all your individual functions, and expect the correct data to be in the local bucket. 52 | 53 |
54 | 55 | ## Skills Practiced 56 | 57 | - Storing and reading secrets 58 | 59 | - Connecting to an \[S3\] bucket 60 | 61 |
62 | 63 | ## Bonus 64 | 65 | - Try other "data lake" or "data warehouse" storage types such as Snowflake or Databricks Lakehouse -------------------------------------------------------------------------------- /17_s3_read_write/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /17_s3_read_write/tests/s3_read_write.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn reads_and_writes() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /18_database_crud/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mongo_crud" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /18_database_crud/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/18_database_crud/Hints.md -------------------------------------------------------------------------------- /18_database_crud/README.md: -------------------------------------------------------------------------------- 1 | # Database CRUD 2 | Create a cli tool that creates, reads, updates, and deletes data to and from a database! 3 | 4 |
5 | 6 | ## Backstory 7 | Being able to read from and write to a databases is such an empowering skill that we had to create an example here! 8 | 9 | Data needs to exist outside of the user's own device, and a great place for your applications and cli tools to store data is a database! 10 | 11 |
12 | 13 | ## The Exercise 14 | Write a cli tool that allows you to create, read, update, and delete records. 15 | 16 | It's up to you to decide if you would like to accept a "command" as the first a cli tool argument, use an inquire "select" input for the user to choose, or something else! 17 | 18 | Suppose we have a single table / collection named _Users_ with documents that look something like this: 19 | 20 | ```ts 21 | { 22 | _id: ObjectId, 23 | name: String, 24 | favoriteInteger: uint, 25 | } 26 | ``` 27 | 28 | _Note: _id is the idiomatic name of the primary key field for MongoDb. Use whatever makes the most sense for the database you are using._ 29 | 30 | We want to be able to do these db interactions: 31 | 32 | ### Create Operations 33 | 34 | - create(newDoc) - inserts a blob of data as a document into the collection 35 | 36 | ### Read Operations 37 | 38 | - read() - takes no arguments and returns the entire collection (Note: see bonus for a more scalable verion!) 39 | - read(_id) - takes an _id input and returns the document 40 | 41 | ### Update Operations 42 | 43 | - update(_id, ..newFields) - takes an _id input and some list of key value pairs representing the document fields to be updated and the new values 44 | - replace(_id, newDoc) - takes an _id input and completely replaces the existing document with newDoc 45 | - 46 | ### Delete Operations 47 | 48 | - delete(_id) - takes an _id input, deletes the document with that _id 49 | 50 |
51 | 52 | ## Tests 53 | Ensure that your unit tests do NOT actually call to a real database. 54 | 55 | For your integration tests, run a local mongo instance, run your tests that call all your individual functions, and expect the correct data to be in the database. 56 | 57 |
58 | 59 | ## Skills Practiced 60 | 61 | - Storing and reading secrets 62 | 63 | - Connecting to a database 64 | 65 | - Various querying techniques 66 | 67 |
68 | 69 | ## Bonus 70 | - Build a read query that implements cursor pagination 71 | 72 | - When reading an individual document, incorprate a mongo "projection parameter" to efficiently read only a subset of fields from the document 73 | 74 | - Try to implement this with one of more of these databases: 75 | - MySQL 76 | - MongoDB 77 | - Postgres 78 | - Aerospike -------------------------------------------------------------------------------- /18_database_crud/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /18_database_crud/tests/mongo_crud.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn cruds() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /19_audio_stutter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "audio_stutter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /19_audio_stutter/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/19_audio_stutter/Hints.md -------------------------------------------------------------------------------- /19_audio_stutter/README.md: -------------------------------------------------------------------------------- 1 | # Audio Stutter 2 | Create a cli tool that takes a sound file and adds a cool stutter effect to it! 3 | 4 |
5 | 6 | ## Backstory 7 | Suppose you are a music producer or audio engineer, and you want to be able to offer cool audio effects to the artist vocals or sounds. 8 | 9 | You decide to build a cli tool that takes an audio file and outputs the file with the stutter effect applied! 10 | 11 |
12 | 13 | ## The Exercise 14 | Write a cli tool that reads an input audio file, applies the stutter effect, and writes and output file. 15 | 16 | The input file path can be hardcoded, and you can decide what audio filetype(s) the cli tool accepts. 17 | 18 | The stutter effect is defined by copying the beginning portion of the audio clip some number of times, which should be configurable with two optional flags. 19 | 20 | 1) _duration_, --duration or -d, should accept a u32 integer parameter representing the number of milliseconds from the beginning of the clip that should be copied, with a default value of ( 1000 ). 21 | 22 | 2) _repeats_, --repeats or -r, should accept a u32 integer parameter representing the number of times the stutter clip should be copied, with a default value of ( 2 ). 23 | 24 |
25 | 26 | ## Tests 27 | It's up to you to decide how to unit test this code. 28 | 29 | Have at least one integration test that verifies the created output file matches some known-to-be-good example output file when passed the same input audio file. 30 | 31 |
32 | 33 | ## Skills Practiced 34 | 35 | - Reading and writing audio files 36 | 37 | - Manipulating audio buffers 38 | 39 |
40 | 41 | ## Bonus 42 | - Rather than a cli tool, try deploying this as a VST plugin for DAW applications like Logic Pro and Pro Tools! 43 | 44 | - Add an additional argument for starting the stutter clip from a point within the clip rather than always from the beginning. -------------------------------------------------------------------------------- /19_audio_stutter/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /19_audio_stutter/tests/audio_stutter.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn creates_audio_stutter_effect() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /20_traveling_salesman/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "audio_stutter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | 10 | -------------------------------------------------------------------------------- /20_traveling_salesman/Hints.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/20_traveling_salesman/Hints.md -------------------------------------------------------------------------------- /20_traveling_salesman/README.md: -------------------------------------------------------------------------------- 1 | # Traveling Salesman 2 | Create a cli tool that finds the shortest route for the traveling salesman to take! 3 | 4 |
5 | 6 | ## Backstory 7 | Let's suppose this map represents the world you live in, and you are the traveling salesman. You begin at one city with all your inventory. Then you visit each city to sell your items and in the end you return home. 8 | 9 |
10 | 11 | example visualization: 12 | 13 | 14 |
15 | 16 | To go from any one city to another has a cost (think of it as the amount of gasoline used, or hours traveled, etc). The goal is to write a function that takes: 17 | 18 |
19 | 20 | 1) the city you need to begin and end with, and 21 | 22 |
23 | 24 | 2) this matrix of costs for traveling between cities. 25 | 26 |
27 | 28 | 29 | ## The Exercise 30 | 31 | Part of the challenge here is determining how to represent the data in the visualization in terms of Rust data types. 32 | 33 | The cli tool should accept two arguments: 34 | 35 |
36 | 1) in the information about distances from one city to another, and 37 | 38 | 1) The starting/ending city. 39 | 40 | 41 | The cli tool should output two things: 42 | 43 |
44 | 1) the order of cities to in the most optimal path (or paths), and 45 | 46 |
47 | 2) the total travel cost of the entire path. Good luck! ⭐️ 48 | 49 |
50 | 51 | ## Tests 52 | Unit test the pure logic, number crunching functions here. 53 | 54 | Once you find the optimal route, create an integration test that asserts the correct route and length is calculated! 55 | 56 |
57 | 58 | ## Skills Practiced 59 | 60 | - Working with weighted node/edge style graphs 61 | 62 | - Deciding how to structure abstract data 63 | 64 |
65 | 66 | ## Bonus 67 | 68 | - Provide also the "second best route" 69 | 70 | - If there are multiple routes tied for the best, print all of them 71 | 72 | - Provide a nice error message if given a starting/ending city that is not in the distance data 73 | -------------------------------------------------------------------------------- /20_traveling_salesman/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | todo!("replace this line with your code!") 3 | } 4 | -------------------------------------------------------------------------------- /20_traveling_salesman/tests/traveling_salesman.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn solves_traveling_salesman_problem() -> Result<(), Box> { 3 | todo!("put your integration test code here!"); 4 | Ok(()) 5 | } 6 | -------------------------------------------------------------------------------- /20_traveling_salesman/traveling_salesman_problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JimLynchCodes/rust-cli-exercises/495db708c65e49258a9ecf991fea9abdf3453e0b/20_traveling_salesman/traveling_salesman_problem.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jim Lynch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-cli-exercises 2 | Little exercises for learning Rust and building awesome cli tools! ⭐️ 3 | 4 |
5 | 6 | ## What's Here 7 | This repo is a collection of many extremely small Rust projects. 8 | 9 | This idea is to break down otherwise _scary_ Rust code into small, approachable, and easily testable little functions. 10 | 11 |
12 | 13 | ## Running The Projects 14 | Each folder in the root directory contains a completely independent Rust project meant to be run with `cargo`, which can be installed via [rustup](https://rustup.rs/). 15 | 16 | First ensure you have rust and cargo installed: 17 | ```bash 18 | cargo --version 19 | ``` 20 | 21 |
22 | 23 | You can then navigate into each project folder and run the cli tool: 24 | ``` 25 | cargo run 26 | ``` 27 | 28 |
29 | 30 | You can also run the automated tests: 31 | ``` 32 | cargo test 33 | ``` 34 | 35 |
36 | 37 | _Note: This runs both unit tests and integration tests!_ 38 | 39 |
40 | 41 | In addition to just running the tests, you can check code coverage: 42 | ``` 43 | cargo tarpaulin --out Html 44 | ``` 45 | 46 | Then view the `tarpaulin-report.html` file in a browser. 47 | 48 | You can also run _mutation tests_ to test your tests using libraries such as [cargo-mutants](https://crates.io/crates/cargo-mutants)! 49 | 50 |
51 | 52 | ## Work Through In any Order 53 | The numbers prefixing each folder are meant to be a recommended path to following in exploring these projects, but feel free to jump around as they are completely independent of each other! 54 | 55 | Each project has a README that explains the problem to be solved and skills practiced. 56 | 57 |
58 | 59 | ## Anatomy of An Exercise 60 | 61 | Each exercise will have a README.md file in the root of the project with these sections: 62 | 63 | - ## Title 64 | Name for the project with a little one sentence blurb. 65 | 66 |
67 | 68 | - ## Backstory 69 | An imaginary real world situation where this code could be useful. 70 | 71 |
72 | 73 | - ## The Exercise 74 | The goal / success criteria of what you need to build. 75 | 76 |
77 | 78 | - ## Tests 79 | Some suggestions around what unit and integration tests to write. 80 | 81 |
82 | 83 | - ## Skills Practiced 84 | Specific "toolbox" skills used to complete the exercise. 85 | 86 |
87 | 88 | - ## Bonus 89 | Some ideas for how to take the exercise further and expand on it. 90 | 91 |
92 | 93 | - ## Hints 94 | Located in a Hints.md file, these are meant to get you going in the right direction without completely giving away the solution. 95 | 96 |
97 | 98 | ## Build Your Own "Rust Toolbox" 99 | By building little Rust applications like these you are documenting how to accomplish and test different things you might want a command line utility to do. 100 | 101 | If you starting building your own portfolio of Rust cli tools then one day when you _actually want to solve some business problem_ with Rust you won't be starting from scratch and can use some of your old code and/or experiences to help you! 102 | 103 |
104 | 105 | ## Why Rust For CLI Tools? 106 | 107 | There are just a few reasons why Rust makes an excellent language for writing command line utilities: 108 | 109 | - ### Fast Startup & Execution Time 110 | Often times there is a delay on startup and during heavy processing for languages with a garbage collector. 111 | 112 | Since Rust CLI programs do not have to boot up a vm or gc they can extremely fast, thus saving developer time, energy, and happiness in addition to company dollars! 113 | 114 | - ### Low Level Control With A Nice Syntax 115 | 116 | The borrowing, lifetimes, and `mut` keyword gives the programmer very fine control over memory usage 117 | 118 | - ### Cargo Is Awesome 119 | 120 | Cargo makes it very easy to build and deploy cli tools, and not just to cargo- you can also publish your super efficient binary to other package managers like npm! 121 | 122 | - ### Coding In Rust Is Fun!! 123 | 124 | Once you get the hang of it you may find that you actually really enjoy working on projects in Rust. There is a reason why it is voted the "Most Loved Programming Language" on the Stack Overflow survey every year! 125 | 126 |
127 | 128 | ## Solutions 129 | If you really get stuck and want to see Jim's solutions, see the [rust-cli-examples](https://github.com/JimLynchCodes/rust-cli-examples) repo. 130 | 131 | It is highly recommended to work through them on your own because by just reading the final code you are missing out on seeing all the compiler and linter errors along the way that led the code to end up this way... 132 | 133 |
134 | 135 | ## Contributing 136 | 137 | Have a cool little cli tool you want to show off to others? Feel free to add a pull request! Also, feel free to open an issue just to discuss before hand or if you have any questions about the code / projects here. 138 | 139 |
140 | 141 | ## MIT Open Source 142 | 143 | Feel free to copy, modify, or distribute this code for your own personal or commercial use. Use at your own risk! 144 | 145 |
146 | 147 | --- 148 | 149 |
150 | 151 | Thanks for stopping by! 🦀 152 | --------------------------------------------------------------------------------