├── .gitignore ├── .gitmodules ├── Chapter01 ├── data-types │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── debug-me │ ├── .vscode │ │ └── launch.json │ ├── Cargo.toml │ ├── launch.json │ └── src │ │ └── main.rs ├── execution-flow │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── hello-world │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── pi-estimator │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── rounding │ │ └── mod.rs ├── rust-pilib │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── sequences │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── testing │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── list_integration.rs └── traits │ ├── Cargo.toml │ └── src │ └── lib.rs ├── Chapter02 ├── custom-iterators │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── enums │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── generics │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── iteration │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── lifetimes │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── mut-sharing-ownership │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── not-null │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── pattern-matching │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── sharing-ownership │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── trait-bounds │ ├── Cargo.toml │ └── src │ │ └── main.rs └── unsafe-ways │ ├── Cargo.toml │ └── src │ └── lib.rs ├── Chapter03 ├── cargo-hello │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── custom-build │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── external-deps │ ├── Cargo.toml │ ├── benches │ │ └── cooking_with_rust.rs │ └── src │ │ └── main.rs ├── hello_world │ ├── Cargo.lock │ ├── Cargo.toml │ ├── _gitignore │ └── src │ │ └── main.rs ├── my-workspace │ ├── Cargo.toml │ ├── a-lib │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── a-project │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs └── publish-crate │ ├── Cargo.toml │ ├── README.md │ └── src │ └── lib.rs ├── Chapter04 ├── actors │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── async-await │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── black-white │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── channels │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── child-processes │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── concurrent-processing │ ├── Cargo.toml │ ├── benches │ │ └── seq_vs_par.rs │ └── src │ │ └── lib.rs ├── immutable-states │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── multiple-threads │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── simple-threads │ ├── Cargo.toml │ └── src │ │ └── main.rs └── use-rayon │ ├── Cargo.toml │ ├── benches │ └── seq_vs_par.rs │ └── src │ └── lib.rs ├── Chapter05 ├── custom-errors │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── exceptional-results │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── external-crates │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── multiple-errors │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── options-results │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── panicking-responsibly │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── resilient-programming │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── testfile.txt └── seamless-errors │ ├── Cargo.toml │ └── src │ └── lib.rs ├── Chapter06 ├── code-generation │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── custom-macros │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── dry-macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── macro-overloading │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── matching │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── parameter-ranges │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── std-macros │ ├── Cargo.toml │ └── src │ ├── a.txt │ └── main.rs ├── Chapter07 ├── bindgen │ ├── rust-tinyexpr │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ └── main.rs │ └── tinyexpr │ │ ├── .travis.yml │ │ ├── CONTRIBUTING │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── benchmark.c │ │ ├── doc │ │ ├── e1.dot │ │ ├── e1.png │ │ ├── e2.dot │ │ └── e2.png │ │ ├── example.c │ │ ├── example2.c │ │ ├── example3.c │ │ ├── minctest.h │ │ ├── test.c │ │ ├── tinyexpr.c │ │ └── tinyexpr.h ├── browser-rust │ ├── rust-digest │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── web │ │ └── index.html ├── legacy-c-code │ ├── C │ │ ├── Makefile │ │ └── src │ │ │ └── main.c │ └── rust-digest │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── node-js-rust │ ├── node │ │ ├── package.json │ │ └── src │ │ │ └── index.js │ └── rust-digest │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs └── python-rust │ ├── python │ ├── setup.py │ └── src │ │ └── digest.py │ └── rust-digest │ ├── Cargo.toml │ └── src │ └── lib.rs ├── Chapter08 ├── advanced-orm │ ├── Cargo.toml │ ├── db │ │ └── bookmarks.sqlite │ └── src │ │ ├── main.rs │ │ ├── models.rs │ │ └── schema.rs ├── api │ ├── Cargo.toml │ ├── foxes.b64 │ ├── src │ │ └── main.rs │ └── static │ │ └── foxes.jpg ├── authentication │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── middlewares.rs ├── html-templates │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ ├── static │ │ ├── packtpub.com.b64 │ │ ├── packtpub.com.png │ │ ├── placeholder.b64 │ │ ├── placeholder.png │ │ ├── x5ff.xyz.b64 │ │ └── x5ff.xyz.png │ └── templates │ │ ├── index.hbs │ │ └── partials │ │ └── bookmark.hbs ├── json-handling │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── orm │ ├── Cargo.toml │ ├── db │ │ └── bookmarks.sqlite │ └── src │ │ ├── main.rs │ │ ├── models.rs │ │ └── schema.rs ├── static-web │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── static │ │ ├── foxes.jpg │ │ └── index.html └── web-errors │ ├── Cargo.toml │ └── src │ └── main.rs ├── Chapter09 ├── cross-compile │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── i2cdevice-drivers │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── sensor.rs └── reading-hardware │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── sensor.rs ├── Chapter10 ├── command-line-args │ ├── Cargo.toml │ ├── args.yml │ └── src │ │ └── main.rs ├── dynamic-data │ ├── Cargo.toml │ ├── create_pickle.py │ ├── src │ │ └── lib.rs │ └── user.pkl ├── file-stuff │ ├── Cargo.toml │ ├── hello.txt │ ├── lorem.txt │ └── src │ │ └── main.rs ├── filesystem │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── logging │ ├── Cargo.toml │ ├── log4rs.yml │ ├── outfile.log │ └── src │ │ └── main.rs ├── pipes │ ├── Cargo.toml │ ├── cargo │ ├── cars.csv │ └── src │ │ └── main.rs ├── random-numbers │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── regex │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── rusty-ml │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── sub-processes │ ├── Cargo.toml │ └── src │ │ └── main.rs └── web-requests │ ├── Cargo.toml │ ├── history.txt │ └── src │ └── main.rs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | 50 | #Added by cargo 51 | # 52 | #already existing elements are commented out 53 | 54 | /target 55 | **/*.rs.bk 56 | Cargo.lock 57 | 58 | 59 | **/target 60 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Chapter10/rusty-ml/fashion-mnist"] 2 | path = Chapter10/rusty-ml/fashion-mnist 3 | url = https://github.com/zalandoresearch/fashion-mnist.git 4 | -------------------------------------------------------------------------------- /Chapter01/data-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data-types" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/data-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(arithmetic_overflow)] 2 | // Rust allows another macro type: derive. It allows to "auto-implement" 3 | // supported traits. Clone, Debug, Copy are typically handy to derive. 4 | #[derive(Clone, Debug, Copy)] 5 | struct MyCustomStruct { 6 | a: i32, 7 | b: u32, 8 | pub c: f32, 9 | } 10 | 11 | // A typical Rust struct has an impl block for behavior 12 | impl MyCustomStruct { 13 | // The new function is static function, and by convention a constructor 14 | pub fn new(a: i32, b: u32, c: f32) -> MyCustomStruct { 15 | MyCustomStruct { a: a, b: b, c: c } 16 | } 17 | 18 | // Instance functions feature a "self" reference as the first parameter 19 | // This self reference can be mutable or owned, just like other variables 20 | pub fn sum(&self) -> f32 { 21 | self.a as f32 + self.b as f32 + self.c 22 | } 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::MyCustomStruct; 28 | use std::mem; 29 | 30 | #[test] 31 | fn test_custom_struct() { 32 | // Rust features zero-overhead structs! 33 | assert_eq!( 34 | mem::size_of::(), 35 | mem::size_of::() + mem::size_of::() + mem::size_of::() 36 | ); 37 | 38 | let m = MyCustomStruct::new(1, 2, 3_f32); 39 | assert_eq!(m.a, 1); 40 | assert_eq!(m.b, 2); 41 | assert_eq!(m.c, 3_f32); 42 | 43 | // Let's call the instance method 44 | assert_eq!(m.sum(), 6_f32); 45 | 46 | // The derived clone trait adds a method clone() and does a deep copy 47 | let m2 = m.clone(); 48 | // We use the Debug formatter to format the struct 49 | assert_eq!(format!("{:?}", m2), "MyCustomStruct { a: 1, b: 2, c: 3.0 }"); 50 | 51 | // This is an implicit (deep) copy, possible only with the Copy trait 52 | // Added mutability allows to change struct members 53 | let mut m3 = m; 54 | 55 | // As a copy, this should not affect the other instances 56 | m3.a = 100; 57 | 58 | // We'll make sure that the values didn't change anywhere else 59 | assert_eq!(m2.a, 1); 60 | assert_eq!(m.a, 1); 61 | assert_eq!(m3.a, 100); 62 | } 63 | 64 | #[test] 65 | fn basic_math_stuff() { 66 | // Works as expected 67 | assert_eq!(2 + 2, 4); 68 | 69 | // Rust lets you specify the datatype on literals by appending 70 | // them to the constant. Splitting them by _ is optional. 71 | assert_eq!(3.14 + 22.86, 26_f32); 72 | 73 | // Some functions are only available on certain types 74 | assert_eq!(2_i32.pow(2), 4); 75 | assert_eq!(4_f32.sqrt(), 2_f32); 76 | 77 | // Rust features unsigned variations of integer types 78 | let a: u64 = 32; 79 | let b: u64 = 64; 80 | 81 | // Risky, this could overflow 82 | assert_eq!(b - a, 32); 83 | 84 | // ... this is why there is an overflowing_sub() function available 85 | assert_eq!(a.overflowing_sub(b), (18446744073709551584, true)); 86 | 87 | // By default, Rust variables are immutable, add the mut qualifier 88 | // to be able to change the value 89 | let mut c = 100; 90 | c += 1; 91 | assert_eq!(c, 101); 92 | } 93 | 94 | #[test] 95 | #[should_panic] 96 | fn attempt_overflows() { 97 | let a = 10_u32; 98 | let b = 11_u32; 99 | 100 | // This will panic since the result is going to be an unsigned 101 | // type which cannot handle negative numbers 102 | // Note: _ means ignore the result 103 | // Note: In the Rust 2021 edition, the rust compiler will catch this 104 | // error before the runtime has a chance to panic! 105 | // let _ = a - b; 106 | // ^^^^^ attempt to compute `10_u32 - 11_u32`, which would overflow 107 | let _ = a - b; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Chapter01/debug-me/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'debug-me'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=debug-me", 15 | "--package=debug-me" 16 | ], 17 | "filter": { 18 | "kind": "bin" 19 | } 20 | }, 21 | "args": [], 22 | "cwd": "${workspaceFolder}" 23 | }, 24 | { 25 | "type": "lldb", 26 | "request": "launch", 27 | "name": "Debug unit tests in executable 'debug-me'", 28 | "cargo": { 29 | "args": [ 30 | "test", 31 | "--no-run", 32 | "--bin=debug-me", 33 | "--package=debug-me" 34 | ], 35 | "filter": { 36 | "kind": "bin" 37 | } 38 | }, 39 | "args": [], 40 | "cwd": "${workspaceFolder}" 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /Chapter01/debug-me/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "debug-me" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/debug-me/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | 5 | { 6 | "type": "lldb", 7 | "request": "launch", 8 | "name": "Debug", 9 | "program": "${workspaceRoot}/target/debug/${workspaceRootFolderName}", 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "sourceLanguages": ["rust"] 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /Chapter01/debug-me/src/main.rs: -------------------------------------------------------------------------------- 1 | struct MyStruct { 2 | prop: usize, 3 | } 4 | 5 | struct Point(f32, f32); 6 | 7 | fn main() { 8 | let a = 42; 9 | let b = vec![0, 0, 0, 100]; 10 | let c = [1, 2, 3, 4, 5]; 11 | let d = 0x5ff; 12 | let e = MyStruct { prop: 10 }; 13 | let p = Point(3.14, 3.14); 14 | 15 | println!("Hello, world!"); 16 | } 17 | -------------------------------------------------------------------------------- /Chapter01/execution-flow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "execution-flow" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/execution-flow/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | fn conditionals() { 5 | let i = 20; 6 | // Rust's if statement does not require parenthesis 7 | if i < 2 { 8 | assert!(i < 2); 9 | } else if i > 2 { 10 | assert!(i > 2); 11 | } else { 12 | assert_eq!(i, 2); 13 | } 14 | } 15 | 16 | #[test] 17 | fn loops() { 18 | 19 | let mut i = 42; 20 | let mut broke = false; 21 | 22 | // a basic loop with control statements 23 | loop { 24 | i -= 1; 25 | if i < 2 { 26 | broke = true; 27 | break; 28 | } else if i > 2 { 29 | continue; 30 | } 31 | } 32 | assert!(broke); 33 | 34 | // loops and other constructs can be named for better readability ... 35 | 'outer: loop { 36 | 'inner: loop { 37 | break 'inner; // ... and specifically jumped out of 38 | } 39 | break 'outer; 40 | } 41 | let mut iterations: u32 = 0; 42 | 43 | // loops can even have return values on breaks 44 | let total_squared = loop { 45 | iterations += 1; 46 | 47 | if iterations >= 10 { 48 | break iterations.pow(2); 49 | } 50 | }; 51 | assert_eq!(total_squared, 100); 52 | 53 | // for loops can use ranges ... 54 | for i in 0..10 { 55 | assert!(i >= 0 && i < 10) 56 | } 57 | 58 | // or anything that is an iterator 59 | for v in vec![1, 1, 1, 1].iter() { 60 | assert_eq!(v, &1); 61 | } 62 | } 63 | 64 | #[test] 65 | fn more_conditionals() { 66 | let my_option = Some(10); 67 | 68 | // If let statements can do simple pattern matching 69 | if let Some(unpacked) = my_option { 70 | assert_eq!(unpacked, 10); 71 | } 72 | 73 | let mut other_option = Some(2); 74 | // there is also while let, which does the same thing 75 | while let Some(unpacked) = other_option { 76 | 77 | // if can also return values in assignments 78 | other_option = if unpacked > 0 { 79 | Some(unpacked - 1) 80 | } else { 81 | None 82 | } 83 | } 84 | assert_eq!(other_option, None) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Chapter01/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk -------------------------------------------------------------------------------- /Chapter01/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | use std::f64; 3 | 4 | fn main() { 5 | // Basic printing with arguments 6 | println!("Let's print some lines:"); 7 | println!(); 8 | println!("Hello, world!"); 9 | println!("{}, {}!", "Hello", "world"); 10 | // No newlines 11 | print!("Hello, "); 12 | println!("world!"); 13 | 14 | println!("Arguments can be referred to by their position: {0}, {1}! and {1}, {0}! are built from the same arguments", "Hello", "world"); 15 | 16 | // More complex arguments 17 | println!("Furthermore the arguments can be named: \"{greeting}, {object}!\"", greeting = "Hello", object = "World"); 18 | 19 | // Number formatting 20 | println!("Number formatting: Pi is {0:.3} or {0:.0} for short", f64::consts::PI); 21 | 22 | // Padding and hex formatting 23 | println!("... and there is more: {0:>0width$}={0:>width$}={0:#x}", 1535, width = 5); 24 | 25 | // Writing to a stream directly 26 | let _ = write!(&mut io::stdout(), "Underneath, it's all writing to a stream..."); 27 | println!(); 28 | 29 | // Reading from std::in 30 | println!("Write something!"); 31 | let mut input = String::new(); 32 | if let Ok(n) = io::stdin().read_line(&mut input) { 33 | println!("You wrote: {} ({} bytes) ", input, n); 34 | } 35 | else { 36 | // Printing to std::err 37 | eprintln!("There was an error :("); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Chapter01/pi-estimator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pi-estimator" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rust-pilib = { path = '../rust-pilib', version = '*'} -------------------------------------------------------------------------------- /Chapter01/pi-estimator/src/main.rs: -------------------------------------------------------------------------------- 1 | // declare the module by its file name 2 | mod rounding; 3 | 4 | // Rust will also accept if you implement it right away 5 | mod printer { 6 | // import a function from an external crate (no more extern declaration required!) 7 | use rust_pilib::monte_carlo_pi; 8 | 9 | // crates present in the parent can be imported using the crate prefix 10 | use crate::rounding::round; 11 | 12 | pub fn pretty_print_pi_approx(iterations: usize) { 13 | let pi = monte_carlo_pi(iterations); 14 | let places: usize = 2; 15 | 16 | println!("Pi is ~ {} and rounded to {} places {}", pi, places, round(pi, places)); 17 | } 18 | } 19 | 20 | // import from the module above 21 | use printer::pretty_print_pi_approx; 22 | 23 | 24 | fn main() { 25 | pretty_print_pi_approx(100_000); 26 | } 27 | -------------------------------------------------------------------------------- /Chapter01/pi-estimator/src/rounding/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | pub fn round(nr: f32, places: usize) -> f32 { 3 | let multiplier = 10_f32.powi(places as i32); 4 | (nr * multiplier + 0.5).floor() / multiplier 5 | } 6 | 7 | 8 | #[cfg(test)] 9 | mod tests { 10 | use super::round; 11 | 12 | #[test] 13 | fn round_positive() { 14 | assert_eq!(round(3.123456, 2), 3.12); 15 | assert_eq!(round(3.123456, 4), 3.1235); 16 | assert_eq!(round(3.999999, 2), 4.0); 17 | assert_eq!(round(3.0, 2), 3.0); 18 | assert_eq!(round(9.99999, 2), 10.0); 19 | assert_eq!(round(0_f32, 2), 0_f32); 20 | } 21 | 22 | #[test] 23 | fn round_negative() { 24 | assert_eq!(round(-3.123456, 2), -3.12); 25 | assert_eq!(round(-3.123456, 4), -3.1235); 26 | assert_eq!(round(-3.999999, 2), -4.0); 27 | assert_eq!(round(-3.0, 2), -3.0); 28 | assert_eq!(round(-9.99999, 2), -10.0); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Chapter01/rust-pilib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-pilib" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rand = "^0.5" -------------------------------------------------------------------------------- /Chapter01/rust-pilib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::prelude::*; 2 | 3 | pub fn monte_carlo_pi(iterations: usize) -> f32 { 4 | let mut inside_circle = 0; 5 | for _ in 0..iterations { 6 | 7 | // generate two random coordinates between 0 and 1 8 | let x: f32 = random::(); 9 | let y: f32 = random::(); 10 | 11 | // calculate the circular distance from 0, 0 12 | if x.powi(2) + y.powi(2) <= 1_f32 { 13 | // if it's within the circle, increase the count 14 | inside_circle += 1; 15 | } 16 | } 17 | // return the ratio of 4 times the hits to the total iterations 18 | (4_f32 * inside_circle as f32) / iterations as f32 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | // import the parent crate's functions 24 | 25 | use super::*; 26 | 27 | fn is_reasonably_pi(pi: f32) -> bool { 28 | pi >= 3_f32 && pi <= 4.5_f32 29 | } 30 | 31 | #[test] 32 | fn test_monte_carlo_pi_1() { 33 | let pi = monte_carlo_pi(1); 34 | assert!(pi == 0_f32 || pi == 4_f32); 35 | } 36 | 37 | #[test] 38 | fn test_monte_carlo_pi_500() { 39 | let pi = monte_carlo_pi(500); 40 | assert!(is_reasonably_pi(pi)); 41 | } 42 | 43 | #[test] 44 | fn test_monte_carlo_pi_1000() { 45 | let pi = monte_carlo_pi(1000); 46 | assert!(is_reasonably_pi(pi)); 47 | } 48 | 49 | #[test] 50 | fn test_monte_carlo_pi_5000() { 51 | let pi = monte_carlo_pi(5000); 52 | assert!(is_reasonably_pi(pi)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter01/sequences/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sequences" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/sequences/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::mem; 4 | 5 | #[test] 6 | fn exploring_vec() { 7 | // a Vec is almost always initialized using a macro 8 | assert_eq!(vec![0; 3], [0, 0, 0]); 9 | let mut v: Vec = vec![]; 10 | 11 | // a Vec is defined by a triple (pointer, capacity, length) 12 | assert_eq!(mem::size_of::>(), mem::size_of::() * 3); 13 | 14 | // empty vectors point to no memory (yet) 15 | assert_eq!(mem::size_of_val(&*v), 0); 16 | 17 | v.push(10); 18 | 19 | // a vector will also over-allocate on insert 20 | // *by how much is an implementation detail and may change!* 21 | assert_eq!(mem::size_of::>(), mem::size_of::() * 6); 22 | 23 | // vectors support indexing 24 | assert_eq!(v[0], 10); 25 | 26 | // vectors have some convenience methods 27 | v.insert(0, 11); 28 | v.push(12); 29 | assert_eq!(v, [11, 10, 12]); 30 | assert!(!v.is_empty()); 31 | 32 | // ... like one to create a heap in only a few lines 33 | assert_eq!(v.swap_remove(0), 11); 34 | assert_eq!(v, [12, 10]); 35 | 36 | // ... or a stack 37 | assert_eq!(v.pop(), Some(10)); 38 | assert_eq!(v, [12]); 39 | 40 | // vectors also support regular removals 41 | assert_eq!(v.remove(0), 12); 42 | 43 | // and can go back to occupying no memory.. 44 | v.shrink_to_fit(); 45 | assert_eq!(mem::size_of_val(&*v), 0); 46 | } 47 | 48 | struct Point(f32, f32); 49 | 50 | #[test] 51 | fn exploring_tuples() { 52 | let mut my_tuple: (i32, usize, f32) = (10, 0, -3.42); 53 | 54 | // members can be accessed like that 55 | assert_eq!(my_tuple.0, 10); 56 | assert_eq!(my_tuple.1, 0); 57 | assert_eq!(my_tuple.2, -3.42); 58 | 59 | my_tuple.0 = 100; 60 | assert_eq!(my_tuple.0, 100); 61 | 62 | 63 | 64 | // tuples can be unpacked 65 | let (_val1, _val2, _val3) = my_tuple; 66 | 67 | // structs can be based on tuples too 68 | let point = Point(1.2, 2.1); 69 | assert_eq!(point.0, 1.2); 70 | assert_eq!(point.1, 2.1); 71 | } 72 | 73 | 74 | #[test] 75 | fn exploring_arrays() { 76 | // arrays use a familiar signature 77 | // (type declarations are not necessary) 78 | let mut arr: [usize; 3] = [0; 3]; 79 | assert_eq!(arr, [0, 0, 0]); 80 | 81 | let arr2: [usize; 5] = [1,2,3,4,5]; 82 | assert_eq!(arr2, [1,2,3,4,5]); 83 | 84 | arr[0] = 1; 85 | assert_eq!(arr, [1, 0, 0]); 86 | assert_eq!(arr[0], 1); 87 | assert_eq!(mem::size_of_val(&arr), mem::size_of::() * 3); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Chapter01/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "testing" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/testing/tests/list_integration.rs: -------------------------------------------------------------------------------- 1 | use testing::List; 2 | 3 | 4 | #[test] 5 | fn test_list_insert_10k_items() { 6 | let mut list = List::new_empty(); 7 | for _ in 0..10_000 { 8 | list.append(100); 9 | } 10 | assert_eq!(list.length, 10_000); 11 | } -------------------------------------------------------------------------------- /Chapter01/traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "traits" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter01/traits/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | // Structs 3 | 4 | /// 5 | /// Configuration for our application 6 | /// 7 | pub struct Config { 8 | values: Vec<(String, String)>, 9 | } 10 | 11 | /// 12 | /// A service for managing a configuration 13 | /// 14 | pub struct KeyValueConfigService {} 15 | 16 | // Traits 17 | 18 | /// 19 | /// Provides a get() function to return values associated with 20 | /// the specified key. 21 | /// 22 | pub trait ValueGetter { 23 | fn get(&self, s: &str) -> Option; 24 | } 25 | 26 | /// 27 | /// Write a config 28 | /// 29 | pub trait ConfigWriter { 30 | fn write(&self, config: Config, to: &mut impl Write) -> std::io::Result<()>; 31 | } 32 | 33 | /// 34 | /// Read a config 35 | /// 36 | pub trait ConfigReader { 37 | fn read(&self, from: &mut impl Read) -> std::io::Result; 38 | } 39 | 40 | // Impls 41 | 42 | impl Config { 43 | pub fn new(values: Vec<(String, String)>) -> Config { 44 | Config { values: values } 45 | } 46 | } 47 | 48 | impl KeyValueConfigService { 49 | pub fn new() -> KeyValueConfigService { 50 | KeyValueConfigService {} 51 | } 52 | } 53 | 54 | impl ConfigWriter for KeyValueConfigService { 55 | fn write(&self, config: Config, mut to: &mut impl Write) -> std::io::Result<()> { 56 | for v in config.values { 57 | writeln!(&mut to, "{0}={1}", v.0, v.1)?; 58 | } 59 | Ok(()) 60 | } 61 | } 62 | 63 | impl ConfigReader for KeyValueConfigService { 64 | fn read(&self, from: &mut impl Read) -> std::io::Result { 65 | let mut buffer = String::new(); 66 | from.read_to_string(&mut buffer)?; 67 | 68 | // chain iterators together and collect the results 69 | let values: Vec<(String, String)> = buffer 70 | .split_terminator("\n") // split 71 | .map(|line| line.trim()) // remove whitespace 72 | .filter(|line| { 73 | // filter invalid lines 74 | let pos = line.find("=").unwrap_or(0); 75 | pos > 0 && pos < line.len() - 1 76 | }) 77 | .map(|line| { 78 | // create a tuple from a line 79 | let parts = line.split("=").collect::>(); 80 | (parts[0].to_string(), parts[1].to_string()) 81 | }) 82 | .collect(); // transform it into a vector 83 | Ok(Config::new(values)) 84 | } 85 | } 86 | 87 | impl ValueGetter for Config { 88 | fn get(&self, s: &str) -> Option { 89 | self.values.iter().find_map(|tuple| { 90 | if &tuple.0 == s { 91 | Some(tuple.1.clone()) 92 | } else { 93 | None 94 | } 95 | }) 96 | } 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | use std::io::Cursor; 103 | 104 | #[test] 105 | fn config_get_value() { 106 | let config = Config::new(vec![("hello".to_string(), "world".to_string())]); 107 | assert_eq!(config.get("hello"), Some("world".to_string())); 108 | assert_eq!(config.get("HELLO"), None); 109 | } 110 | 111 | #[test] 112 | fn keyvalueconfigservice_write_config() { 113 | let config = Config::new(vec![("hello".to_string(), "world".to_string())]); 114 | 115 | let service = KeyValueConfigService::new(); 116 | let mut target = vec![]; 117 | assert!(service.write(config, &mut target).is_ok()); 118 | 119 | assert_eq!( 120 | String::from_utf8(target).unwrap(), 121 | "hello=world\n".to_string() 122 | ); 123 | } 124 | 125 | #[test] 126 | fn keyvalueconfigservice_read_config() { 127 | let service = KeyValueConfigService::new(); 128 | let readable = &format!("{}\n{}", "hello=world", "a=b").into_bytes(); 129 | 130 | let config = service 131 | .read(&mut Cursor::new(readable)) 132 | .expect("Couldn't read from the vector"); 133 | 134 | assert_eq!( 135 | config.values, 136 | vec![ 137 | ("hello".to_string(), "world".to_string()), 138 | ("a".to_string(), "b".to_string()) 139 | ] 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Chapter02/custom-iterators/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-iterators" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/enums/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enums" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/enums/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | /// 4 | /// An enum to encapsulate errors without too much code 5 | /// 6 | pub enum ApplicationError { 7 | Code { full: usize, short: u16 }, 8 | Message(String), 9 | IOWrapper(io::Error), 10 | Unknown 11 | } 12 | 13 | // Even enums can have an implementation! 14 | impl ApplicationError { 15 | 16 | /// 17 | /// A function to quickly write the enum's name somewhere. 18 | /// 19 | pub fn print_kind(&self, mut to: &mut impl io::Write) -> io::Result<()> { 20 | // A simple match clause to react to each enum variant 21 | let kind = match self { 22 | ApplicationError::Code { full: _, short: _ } => "Code", 23 | ApplicationError::Unknown => "Unknown", 24 | ApplicationError::IOWrapper(_) => "IOWrapper", 25 | ApplicationError::Message(_) => "Message" 26 | }; 27 | // the write trait lets us use any implementation and the write! macro 28 | // using the question mark operator lets the function return early in 29 | // case of an Err() result 30 | write!(&mut to, "{}", kind)?; 31 | Ok(()) 32 | } 33 | } 34 | 35 | /// 36 | /// An arbitrary function that simulates work and returns enum variants 37 | /// based on the input parameter. 38 | /// 39 | pub fn do_work(choice: i32) -> Result<(), ApplicationError> { 40 | if choice < -100 { 41 | Err(ApplicationError::IOWrapper(io::Error::from(io::ErrorKind::Other))) 42 | } else if choice == 42 { 43 | Err(ApplicationError::Code { full: choice as usize, short: (choice % u16::max_value() as i32) as u16 } ) 44 | } else if choice > 42 { 45 | Err(ApplicationError::Message( 46 | format!("{} lead to a terrible error", choice) 47 | )) 48 | } else { 49 | Err(ApplicationError::Unknown) 50 | } 51 | } 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | use super::{ApplicationError, do_work}; 56 | use std::io; 57 | 58 | #[test] 59 | fn test_do_work() { 60 | let choice = 10; 61 | if let Err(error) = do_work(choice) { 62 | match error { 63 | ApplicationError::Code { full: code, short: _ } => assert_eq!(choice as usize, code), 64 | // the following arm matches both variants (OR) 65 | ApplicationError::Unknown | ApplicationError::IOWrapper(_) => assert!(choice < 42), 66 | ApplicationError::Message(msg) => assert_eq!(format!("{} lead to a terrible error", choice), msg) 67 | } 68 | } 69 | } 70 | 71 | #[test] 72 | fn test_application_error_get_kind() { 73 | let mut target = vec![]; 74 | let _ = ApplicationError::Code { full: 100, short: 100 }.print_kind(&mut target); 75 | assert_eq!(String::from_utf8(target).unwrap(), "Code".to_string()); 76 | 77 | let mut target = vec![]; 78 | let _ = ApplicationError::Message("0".to_string()).print_kind(&mut target); 79 | assert_eq!(String::from_utf8(target).unwrap(), "Message".to_string()); 80 | 81 | let mut target = vec![]; 82 | let _ = ApplicationError::Unknown.print_kind(&mut target); 83 | assert_eq!(String::from_utf8(target).unwrap(), "Unknown".to_string()); 84 | 85 | let mut target = vec![]; 86 | let error = io::Error::from(io::ErrorKind::WriteZero); 87 | let _ = ApplicationError::IOWrapper(error).print_kind(&mut target); 88 | assert_eq!(String::from_utf8(target).unwrap(), "IOWrapper".to_string()); 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Chapter02/generics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generics" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/iteration/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iteration" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | 9 | [dev-dependencies] 10 | rand = "^0.5" -------------------------------------------------------------------------------- /Chapter02/iteration/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | 4 | #[test] 5 | fn getting_the_iterator() { 6 | let v = vec![10, 10, 10]; 7 | 8 | // borrowed iterator (v remains as is) 9 | let mut iter = v.iter(); 10 | assert_eq!(iter.next(), Some(&10)); 11 | assert_eq!(iter.next(), Some(&10)); 12 | assert_eq!(iter.next(), Some(&10)); 13 | assert_eq!(iter.next(), None); 14 | 15 | // owned iterator (consumes v) 16 | // this is the same as calling into_iter() 17 | for i in v { 18 | assert_eq!(i, 10); 19 | } 20 | } 21 | 22 | fn count_files(path: &String) -> usize { 23 | path.len() 24 | } 25 | 26 | #[test] 27 | fn data_transformations() { 28 | let v = vec![10, 10, 10]; 29 | let hexed = v.iter().map(|i| format!("{:x}", i)); 30 | assert_eq!( 31 | hexed.collect::>(), 32 | vec!["a".to_string(), "a".to_string(), "a".to_string()] 33 | ); 34 | assert_eq!(v.iter().fold(0, |p, c| p + c), 30); 35 | 36 | // as an example: directories and their file count 37 | let dirs = vec![ 38 | "/home/alice".to_string(), 39 | "/home/bob".to_string(), 40 | "/home/carl".to_string(), 41 | "/home/debra".to_string(), 42 | ]; 43 | 44 | // get the no of files with some function 45 | // based on the directory name 46 | let file_counter = dirs.iter().map(count_files); 47 | 48 | // for easier handling, merge both collections into one 49 | let dir_file_counts: Vec<(&String, usize)> = dirs.iter().zip(file_counter).collect(); 50 | 51 | // sorry for the messy string handling here ... 52 | // "hello" is a &str (a slice) so either we work 53 | // with &&str (slice reference) or &String (String reference) 54 | // We opted for the latter. 55 | assert_eq!( 56 | dir_file_counts, 57 | vec![ 58 | (&"/home/alice".to_string(), 11), 59 | (&"/home/bob".to_string(), 9), 60 | (&"/home/carl".to_string(), 10), 61 | (&"/home/debra".to_string(), 11) 62 | ] 63 | ) 64 | } 65 | 66 | #[test] 67 | fn data_filtering() { 68 | let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; 69 | // a simple filter to only get the even elements. 70 | // confirmed by an "all" function where a predicate has to apply to all elements 71 | assert!(data.iter().filter(|&n| n % 2 == 0).all(|&n| n % 2 == 0)); 72 | // similarly, find and position can be used to find the *first* occurance of an element 73 | assert_eq!(data.iter().find(|&&n| n == 5), Some(&5)); 74 | assert_eq!(data.iter().find(|&&n| n == 0), None); 75 | assert_eq!(data.iter().position(|&n| n == 5), Some(4)); 76 | 77 | // we can also simply skip a number of elements 78 | assert_eq!(data.iter().skip(1).next(), Some(&2)); 79 | let mut data_iter = data.iter().take(2); 80 | assert_eq!(data_iter.next(), Some(&1)); 81 | assert_eq!(data_iter.next(), Some(&2)); 82 | assert_eq!(data_iter.next(), None); 83 | 84 | // another handy use is for splitting data (e.g. for machine learning) 85 | // this splits the original Vec into a training (~80%) and validation set (~20%) 86 | let (validation, train): (Vec, Vec) = data 87 | .iter() 88 | .partition(|&_| (rand::random::() % 1.0) > 0.8); 89 | 90 | assert!(train.len() > validation.len()); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Chapter02/lifetimes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lifetimes" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/mut-sharing-ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mut-sharing-ownership" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/mut-sharing-ownership/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | extern crate test; 6 | use test::Bencher; 7 | 8 | #[bench] 9 | fn bench_regular_push(b: &mut Bencher) { 10 | let mut v = vec![]; 11 | b.iter(|| { 12 | for _ in 0..1_000 { 13 | v.push(10); 14 | } 15 | }); 16 | } 17 | 18 | #[bench] 19 | fn bench_refcell_push(b: &mut Bencher) { 20 | let v = RefCell::new(vec![]); 21 | b.iter(|| { 22 | for _ in 0..1_000 { 23 | v.borrow_mut().push(10); 24 | } 25 | }); 26 | } 27 | 28 | #[bench] 29 | fn bench_cell_push(b: &mut Bencher) { 30 | let v = Cell::new(vec![]); 31 | b.iter(|| { 32 | for _ in 0..1_000 { 33 | let mut vec = v.take(); 34 | vec.push(10); 35 | v.set(vec); 36 | } 37 | }); 38 | } 39 | 40 | use std::borrow::Cow; 41 | use std::ptr::eq; 42 | 43 | fn min_sum_cow(min: i32, v: &mut Cow<[i32]>) { 44 | let sum: i32 = v.iter().sum(); 45 | if sum < min { 46 | v.to_mut().push(min - sum); 47 | } 48 | } 49 | 50 | #[test] 51 | fn handling_cows() { 52 | let v = vec![10, 20, 30]; 53 | 54 | // cows - Copy on Writes - encapsulate the cloning process 55 | // we'll wrap the Vec into a Cow 56 | let mut cow = Cow::from(&v); 57 | 58 | // the memory locations are now the same 59 | assert!(eq(&v[..], &*cow)); 60 | 61 | // ... until we pass it mutably into a mutating function 62 | min_sum_cow(70, &mut cow); 63 | 64 | // on some cases, the Cow will NOT contain the 65 | // original value! 66 | assert_eq!(v, vec![10, 20, 30]); 67 | assert_eq!(cow, vec![10, 20, 30, 10]); 68 | 69 | // both pointers are not equal either 70 | assert!(!eq(&v[..], &*cow)); 71 | 72 | // retrieve the owned value. this is a clone operation 73 | let v2 = cow.into_owned(); 74 | 75 | // let's repeat without mutation 76 | let mut cow2 = Cow::from(&v2); 77 | min_sum_cow(70, &mut cow2); 78 | 79 | // they are now equal ... 80 | assert_eq!(cow2, v2); 81 | 82 | // ... and point to the same memory location 83 | assert!(eq(&v2[..], &*cow2)); 84 | } 85 | 86 | use std::cell::{Cell, RefCell}; 87 | 88 | fn min_sum_refcell(min: i32, v: &RefCell>) { 89 | let sum: i32 = v.borrow().iter().sum(); 90 | if sum < min { 91 | v.borrow_mut().push(min - sum); 92 | } 93 | } 94 | 95 | fn min_sum_cell(min: i32, v: &Cell>) { 96 | // we first take the Vec ownership 97 | let mut vec = v.take(); 98 | 99 | // work with it ... 100 | let sum: i32 = vec.iter().sum(); 101 | 102 | // change if needed 103 | if sum < min { 104 | vec.push(min - sum); 105 | } 106 | // then we put it back! no mut required 107 | v.set(vec); 108 | } 109 | 110 | #[test] 111 | fn about_cells() { 112 | // we allocate memory and use a RefCell to dynamically 113 | // manage ownership 114 | let ref_cell = RefCell::new(vec![10, 20, 30]); 115 | 116 | // mutable borrows are fine, 117 | min_sum_refcell(70, &ref_cell); 118 | 119 | // they are equal! 120 | assert!(ref_cell.borrow().eq(&vec![10, 20, 30, 10])); 121 | 122 | // cells are a bit different 123 | let cell = Cell::from(vec![10, 20, 30]); 124 | 125 | // pass the immutable cell into the function 126 | min_sum_cell(70, &cell); 127 | 128 | // unwrap 129 | let v = cell.into_inner(); 130 | 131 | // check the contents, and they changed! 132 | assert_eq!(v, vec![10, 20, 30, 10]); 133 | } 134 | 135 | #[test] 136 | #[should_panic] 137 | fn failing_cells() { 138 | let ref_cell = RefCell::new(vec![10, 20, 30]); 139 | 140 | // multiple borrows are fine 141 | let _v = ref_cell.borrow(); 142 | min_sum_refcell(60, &ref_cell); 143 | 144 | // ... until they are mutable borrows 145 | min_sum_refcell(70, &ref_cell); // panics! 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Chapter02/not-null/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "not-null" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/not-null/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | #[should_panic] 5 | fn option_unwrap() { 6 | // Options to unwrap Options 7 | assert_eq!(Some(10).unwrap(), 10); 8 | assert_eq!(None.unwrap_or(10), 10); 9 | assert_eq!(None.unwrap_or_else(|| 5 * 2), 10); 10 | 11 | // panic ensues 12 | // Explicitly type None to an Option 13 | // expect is always preferred since it has a message attached 14 | Option::::None.unwrap(); 15 | Option::::None.expect("Better say something when panicking"); 16 | } 17 | 18 | #[test] 19 | fn option_working_with_values() { 20 | let mut o = Some(42); 21 | 22 | // take the value out and replace it with None 23 | let nr = o.take(); 24 | assert!(o.is_none()); 25 | // nr now holds an option containing the value 26 | assert_eq!(nr, Some(42)); 27 | 28 | let mut o = Some(42); 29 | // sometimes it's better to replace the value right away 30 | assert_eq!(o.replace(1535), Some(42)); 31 | assert_eq!(o, Some(1535)); 32 | 33 | let o = Some(1535); 34 | // the map() function works only on Some() values (not None) 35 | assert_eq!(o.map(|v| format!("{:#x}", v)), Some("0x5ff".to_owned())); 36 | 37 | let o = Some(1535); 38 | // Options can be transformed into a Result easily. "Nope" is the Err() 39 | // value for the new Result. 40 | match o.ok_or("Nope") { 41 | Ok(nr) => assert_eq!(nr, 1535), 42 | Err(_) => assert!(false), 43 | } 44 | } 45 | 46 | #[test] 47 | fn option_sequentials() { 48 | // Options have a range of abilities and sometimes even behave like sequences 49 | let a = Some(42); 50 | let b = Some(1535); 51 | // boolean logic with options. Note the returned values 52 | assert_eq!(a.and(b), Some(1535)); 53 | assert_eq!(a.and(Option::::None), None); 54 | assert_eq!(a.or(None), Some(42)); 55 | assert_eq!(a.or(b), Some(42)); 56 | assert_eq!(None.or(a), Some(42)); 57 | 58 | // chain together Option instances to skip tedious unwrapping 59 | let new_a = a.and_then(|v| Some(v + 100)).filter(|&v| v != 42); 60 | 61 | // iterate over Options 62 | assert_eq!(new_a, Some(142)); 63 | let mut a_iter = new_a.iter(); 64 | assert_eq!(a_iter.next(), Some(&142)); 65 | assert_eq!(a_iter.next(), None); 66 | } 67 | 68 | #[test] 69 | fn option_pattern_matching() { 70 | match Some(100) { 71 | Some(v) => assert_eq!(v, 100), 72 | None => assert!(false), 73 | }; 74 | 75 | if let Some(v) = Some(42) { 76 | assert_eq!(v, 42); 77 | } else { 78 | assert!(false); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Chapter02/pattern-matching/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pattern-matching" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/pattern-matching/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | enum Background { 3 | Color(u8, u8, u8), 4 | Image(&'static str), 5 | } 6 | 7 | enum UserType { 8 | Casual, 9 | Power 10 | } 11 | 12 | struct MyApp { 13 | theme: Background, 14 | user_type: UserType, 15 | secret_user_id: usize 16 | } 17 | 18 | fn guarded_match(app: MyApp) -> String { 19 | match app { 20 | MyApp { secret_user_id: uid, .. } if uid <= 100 => "You are an early bird!".to_owned(), 21 | MyApp { .. } => "Thank you for also joining".to_owned() 22 | } 23 | } 24 | 25 | fn destructuring_match(app: MyApp) -> String { 26 | match app { 27 | MyApp { user_type: UserType::Power, 28 | secret_user_id: uid, 29 | theme: Background::Color(b1, b2, b3) } => 30 | format!("A power user with id >{}< and color background (#{:02x}{:02x}{:02x})", uid, b1, b2, b3), 31 | MyApp { user_type: UserType::Power, 32 | secret_user_id: uid, 33 | theme: Background::Image(path) } => 34 | format!("A power user with id >{}< and image background (path: {})", uid, path), 35 | MyApp { user_type: _, secret_user_id: uid, .. } => format!("A regular user with id >{}<, individual backgrounds not supported", uid), 36 | } 37 | } 38 | 39 | fn literal_match(choice: usize) -> String { 40 | match choice { 41 | 0 | 1 => "zero or one".to_owned(), 42 | 2 ... 9 => "two to nine".to_owned(), 43 | 10 => "ten".to_owned(), 44 | _ => "anything else".to_owned() 45 | } 46 | } 47 | 48 | fn literal_str_match(choice: &str) -> String { 49 | match choice { 50 | "🏋️" => "Power lifting".to_owned(), 51 | "🏈" => "Football".to_owned(), 52 | "🥋" => "BJJ".to_owned(), 53 | _ => "Competitive BBQ".to_owned() 54 | } 55 | } 56 | 57 | 58 | fn reference_match(m: &Option<&str>) -> String { 59 | match m { 60 | Some(ref s) => s.to_string(), 61 | _ => "Nothing".to_string() 62 | } 63 | } 64 | 65 | fn tuple_match(choices: (i32, i32, i32, i32)) -> String { 66 | match choices { 67 | (_, second, _, fourth) => format!("Numbers at positions 1 and 3 are {} and {} respectively", second, fourth) 68 | } 69 | } 70 | 71 | 72 | pub fn main() { 73 | let opt = Some(42); 74 | // the most common match: 75 | match opt { 76 | Some(nr) => println!("Got {}", nr), 77 | // _ matches everything else as a placeholder for 78 | // don't care. 79 | _ => println!("Found None") 80 | } 81 | println!(); 82 | println!("Literal match for 0: {}", literal_match(0)); 83 | println!("Literal match for 10: {}", literal_match(10)); 84 | println!("Literal match for 100: {}", literal_match(100)); 85 | 86 | println!(); 87 | println!("Literal match for 0: {}", tuple_match((0, 10, 0, 100))); 88 | 89 | println!(); 90 | let mystr = Some("Hello"); 91 | println!("Mathing on a reference: {}", reference_match(&mystr)); 92 | println!("It's still owned here: {:?}", mystr); 93 | 94 | println!(); 95 | let power = MyApp { 96 | secret_user_id: 99, 97 | theme: Background::Color(255, 255, 0), 98 | user_type: UserType::Power 99 | }; 100 | println!("Destructuring a power user: {}", destructuring_match(power)); 101 | 102 | let casual = MyApp { 103 | secret_user_id: 10, 104 | theme: Background::Image("my/fav/image.png"), 105 | user_type: UserType::Casual 106 | }; 107 | println!("Destructuring a casual user: {}", destructuring_match(casual)); 108 | 109 | let power2 = MyApp { 110 | secret_user_id: 150, 111 | theme: Background::Image("a/great/landscape.png"), 112 | user_type: UserType::Power 113 | }; 114 | println!("Destructuring another power user: {}", destructuring_match(power2)); 115 | 116 | println!(); 117 | let early = MyApp { 118 | secret_user_id: 4, 119 | theme: Background::Color(255, 255, 0), 120 | user_type: UserType::Power 121 | }; 122 | println!("Guarded matching (early): {}", guarded_match(early)); 123 | 124 | let not_so_early = MyApp { 125 | secret_user_id: 1003942, 126 | theme: Background::Color(255, 255, 0), 127 | user_type: UserType::Power 128 | }; 129 | println!("Guarded matching (late): {}", guarded_match(not_so_early)); 130 | println!(); 131 | 132 | println!("Literal match for 🥋: {}", literal_str_match("🥋")); 133 | println!("Literal match for 🏈: {}", literal_str_match("🏈")); 134 | println!("Literal match for 🏋️: {}", literal_str_match("🏋️")); 135 | println!("Literal match for ⛳: {}", literal_str_match("⛳")); 136 | } -------------------------------------------------------------------------------- /Chapter02/sharing-ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sharing-ownership" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/sharing-ownership/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | extern crate test; 7 | use std::rc::Rc; 8 | use test::{black_box, Bencher}; 9 | 10 | /// 11 | /// A length function that takes ownership of the input variable 12 | /// 13 | fn length(s: String) -> usize { 14 | s.len() 15 | } 16 | 17 | /// 18 | /// The same length function, taking ownership of a Rc 19 | /// 20 | fn rc_length(s: Rc) -> usize { 21 | s.len() // calls to the wrapped object require no additions 22 | } 23 | 24 | #[bench] 25 | fn bench_string_clone(b: &mut Bencher) { 26 | let s: String = (0..100_000).map(|_| 'a').collect(); 27 | b.iter(|| { 28 | black_box(length(s.clone())); 29 | }); 30 | } 31 | 32 | #[bench] 33 | fn bench_string_rc(b: &mut Bencher) { 34 | let s: String = (0..100_000).map(|_| 'a').collect(); 35 | let rc_s = Rc::new(s); 36 | b.iter(|| { 37 | black_box(rc_length(rc_s.clone())); 38 | }); 39 | } 40 | 41 | 42 | #[test] 43 | fn cloning() { 44 | let s = "abcdef".to_owned(); 45 | assert_eq!(length(s), 6); 46 | // s is now "gone", we can't use it anymore 47 | // therefore we can't use it in a loop either! 48 | // ... unless we clone s - at a cost! (see benchmark) 49 | let s = "abcdef".to_owned(); 50 | 51 | for _ in 0..10 { 52 | // clone is typically an expensive deep copy 53 | assert_eq!(length(s.clone()), 6); 54 | } 55 | } 56 | 57 | #[test] 58 | fn refcounting() { 59 | let s = Rc::new("abcdef".to_owned()); 60 | // we can clone Rc (reference counters) with low cost 61 | assert_eq!(rc_length(s.clone()), 6); 62 | 63 | for _ in 0..10 { 64 | // clone is typically an expensive deep copy 65 | assert_eq!(rc_length(s.clone()), 6); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Chapter02/trait-bounds/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trait-bounds" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/trait-bounds/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | /// 4 | /// An interface that can be used for quick and easy logging 5 | /// 6 | pub trait Loggable: Debug + Sized { 7 | fn log(self) { 8 | println!("{:?}", &self) 9 | } 10 | } 11 | /// 12 | /// A simple print function for printing debug formatted variables 13 | /// 14 | fn log_debug(t: T) { 15 | println!("{:?}", t); 16 | } 17 | 18 | #[derive(Debug)] 19 | struct ArbitraryType { 20 | v: Vec 21 | } 22 | 23 | impl ArbitraryType { 24 | pub fn new() -> ArbitraryType { 25 | ArbitraryType { 26 | v: vec![1,2,3,4] 27 | } 28 | } 29 | } 30 | impl Loggable for ArbitraryType {} 31 | 32 | #[derive(Debug)] 33 | struct AnotherType(usize); 34 | 35 | fn main() { 36 | let a = ArbitraryType::new(); 37 | a.log(); 38 | let b = AnotherType(2); 39 | log_debug(b); 40 | } 41 | -------------------------------------------------------------------------------- /Chapter02/unsafe-ways/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unsafe-ways" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter02/unsafe-ways/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::slice; 3 | 4 | fn split_into_equal_parts(slice: &mut [T], parts: usize) -> Vec<&mut [T]> { 5 | let len = slice.len(); 6 | assert!(parts <= len); 7 | let step = len / parts; 8 | unsafe { 9 | let ptr = slice.as_mut_ptr(); 10 | 11 | (0..step + 1) 12 | .map(|i| { 13 | let offset = (i * step) as isize; 14 | let a = ptr.offset(offset); 15 | slice::from_raw_parts_mut(a, step) 16 | }) 17 | .collect() 18 | } 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | #[test] 25 | fn test_str_to_bytes_horribly_unsafe() { 26 | let bytes = unsafe { std::mem::transmute::<&str, &[u8]>("Going off the menu") }; 27 | assert_eq!( 28 | bytes, 29 | &[ 30 | 71, 111, 105, 110, 103, 32, 111, 102, 102, 32, 116, 104, 101, 32, 109, 101, 110, 31 | 117 32 | ] 33 | ); 34 | } 35 | 36 | #[test] 37 | fn test_split_into_equal_parts() { 38 | let mut v = vec![1, 2, 3, 4, 5, 6]; 39 | assert_eq!( 40 | split_into_equal_parts(&mut v, 3), 41 | &[&[1, 2], &[3, 4], &[5, 6]] 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Chapter03/cargo-hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-hello" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter03/cargo-hello/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /Chapter03/custom-build/.cargo/config: -------------------------------------------------------------------------------- 1 | [cargo-new] 2 | # This is your name/email to place in the `authors` section of a new Cargo.toml 3 | # that is generated. If not present, then `git` will be probed, and if that is 4 | # not present then `$USER` and `$EMAIL` will be used. 5 | name = "..." 6 | email = "..." 7 | 8 | 9 | [build] 10 | jobs = 4 11 | target = "wasm32-unknown-unknown" 12 | target-dir = "out" 13 | -------------------------------------------------------------------------------- /Chapter03/custom-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-build" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | 9 | 10 | # Let's modify the release build 11 | [profile.release] 12 | opt-level = 2 13 | incremental = true # default is false 14 | overflow-checks = true -------------------------------------------------------------------------------- /Chapter03/custom-build/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Overflow! {}", 128 + 129); 3 | } 4 | -------------------------------------------------------------------------------- /Chapter03/external-deps/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "external-deps" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | regex = { git = "https://github.com/rust-lang/regex" } # bleeding edge libraries 9 | 10 | # specifying crate features 11 | serde = { version = "1", features = ["derive"] } 12 | serde_json = "*" # pick whatever version 13 | 14 | 15 | [dev-dependencies] 16 | criterion = "0.2.11" 17 | 18 | 19 | [[bench]] 20 | name = "cooking_with_rust" 21 | harness = false -------------------------------------------------------------------------------- /Chapter03/external-deps/benches/cooking_with_rust.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use criterion::black_box; 5 | use criterion::Criterion; 6 | 7 | pub fn bubble_sort(collection: &[T]) -> Vec { 8 | let mut result: Vec = collection.into(); 9 | for _ in 0..result.len() { 10 | let mut swaps = 0; 11 | for i in 1..result.len() { 12 | if result[i - 1] > result[i] { 13 | result.swap(i - 1, i); 14 | swaps += 1; 15 | } 16 | } 17 | if swaps == 0 { 18 | break; 19 | } 20 | } 21 | result 22 | } 23 | 24 | fn bench_bubble_sort_1k_asc(c: &mut Criterion) { 25 | c.bench_function("Bubble sort 1k descending numbers", |b| { 26 | let items: Vec = (0..1_000).rev().collect(); 27 | b.iter(|| black_box(bubble_sort(&items))) 28 | }); 29 | } 30 | 31 | criterion_group!(benches, bench_bubble_sort_1k_asc); 32 | criterion_main!(benches); 33 | -------------------------------------------------------------------------------- /Chapter03/external-deps/src/main.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use serde::Serialize; 3 | 4 | #[derive(Serialize)] 5 | struct Person { 6 | pub full_name: String, 7 | pub call_me: String, 8 | pub age: usize, 9 | } 10 | 11 | fn main() { 12 | let a_person = Person { 13 | full_name: "John Smith".to_owned(), 14 | call_me: "Smithy".to_owned(), 15 | age: 42, 16 | }; 17 | let serialized = serde_json::to_string(&a_person).unwrap(); 18 | println!("A serialized Person instance: {}", serialized); 19 | 20 | let re = Regex::new(r"(?x)(?P\d{4})-(?P\d{2})-(?P\d{2})").unwrap(); 21 | println!("Some regex parsing:"); 22 | let d = "2019-01-31"; 23 | println!(" Is {} valid? {}", d, re.captures(d).is_some()); 24 | let d = "9999-99-00"; 25 | println!(" Is {} valid? {}", d, re.captures(d).is_some()); 26 | let d = "2019-1-10"; 27 | println!(" Is {} valid? {}", d, re.captures(d).is_some()); 28 | } 29 | -------------------------------------------------------------------------------- /Chapter03/hello_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_world" 3 | version = "0.1.0" 4 | authors = ["Vigneshwer.D "] 5 | 6 | [dependencies] 7 | time = "0.1.12" 8 | regex = "0.1.41" 9 | rand = { git = "https://github.com/rust-lang-nursery/rand.git", rev = "9f35b8e" } 10 | -------------------------------------------------------------------------------- /Chapter03/hello_world/_gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Chapter03/hello_world/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use regex::Regex; 4 | 5 | fn main() { 6 | let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); 7 | println!("Did our date match? {}", re.is_match("2014-01-01")); 8 | } 9 | -------------------------------------------------------------------------------- /Chapter03/my-workspace/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ "a-lib", "a-project" ] 4 | -------------------------------------------------------------------------------- /Chapter03/my-workspace/a-lib/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Chapter03/my-workspace/a-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "a-lib" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dev-dependencies] 8 | rand = "*" -------------------------------------------------------------------------------- /Chapter03/my-workspace/a-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | pub fn stringify(v: &T) -> String { 4 | format!("{:#?}", v) 5 | } 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use rand::prelude::*; 10 | use super::stringify; 11 | 12 | #[test] 13 | fn test_numbers() { 14 | let a_nr: f64 = random(); 15 | assert_eq!(stringify(&a_nr), format!("{:#?}", a_nr)); 16 | assert_eq!(stringify(&1i32), "1"); 17 | assert_eq!(stringify(&1usize), "1"); 18 | assert_eq!(stringify(&1u32), "1"); 19 | assert_eq!(stringify(&1i64), "1"); 20 | } 21 | 22 | #[test] 23 | fn test_sequences() { 24 | assert_eq!(stringify(&vec![0, 1, 2]), "[\n 0,\n 1,\n 2,\n]"); 25 | assert_eq!( 26 | stringify(&(1, 2, 3, 4)), 27 | "(\n 1,\n 2,\n 3,\n 4,\n)" 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Chapter03/my-workspace/a-project/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Chapter03/my-workspace/a-project/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "a-project" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | a-lib = { path = "../a-lib" } 9 | rand = "0.5" -------------------------------------------------------------------------------- /Chapter03/my-workspace/a-project/src/main.rs: -------------------------------------------------------------------------------- 1 | use a_lib::stringify; 2 | use rand::prelude::*; 3 | 4 | fn main() { 5 | println!("{{ \"values\": {}, \"sensor\": {} }}", stringify(&vec![random::(); 6]), stringify(&"temperature")); 6 | } 7 | -------------------------------------------------------------------------------- /Chapter03/publish-crate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bubble-sort" 3 | description = "A quick and non-optimized, cloning version of the bubble sort algorithm. Created as a showcase for publishing crates in the Rust Cookbook 2018" 4 | version = "0.2.0" 5 | authors = ["Claus Matzinger "] 6 | edition = "2018" 7 | homepage = "https://blog.x5ff.xyz" 8 | repository = "https://github.com/PacktPublishing/Rust-Cookbook" 9 | license = "MIT" 10 | categories = [ 11 | "algorithms", 12 | ] 13 | keywords = [ 14 | "cookbook", 15 | "packt", 16 | "x5ff", 17 | "bubble", 18 | "sort", 19 | ] 20 | readme = "README.md" 21 | maintenance = { status = "experimental" } 22 | #azure-devops = { project = "...", pipeline = "...", build="2" } 23 | 24 | -------------------------------------------------------------------------------- /Chapter03/publish-crate/README.md: -------------------------------------------------------------------------------- 1 | # Bubble Sort 2 | 3 | A non-optimal implemenation of the bubble sort algorithm. Best case runtime is `O(n)` - worst case `O(n^2)`. Read more on [wikipedia](https://en.wikipedia.org/wiki/Bubble_sort). 4 | 5 | 6 | This crate was published to support a new version of the Rust Cookbook published by Packt Publishing and written by [Claus Matzinger](https://blog.x5ff.xyz). 7 | 8 | [Source code until the book is published](https://github.com/PacktPublishing/Hands-On-Data-Structures-and-Algorithms-with-Rust/blob/master/Chapter09/src/lib.rs) 9 | 10 | # License 11 | 12 | MIT -------------------------------------------------------------------------------- /Chapter03/publish-crate/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is a non-optimized implementation of the [bubble sort](https://en.wikipedia.org/wiki/Bubble_sort) algorithm for the book Rust Cookbook by Packt. This implementation also clones the input vector. 2 | //! 3 | //! # Examples 4 | //!``` 5 | //!# use bubble_sort::bubble_sort; 6 | //! let v = vec![2, 2, 10, 1, 5, 4, 3]; 7 | //! assert_eq!(bubble_sort(&v), vec![1, 2, 2, 3, 4, 5, 10]); 8 | //!``` 9 | 10 | /// 11 | /// See module level documentation. 12 | /// 13 | pub fn bubble_sort(collection: &[T]) -> Vec { 14 | let mut result: Vec = collection.into(); 15 | for _ in 0..result.len() { 16 | let mut swaps = 0; 17 | for i in 1..result.len() { 18 | if result[i - 1] > result[i] { 19 | result.swap(i - 1, i); 20 | swaps += 1; 21 | } 22 | } 23 | if swaps == 0 { 24 | break; 25 | } 26 | } 27 | result 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::bubble_sort; 33 | #[test] 34 | fn test_bubble_sort() { 35 | assert_eq!(bubble_sort(&vec![9, 8, 7, 6]), vec![6, 7, 8, 9]); 36 | assert_eq!(bubble_sort(&vec![9_f32, 8_f32, 7_f32, 6_f32]), vec![6_f32, 7_f32, 8_f32, 9_f32]); 37 | 38 | assert_eq!(bubble_sort(&vec!['c','f','a','x']), vec!['a', 'c', 'f', 'x']); 39 | 40 | assert_eq!(bubble_sort(&vec![6, 8, 7, 9]), vec![6, 7, 8, 9]); 41 | assert_eq!(bubble_sort(&vec![2, 1, 1, 1, 1]), vec![1, 1, 1, 1, 2]); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Chapter04/actors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actors" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | actix = "^0.8" 9 | rand = "0.5" -------------------------------------------------------------------------------- /Chapter04/actors/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | use actix::prelude::*; 3 | use std::thread; 4 | use std::time::Duration; 5 | use rand::prelude::*; 6 | 7 | const N_THREADS : usize = 3; 8 | 9 | /// 10 | /// A mock sensor function 11 | /// 12 | fn read_sensordata() -> f32 { 13 | random::() * 10.0 14 | } 15 | 16 | #[derive(Debug, Message)] 17 | struct Sensordata(pub u64, pub f32); 18 | 19 | struct DBWriter; 20 | 21 | impl Actor for DBWriter { 22 | type Context = SyncContext; 23 | } 24 | 25 | impl Handler for DBWriter { 26 | type Result = (); 27 | 28 | fn handle(&mut self, msg: Sensordata, _: &mut Self::Context) -> Self::Result { 29 | 30 | // send stuff somewhere and handle the results 31 | println!(" {:?}", msg); 32 | thread::sleep(Duration::from_millis(300)); 33 | } 34 | } 35 | 36 | fn main() -> std::io::Result<()> { 37 | System::run(|| { 38 | println!(">> Press Ctrl-C to stop the program"); 39 | // start multi threaded actor host (arbiter) with 2 threads 40 | let sender = SyncArbiter::start(N_THREADS, || DBWriter); 41 | 42 | // send messages to the actor 43 | for n in 0..10_000 { 44 | let my_timestamp = n as u64; 45 | let data = read_sensordata(); 46 | sender.do_send(Sensordata(my_timestamp, data)); 47 | } 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /Chapter04/async-await/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-await" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | runtime = "0.3.0-alpha.6" 9 | surf = "1.0" 10 | -------------------------------------------------------------------------------- /Chapter04/async-await/src/main.rs: -------------------------------------------------------------------------------- 1 | use surf::Exception; 2 | use surf::http::StatusCode; 3 | 4 | async fn response_code(url: &str) -> Result { 5 | let res = surf::get(url).await?; 6 | Ok(res.status()) 7 | } 8 | 9 | #[runtime::main] 10 | async fn main() -> Result<(), Exception> { 11 | let url = "https://www.rust-lang.org"; 12 | let status = response_code(url).await?; 13 | println!("{} responded with HTTP {}", url, status); 14 | Ok(()) 15 | } -------------------------------------------------------------------------------- /Chapter04/black-white/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "black-white" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter04/black-white/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use std::thread; 3 | use std::time::Duration; 4 | 5 | /// 6 | /// A simple enum with only two variations: black and white 7 | /// 8 | #[derive(Debug)] 9 | enum Shade { 10 | Black, 11 | White, 12 | } 13 | 14 | fn new_painter_thread(data: Arc>>) -> thread::JoinHandle<()> { 15 | thread::spawn(move || loop { 16 | { 17 | // create a scope to release the mutex as quickly as possible 18 | let mut d = data.lock().unwrap(); 19 | if d.len() > 0 { 20 | match d[d.len() - 1] { 21 | Shade::Black => d.push(Shade::White), 22 | Shade::White => d.push(Shade::Black), 23 | } 24 | } else { 25 | d.push(Shade::Black) 26 | } 27 | if d.len() > 5 { 28 | break; 29 | } 30 | } 31 | // slow things down a little 32 | thread::sleep(Duration::from_secs(1)); 33 | }) 34 | } 35 | 36 | fn main() { 37 | let data = Arc::new(Mutex::new(vec![])); 38 | let threads: Vec> = 39 | (0..2).map(|_| new_painter_thread(data.clone())).collect(); 40 | let _: Vec<()> = threads.into_iter().map(|t| t.join().unwrap()).collect(); 41 | println!("Result: {:?}", data); 42 | } 43 | -------------------------------------------------------------------------------- /Chapter04/channels/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "channels" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rand = "^0.5" -------------------------------------------------------------------------------- /Chapter04/channels/src/main.rs: -------------------------------------------------------------------------------- 1 | // Using standard libraries 2 | use std::sync::mpsc::{Sender, Receiver}; 3 | use std::sync::mpsc; 4 | use std::thread; 5 | 6 | use rand::prelude::*; 7 | use std::time::Duration; 8 | 9 | enum ChartValue { 10 | Star(usize), 11 | Pipe(usize) 12 | } 13 | 14 | fn main() { 15 | let (tx, rx): (Sender, Receiver) = mpsc::channel(); 16 | 17 | let pipe_sender = tx.clone(); 18 | 19 | thread::spawn(move || { 20 | loop { 21 | pipe_sender.send(ChartValue::Pipe(random::() % 80)).unwrap(); 22 | thread::sleep(Duration::from_millis(random::() % 800)); 23 | } 24 | }); 25 | 26 | let star_sender = tx.clone(); 27 | thread::spawn(move || { 28 | loop { 29 | star_sender.send(ChartValue::Star(random::() % 80)).unwrap(); 30 | thread::sleep(Duration::from_millis(random::() % 800)); 31 | } 32 | }); 33 | 34 | while let Ok(val) = rx.recv_timeout(Duration::from_secs(3)) { 35 | 36 | println!("{}", match val { 37 | ChartValue::Pipe(v) => "|".repeat(v + 1), 38 | ChartValue::Star(v) => "*".repeat(v + 1) 39 | }); 40 | } 41 | } -------------------------------------------------------------------------------- /Chapter04/child-processes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "child-processes" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter04/child-processes/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use std::process::{Command, Stdio}; 3 | 4 | #[derive(Debug)] 5 | struct SearchResult { 6 | query: String, 7 | results: Vec, 8 | } 9 | 10 | fn search_file(name: String) -> SearchResult { 11 | let ps_child = Command::new("find") 12 | .args(&[".", "-iname", &format!("{}", name)]) 13 | .stdout(Stdio::piped()) 14 | .output() 15 | .expect("Could not spawn process"); 16 | 17 | let results = String::from_utf8_lossy(&ps_child.stdout); 18 | let result_rows: Vec = results 19 | .split("\n") 20 | .map(|e| e.to_string()) 21 | .filter(|s| s.len() > 1) 22 | .collect(); 23 | 24 | SearchResult { 25 | query: name, 26 | results: result_rows, 27 | } 28 | } 29 | 30 | fn process_roundtrip() -> String { 31 | let mut cat_child = Command::new("cat") 32 | .stdin(Stdio::piped()) 33 | .stdout(Stdio::piped()) 34 | .spawn() 35 | .expect("Could not spawn process"); 36 | 37 | let stdin = cat_child.stdin.as_mut().expect("Could not attach to stdin"); 38 | 39 | stdin 40 | .write_all(b"datadatadata") 41 | .expect("Could not write to child process"); 42 | String::from_utf8( 43 | cat_child 44 | .wait_with_output() 45 | .expect("Something went wrong") 46 | .stdout 47 | .as_slice() 48 | .iter() 49 | .cloned() 50 | .collect(), 51 | ) 52 | .unwrap() 53 | } 54 | 55 | fn main() { 56 | println!("Reading from /bin/cat > {:?}", process_roundtrip()); 57 | println!( 58 | "Using 'find' to search for '*.rs': {:?}", 59 | search_file("*.rs".to_owned()) 60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /Chapter04/concurrent-processing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concurrent-processing" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rayon = "1.0.3" 9 | 10 | 11 | [dev-dependencies] 12 | criterion = "0.2.11" 13 | rand = "^0.5" 14 | 15 | 16 | [[bench]] 17 | name = "seq_vs_par" 18 | harness = false -------------------------------------------------------------------------------- /Chapter04/concurrent-processing/benches/seq_vs_par.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | use concurrent_processing::{ssqe, ssqe_sequential, seq_count_alpha_nums, par_count_alpha_nums}; 4 | use criterion::{black_box, Criterion}; 5 | use std::cell::RefCell; 6 | use rand::prelude::*; 7 | 8 | const SEQ_LEN: usize = 1_000_000; 9 | thread_local!(static ITEMS: RefCell<(Vec, Vec)> = { 10 | let y_values: (Vec, Vec) = (0..SEQ_LEN).map(|_| (random::(), random::()) ) 11 | .unzip(); 12 | RefCell::new(y_values) 13 | }); 14 | 15 | 16 | const MAX_CHARS: usize = 100_000; 17 | thread_local!(static CHARS: RefCell = { 18 | let items: String = (0..MAX_CHARS).map(|_| random::()).collect(); 19 | RefCell::new(items) 20 | }); 21 | 22 | fn bench_count_seq(c: &mut Criterion) { 23 | c.bench_function("Counting in sequence", |b| { 24 | CHARS.with(|item| b.iter(|| black_box(seq_count_alpha_nums(&item.borrow())))) 25 | }); 26 | } 27 | 28 | fn bench_count_par(c: &mut Criterion) { 29 | c.bench_function("Counting in parallel", |b| { 30 | CHARS.with(|item| b.iter(|| black_box(par_count_alpha_nums(&item.borrow())))) 31 | }); 32 | } 33 | 34 | 35 | fn bench_seq(c: &mut Criterion) { 36 | c.bench_function("Sequential vector operation", |b| { 37 | ITEMS.with(|y_values| { 38 | let y_borrowed = y_values.borrow(); 39 | b.iter(|| black_box(ssqe_sequential(&y_borrowed.0, &y_borrowed.1))) 40 | }) 41 | }); 42 | } 43 | 44 | fn bench_par(c: &mut Criterion) { 45 | c.bench_function("Parallel vector operation", |b| { 46 | ITEMS.with(|y_values| { 47 | let y_borrowed = y_values.borrow(); 48 | b.iter(|| black_box(ssqe(&y_borrowed.0, &y_borrowed.1))) 49 | }) 50 | }); 51 | } 52 | 53 | criterion_group!(benches, bench_seq, bench_par,bench_count_par, bench_count_seq); 54 | 55 | criterion_main!(benches); 56 | -------------------------------------------------------------------------------- /Chapter04/concurrent-processing/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rayon::prelude::*; 2 | 3 | /// 4 | /// Sum of squared errors, a statistical error measure that squares and sums up the differences between predicted values and their ground truths. 5 | /// 6 | /// 7 | pub fn ssqe(y: &[f32], y_predicted: &[f32]) -> Option { 8 | if y.len() == y_predicted.len() { 9 | let y_iter = y.par_iter(); 10 | let y_pred_iter = y_predicted.par_iter(); 11 | 12 | Some( 13 | y_iter 14 | .zip(y_pred_iter) 15 | .map(|(y, y_pred)| (y - y_pred).powi(2)) 16 | .reduce(|| 0.0, |a, b| a + b), 17 | ) // or sum() 18 | } else { 19 | None 20 | } 21 | } 22 | 23 | 24 | pub fn ssqe_sequential(y: &[f32], y_predicted: &[f32]) -> Option { 25 | if y.len() == y_predicted.len() { 26 | let y_iter = y.iter(); 27 | let y_pred_iter = y_predicted.iter(); 28 | 29 | Some( 30 | y_iter 31 | .zip(y_pred_iter) 32 | .map(|(y, y_pred)| (y - y_pred).powi(2)) 33 | .sum() 34 | ) 35 | } else { 36 | None 37 | } 38 | } 39 | 40 | 41 | pub fn seq_count_alpha_nums(corpus: &str) -> usize { 42 | corpus.chars().filter(|c| c.is_alphanumeric()).count() 43 | } 44 | 45 | 46 | pub fn par_count_alpha_nums(corpus: &str) -> usize { 47 | corpus.par_chars().filter(|c| c.is_alphanumeric()).count() 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use super::*; 53 | 54 | #[test] 55 | fn test_sum_of_sq_errors() { 56 | assert_eq!( 57 | ssqe(&[1.0, 1.0, 1.0, 1.0], &[2.0, 2.0, 2.0, 2.0]), 58 | Some(4.0) 59 | ); 60 | assert_eq!( 61 | ssqe(&[-1.0, -1.0, -1.0, -1.0], &[-2.0, -2.0, -2.0, -2.0]), 62 | Some(4.0) 63 | ); 64 | assert_eq!( 65 | ssqe(&[-1.0, -1.0, -1.0, -1.0], &[2.0, 2.0, 2.0, 2.0]), 66 | Some(36.0) 67 | ); 68 | assert_eq!( 69 | ssqe(&[1.0, 1.0, 1.0, 1.0], &[2.0, 2.0, 2.0, 2.0]), 70 | Some(4.0) 71 | ); 72 | assert_eq!( 73 | ssqe(&[1.0, 1.0, 1.0, 1.0], &[2.0, 2.0, 2.0, 2.0]), 74 | Some(4.0) 75 | ); 76 | } 77 | 78 | 79 | #[test] 80 | fn test_sum_of_sq_errors_seq() { 81 | assert_eq!( 82 | ssqe_sequential(&[1.0, 1.0, 1.0, 1.0], &[2.0, 2.0, 2.0, 2.0]), 83 | Some(4.0) 84 | ); 85 | assert_eq!( 86 | ssqe_sequential(&[-1.0, -1.0, -1.0, -1.0], &[-2.0, -2.0, -2.0, -2.0]), 87 | Some(4.0) 88 | ); 89 | assert_eq!( 90 | ssqe_sequential(&[-1.0, -1.0, -1.0, -1.0], &[2.0, 2.0, 2.0, 2.0]), 91 | Some(36.0) 92 | ); 93 | assert_eq!( 94 | ssqe_sequential(&[1.0, 1.0, 1.0, 1.0], &[2.0, 2.0, 2.0, 2.0]), 95 | Some(4.0) 96 | ); 97 | assert_eq!( 98 | ssqe_sequential(&[1.0, 1.0, 1.0, 1.0], &[2.0, 2.0, 2.0, 2.0]), 99 | Some(4.0) 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Chapter04/immutable-states/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "immutable-states" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter04/immutable-states/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::rc::Rc; 3 | use std::sync::Arc; 4 | use std::sync::mpsc::channel; 5 | 6 | 7 | fn noop(_: T) {} 8 | 9 | fn main() { 10 | let (sender, receiver) = channel::(); 11 | 12 | thread::spawn(move || { 13 | let thread_local_read_only_clone = sender.clone(); 14 | noop(thread_local_read_only_clone); 15 | }); 16 | 17 | 18 | let b = Arc::new(Rc::new(vec![])); 19 | thread::spawn(move || { 20 | let thread_local_read_only_clone = b.clone(); 21 | noop(thread_local_read_only_clone); 22 | }); 23 | /* This won't compile: 24 | 25 | let c = Arc::new(receiver); 26 | thread::spawn(move || { 27 | noop(c.clone()); 28 | });*/ 29 | } -------------------------------------------------------------------------------- /Chapter04/multiple-threads/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multiple-threads" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter04/multiple-threads/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | 4 | /// 5 | /// Doubles each element in the provided chunks in parallel and returns the results. 6 | /// 7 | fn parallel_map(data: Vec>) -> Vec>> { 8 | data.into_iter() 9 | .map(|chunk| thread::spawn(move || chunk.into_iter().map(|c| c * 2).collect())) 10 | .collect() 11 | } 12 | 13 | fn main() { 14 | 15 | // Prepare chunked data 16 | let data = vec![vec![1, 2, 3], vec![4, 4, 5], vec![6, 7, 7]]; 17 | 18 | // work on the data in parallel 19 | let results: Vec = parallel_map(data.clone()) 20 | .into_iter() // an owned iterator over the results 21 | .flat_map(|thread| thread.join().unwrap()) // join each thread 22 | .collect(); // collect the results into a Vec 23 | 24 | // flatten the original data structure 25 | let data: Vec = data.into_iter().flat_map(|e| e).collect(); 26 | 27 | // print the results 28 | println!("{:?} -> {:?}", data, results); 29 | } 30 | -------------------------------------------------------------------------------- /Chapter04/simple-threads/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple-threads" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter04/simple-threads/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::time::Duration; 3 | 4 | /// 5 | /// Starts a thread that waits for 3 seconds before returning. Prints stuff. 6 | /// 7 | fn start_no_shared_data_thread() -> thread::JoinHandle<()> { 8 | thread::spawn(|| { 9 | // since we are not using a parent scope variable in here 10 | // no move is required 11 | println!("Waiting for three seconds."); 12 | thread::sleep(Duration::from_secs(3)); 13 | println!("Done") 14 | }) 15 | } 16 | 17 | 18 | /// 19 | /// Starts a thread moving the function's input parameters 20 | /// 21 | fn start_shared_data_thread(a_number: i32, a_vec: Vec) -> thread::JoinHandle> { 22 | thread::spawn(move || { 23 | // thread::spawn(|| { 24 | print!(" a_vec ---> ["); 25 | for i in a_vec.iter() { 26 | print!(" {} ", i); 27 | } 28 | println!("]"); 29 | println!(" A number from inside the thread: {}", a_number); 30 | a_vec // let's return ownership 31 | }) 32 | } 33 | 34 | fn main() { 35 | let no_move_thread = start_no_shared_data_thread(); 36 | 37 | // do some work in between 38 | for _ in 0..10 { 39 | print!(":"); 40 | } 41 | 42 | println!("Waiting for the thread to finish ... {:?}", no_move_thread.join()); 43 | 44 | let a_number = 42; 45 | let a_vec = vec![1,2,3,4,5]; 46 | 47 | let move_thread = start_shared_data_thread(a_number, a_vec); 48 | 49 | println!("We can still use a Copy-enabled type: {}", a_number); 50 | println!("Waiting for the thread to finish ... {:?}", move_thread.join()); 51 | } -------------------------------------------------------------------------------- /Chapter04/use-rayon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "use-rayon" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rayon = "1.0.3" 9 | 10 | 11 | [dev-dependencies] 12 | criterion = "0.2.11" 13 | rand = "^0.5" 14 | 15 | 16 | [[bench]] 17 | name = "seq_vs_par" 18 | harness = false -------------------------------------------------------------------------------- /Chapter04/use-rayon/benches/seq_vs_par.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | use criterion::black_box; 4 | use criterion::Criterion; 5 | use rand::prelude::*; 6 | use std::cell::RefCell; 7 | use use_rayon::{merge_sort_par, merge_sort_seq}; 8 | 9 | fn random_number_vec(size: usize) -> Vec { 10 | let mut v: Vec = (0..size as i64).collect(); 11 | let mut rng = thread_rng(); 12 | rng.shuffle(&mut v); 13 | v 14 | } 15 | 16 | thread_local!(static ITEMS: RefCell> = RefCell::new(random_number_vec(100_000))); 17 | 18 | fn bench_seq(c: &mut Criterion) { 19 | c.bench_function("10k merge sort (sequential)", |b| { 20 | ITEMS.with(|item| b.iter(|| black_box(merge_sort_seq(&item.borrow())))); 21 | }); 22 | } 23 | 24 | fn bench_par(c: &mut Criterion) { 25 | c.bench_function("10k merge sort (parallel)", |b| { 26 | ITEMS.with(|item| b.iter(|| black_box(merge_sort_par(&item.borrow())))); 27 | }); 28 | } 29 | criterion_group!(benches, bench_seq, bench_par); 30 | 31 | criterion_main!(benches); 32 | -------------------------------------------------------------------------------- /Chapter04/use-rayon/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rayon; 2 | 3 | /// 4 | /// Merges two collections into one. 5 | /// 6 | fn sorted_merge(sorted_l: Vec, sorted_r: Vec) -> Vec { 7 | let mut result: Vec = vec![Default::default(); sorted_l.len() + sorted_r.len()]; 8 | 9 | let (mut i, mut j) = (0, 0); 10 | let mut k = 0; 11 | while i < sorted_l.len() && j < sorted_r.len() { 12 | if sorted_l[i] <= sorted_r[j] { 13 | result[k] = sorted_l[i].clone(); 14 | i += 1; 15 | } else { 16 | result[k] = sorted_r[j].clone(); 17 | j += 1; 18 | } 19 | k += 1; 20 | } 21 | while i < sorted_l.len() { 22 | result[k] = sorted_l[i].clone(); 23 | k += 1; 24 | i += 1; 25 | } 26 | 27 | while j < sorted_r.len() { 28 | result[k] = sorted_r[j].clone(); 29 | k += 1; 30 | j += 1; 31 | } 32 | result 33 | } 34 | 35 | /// 36 | /// Merge sort implementation using parallelism. 37 | /// 38 | pub fn merge_sort_par(collection: &[T]) -> Vec 39 | where 40 | T: PartialOrd + Clone + Default + Send + Sync, 41 | { 42 | if collection.len() > 1 { 43 | let (l, r) = collection.split_at(collection.len() / 2); 44 | let (sorted_l, sorted_r) = rayon::join(|| merge_sort_par(l), || merge_sort_par(r)); 45 | sorted_merge(sorted_l, sorted_r) 46 | } else { 47 | collection.to_vec() 48 | } 49 | } 50 | 51 | 52 | /// 53 | /// Regular, sequential merge sort implementation 54 | /// 55 | pub fn merge_sort_seq(collection: &[T]) -> Vec { 56 | if collection.len() > 1 { 57 | let (l, r) = collection.split_at(collection.len() / 2); 58 | let (sorted_l, sorted_r) = (merge_sort_seq(l), merge_sort_seq(r)); 59 | sorted_merge(sorted_l, sorted_r) 60 | } else { 61 | collection.to_vec() 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | fn test_merge_sort_seq() { 71 | assert_eq!(merge_sort_seq(&vec![9, 8, 7, 6]), vec![6, 7, 8, 9]); 72 | assert_eq!(merge_sort_seq(&vec![6, 8, 7, 9]), vec![6, 7, 8, 9]); 73 | assert_eq!(merge_sort_seq(&vec![2, 1, 1, 1, 1]), vec![1, 1, 1, 1, 2]); 74 | } 75 | 76 | #[test] 77 | fn test_merge_sort_par() { 78 | assert_eq!(merge_sort_par(&vec![9, 8, 7, 6]), vec![6, 7, 8, 9]); 79 | assert_eq!(merge_sort_par(&vec![6, 8, 7, 9]), vec![6, 7, 8, 9]); 80 | assert_eq!(merge_sort_par(&vec![2, 1, 1, 1, 1]), vec![1, 1, 1, 1, 2]); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Chapter05/custom-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-errors" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/custom-errors/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::error::Error; 3 | 4 | #[derive(Debug)] 5 | pub struct MyError { 6 | code: usize, 7 | } 8 | 9 | impl Error for MyError { 10 | fn description(&self) -> &str { 11 | "Occurs when someone makes a mistake" 12 | } 13 | } 14 | 15 | impl fmt::Display for MyError { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | write!(f, "Error code {:#X}", self.code) 18 | } 19 | } 20 | 21 | 22 | fn main() { 23 | println!("Display: {}", MyError{ code: 1535 }); 24 | println!("Debug: {:?}", MyError{ code: 42 }); 25 | println!("Description: {:?}", (MyError{ code: 42 }).description()); 26 | } -------------------------------------------------------------------------------- /Chapter05/exceptional-results/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exceptional-results" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/exceptional-results/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | /// 3 | /// Finds a needle in a haystack, returns -1 on error 4 | /// 5 | pub fn bad_practice_find(needle: &str, haystack: &str) -> i32 { 6 | haystack.find(needle).map(|p| p as i32).unwrap_or(-1) 7 | } 8 | 9 | /// 10 | /// Finds a needle in a haystack, returns None on error 11 | /// 12 | pub fn better_find(needle: &str, haystack: &str) -> Option { 13 | haystack.find(needle) 14 | } 15 | 16 | #[derive(Debug, PartialEq)] 17 | pub enum FindError { 18 | EmptyNeedle, 19 | EmptyHaystack, 20 | NotFound, 21 | } 22 | 23 | /// 24 | /// Finds a needle in a haystack, returns a proper Result 25 | /// 26 | pub fn best_find(needle: &str, haystack: &str) -> Result { 27 | if needle.len() <= 0 { 28 | Err(FindError::EmptyNeedle) 29 | } else if haystack.len() <= 0 { 30 | Err(FindError::EmptyHaystack) 31 | } else { 32 | haystack.find(needle).map_or(Err(FindError::NotFound), |n| Ok(n)) 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | 39 | use super::*; 40 | 41 | #[test] 42 | fn test_bad_practice() { 43 | assert_eq!(bad_practice_find("a", "hello world"), -1); 44 | assert_eq!(bad_practice_find("e", "hello world"), 1); 45 | assert_eq!(bad_practice_find("", "hello world"), 0); 46 | assert_eq!(bad_practice_find("a", ""), -1); 47 | } 48 | 49 | #[test] 50 | fn test_better_practice() { 51 | assert_eq!(better_find("a", "hello world"), None); 52 | assert_eq!(better_find("e", "hello world"), Some(1)); 53 | assert_eq!(better_find("", "hello world"), Some(0)); 54 | assert_eq!(better_find("a", ""), None); 55 | } 56 | 57 | #[test] 58 | fn test_best_practice() { 59 | assert_eq!(best_find("a", "hello world"), Err(FindError::NotFound)); 60 | assert_eq!(best_find("e", "hello world"), Ok(1)); 61 | assert_eq!(best_find("", "hello world"), Err(FindError::EmptyNeedle)); 62 | assert_eq!(best_find("e", ""), Err(FindError::EmptyHaystack)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Chapter05/external-crates/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "external-crates" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | quick-error = "1.2" -------------------------------------------------------------------------------- /Chapter05/external-crates/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate quick_error; 2 | 3 | use std::convert::From; 4 | use std::io; 5 | use std::error::Error; 6 | 7 | quick_error! { 8 | #[derive(Debug)] 9 | pub enum ErrorWrapper { 10 | /// 11 | /// 12 | /// 13 | InvalidDeviceIdError(device_id: usize) { 14 | from(device_id: usize) -> (device_id) 15 | description("No device present with this id, check formatting.") 16 | } 17 | 18 | DeviceNotPresentError(device_id: usize) { 19 | display("Device with id \"{}\" not found", device_id) 20 | } 21 | 22 | UnexpectedDeviceStateError {} 23 | 24 | Io(err: io::Error) { 25 | from(kind: io::ErrorKind) -> (io::Error::from(kind)) 26 | description(err.description()) 27 | display("I/O Error: {}", err) 28 | } 29 | } 30 | } 31 | 32 | 33 | fn main() { 34 | println!("(IOError) {}", ErrorWrapper::from(io::ErrorKind::InvalidData)); 35 | println!("(InvalidDeviceIdError) {}", ErrorWrapper::InvalidDeviceIdError(42)); 36 | println!("(DeviceNotPresentError) {}", ErrorWrapper::DeviceNotPresentError(42)); 37 | println!("(UnexpectedDeviceStateError) {}", ErrorWrapper::UnexpectedDeviceStateError {}); 38 | } 39 | -------------------------------------------------------------------------------- /Chapter05/multiple-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multiple-errors" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/multiple-errors/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::error::Error; 4 | 5 | #[derive(Debug)] 6 | pub struct InvalidDeviceIdError(usize); 7 | #[derive(Debug)] 8 | pub struct DeviceNotPresentError(usize); 9 | #[derive(Debug)] 10 | pub struct UnexpectedDeviceStateError {} 11 | 12 | #[derive(Debug)] 13 | pub enum ErrorWrapper { 14 | Io(io::Error), 15 | Db(InvalidDeviceIdError), 16 | Device(DeviceNotPresentError), 17 | Agent(UnexpectedDeviceStateError) 18 | } 19 | 20 | impl Error for ErrorWrapper { 21 | fn description(&self) -> &str { 22 | match *self { 23 | ErrorWrapper::Io(ref e) => e.description(), 24 | ErrorWrapper::Db(_) | ErrorWrapper::Device(_) => "No device present with this id, check formatting.", 25 | _ => "Unexpected error. Sorry for the inconvenience." 26 | } 27 | } 28 | } 29 | 30 | impl fmt::Display for ErrorWrapper { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | match *self { 33 | ErrorWrapper::Io(ref e) => write!(f, "{} [{}]", e, self.description()), 34 | ErrorWrapper::Db(ref e) => write!(f, "Device with id \"{}\" not found [{}]", e.0, self.description()), 35 | ErrorWrapper::Device(ref e) => write!(f, "Device with id \"{}\" is currently unavailable [{}]", e.0, self.description()), 36 | ErrorWrapper::Agent(_) => write!(f, "Unexpected device state [{}]", self.description()) 37 | } 38 | } 39 | } 40 | 41 | 42 | fn main() { 43 | println!("{}", ErrorWrapper::Io(io::Error::from(io::ErrorKind::InvalidData))); 44 | println!("{}", ErrorWrapper::Db(InvalidDeviceIdError(42))); 45 | println!("{}", ErrorWrapper::Device(DeviceNotPresentError(42))); 46 | println!("{}", ErrorWrapper::Agent(UnexpectedDeviceStateError {})); 47 | } -------------------------------------------------------------------------------- /Chapter05/options-results/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "options-results" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/options-results/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | 4 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] 5 | struct MyError; 6 | 7 | #[test] 8 | fn transposing() { 9 | let this: Result, MyError> = Ok(Some(42)); 10 | let other: Option> = Some(Ok(42)); 11 | assert_eq!(this, other.transpose()); 12 | 13 | let this: Result, MyError> = Err(MyError); 14 | let other: Option> = Some(Err(MyError)); 15 | assert_eq!(this, other.transpose()); 16 | 17 | assert_eq!(None::>.transpose(), Ok(None::)); 18 | } 19 | 20 | 21 | #[test] 22 | fn conversion() { 23 | let opt = Some(42); 24 | assert_eq!(opt.ok_or(MyError), Ok(42)); 25 | 26 | let res: Result = Ok(42); 27 | assert_eq!(res.ok(), opt); 28 | assert_eq!(res.err(), None); 29 | 30 | let opt: Option = None; 31 | assert_eq!(opt.ok_or(MyError), Err(MyError)); 32 | 33 | let res: Result = Err(MyError); 34 | assert_eq!(res.ok(), None); 35 | assert_eq!(res.err(), Some(MyError)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Chapter05/panicking-responsibly/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panicking-responsibly" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/panicking-responsibly/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | 4 | #[test] 5 | #[should_panic] 6 | fn test_regular_panic() { 7 | panic!(); 8 | } 9 | 10 | #[test] 11 | #[should_panic(expected = "Everything is lost!")] 12 | fn test_panic_message() { 13 | panic!("Everything is lost!"); 14 | } 15 | 16 | #[test] 17 | #[should_panic(expected = "String formatting also works")] 18 | fn test_panic_format() { 19 | panic!("{} formatting also works.", "String"); 20 | } 21 | 22 | #[test] 23 | #[should_panic] 24 | fn test_panic_return_value() { 25 | panic!(42); 26 | } 27 | 28 | #[test] 29 | #[should_panic] 30 | fn test_assert() { 31 | // assert a condition and panic if it's not met 32 | assert!(1 == 2); 33 | } 34 | 35 | #[test] 36 | #[should_panic] 37 | fn test_assert_eq() { 38 | // assert a condition and panic if it's not met 39 | assert_eq!(1, 2); 40 | } 41 | 42 | #[test] 43 | #[should_panic] 44 | fn test_assert_neq() { 45 | // assert a condition and panic if it's not met 46 | assert_ne!(1, 1); 47 | } 48 | 49 | #[test] 50 | #[should_panic] 51 | fn test_unwrap() { 52 | // panics if "None" 53 | None::.unwrap(); 54 | } 55 | 56 | #[test] 57 | #[should_panic(expected = "Unwrap with a message")] 58 | fn test_expect() { 59 | None::.expect("Unwrap with a message"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Chapter05/resilient-programming/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "resilient-programming" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/resilient-programming/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::io; 3 | 4 | fn print_file_contents_qm(filename: &str) -> Result<(), io::Error> { 5 | let contents = fs::read_to_string(filename)?; 6 | println!("File contents, external fn: {:?}", contents); 7 | Ok(()) 8 | } 9 | 10 | fn main() -> Result<(), std::io::Error> { 11 | println!("Ok: {:?}", print_file_contents_qm("testfile.txt")); 12 | println!("Err: {:?}", print_file_contents_qm("not-a-file")); 13 | 14 | let contents = fs::read_to_string("testfile.txt")?; 15 | println!("File contents, main fn: {:?}", contents); 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /Chapter05/resilient-programming/testfile.txt: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /Chapter05/seamless-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "seamless-errors" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter05/seamless-errors/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | #[cfg(test)] 3 | mod tests { 4 | 5 | #[test] 6 | fn positive_results() { 7 | 8 | let ok: Result = Ok(42); 9 | 10 | assert_eq!(ok.and_then(|r| Ok(r + 1)), Ok(43)); 11 | assert_eq!(ok.map(|r| r + 1), Ok(43)); 12 | 13 | // Boolean operations with Results. Take a close look at what's returned 14 | assert_eq!(ok.and(Ok(43)), Ok(43)); 15 | let err: Result = Err(-42.0); 16 | assert_eq!(ok.and(err), err); 17 | assert_eq!(ok.or(err), ok); 18 | } 19 | 20 | #[test] 21 | fn negative_results() { 22 | let err: Result = Err(-42.0); 23 | let ok: Result = Ok(-41); 24 | 25 | assert_eq!(err.or_else(|r| Ok(r as i32 + 1)), ok); 26 | assert_eq!(err.map(|r| r + 1), Err(-42.0)); 27 | assert_eq!(err.map_err(|r| r + 1.0), Err(-41.0)); 28 | 29 | let err2: Result = Err(43.0); 30 | let ok: Result = Ok(42); 31 | assert_eq!(err.and(err2), err); 32 | assert_eq!(err.and(ok), err); 33 | assert_eq!(err.or(ok), ok); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Chapter06/code-generation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-designators" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter06/code-generation/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | macro_rules! default_enum { 4 | ($name: ident, $($variant: ident => $val:expr),+) => { 5 | #[derive(Eq, PartialEq, Clone, Debug)] 6 | pub enum $name { 7 | Invalid, 8 | $($variant = $val),+ 9 | } 10 | 11 | impl Default for $name { 12 | fn default() -> Self { $name::Invalid } 13 | } 14 | }; 15 | } 16 | 17 | // Declare a function in a macro! 18 | macro_rules! make_fn { 19 | ($i: ident, $body: block) => { 20 | fn $i () $body 21 | } 22 | } 23 | 24 | 25 | // Repeat the statement that was passed in n times 26 | macro_rules! n_times { 27 | // `()` indicates that the macro takes no argument. 28 | ($n: expr, $f: block) => { 29 | for _ in 0..$n { 30 | $f() 31 | } 32 | } 33 | } 34 | 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | #[test] 39 | fn test_n_times() { 40 | let mut i = 0; 41 | n_times!(5, { 42 | i += 1; 43 | }); 44 | assert_eq!(i, 5); 45 | } 46 | 47 | #[test] 48 | #[should_panic] 49 | fn test_failing_make_fn() { 50 | make_fn!(fail, {assert!(false)}); 51 | fail(); 52 | } 53 | 54 | #[test] 55 | fn test_make_fn() { 56 | make_fn!(fail, {assert!(false)}); 57 | // nothing happens if we don't call the function 58 | } 59 | 60 | #[test] 61 | fn test_default_enum() { 62 | default_enum!(Colors, Red => 0xFF0000, Blue => 0x0000FF); 63 | let color: Colors = Default::default(); 64 | assert_eq!(color, Colors::Invalid); 65 | assert_eq!(Colors::Red as i32, 0xFF0000); 66 | assert_eq!(Colors::Blue as i32, 0x0000FF); 67 | 68 | } 69 | 70 | 71 | } -------------------------------------------------------------------------------- /Chapter06/custom-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-macros" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter06/custom-macros/src/main.rs: -------------------------------------------------------------------------------- 1 | mod macros { 2 | // Exporting macros to other modules 3 | #[macro_export] 4 | macro_rules! two_plus_two { 5 | () => { 2 + 2 }; 6 | } 7 | } 8 | 9 | // A simple macro without arguments 10 | macro_rules! one_plus_one { 11 | () => { 1 + 1 }; 12 | } 13 | 14 | 15 | // A simple pattern matching argument 16 | macro_rules! one_and_one { 17 | (plus) => { 1 + 1 }; 18 | (minus) => { 1 - 1 }; 19 | (mult) => { 1 * 1 }; 20 | } 21 | 22 | fn main() { 23 | 24 | println!("Did the pattern match? '{}'", strange_patterns!(The pattern must match precisely)); 25 | 26 | make_fn!(awesome_fn, { println!("Called awesome_fn")}); 27 | awesome_fn(); 28 | 29 | println!("1 + 1 = {}", one_plus_one!()); 30 | println!("1 + 1 = {}", one_and_one!(plus)); 31 | println!("1 - 1 = {}", one_and_one!(minus)); 32 | println!("1 * 1 = {}", one_and_one!(mult)); 33 | 34 | println!("2 + 2 = {}", two_plus_two!()); 35 | 36 | n_times!({10}, || println!("Hello World!")); 37 | // n_times!(-10, || println!("negative?")); 38 | // n_times!(2.2, || println!("floats?")); 39 | } 40 | -------------------------------------------------------------------------------- /Chapter06/dry-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dry-macros" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /Chapter06/dry-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Mul, Sub}; 2 | 3 | macro_rules! assert_equal_len { 4 | // The `tt` (token tree) designator is used for 5 | // operators and tokens. 6 | ($a:ident, $b: ident, $func:ident) => ( 7 | assert_eq!($a.len(), $b.len(), 8 | "{:?}: dimension mismatch: {:?} {:?}", 9 | stringify!($func), 10 | ($a.len(),), 11 | ($b.len(),)); 12 | ) 13 | } 14 | 15 | macro_rules! op { 16 | ($func:ident, $bound:ident, $method:ident) => ( 17 | pub fn $func + Copy>(xs: &mut Vec, ys: &Vec) { 18 | assert_equal_len!(xs, ys, $func); 19 | 20 | for (x, y) in xs.iter_mut().zip(ys.iter()) { 21 | *x = $bound::$method(*x, *y); 22 | } 23 | } 24 | ) 25 | } 26 | 27 | // Implement `add_assign`, `mul_assign`, and `sub_assign` functions. 28 | op!(add_assign, Add, add); 29 | op!(mul_assign, Mul, mul); 30 | op!(sub_assign, Sub, sub); 31 | 32 | #[cfg(test)] 33 | mod test { 34 | 35 | use std::iter; 36 | macro_rules! test { 37 | ($func: ident, $x:expr, $y:expr, $z:expr) => { 38 | #[test] 39 | fn $func() { 40 | for size in 0usize..10 { 41 | let mut x: Vec<_> = iter::repeat($x).take(size).collect(); 42 | let y: Vec<_> = iter::repeat($y).take(size).collect(); 43 | let z: Vec<_> = iter::repeat($z).take(size).collect(); 44 | 45 | super::$func(&mut x, &y); 46 | 47 | assert_eq!(x, z); 48 | } 49 | } 50 | } 51 | } 52 | 53 | // Test `add_assign`, `mul_assign` and `sub_assign` 54 | test!(add_assign, 1u32, 2u32, 3u32); 55 | test!(mul_assign, 2u32, 3u32, 6u32); 56 | test!(sub_assign, 3u32, 2u32, 1u32); 57 | } 58 | -------------------------------------------------------------------------------- /Chapter06/macro-overloading/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macro-overloading" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /Chapter06/macro-overloading/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | 4 | macro_rules! print_debug { 5 | (stdout, $($o:expr),*) => { 6 | $(print!("{:?}", $o));*; 7 | println!(); 8 | }; 9 | (error, $($o:expr),*) => { 10 | $(eprint!("{:?}", $o));*; 11 | eprintln!(); 12 | }; 13 | ($stream:expr, $($o:expr),*) => { 14 | $(let _ = write!($stream, "{:?}", $o));*; 15 | let _ = writeln!($stream); 16 | } 17 | } 18 | 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use std::io::Write; 23 | 24 | #[test] 25 | fn test_printer() { 26 | print_debug!(error, "hello std err"); 27 | print_debug!(stdout, "hello std out"); 28 | let mut v = vec![]; 29 | print_debug!(&mut v, "a"); 30 | assert_eq!(v, vec![34, 97, 34, 10]); 31 | 32 | } 33 | 34 | macro_rules! mock { 35 | ($type: ty, $name: ident, $ret_val: ty, $val: block) => { 36 | pub trait $name { 37 | fn $name(&self) -> $ret_val; 38 | } 39 | 40 | impl $name for $type { 41 | fn $name(&self) -> $ret_val $val 42 | } 43 | }; 44 | ($name: ident, $($variant: ident => $type:ty),+) => { 45 | #[derive(PartialEq, Clone, Debug)] 46 | struct $name { 47 | $(pub $variant: $type),+ 48 | } 49 | }; 50 | } 51 | 52 | mock!(String, hello, &'static str, { "Hi!" }); 53 | mock!(HelloWorld, greeting => String, when => u64); 54 | 55 | #[test] 56 | fn test_mock() { 57 | let mystr = "Hello".to_owned(); 58 | assert_eq!(mystr.hello(), "Hi!"); 59 | 60 | let g = HelloWorld { greeting: "Hello World".to_owned(), when: 1560887098 }; 61 | 62 | assert_eq!(g.greeting, "Hello World"); 63 | assert_eq!(g.when, 1560887098); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Chapter06/matching/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "matching" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter06/matching/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables, unused_macros)] 2 | 3 | macro_rules! strange_patterns { 4 | (The pattern must match precisely) => { "Text" }; 5 | (42) => { "Numeric" }; 6 | (;<=,<=;) => { "Alpha" }; 7 | } 8 | 9 | macro_rules! compare { 10 | ($x:literal => $y:block) => { $x == $y }; 11 | } 12 | 13 | #[derive(Debug)] 14 | pub struct Response(usize); 15 | pub fn register_handler(method: &str, path: &str, handler: &Fn() -> Response ) { 16 | // do stuff here 17 | } 18 | 19 | // Example of more sophisticated pattern matching 20 | macro_rules! web { 21 | (GET $path:literal => $b:block) => { register_handler("GET", $path, &|| $b) }; 22 | (POST $path:literal => $b:block) => { register_handler("POST", $path, &|| $b) }; 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | 29 | #[test] 30 | fn test_web() { 31 | // this doesn't actually do anything, so as long as it compiles, it's fine 32 | web!(GET "/" => { Response(200) }); 33 | web!(POST "/" => { Response(403) }); 34 | } 35 | 36 | 37 | #[test] 38 | fn test_strange_patterns() { 39 | assert_eq!(strange_patterns!(The pattern must match precisely), "Text"); 40 | assert_eq!(strange_patterns!(42), "Numeric"); 41 | assert_eq!(strange_patterns!(;<=,<=;), "Alpha"); 42 | } 43 | 44 | #[test] 45 | fn test_compare() { 46 | assert!(compare!(1 => { 1 })); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Chapter06/parameter-ranges/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parameter-ranges" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /Chapter06/parameter-ranges/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | 4 | macro_rules! set { 5 | ( $( $item:expr ),* ) => { 6 | { 7 | let mut s = HashSet::new(); 8 | $( 9 | s.insert($item); 10 | )* 11 | s 12 | } 13 | }; 14 | } 15 | 16 | macro_rules! dto { 17 | ($name: ident, $($variant: ident => $type:ty),+) => { 18 | #[derive(PartialEq, Clone, Debug)] 19 | pub struct $name { 20 | $(pub $variant: $type),+ 21 | } 22 | 23 | impl $name { 24 | pub fn new($($variant:$type),+) -> Self { 25 | $name { 26 | $($variant: $variant),+ 27 | } 28 | } 29 | } 30 | }; 31 | } 32 | 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use std::collections::HashSet; 37 | 38 | 39 | #[test] 40 | fn test_set() { 41 | let actual = set!("a", "b", "c", "a"); 42 | let mut desired = HashSet::new(); 43 | desired.insert("a"); 44 | desired.insert("b"); 45 | desired.insert("c"); 46 | assert_eq!(actual, desired); 47 | } 48 | 49 | #[test] 50 | fn test_dto() { 51 | dto!(Sensordata, value => f32, timestamp => u64); 52 | let s = Sensordata::new(1.23f32, 123456); 53 | assert_eq!(s.value, 1.23f32); 54 | assert_eq!(s.timestamp, 123456); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Chapter06/std-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "std-macros" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /Chapter06/std-macros/src/a.txt: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /Chapter06/std-macros/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Debug)] 3 | pub struct MyStruct(usize); 4 | 5 | 6 | fn main() { 7 | println!("Hello, world!"); 8 | 9 | println!("a vec: {:?}", vec!{1, 2, 3}); 10 | println!("concat: {}", concat!(0, 'x', "5ff")); 11 | println!("MyStruct stringified: {}", stringify!(MyStruct(10))); 12 | println!("some random word stringified: {}", stringify!(helloworld)); 13 | 14 | println!("Running on Windows? {}", cfg!(windows)); 15 | println!("From a file: {}", include_str!("a.txt")); 16 | println!("$PATH: {:?}", option_env!("lala")); // evaluated at compile time! 17 | 18 | eprintln!("Oh no!"); 19 | debug_assert!(true); 20 | } -------------------------------------------------------------------------------- /Chapter07/bindgen/rust-tinyexpr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-tinyexpr" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | 10 | [build-dependencies] 11 | bindgen = "0.49" -------------------------------------------------------------------------------- /Chapter07/bindgen/rust-tinyexpr/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::env::var; 3 | 4 | use std::path::PathBuf; 5 | 6 | const HEADER_FILE_NAME: &'static str = "../tinyexpr/tinyexpr.h"; 7 | 8 | fn main() { 9 | let project_dir = var("CARGO_MANIFEST_DIR").unwrap(); 10 | 11 | println!("cargo:rustc-link-search={}/../tinyexpr/", project_dir); 12 | println!("cargo:rustc-link-lib=static=tinyexpr"); 13 | 14 | if cfg!(target_env = "msvc") { 15 | println!("cargo:rustc-link-lib=static=legacy_stdio_definitions"); 16 | } 17 | 18 | let bindings = bindgen::Builder::default() 19 | .header(HEADER_FILE_NAME) 20 | .generate() 21 | .expect("Error generating bindings"); 22 | 23 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 24 | bindings 25 | .write_to_file(out_path.join("bindings.rs")) 26 | .expect("Error writing bindings"); 27 | } -------------------------------------------------------------------------------- /Chapter07/bindgen/rust-tinyexpr/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | use std::ffi::CString; 5 | 6 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 7 | 8 | 9 | fn main() { 10 | let expr = "sqrt(5^2+7^2+11^2+(8-2)^2)".to_owned(); 11 | let result = unsafe { 12 | te_interp(CString::new(expr.clone()).unwrap().into_raw(), 0 as *mut i32) 13 | }; 14 | println!("{} = {}", expr, result); 15 | } -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: 4 | - clang 5 | - gcc 6 | 7 | script: make 8 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | A core strength of TinyExpr is that it is small and simple. This makes it easy 2 | to add new features. However, if we keep adding new features, it'll no longer 3 | be small or simple. In other words, each new feature corrodes away at the core 4 | strength of TinyExpr. 5 | 6 | If you want to add a new feature, and you expect me to merge it, please discuss 7 | it with me before you go to that work. Open an issue at 8 | https://github.com/codeplea/tinyexpr and let us know what you're proposing. 9 | 10 | Bug fixes are always welcome and appreciated, of course. 11 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/LICENSE: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | Copyright (C) 2015, 2016 Lewis Van Winkle 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgement in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | 21 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/Makefile: -------------------------------------------------------------------------------- 1 | CCFLAGS = -ansi -Wall -Wshadow -O2 2 | LFLAGS = -lm 3 | 4 | .PHONY = all clean 5 | 6 | all: test test_pr bench example example2 example3 7 | 8 | 9 | test: test.c tinyexpr.c 10 | $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) 11 | ./$@ 12 | 13 | test_pr: test.c tinyexpr.c 14 | $(CC) $(CCFLAGS) -DTE_POW_FROM_RIGHT -DTE_NAT_LOG -o $@ $^ $(LFLAGS) 15 | ./$@ 16 | 17 | bench: benchmark.o tinyexpr.o 18 | $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) 19 | 20 | example: example.o tinyexpr.o 21 | $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) 22 | 23 | example2: example2.o tinyexpr.o 24 | $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) 25 | 26 | example3: example3.o tinyexpr.o 27 | $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) 28 | 29 | .c.o: 30 | $(CC) -c $(CCFLAGS) $< -o $@ 31 | 32 | clean: 33 | rm -f *.o *.exe example example2 example3 bench test_pr test 34 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/benchmark.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TINYEXPR - Tiny recursive descent parser and evaluation engine in C 3 | * 4 | * Copyright (c) 2015, 2016 Lewis Van Winkle 5 | * 6 | * http://CodePlea.com 7 | * 8 | * This software is provided 'as-is', without any express or implied 9 | * warranty. In no event will the authors be held liable for any damages 10 | * arising from the use of this software. 11 | * 12 | * Permission is granted to anyone to use this software for any purpose, 13 | * including commercial applications, and to alter it and redistribute it 14 | * freely, subject to the following restrictions: 15 | * 16 | * 1. The origin of this software must not be misrepresented; you must not 17 | * claim that you wrote the original software. If you use this software 18 | * in a product, an acknowledgement in the product documentation would be 19 | * appreciated but is not required. 20 | * 2. Altered source versions must be plainly marked as such, and must not be 21 | * misrepresented as being the original software. 22 | * 3. This notice may not be removed or altered from any source distribution. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include "tinyexpr.h" 29 | 30 | 31 | 32 | #define loops 10000 33 | 34 | 35 | 36 | typedef double (*function1)(double); 37 | 38 | void bench(const char *expr, function1 func) { 39 | int i, j; 40 | volatile double d; 41 | double tmp; 42 | clock_t start; 43 | 44 | te_variable lk = {"a", &tmp}; 45 | 46 | printf("Expression: %s\n", expr); 47 | 48 | printf("native "); 49 | start = clock(); 50 | d = 0; 51 | for (j = 0; j < loops; ++j) 52 | for (i = 0; i < loops; ++i) { 53 | tmp = i; 54 | d += func(tmp); 55 | } 56 | const int nelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; 57 | 58 | /*Million floats per second input.*/ 59 | printf(" %.5g", d); 60 | if (nelapsed) 61 | printf("\t%5dms\t%5dmfps\n", nelapsed, loops * loops / nelapsed / 1000); 62 | else 63 | printf("\tinf\n"); 64 | 65 | 66 | 67 | 68 | printf("interp "); 69 | te_expr *n = te_compile(expr, &lk, 1, 0); 70 | start = clock(); 71 | d = 0; 72 | for (j = 0; j < loops; ++j) 73 | for (i = 0; i < loops; ++i) { 74 | tmp = i; 75 | d += te_eval(n); 76 | } 77 | const int eelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; 78 | te_free(n); 79 | 80 | /*Million floats per second input.*/ 81 | printf(" %.5g", d); 82 | if (eelapsed) 83 | printf("\t%5dms\t%5dmfps\n", eelapsed, loops * loops / eelapsed / 1000); 84 | else 85 | printf("\tinf\n"); 86 | 87 | 88 | printf("%.2f%% longer\n", (((double)eelapsed / nelapsed) - 1.0) * 100.0); 89 | 90 | 91 | printf("\n"); 92 | } 93 | 94 | 95 | double a5(double a) { 96 | return a+5; 97 | } 98 | 99 | double a52(double a) { 100 | return (a+5)*2; 101 | } 102 | 103 | double a10(double a) { 104 | return a+(5*2); 105 | } 106 | 107 | double as(double a) { 108 | return sqrt(pow(a, 1.5) + pow(a, 2.5)); 109 | } 110 | 111 | double al(double a) { 112 | return (1/(a+1)+2/(a+2)+3/(a+3)); 113 | } 114 | 115 | int main(int argc, char *argv[]) 116 | { 117 | 118 | bench("sqrt(a^1.5+a^2.5)", as); 119 | bench("a+5", a5); 120 | bench("a+(5*2)", a10); 121 | bench("(a+5)*2", a52); 122 | bench("(1/(a+1)+2/(a+2)+3/(a+3))", al); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/doc/e1.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "+" -> "sin"; 3 | "+" -> div; 4 | "sin" -> "x"; 5 | div -> "1"; 6 | div -> "4"; 7 | div [label="÷"] 8 | } 9 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/doc/e1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter07/bindgen/tinyexpr/doc/e1.png -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/doc/e2.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "+" -> "sin"; 3 | "+" -> "0.25"; 4 | "sin" -> "x"; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/doc/e2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter07/bindgen/tinyexpr/doc/e2.png -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/example.c: -------------------------------------------------------------------------------- 1 | #include "tinyexpr.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)"; 7 | double r = te_interp(c, 0); 8 | printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/example2.c: -------------------------------------------------------------------------------- 1 | #include "tinyexpr.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | if (argc < 2) { 7 | printf("Usage: example2 \"expression\"\n"); 8 | return 0; 9 | } 10 | 11 | const char *expression = argv[1]; 12 | printf("Evaluating:\n\t%s\n", expression); 13 | 14 | /* This shows an example where the variables 15 | * x and y are bound at eval-time. */ 16 | double x, y; 17 | te_variable vars[] = {{"x", &x}, {"y", &y}}; 18 | 19 | /* This will compile the expression and check for errors. */ 20 | int err; 21 | te_expr *n = te_compile(expression, vars, 2, &err); 22 | 23 | if (n) { 24 | /* The variables can be changed here, and eval can be called as many 25 | * times as you like. This is fairly efficient because the parsing has 26 | * already been done. */ 27 | x = 3; y = 4; 28 | const double r = te_eval(n); printf("Result:\n\t%f\n", r); 29 | 30 | te_free(n); 31 | } else { 32 | /* Show the user where the error is at. */ 33 | printf("\t%*s^\nError near here", err-1, ""); 34 | } 35 | 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/example3.c: -------------------------------------------------------------------------------- 1 | #include "tinyexpr.h" 2 | #include 3 | 4 | 5 | /* An example of calling a C function. */ 6 | double my_sum(double a, double b) { 7 | printf("Called C function with %f and %f.\n", a, b); 8 | return a + b; 9 | } 10 | 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | te_variable vars[] = { 15 | {"mysum", my_sum, TE_FUNCTION2} 16 | }; 17 | 18 | const char *expression = "mysum(5, 6)"; 19 | printf("Evaluating:\n\t%s\n", expression); 20 | 21 | int err; 22 | te_expr *n = te_compile(expression, vars, 1, &err); 23 | 24 | if (n) { 25 | const double r = te_eval(n); 26 | printf("Result:\n\t%f\n", r); 27 | te_free(n); 28 | } else { 29 | /* Show the user where the error is at. */ 30 | printf("\t%*s^\nError near here", err-1, ""); 31 | } 32 | 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/minctest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * MINCTEST - Minimal C Test Library - 0.1 4 | * 5 | * Copyright (c) 2014, 2015 Lewis Van Winkle 6 | * 7 | * http://CodePlea.com 8 | * 9 | * This software is provided 'as-is', without any express or implied 10 | * warranty. In no event will the authors be held liable for any damages 11 | * arising from the use of this software. 12 | * 13 | * Permission is granted to anyone to use this software for any purpose, 14 | * including commercial applications, and to alter it and redistribute it 15 | * freely, subject to the following restrictions: 16 | * 17 | * 1. The origin of this software must not be misrepresented; you must not 18 | * claim that you wrote the original software. If you use this software 19 | * in a product, an acknowledgement in the product documentation would be 20 | * appreciated but is not required. 21 | * 2. Altered source versions must be plainly marked as such, and must not be 22 | * misrepresented as being the original software. 23 | * 3. This notice may not be removed or altered from any source distribution. 24 | * 25 | */ 26 | 27 | 28 | 29 | /* 30 | * MINCTEST - Minimal testing library for C 31 | * 32 | * 33 | * Example: 34 | * 35 | * void test1() { 36 | * lok('a' == 'a'); 37 | * } 38 | * 39 | * void test2() { 40 | * lequal(5, 6); 41 | * lfequal(5.5, 5.6); 42 | * } 43 | * 44 | * int main() { 45 | * lrun("test1", test1); 46 | * lrun("test2", test2); 47 | * lresults(); 48 | * return lfails != 0; 49 | * } 50 | * 51 | * 52 | * 53 | * Hints: 54 | * All functions/variables start with the letter 'l'. 55 | * 56 | */ 57 | 58 | 59 | #ifndef __MINCTEST_H__ 60 | #define __MINCTEST_H__ 61 | 62 | #include 63 | #include 64 | #include 65 | 66 | 67 | /* How far apart can floats be before we consider them unequal. */ 68 | #define LTEST_FLOAT_TOLERANCE 0.001 69 | 70 | 71 | /* Track the number of passes, fails. */ 72 | /* NB this is made for all tests to be in one file. */ 73 | static int ltests = 0; 74 | static int lfails = 0; 75 | 76 | 77 | /* Display the test results. */ 78 | #define lresults() do {\ 79 | if (lfails == 0) {\ 80 | printf("ALL TESTS PASSED (%d/%d)\n", ltests, ltests);\ 81 | } else {\ 82 | printf("SOME TESTS FAILED (%d/%d)\n", ltests-lfails, ltests);\ 83 | }\ 84 | } while (0) 85 | 86 | 87 | /* Run a test. Name can be any string to print out, test is the function name to call. */ 88 | #define lrun(name, test) do {\ 89 | const int ts = ltests;\ 90 | const int fs = lfails;\ 91 | const clock_t start = clock();\ 92 | printf("\t%-14s", name);\ 93 | test();\ 94 | printf("pass:%2d fail:%2d %4dms\n",\ 95 | (ltests-ts)-(lfails-fs), lfails-fs,\ 96 | (int)((clock() - start) * 1000 / CLOCKS_PER_SEC));\ 97 | } while (0) 98 | 99 | 100 | /* Assert a true statement. */ 101 | #define lok(test) do {\ 102 | ++ltests;\ 103 | if (!(test)) {\ 104 | ++lfails;\ 105 | printf("%s:%d error \n", __FILE__, __LINE__);\ 106 | }} while (0) 107 | 108 | 109 | /* Assert two integers are equal. */ 110 | #define lequal(a, b) do {\ 111 | ++ltests;\ 112 | if ((a) != (b)) {\ 113 | ++lfails;\ 114 | printf("%s:%d (%d != %d)\n", __FILE__, __LINE__, (a), (b));\ 115 | }} while (0) 116 | 117 | 118 | /* Assert two floats are equal (Within LTEST_FLOAT_TOLERANCE). */ 119 | #define lfequal(a, b) do {\ 120 | ++ltests;\ 121 | const double __LF_COMPARE = fabs((double)(a)-(double)(b));\ 122 | if (__LF_COMPARE > LTEST_FLOAT_TOLERANCE || (__LF_COMPARE != __LF_COMPARE)) {\ 123 | ++lfails;\ 124 | printf("%s:%d (%f != %f)\n", __FILE__, __LINE__, (double)(a), (double)(b));\ 125 | }} while (0) 126 | 127 | 128 | #endif /*__MINCTEST_H__*/ 129 | -------------------------------------------------------------------------------- /Chapter07/bindgen/tinyexpr/tinyexpr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TINYEXPR - Tiny recursive descent parser and evaluation engine in C 3 | * 4 | * Copyright (c) 2015-2018 Lewis Van Winkle 5 | * 6 | * http://CodePlea.com 7 | * 8 | * This software is provided 'as-is', without any express or implied 9 | * warranty. In no event will the authors be held liable for any damages 10 | * arising from the use of this software. 11 | * 12 | * Permission is granted to anyone to use this software for any purpose, 13 | * including commercial applications, and to alter it and redistribute it 14 | * freely, subject to the following restrictions: 15 | * 16 | * 1. The origin of this software must not be misrepresented; you must not 17 | * claim that you wrote the original software. If you use this software 18 | * in a product, an acknowledgement in the product documentation would be 19 | * appreciated but is not required. 20 | * 2. Altered source versions must be plainly marked as such, and must not be 21 | * misrepresented as being the original software. 22 | * 3. This notice may not be removed or altered from any source distribution. 23 | */ 24 | 25 | #ifndef __TINYEXPR_H__ 26 | #define __TINYEXPR_H__ 27 | 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | 34 | 35 | typedef struct te_expr { 36 | int type; 37 | union {double value; const double *bound; const void *function;}; 38 | void *parameters[1]; 39 | } te_expr; 40 | 41 | 42 | enum { 43 | TE_VARIABLE = 0, 44 | 45 | TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, 46 | TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7, 47 | 48 | TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3, 49 | TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7, 50 | 51 | TE_FLAG_PURE = 32 52 | }; 53 | 54 | typedef struct te_variable { 55 | const char *name; 56 | const void *address; 57 | int type; 58 | void *context; 59 | } te_variable; 60 | 61 | 62 | 63 | /* Parses the input expression, evaluates it, and frees it. */ 64 | /* Returns NaN on error. */ 65 | double te_interp(const char *expression, int *error); 66 | 67 | /* Parses the input expression and binds variables. */ 68 | /* Returns NULL on error. */ 69 | te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error); 70 | 71 | /* Evaluates the expression. */ 72 | double te_eval(const te_expr *n); 73 | 74 | /* Prints debugging information on the syntax tree. */ 75 | void te_print(const te_expr *n); 76 | 77 | /* Frees the expression. */ 78 | /* This is safe to call on NULL pointers. */ 79 | void te_free(te_expr *n); 80 | 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | 86 | #endif /*__TINYEXPR_H__*/ 87 | -------------------------------------------------------------------------------- /Chapter07/browser-rust/rust-digest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-digest" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "digest" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | sha2 = "0.8" 13 | wasm-bindgen = "0.2.48" 14 | 15 | [dependencies.web-sys] 16 | version = "0.3.25" 17 | features = [ 18 | 'Document', 19 | 'Element', 20 | 'HtmlElement', 21 | 'Node', 22 | 'Window', 23 | ] 24 | -------------------------------------------------------------------------------- /Chapter07/browser-rust/rust-digest/src/lib.rs: -------------------------------------------------------------------------------- 1 | use sha2::{Sha256, Digest}; 2 | use wasm_bindgen::prelude::*; 3 | 4 | fn hex_digest(data: &str) -> String { 5 | let mut hasher = Sha256::new(); 6 | hasher.input(data.as_bytes()); 7 | let signature = hasher.result(); 8 | signature 9 | .as_ref() 10 | .iter() 11 | .map(|b| format!("{:X}", b)) 12 | .collect::() 13 | } 14 | 15 | #[wasm_bindgen] 16 | pub extern "C" fn digest(data: String) -> String { 17 | hex_digest(&data) 18 | } 19 | 20 | #[wasm_bindgen(start)] 21 | pub fn start() -> Result<(), JsValue> { 22 | // This function is getting called when initializing the WASM module 23 | Ok(()) 24 | } 25 | 26 | 27 | #[wasm_bindgen] 28 | pub extern "C" fn digest_attach(data: String, elem_id: String) -> Result<(), JsValue> { 29 | web_sys::window().map_or(Err("No window found".into()), |win| { 30 | if let Some(doc) = win.document() { 31 | doc.get_element_by_id(&elem_id).map_or(Err(format!("No element with id {} found", elem_id).into()), |val|{ 32 | let signature = hex_digest(&data); 33 | val.set_inner_html(&signature); 34 | Ok(()) 35 | }) 36 | } 37 | else { 38 | Err("No document found".into()) 39 | } 40 | }) 41 | } 42 | // No tests :( -------------------------------------------------------------------------------- /Chapter07/browser-rust/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 37 | 38 | 39 |

Hello World in SHA256

40 | 41 | -------------------------------------------------------------------------------- /Chapter07/legacy-c-code/C/Makefile: -------------------------------------------------------------------------------- 1 | # Include the Rust library 2 | LIBS := -ldigest -L../rust-digest/target/release 3 | 4 | ifeq ($(shell uname),Darwin) 5 | LDFLAGS := -Wl,-dead_strip $(LIBS) 6 | else 7 | LDFLAGS := -Wl,--gc-sections $(LIBS) 8 | endif 9 | 10 | all: target/main 11 | 12 | target: 13 | @mkdir -p $@ 14 | 15 | target/main: target/main.o 16 | @echo "Linking ... " 17 | $(CC) -o $@ $^ $(LDFLAGS) 18 | 19 | target/main.o: src/main.c | target 20 | @echo "Compiling ..." 21 | $(CC) -o $@ -c $< 22 | 23 | clean: 24 | @echo "Removing target/" 25 | @rm -rf target 26 | -------------------------------------------------------------------------------- /Chapter07/legacy-c-code/C/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // A function with that name is expected to be linked to the project 4 | extern char* digest(char *str); 5 | 6 | // This function is exported under the name pre_digest 7 | extern void pre_digest() { 8 | printf("pre_digest called\n"); 9 | } 10 | 11 | int main() { 12 | char *result = digest("Hello World"); 13 | printf("SHA digest of \"Hello World\": %s", result); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /Chapter07/legacy-c-code/rust-digest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-digest" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "digest" 9 | crate-type = ["staticlib"] 10 | 11 | [dependencies] 12 | libc = "0.2" 13 | ring = "0.14" 14 | -------------------------------------------------------------------------------- /Chapter07/legacy-c-code/rust-digest/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::os::raw::{c_char, c_void}; 3 | 4 | use ring::digest; 5 | 6 | extern "C" { 7 | fn pre_digest() -> c_void; 8 | } 9 | 10 | #[no_mangle] 11 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char { 12 | unsafe { 13 | pre_digest(); 14 | 15 | let data = CStr::from_ptr(data); 16 | let signature = digest::digest(&digest::SHA256, data.to_bytes()); 17 | 18 | let hex_digest = signature 19 | .as_ref() 20 | .iter() 21 | .map(|b| format!("{:X}", b)) 22 | .collect::(); 23 | 24 | CString::new(hex_digest).unwrap().into_raw() 25 | } 26 | } 27 | 28 | // No tests :( -------------------------------------------------------------------------------- /Chapter07/node-js-rust/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "ffi": "^2.3.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter07/node-js-rust/node/src/index.js: -------------------------------------------------------------------------------- 1 | const ffi = require('ffi'); 2 | const ref = require('ref'); 3 | 4 | const libPath = '../rust-digest/target/debug/libdigest'; 5 | 6 | 7 | const libWeb = ffi.Library(libPath, { 8 | 'digest': [ "string", ["string"]], 9 | }); 10 | 11 | const { digest } = libWeb; 12 | console.log('Hello World SHA256', digest("Hello World")); -------------------------------------------------------------------------------- /Chapter07/node-js-rust/rust-digest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-digest" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "digest" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | libc = "0.2" 13 | ring = "0.14" 14 | -------------------------------------------------------------------------------- /Chapter07/node-js-rust/rust-digest/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::os::raw::c_char; 3 | 4 | use ring::digest; 5 | 6 | #[no_mangle] 7 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char { 8 | unsafe { 9 | 10 | let data = CStr::from_ptr(data); 11 | let signature = digest::digest(&digest::SHA256, data.to_bytes()); 12 | 13 | let hex_digest = signature 14 | .as_ref() 15 | .iter() 16 | .map(|b| format!("{:X}", b)) 17 | .collect::(); 18 | 19 | CString::new(hex_digest).unwrap().into_raw() 20 | } 21 | } 22 | 23 | // No tests :( 24 | -------------------------------------------------------------------------------- /Chapter07/python-rust/python/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Courtesy of https://github.com/kennethreitz/setup.py 5 | 6 | from setuptools import find_packages, setup, Command 7 | 8 | # Package meta-data. 9 | NAME = 'digest' 10 | DESCRIPTION = 'A simple Python package that loads and executes a Rust function.' 11 | URL = 'https://blog.x5ff.xyz' 12 | AUTHOR = 'Claus Matzinger' 13 | REQUIRES_PYTHON = '>=3.7.0' 14 | VERSION = '0.1.0' 15 | LICENSE = 'MIT' 16 | 17 | setup( 18 | # Meta stuff 19 | name=NAME, 20 | version=VERSION, 21 | description=DESCRIPTION, 22 | long_description=DESCRIPTION, 23 | long_description_content_type='text/markdown', 24 | # --- 25 | package_dir={'':'src'}, # Declare src as root folder 26 | packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]), # Auto discover any Python packages 27 | python_requires=REQUIRES_PYTHON, 28 | # Scripts that will be generated invoke this method 29 | entry_points={ 30 | 'setuptools.installation': ['eggsecutable=digest:main'], 31 | }, 32 | include_package_data=True, 33 | license=LICENSE, 34 | classifiers=[ 35 | # Trove classifiers 36 | # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers 37 | 'License :: OSI Approved :: MIT License', 38 | 'Programming Language :: Python', 39 | 'Programming Language :: Python :: 3', 40 | 'Programming Language :: Python :: 3.7', 41 | 'Programming Language :: Python :: Implementation :: CPython', 42 | 'Programming Language :: Python :: Implementation :: PyPy' 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /Chapter07/python-rust/python/src/digest.py: -------------------------------------------------------------------------------- 1 | from ctypes import cdll, c_char_p 2 | from sys import platform 3 | 4 | def build_lib_name(name): 5 | prefix = "lib" 6 | ext = "so" 7 | 8 | if platform == 'darwin': 9 | ext = 'dylib' 10 | elif platform == 'win32': 11 | prefix = "" 12 | ext = 'dll' 13 | 14 | return "{prefix}{name}.{ext}".format(prefix=prefix, name=name, ext=ext) 15 | 16 | def main(): 17 | lib = cdll.LoadLibrary(build_lib_name("digest")) 18 | lib.digest.restype = c_char_p 19 | print("SHA256 of Hello World =", lib.digest(b"Hello World")) 20 | 21 | if __name__ == "__main__": 22 | main() -------------------------------------------------------------------------------- /Chapter07/python-rust/rust-digest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-digest" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "digest" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | libc = "0.2" 13 | ring = "0.14" 14 | -------------------------------------------------------------------------------- /Chapter07/python-rust/rust-digest/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::os::raw::c_char; 3 | 4 | use ring::digest; 5 | 6 | #[no_mangle] 7 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char { 8 | unsafe { 9 | 10 | let data = CStr::from_ptr(data); 11 | let signature = digest::digest(&digest::SHA256, data.to_bytes()); 12 | 13 | let hex_digest = signature 14 | .as_ref() 15 | .iter() 16 | .map(|b| format!("{:X}", b)) 17 | .collect::(); 18 | 19 | CString::new(hex_digest).unwrap().into_raw() 20 | } 21 | } 22 | 23 | // No tests :( 24 | -------------------------------------------------------------------------------- /Chapter08/advanced-orm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "advanced-orm" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | serde = "1" 12 | serde_derive = "1" 13 | env_logger = "0.6" 14 | diesel = {version = "1.4", features = ["sqlite"] } 15 | uuid = { version = "0.7", features = ["serde", "v4"] } 16 | futures = "0.1" 17 | chrono = "0.4" -------------------------------------------------------------------------------- /Chapter08/advanced-orm/db/bookmarks.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/advanced-orm/db/bookmarks.sqlite -------------------------------------------------------------------------------- /Chapter08/advanced-orm/src/models.rs: -------------------------------------------------------------------------------- 1 | use crate::schema::{bookmarks, comments}; 2 | use serde_derive::Serialize; 3 | 4 | #[derive(Debug, Clone, Insertable)] 5 | #[table_name = "bookmarks"] 6 | pub struct NewBookmark<'a> { 7 | pub id: &'a str, 8 | pub url: &'a str, 9 | pub added: &'a str, 10 | } 11 | 12 | #[derive(Debug, Serialize, Queryable)] 13 | pub struct Bookmark { 14 | pub id: String, 15 | pub url: String, 16 | pub added: String, 17 | } 18 | 19 | #[derive(Serialize, Queryable)] 20 | pub struct JulianBookmark { 21 | pub id: String, 22 | pub url: String, 23 | pub julian: f32, 24 | } 25 | 26 | #[derive(Debug, Serialize, Queryable)] 27 | pub struct Comment { 28 | pub bookmark_id: String, 29 | pub comment_id: String, 30 | pub comment: String, 31 | } 32 | 33 | #[derive(Debug, Clone, Insertable)] 34 | #[table_name = "comments"] 35 | pub struct NewComment<'a> { 36 | pub bookmark_id: &'a str, 37 | pub comment_id: &'a str, 38 | pub comment: &'a str, 39 | } 40 | -------------------------------------------------------------------------------- /Chapter08/advanced-orm/src/schema.rs: -------------------------------------------------------------------------------- 1 | use diesel::sql_types::Text; 2 | joinable!(comments -> bookmarks (bookmark_id)); 3 | allow_tables_to_appear_in_same_query!(comments, bookmarks); 4 | 5 | sql_function! { 6 | fn julianday(t: Text) -> Float; 7 | } 8 | sql_function! { 9 | fn date(t: Text) -> Text; 10 | } 11 | 12 | table! { 13 | bookmarks (id) { 14 | id -> Text, 15 | url -> Text, 16 | added -> Text, 17 | } 18 | } 19 | 20 | table! { 21 | comments (comment_id) { 22 | comment_id -> Text, 23 | bookmark_id -> Text, 24 | comment -> Text, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter08/api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "api" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | env_logger = "0.6" 12 | actix-files = "0" -------------------------------------------------------------------------------- /Chapter08/api/foxes.b64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/api/foxes.b64 -------------------------------------------------------------------------------- /Chapter08/api/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate actix_web; 3 | 4 | use actix_files as fs; 5 | use actix_web::{ guard, 6 | http::header, http::Method, middleware, web, App, HttpRequest, HttpResponse, HttpServer, 7 | Responder, Result, 8 | }; 9 | use std::env; 10 | use std::path::PathBuf; 11 | 12 | #[get("by-id/{id}")] 13 | fn bookmark_by_id(id: web::Path<(i32)>) -> impl Responder { 14 | format!("{{ \"id\": {}, \"url\": \"https://blog.x5ff.xyz\" }}", id) 15 | } 16 | 17 | fn echo_bookmark(req: HttpRequest) -> impl Responder { 18 | let id: i32 = req.match_info().query("id").parse().unwrap(); 19 | format!("{:?}", id) 20 | } 21 | 22 | #[get("/captures/{tail:.*}")] 23 | fn captures(req: HttpRequest) -> Result { 24 | let mut root = PathBuf::from("static/"); 25 | let tail: PathBuf = req.match_info().query("tail").parse().unwrap(); 26 | root.push(tail); 27 | 28 | Ok(fs::NamedFile::open(root)?) 29 | } 30 | 31 | #[get("from-bitly/{bitlyid}")] 32 | fn bit_ly(req: HttpRequest) -> HttpResponse { 33 | let bitly_id = req.match_info().get("bitlyid").unwrap(); 34 | let url = req.url_for("bitly", &[bitly_id]).unwrap(); 35 | HttpResponse::Found() 36 | .header(header::LOCATION, url.into_string()) 37 | .finish() 38 | .into_body() 39 | } 40 | 41 | #[get("/")] 42 | fn bookmarks_index() -> impl Responder { 43 | format!("Welcome to your quick and easy bookmarking service!") 44 | } 45 | 46 | fn main() -> std::io::Result<()> { 47 | env::set_var("RUST_LOG", "actix_web=debug"); 48 | env_logger::init(); 49 | HttpServer::new(|| { 50 | App::new() 51 | .wrap(middleware::Logger::default()) 52 | .service( 53 | web::scope("/api") 54 | .service( 55 | web::scope("/bookmarks") 56 | .service(captures) 57 | .service(bookmark_by_id) 58 | .service(bit_ly) 59 | .service(web::resource("add/{id}") 60 | .name("add") 61 | .guard(guard::Any(guard::Put()).or(guard::Post())) 62 | .to(echo_bookmark)) 63 | )) 64 | .service( 65 | web::scope("/bookmarks") 66 | .service(bookmarks_index) 67 | ) 68 | .external_resource("bitly", "https://bit.ly/{bitly}") 69 | 70 | }) 71 | .bind("127.0.0.1:8081")? 72 | .run() 73 | } 74 | -------------------------------------------------------------------------------- /Chapter08/api/static/foxes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/api/static/foxes.jpg -------------------------------------------------------------------------------- /Chapter08/authentication/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "authentication" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | serde = "1" 12 | serde_derive = "1" 13 | env_logger = "0.6" 14 | jsonwebtoken = "6" 15 | futures = "0.1" 16 | actix-service = "0.4" 17 | -------------------------------------------------------------------------------- /Chapter08/authentication/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate actix_web; 3 | mod middlewares; 4 | use actix_web::{http, middleware, web, App, HttpResponse, HttpServer, Responder}; 5 | use jsonwebtoken::{encode, Header}; 6 | use middlewares::Claims; 7 | use serde_derive::{Deserialize, Serialize}; 8 | use std::env; 9 | 10 | const PASSWORD: &str = "swordfish"; 11 | pub const TOKEN_SECRET: &str = "0fd2af6f"; 12 | 13 | #[derive(Debug, Serialize, Deserialize)] 14 | struct Login { 15 | password: String, 16 | } 17 | 18 | #[get("/secret")] 19 | fn authed() -> impl Responder { 20 | format!("Congrats, you are authenticated") 21 | } 22 | 23 | fn login(login: web::Json) -> HttpResponse { 24 | // TODO: have a proper security concept 25 | if &login.password == PASSWORD { 26 | let claims = Claims { 27 | user_id: "1".into(), 28 | }; 29 | encode(&Header::default(), &claims, TOKEN_SECRET.as_ref()) 30 | .map(|token| { 31 | HttpResponse::Ok() 32 | .header(http::header::AUTHORIZATION, format!("Bearer {}", token)) 33 | .finish() 34 | }) 35 | .unwrap_or(HttpResponse::InternalServerError().into()) 36 | } else { 37 | HttpResponse::Unauthorized().into() 38 | } 39 | } 40 | 41 | fn main() -> std::io::Result<()> { 42 | env::set_var("RUST_LOG", "actix_web=debug"); 43 | env_logger::init(); 44 | HttpServer::new(|| { 45 | App::new() 46 | .wrap(middleware::Logger::default()) 47 | .wrap(middlewares::JwtLogin) 48 | .service(authed) 49 | .service(web::resource("/login").route(web::post().to(login))) 50 | }) 51 | .bind("127.0.0.1:8081")? 52 | .run() 53 | } 54 | -------------------------------------------------------------------------------- /Chapter08/authentication/src/middlewares.rs: -------------------------------------------------------------------------------- 1 | use actix_service::{Service, Transform}; 2 | use actix_web::dev::{ServiceRequest, ServiceResponse}; 3 | use actix_web::{http, Error, HttpResponse}; 4 | use futures::future::{ok, Either, FutureResult}; 5 | use futures::Poll; 6 | use jsonwebtoken::{decode, Validation}; 7 | use serde_derive::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct Claims { 11 | pub user_id: String, 12 | } 13 | 14 | pub struct JwtLogin; 15 | 16 | impl Transform for JwtLogin 17 | where 18 | S: Service, Error = Error>, 19 | S::Future: 'static, 20 | { 21 | type Request = ServiceRequest; 22 | type Response = ServiceResponse; 23 | type Error = Error; 24 | type InitError = (); 25 | type Transform = JwtLoginMiddleware; 26 | type Future = FutureResult; 27 | 28 | fn new_transform(&self, service: S) -> Self::Future { 29 | ok(JwtLoginMiddleware { service }) 30 | } 31 | } 32 | pub struct JwtLoginMiddleware { 33 | service: S, 34 | } 35 | 36 | impl Service for JwtLoginMiddleware 37 | where 38 | S: Service, Error = Error>, 39 | S::Future: 'static, 40 | { 41 | type Request = ServiceRequest; 42 | type Response = ServiceResponse; 43 | type Error = Error; 44 | type Future = Either>; 45 | 46 | fn poll_ready(&mut self) -> Poll<(), Self::Error> { 47 | self.service.poll_ready() 48 | } 49 | 50 | fn call(&mut self, req: ServiceRequest) -> Self::Future { 51 | if req.path() == "/login" { 52 | Either::A(self.service.call(req)) 53 | } else { 54 | if let Some(header_value) = req.headers().get(http::header::AUTHORIZATION) { 55 | let token = header_value.to_str().unwrap().replace("Bearer", ""); 56 | let mut validation = Validation::default(); 57 | validation.validate_exp = false; // our logins don't expire 58 | if let Ok(_) = 59 | decode::(&token.trim(), crate::TOKEN_SECRET.as_ref(), &validation) 60 | { 61 | Either::A(self.service.call(req)) 62 | } else { 63 | Either::B(ok( 64 | req.into_response(HttpResponse::Unauthorized().finish().into_body()) 65 | )) 66 | } 67 | } else { 68 | Either::B(ok( 69 | req.into_response(HttpResponse::Unauthorized().finish().into_body()) 70 | )) 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Chapter08/html-templates/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html-templates" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | serde = "1" 12 | serde_derive = "1" 13 | env_logger = "0.6" 14 | base64 = "0.10.1" 15 | yarte = {version = "0", features=["with-actix-web"]} 16 | chrono = "0.4" -------------------------------------------------------------------------------- /Chapter08/html-templates/static/packtpub.com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/html-templates/static/packtpub.com.png -------------------------------------------------------------------------------- /Chapter08/html-templates/static/placeholder.b64: -------------------------------------------------------------------------------- 1 | iVBORw0KGgoAAAANSUhEUgAAAJoAAABjCAYAAABjYhnGAAAIWklEQVR42u2cIVBbSxSGK56oqKhA 2 | PFFR8QTiCUTFE4jOVFRUIBAIRDpDWzqNqEBUINKZCkQFAlGBqEAgEIgnEBWIiApEBSIiIgIRERGB 3 | qIhI92f+kzk57L25afPekPJ/M3cgN3v37t399+zZvWdz544QQgghhBBCCCGEEEIIIYQQQgghhBBC 4 | CCGEEEL8djx//vz+69evH6a/d2/NQ7969Wo5HbUXL148yH3/5s2be/j+5cuXS5LIzOq8kY5hEtvj 5 | W/PQm5ubn/HQSUinue/R8/A9KkcSudlCg0GA4bjRQuOxKaHNtdCGaM+bLrQ+jjiEThJaGlr/THk8 6 | St8vTHvvjY2NvzAkY3guSvP+/fs/Ut6LqRx/T/JpkAblidfjPO41wW+6i7IUuRDxmSeVG/WBeonl 7 | yQkNZZv0fLgX7ln2HHMhtPR3g3//rSI0PHQ6d+asIY7jXMVm7rmS0nbddd/TsQdRZIYCnw6dYSuW 8 | LZXlU/r7LVY0n6nnrr9Ix7OMkBssg6VrQSTuPo/N4sPFCOWuZ0To06BMX9FZotDwLOloh+d7FjsA 9 | 6iaUr+3Tpf87oR1u3ghkQkOjOetWKxMaz6FSuumaNVQiKjwdl+k4L+uZFOggHU34E/jsxP7RpVs3 10 | 4Vu69PeAwnobyob8vqAMKf0TJ7Ir4eFaiIUNPoD1cI20yzx2cB4NyIbsYXYYhIb77DGvFaZDfg9d 11 | fuh8fZZ/keVAvVxYvTihDdL375h/jel6vv7QeX35Ur5PeY8B/keaZIX/cWU8wf++TDdOaKhY9vrR 12 | EJoTWvp/nw2+FPJa80IoMO/bOf8EwvMTEpajlbFysFydYNFOM1agH8/D2lAs++760WcDDWcWJwht 13 | N3Sadd5/ndc9yA1fqA+U2aykE1o9PNsOr3/kVgSQbi8uj9DSt+Zu6LQewB6NyjsqERqs2VmBP4Xv 14 | vpRYtFXLv8jfoGW56sVFyzG+bJmGfZprSGdxzp0AhmYFQzpYl+MgtFoo59h59/wXKEPsJJMmA7Rq 15 | o/Nmbb0FdmkxnA59Hc6V0FhgG6JWC4RW+EDe4kyYdZnPAf9i31vHooYtmhHHsliDlRwd31hFh1nE 16 | qkJzHcH8Loj1BBbPi66q0KxtCupwM+Yxj0JboBPd44zymtAgxoIKgHDaFabimJWt0ZHv+uUV73z/ 17 | itDg8/H/sQMdKPhnWwXpnk4rNDfTxXcN+oVI0zSxTSu0nM8L327uheaHOFdRjSCmVmaJ4D59nuOi 18 | +8H/wZF5+4A8e07o13yTnxg6t8qe3YbOONPLCLqS0FAenLNJhKvjj/4+Uwyd27xuOVMPh/xuYa6F 19 | xoY4yk2XraLMCc44sysllgyzqO9xvYpDbt9PDvDZL5dQkG3zD4uExskArORFXG6BlTRnm847hvAz 20 | vyZGX2vb6qSq0KxzRoE7X3BlGqHB/7IZtR966cOi3CdxMgABzp3Q3BA6JjQ2+DebsbHijqs8KIfi 21 | S84qd3jtSbwHlzNG6dLnD7CiPLdcJjQ3qRlQcPE+m9HXgYB5j4Y927RDJwXa5LLFZ+a1xzK34vLG 22 | JKHxXN2NLA1ax6ulpTiZcvWzY2W/SUJ7B6e3aKGVftRprGSKDRV2zmEPs7l60UwrM6s8oHXqsBJr 23 | GbEsMl2Hx6GfgdniKJ6hZM3u0F1/kmsADrUnlg6W3A/vyAf3idfmzrNetm1SxMbfDUNcjdctxXLk 24 | zqPTsA067BCfcm8w6FacMd2mXvYJIYQQQgghhBBC/F4wfKc2KQo2B9eeVqveQ5tsbhFc3R69Qaga 25 | rVEgtNNJ0SLhHtr7cFvgqvmlvV+U0MR/At75+QgHCe12DWePLA4rRnTiuxjaQ8Fgx/Vj/97NxWLB 26 | H1oviApZ8vmVCY1h1RYjtlgmNHff1bhDqUxoKCPDsrP3EDNyxN2unb6LfD2wF+QMThxmQnvG4qX4 27 | ErzFc11GTwwyO6jGrFBOaNzGZ7FwPVeu/UxeXW5OGYYdT4uThMaIjUG4dr9KcICYzl86ZiM+s2HN 28 | Qpwt3sw2ScRNJwyJabvPbYvKNYvHXUzDEA0xUWgUzneLjGC59hkysxbyutrqh44AgTAu7NJvbskJ 29 | zUKEMDnhcH6XwsvuNxC/NmS+i0MWA+7GrAdF1HRD2gPfcIyubcSgR4t29SKtKLRGSbl2Q169GO5s 30 | grGNJ1FojB3DdsGvmc7Xzm2+ETOAsVXmp2zFgEIL1rPh06JG49oXG3DZ+VU7maDGiULL+Y65chVN 31 | BixPi1WLQmOs2pWLwH2Qo8MCJKWK2Q6di7bLm0F/p+Yb+QY1a2KWCdYtWgMK7II+T4si+PYzQgv+ 32 | XruoXCVCG9u9FYXmPhceUsdshXYV5eotU8nuoiaPhejHcOjsQww+WjfnG1UUGsrVnlSuIqHF3Vsl 33 | QmtwU8m1Q+qYrdCubdQtEpr9zACH0YEPT3ZD1Ua45sm0QnM70D/kfLSM0C4yz1W3vak5obnOcigV 34 | /D9Cg2DOzJnm7O6gYHfRfc5Qr22pcxbk0O1fXKAFnEpoTgRNKxd/TeeoQGhDv2+AE5UOd1HdK7Gs 35 | R3EWSx8TMf7bUsdshWbOeo+N1jffKP6yUFHjhCUJ+7WbJkXZyvyAS5Wh03aRd5n+khthRj/XENbR 36 | us4vvOROpJWyIZwv2s/dLiPbyDKo8qJeTD/jXOX2sGNYBu7k2cptwKXlauR2T9Ma1ClGNFrNdkv5 37 | PaBcvX8bhupGZlcQZsGHyA/pcU8ux9R9XrwPFp4/oHNwgXk54w404hY35gmX4ADXokPkfutCCCGE 38 | EEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBC 39 | CCGEEEKIKvwA6uJYps1NR+4AAAAASUVORK5CYII= 40 | -------------------------------------------------------------------------------- /Chapter08/html-templates/static/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/html-templates/static/placeholder.png -------------------------------------------------------------------------------- /Chapter08/html-templates/static/x5ff.xyz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/html-templates/static/x5ff.xyz.png -------------------------------------------------------------------------------- /Chapter08/html-templates/templates/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |

Welcome {{ user }}.

17 |

Your bookmarks:

18 |
19 |
20 | 21 | {{#if bookmarks.is_empty() }} 22 |
23 |
24 | No bookmarks :( 25 |
26 |
27 | {{~/if}} 28 | 29 | {{#each bookmarks}} 30 |
31 | {{> partials/bookmark }} 32 |
33 | {{~/each}} 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /Chapter08/html-templates/templates/partials/bookmark.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |

{{ url.replace("https://", "") }}

6 |
7 | Added {{ timestamp.format("%Y-%m-%d").to_string() }} 8 |
-------------------------------------------------------------------------------- /Chapter08/json-handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "json-handling" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | serde = { version = "1.0", features = ["derive"] } 12 | env_logger = "0.6" -------------------------------------------------------------------------------- /Chapter08/json-handling/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate actix_web; 3 | 4 | use actix_web::{ 5 | guard, http::Method, middleware, web, App, HttpResponse, HttpServer, 6 | }; 7 | use serde::{Deserialize, Serialize}; 8 | use std::env; 9 | 10 | #[derive(Debug, Clone, Serialize, Deserialize)] 11 | struct Bookmark { 12 | id: i32, 13 | url: String, 14 | } 15 | 16 | #[get("by-id/{id}")] 17 | fn bookmarks_by_id(id: web::Path<(i32)>) -> HttpResponse { 18 | let bookmark = Bookmark { 19 | id: *id, 20 | url: "https://blog.x5ff.xyz".into(), 21 | }; 22 | HttpResponse::Ok().json(bookmark) 23 | } 24 | 25 | fn echo_bookmark(bookmark: web::Json) -> HttpResponse { 26 | HttpResponse::Ok().json(bookmark.clone()) 27 | } 28 | 29 | fn main() -> std::io::Result<()> { 30 | env::set_var("RUST_LOG", "actix_web=debug"); 31 | env_logger::init(); 32 | HttpServer::new(|| { 33 | App::new() 34 | .wrap(middleware::Logger::default()) 35 | .service( 36 | web::scope("/api") 37 | .service( 38 | web::scope("/bookmarks") 39 | .service(bookmarks_by_id) 40 | .service( 41 | web::resource("add/{id}") 42 | .name("add") 43 | .guard(guard::Any(guard::Put()).or(guard::Post())) 44 | .to(echo_bookmark), 45 | ) 46 | .default_service(web::route().method(Method::GET)), 47 | )) 48 | }) 49 | .bind("127.0.0.1:8081")? 50 | .run() 51 | } 52 | -------------------------------------------------------------------------------- /Chapter08/orm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "orm" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | serde = "1" 12 | serde_derive = "1" 13 | env_logger = "0.6" 14 | diesel = {version = "1.4", features = ["sqlite"] } 15 | uuid = { version = "0.7", features = ["serde", "v4"] } 16 | futures = "0.1" 17 | -------------------------------------------------------------------------------- /Chapter08/orm/db/bookmarks.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/orm/db/bookmarks.sqlite -------------------------------------------------------------------------------- /Chapter08/orm/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate diesel; 3 | mod models; 4 | mod schema; 5 | 6 | use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer}; 7 | 8 | use std::env; 9 | 10 | use diesel::prelude::*; 11 | use diesel::sqlite::SqliteConnection; 12 | use futures::Future; 13 | use models::{Bookmark, NewBookmark}; 14 | use serde_derive::{Deserialize, Serialize}; 15 | 16 | // Helpers 17 | const SQLITE_DB_URL: &str = "db/bookmarks.sqlite"; 18 | 19 | #[derive(Debug, Serialize, Deserialize)] 20 | struct WebBookmark { 21 | url: String, 22 | } 23 | 24 | fn connect(db_url: &str) -> SqliteConnection { 25 | SqliteConnection::establish(&SQLITE_DB_URL).expect(&format!("Error connecting to {}", db_url)) 26 | } 27 | 28 | // Handlers 29 | fn bookmarks_by_id(req_id: web::Path<(String)>) -> impl Future { 30 | web::block(move || { 31 | use self::schema::bookmarks::dsl::*; 32 | 33 | let conn = connect(&SQLITE_DB_URL); 34 | bookmarks 35 | .filter(id.eq(req_id.as_str())) 36 | .limit(1) 37 | .load::(&conn) 38 | }) 39 | .then(|res| match res { 40 | Ok(obj) => Ok(HttpResponse::Ok().json(obj)), 41 | Err(_) => Ok(HttpResponse::InternalServerError().into()), 42 | }) 43 | } 44 | 45 | fn all_bookmarks() -> impl Future { 46 | web::block(move || { 47 | use self::schema::bookmarks::dsl::*; 48 | 49 | let conn = connect(&SQLITE_DB_URL); 50 | bookmarks.load::(&conn) 51 | }) 52 | .then(|res| match res { 53 | Ok(obj) => Ok(HttpResponse::Ok().json(obj)), 54 | Err(_) => Ok(HttpResponse::InternalServerError().into()), 55 | }) 56 | } 57 | 58 | fn bookmarks_add( 59 | bookmark: web::Json, 60 | ) -> impl Future { 61 | web::block(move || { 62 | use self::schema::bookmarks::dsl::*; 63 | 64 | let conn = connect(&SQLITE_DB_URL); 65 | let new_id = format!("{}", uuid::Uuid::new_v4()); 66 | let new_bookmark = NewBookmark { 67 | id: &new_id, 68 | url: &bookmark.url, 69 | }; 70 | diesel::insert_into(bookmarks) 71 | .values(&new_bookmark) 72 | .execute(&conn) 73 | .map(|_| new_id) 74 | }) 75 | .then(|res| match res { 76 | Ok(obj) => Ok(HttpResponse::Ok().json(obj)), 77 | Err(_) => Ok(HttpResponse::InternalServerError().into()), 78 | }) 79 | } 80 | 81 | fn bookmarks_delete( 82 | req_id: web::Path<(String)>, 83 | ) -> impl Future { 84 | web::block(move || { 85 | use self::schema::bookmarks::dsl::*; 86 | 87 | let conn = connect(&SQLITE_DB_URL); 88 | diesel::delete(bookmarks.filter(id.eq(req_id.as_str()))).execute(&conn) 89 | }) 90 | .then(|res| match res { 91 | Ok(obj) => Ok(HttpResponse::Ok().json(obj)), 92 | Err(_) => Ok(HttpResponse::InternalServerError().into()), 93 | }) 94 | } 95 | 96 | fn main() -> std::io::Result<()> { 97 | env::set_var("RUST_LOG", "actix_web=debug"); 98 | env_logger::init(); 99 | HttpServer::new(move || { 100 | App::new().wrap(middleware::Logger::default()).service( 101 | web::scope("/api").service( 102 | web::scope("/bookmarks") 103 | .service(web::resource("/all") 104 | .route(web::get().to_async(all_bookmarks)) 105 | ) 106 | .service( 107 | web::resource("by-id/{id}") 108 | .route(web::get().to_async(bookmarks_by_id)) 109 | .route(web::delete().to_async(bookmarks_delete)) 110 | ) 111 | .service( 112 | web::resource("/") 113 | .data(web::JsonConfig::default()) 114 | .route(web::post().to_async(bookmarks_add)), 115 | ) 116 | ), 117 | ) 118 | }) 119 | .bind("127.0.0.1:8081")? 120 | .run() 121 | } 122 | -------------------------------------------------------------------------------- /Chapter08/orm/src/models.rs: -------------------------------------------------------------------------------- 1 | use crate::schema::bookmarks; 2 | use serde_derive::Serialize; 3 | 4 | #[derive(Debug, Clone, Insertable)] 5 | #[table_name = "bookmarks"] 6 | pub struct NewBookmark<'a> { 7 | pub id: &'a str, 8 | pub url: &'a str, 9 | } 10 | 11 | #[derive(Serialize, Queryable)] 12 | pub struct Bookmark { 13 | pub id: String, 14 | pub url: String, 15 | } 16 | -------------------------------------------------------------------------------- /Chapter08/orm/src/schema.rs: -------------------------------------------------------------------------------- 1 | table! { 2 | bookmarks (id) { 3 | id -> Text, 4 | url -> Text, 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Chapter08/static-web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "static-web" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "2" 11 | env_logger = "0.6" 12 | actix-files = "0" 13 | actix-rt = "1" -------------------------------------------------------------------------------- /Chapter08/static-web/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate actix_web; 3 | 4 | use actix_web::{web, App, middleware, HttpServer, Responder, Result}; 5 | use std::{env}; 6 | use actix_files::NamedFile; 7 | 8 | 9 | async fn index() -> Result { 10 | Ok(NamedFile::open("static/index.html")?) 11 | } 12 | 13 | async fn path_parser(info: web::Path<(String, i32)>) -> impl Responder { 14 | format!("You tried to reach '{}/{}'", info.0, info.1) 15 | } 16 | 17 | async fn rust_cookbook() -> impl Responder { 18 | format!("Welcome to the Rust Cookbook") 19 | } 20 | 21 | #[get("/foxes")] 22 | async fn foxes() -> Result { 23 | Ok(NamedFile::open("static/foxes.jpg")?) 24 | } 25 | 26 | #[actix_rt::main] 27 | async fn main() -> std::io::Result<()> { 28 | env::set_var("RUST_LOG", "actix_web=debug"); 29 | env_logger::init(); 30 | HttpServer::new(|| { 31 | App::new() 32 | .wrap(middleware::Logger::default()) 33 | .route("/", web::get().to(index)) 34 | .route("/welcome", web::get().to(rust_cookbook)) 35 | .route("/{path}/{id}", web::get().to(path_parser)) 36 | .service(foxes) 37 | }) 38 | .bind("127.0.0.1:8088")? 39 | .run() 40 | .await 41 | } -------------------------------------------------------------------------------- /Chapter08/static-web/static/foxes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter08/static-web/static/foxes.jpg -------------------------------------------------------------------------------- /Chapter08/static-web/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World

4 | 5 | -------------------------------------------------------------------------------- /Chapter08/web-errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "web-errors" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | actix-web = "1" 11 | serde = "1" 12 | serde_derive = "1" 13 | env_logger = "0.6" 14 | failure = "0" -------------------------------------------------------------------------------- /Chapter08/web-errors/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | extern crate actix_web; 4 | use failure::Fail; 5 | 6 | use actix_web::{ http, middleware, web, App, HttpResponse, HttpServer, error 7 | }; 8 | use serde_derive::{Deserialize, Serialize}; 9 | use std::env; 10 | 11 | #[derive(Fail, Debug)] 12 | enum WebError { 13 | #[fail(display = "Invalid id '{}'", id)] 14 | InvalidIdError { id: i32 }, 15 | #[fail(display = "Invalid request, please try again later")] 16 | RandomInternalError, 17 | } 18 | 19 | impl error::ResponseError for WebError { 20 | fn error_response(&self) -> HttpResponse { 21 | match *self { 22 | WebError::InvalidIdError { .. } => HttpResponse::new(http::StatusCode::BAD_REQUEST), 23 | WebError::RandomInternalError => HttpResponse::new(http::StatusCode::INTERNAL_SERVER_ERROR) 24 | } 25 | } 26 | } 27 | 28 | #[derive(Debug, Clone, Serialize, Deserialize)] 29 | struct Bookmark { 30 | id: i32, 31 | url: String, 32 | } 33 | 34 | #[get("by-id/{id}")] 35 | fn bookmarks_by_id(id: web::Path<(i32)>) -> Result { 36 | if *id < 10 { 37 | Ok(HttpResponse::Ok().json(Bookmark { 38 | id: *id, 39 | url: "https://blog.x5ff.xyz".into(), 40 | })) 41 | } 42 | else { 43 | Err(WebError::InvalidIdError { id: *id }) 44 | } 45 | } 46 | 47 | fn main() -> std::io::Result<()> { 48 | env::set_var("RUST_LOG", "actix_web=debug"); 49 | env_logger::init(); 50 | HttpServer::new(|| { 51 | App::new() 52 | .wrap(middleware::Logger::default()) 53 | .service( 54 | web::scope("/bookmarks") 55 | .service(bookmarks_by_id) 56 | ) 57 | .route( 58 | "/underconstruction", 59 | web::get().to(|| Result::::Err(WebError::RandomInternalError)), 60 | ) 61 | }) 62 | .bind("127.0.0.1:8081")? 63 | .run() 64 | } 65 | -------------------------------------------------------------------------------- /Chapter09/cross-compile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cross-compile" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /Chapter09/cross-compile/src/main.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86")] 2 | const ARCH: &str = "x86"; 3 | 4 | #[cfg(target_arch = "x86_64")] 5 | const ARCH: &str = "x64"; 6 | 7 | #[cfg(target_arch = "mips")] 8 | const ARCH: &str = "mips"; 9 | 10 | #[cfg(target_arch = "powerpc")] 11 | const ARCH: &str = "powerpc"; 12 | 13 | #[cfg(target_arch = "powerpc64")] 14 | const ARCH: &str = "powerpc64"; 15 | 16 | #[cfg(target_arch = "arm")] 17 | const ARCH: &str = "ARM"; 18 | 19 | #[cfg(target_arch = "aarch64")] 20 | const ARCH: &str = "ARM64"; 21 | 22 | fn main() { 23 | println!("Hello, world!"); 24 | println!("Compiled for {}", ARCH); 25 | } 26 | -------------------------------------------------------------------------------- /Chapter09/i2cdevice-drivers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i2cdevice-drivers" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | rand = "0.5" -------------------------------------------------------------------------------- /Chapter09/i2cdevice-drivers/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | mod sensor; 3 | 4 | use sensor::{Bmx42Device, RawI2CDeviceMock, Thermometer}; 5 | use std::thread::sleep; 6 | use std::time::Duration; 7 | 8 | 9 | fn main() { 10 | let mut device = Bmx42Device::new(RawI2CDeviceMock::new("/dev/i2c-1".into(), 0x5f)).unwrap(); 11 | let pause = Duration::from_secs(1); 12 | loop { 13 | println!("Current temperature {} °C", device.temp_celsius().unwrap()); 14 | sleep(pause); 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter09/i2cdevice-drivers/src/sensor.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use rand::prelude::*; 3 | 4 | pub trait Thermometer { 5 | fn temp_celsius(&mut self) -> Result; 6 | } 7 | 8 | type Result = std::result::Result; 9 | 10 | enum Register { 11 | Calib0 = 0x00, 12 | Data = 0x01, 13 | } 14 | 15 | #[allow(dead_code)] 16 | pub struct RawI2CDeviceMock { 17 | path: String, 18 | device_id: u8, 19 | } 20 | 21 | 22 | impl RawI2CDeviceMock { 23 | pub fn new(path: String, device_id: u8) -> RawI2CDeviceMock { 24 | RawI2CDeviceMock { 25 | path: path, 26 | device_id: device_id, 27 | } 28 | } 29 | 30 | pub fn read(&self, register: u8) -> Result { 31 | let register = register as usize; 32 | if register == Register::Calib0 as usize { 33 | Ok(1_u8) 34 | } else { // register is the data register 35 | Ok(random::()) 36 | } 37 | } 38 | } 39 | 40 | pub struct Bmx42Device { 41 | raw: RawI2CDeviceMock, 42 | calibration: u8, 43 | } 44 | 45 | impl Bmx42Device { 46 | pub fn new(device: RawI2CDeviceMock) -> Result { 47 | let calib = device.read(Register::Calib0 as u8)?; 48 | Ok(Bmx42Device { 49 | raw: device, 50 | calibration: calib 51 | }) 52 | } 53 | } 54 | 55 | 56 | impl Thermometer for Bmx42Device { 57 | fn temp_celsius(&mut self) -> Result { 58 | let raw_temp = self.raw.read(Register::Data as u8)?; 59 | // converts the result into something usable; from the specification 60 | Ok(((raw_temp as i8) << (self.calibration as i8)) as f32 / 10.0) 61 | } 62 | } -------------------------------------------------------------------------------- /Chapter09/reading-hardware/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reading-hardware" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tokio = "0.1" 11 | tokio-timer = "0.2" 12 | rand = "0.5" -------------------------------------------------------------------------------- /Chapter09/reading-hardware/src/main.rs: -------------------------------------------------------------------------------- 1 | mod sensor; 2 | use tokio::prelude::*; 3 | use tokio::timer::Interval; 4 | 5 | use sensor::{Bmx42Device, RawI2CDeviceMock, Thermometer}; 6 | use std::time::{Duration, UNIX_EPOCH, SystemTime, Instant}; 7 | use std::thread; 8 | 9 | use std::sync::mpsc::channel; 10 | 11 | fn current_timestamp_ms() -> u64 { 12 | SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() 13 | } 14 | 15 | #[derive(Debug)] 16 | struct Reading { 17 | timestamp: u64, 18 | value: f32 19 | } 20 | 21 | 22 | fn main() { 23 | let mut device = Bmx42Device::new(RawI2CDeviceMock::new("/dev/i2c-1".into(), 0x5f)).unwrap(); 24 | 25 | let (sx, rx) = channel(); 26 | 27 | thread::spawn(move || { 28 | while let Ok(reading) = rx.recv() { 29 | 30 | // or batch and save/send to somewhere 31 | println!("{:?}", reading); 32 | } 33 | }); 34 | let task = Interval::new(Instant::now(), Duration::from_secs(1)) 35 | .take(5) 36 | .for_each(move |_instant| { 37 | let sx = sx.clone(); 38 | let temp = device.temp_celsius().unwrap(); 39 | let _ = sx.send(Reading { 40 | timestamp: current_timestamp_ms(), 41 | value: temp 42 | }); 43 | Ok(()) 44 | }) 45 | .map_err(|e| panic!("interval errored; err={:?}", e)); 46 | 47 | tokio::run(task); 48 | } 49 | -------------------------------------------------------------------------------- /Chapter09/reading-hardware/src/sensor.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use rand::prelude::*; 3 | 4 | 5 | type Result = std::result::Result; 6 | 7 | pub trait Thermometer { 8 | fn temp_celsius(&mut self) -> Result; 9 | } 10 | 11 | #[allow(dead_code)] 12 | pub struct RawI2CDeviceMock { 13 | path: String, 14 | device_id: u8, 15 | } 16 | 17 | enum Register { 18 | Calib0 = 0x00, 19 | Data = 0x01, 20 | } 21 | 22 | impl RawI2CDeviceMock { 23 | pub fn new(path: String, device_id: u8) -> RawI2CDeviceMock { 24 | RawI2CDeviceMock { 25 | path: path, 26 | device_id: device_id, 27 | } 28 | } 29 | 30 | pub fn read(&self, register: u8) -> Result { 31 | let register = register as usize; 32 | if register == Register::Calib0 as usize { 33 | Ok(1_u8) 34 | } else { // register is the data register 35 | Ok(random::()) 36 | } 37 | } 38 | } 39 | 40 | pub struct Bmx42Device { 41 | raw: RawI2CDeviceMock, 42 | calibration: u8, 43 | } 44 | 45 | impl Bmx42Device { 46 | pub fn new(device: RawI2CDeviceMock) -> Result { 47 | let calib = device.read(Register::Calib0 as u8)?; 48 | Ok(Bmx42Device { 49 | raw: device, 50 | calibration: calib 51 | }) 52 | } 53 | } 54 | 55 | 56 | impl Thermometer for Bmx42Device { 57 | fn temp_celsius(&mut self) -> Result { 58 | let raw_temp = self.raw.read(Register::Data as u8)?; 59 | // converts the result into something usable; from the specification 60 | Ok(((raw_temp as i8) << (self.calibration as i8)) as f32 / 10.0) 61 | } 62 | } -------------------------------------------------------------------------------- /Chapter10/command-line-args/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "list" 3 | version = "1.0.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = {version= "2.33", features = ["suggestions", "color"]} -------------------------------------------------------------------------------- /Chapter10/command-line-args/args.yml: -------------------------------------------------------------------------------- 1 | name: myapp 2 | version: "1.0" 3 | author: Kevin K. 4 | about: Does awesome things 5 | args: 6 | - config: 7 | short: c 8 | long: config 9 | value_name: FILE 10 | help: Sets a custom config file 11 | takes_value: true 12 | - INPUT: 13 | help: Sets the input file to use 14 | required: true 15 | index: 1 16 | - verbose: 17 | short: v 18 | multiple: true 19 | help: Sets the level of verbosity 20 | subcommands: 21 | - test: 22 | about: controls testing features 23 | version: "1.3" 24 | author: Someone E. 25 | args: 26 | - debug: 27 | short: d 28 | help: print debug information -------------------------------------------------------------------------------- /Chapter10/command-line-args/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg, SubCommand}; 2 | use std::fs::DirEntry; 3 | use std::path::Path; 4 | 5 | use std::io; 6 | 7 | struct Exclusion(String); 8 | 9 | impl Exclusion { 10 | pub fn is_excluded(&self, path: &Path) -> bool { 11 | path.file_name() 12 | .map_or(false, |f| f.to_string_lossy().find(&self.0).is_some()) 13 | } 14 | } 15 | 16 | fn walk( 17 | dir: &Path, 18 | exclusion: &Option, 19 | cb: &dyn Fn(&DirEntry), 20 | recurse: bool, 21 | ) -> io::Result<()> { 22 | for entry in dir.read_dir()? { 23 | let entry = entry?; 24 | let path = entry.path(); 25 | if !exclusion.as_ref().map_or(false, |e| e.is_excluded(&path)) { 26 | if recurse && path.is_dir() { 27 | walk(&path, exclusion, cb, true)?; 28 | } 29 | cb(&entry); 30 | } 31 | } 32 | Ok(()) 33 | } 34 | 35 | fn print_if_file(entry: &DirEntry) { 36 | let path = entry.path(); 37 | if !path.is_dir() { 38 | println!("{}", path.to_string_lossy()) 39 | } 40 | } 41 | fn print_if_dir(entry: &DirEntry) { 42 | let path = entry.path(); 43 | if path.is_dir() { 44 | println!("{}", path.to_string_lossy()) 45 | } 46 | } 47 | 48 | fn main() -> io::Result<()> { 49 | let matches = App::new("list") 50 | .version("1.0") 51 | .author("Claus M - claus.matzinger+kb@gmail.com") 52 | .about("") 53 | .arg( 54 | Arg::with_name("exclude") 55 | .short("e") 56 | .long("exclude") 57 | .value_name("NAME") 58 | .help("Exclude directories/files with this name") 59 | .takes_value(true), 60 | ) 61 | .arg( 62 | Arg::with_name("recursive") 63 | .short("r") 64 | .long("recursive") 65 | .help("Recursively descend into subdirectories"), 66 | ) 67 | .subcommand( 68 | SubCommand::with_name("files") 69 | .about("Lists files only") 70 | .arg( 71 | Arg::with_name("PATH") 72 | .help("The path to start looking") 73 | .required(true) 74 | .index(1), 75 | ), 76 | ) 77 | .subcommand( 78 | SubCommand::with_name("dirs") 79 | .about("Lists directories only") 80 | .arg( 81 | Arg::with_name("PATH") 82 | .help("The path to start looking") 83 | .required(true) 84 | .index(1), 85 | ), 86 | ) 87 | .get_matches(); 88 | 89 | let recurse = matches.is_present("recursive"); 90 | let exclusions = matches.value_of("exclude").map(|e| Exclusion(e.into())); 91 | 92 | match matches.subcommand() { 93 | ("files", Some(subcmd)) => { 94 | let path = Path::new(subcmd.value_of("PATH").unwrap()); 95 | walk(path, &exclusions, &print_if_file, recurse)?; 96 | } 97 | ("dirs", Some(subcmd)) => { 98 | let path = Path::new(subcmd.value_of("PATH").unwrap()); 99 | walk(path, &exclusions, &print_if_dir, recurse)?; 100 | } 101 | _ => {} 102 | } 103 | Ok(()) 104 | } 105 | -------------------------------------------------------------------------------- /Chapter10/dynamic-data/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dynamic-json" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | serde = "1" 11 | serde_json ="1" 12 | toml = "0.5" 13 | serde-pickle = "0.5" 14 | serde_derive = "1" 15 | -------------------------------------------------------------------------------- /Chapter10/dynamic-data/create_pickle.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import json 3 | 4 | 5 | def main(): 6 | val = json.loads("""{ 7 | "userid": 103609, 8 | "verified": true, 9 | "friendly_name": "Jason", 10 | "access_privileges": [ 11 | "user", 12 | "admin" 13 | ] 14 | }""") # load the json string as dictionary 15 | 16 | # open "user.pkl" to write binary data (= wb) 17 | with open("user.pkl", "wb") as out: 18 | pickle.dump(val, out) # write the dictionary 19 | 20 | if __name__ == '__main__': 21 | main() -------------------------------------------------------------------------------- /Chapter10/dynamic-data/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_json; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use serde_json::Value; 7 | use serde_pickle as pickle; 8 | use std::fs::File; 9 | use toml; 10 | 11 | #[test] 12 | fn test_dynamic_json() { 13 | let j = r#"{ 14 | "userid": 103609, 15 | "verified": true, 16 | "friendly_name": "Jason", 17 | "access_privileges": [ 18 | "user", 19 | "admin" 20 | ] 21 | }"#; 22 | 23 | let parsed: Value = serde_json::from_str(j).unwrap(); 24 | let expected = json!({ 25 | "userid": 103609, 26 | "verified": true, 27 | "friendly_name": "Jason", 28 | "access_privileges": [ 29 | "user", 30 | "admin" 31 | ] 32 | }); 33 | assert_eq!(parsed, expected); 34 | 35 | assert_eq!(parsed["userid"], 103609); 36 | assert_eq!(parsed["verified"], true); 37 | assert_eq!(parsed["friendly_name"], "Jason"); 38 | assert_eq!(parsed["access_privileges"][0], "user"); 39 | assert_eq!(parsed["access_privileges"][1], "admin"); 40 | assert_eq!(parsed["access_privileges"][2], Value::Null); 41 | assert_eq!(parsed["not-available"], Value::Null); 42 | } 43 | 44 | #[test] 45 | fn test_dynamic_pickle() { 46 | let parsed: Value = { 47 | let data = File::open("user.pkl").expect("Did you run create_pickle.py?"); 48 | pickle::from_reader(&data).unwrap() 49 | }; 50 | 51 | let expected = json!({ 52 | "userid": 103609, 53 | "verified": true, 54 | "friendly_name": "Jason", 55 | "access_privileges": [ 56 | "user", 57 | "admin" 58 | ] 59 | }); 60 | assert_eq!(parsed, expected); 61 | 62 | assert_eq!(parsed["userid"], 103609); 63 | assert_eq!(parsed["verified"], true); 64 | assert_eq!(parsed["friendly_name"], "Jason"); 65 | assert_eq!(parsed["access_privileges"][0], "user"); 66 | assert_eq!(parsed["access_privileges"][1], "admin"); 67 | assert_eq!(parsed["access_privileges"][2], Value::Null); 68 | assert_eq!(parsed["not-available"], Value::Null); 69 | } 70 | 71 | #[test] 72 | fn test_dynamic_toml() { 73 | let t = r#" 74 | [[user]] 75 | userid = 103609 76 | verified = true 77 | friendly_name = "Jason" 78 | access_privileges = [ "user", "admin" ] 79 | "#; 80 | 81 | let parsed: Value = toml::de::from_str(t).unwrap(); 82 | 83 | let expected = json!({ 84 | "user": [ 85 | { 86 | "userid": 103609, 87 | "verified": true, 88 | "friendly_name": "Jason", 89 | "access_privileges": [ 90 | "user", 91 | "admin" 92 | ] 93 | } 94 | 95 | ] 96 | }); 97 | assert_eq!(parsed, expected); 98 | 99 | let first_user = &parsed["user"][0]; 100 | assert_eq!(first_user["userid"], 103609); 101 | assert_eq!(first_user["verified"], true); 102 | assert_eq!(first_user["friendly_name"], "Jason"); 103 | assert_eq!(first_user["access_privileges"][0], "user"); 104 | assert_eq!(first_user["access_privileges"][1], "admin"); 105 | assert_eq!(first_user["access_privileges"][2], Value::Null); 106 | assert_eq!(first_user["not-available"], Value::Null); 107 | } 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /Chapter10/dynamic-data/user.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Rust-Programming-Cookbook/0afc39caaecd7d2c5343fc3c77bf484cbb9f761d/Chapter10/dynamic-data/user.pkl -------------------------------------------------------------------------------- /Chapter10/file-stuff/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file-stuff" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /Chapter10/file-stuff/hello.txt: -------------------------------------------------------------------------------- 1 | Hello World! 2 | 1535 -------------------------------------------------------------------------------- /Chapter10/file-stuff/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File}; 2 | use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, Write}; 3 | use std::path::Path; 4 | 5 | const TEST_FILE_NAME: &str = "lorem.txt"; 6 | 7 | fn read() -> io::Result<()> { 8 | let path = Path::new(TEST_FILE_NAME); 9 | 10 | let input = File::open(path)?; 11 | let buffered = BufReader::new(input); 12 | 13 | let words: Vec = buffered 14 | .lines() 15 | .map(|line| line.unwrap().split_ascii_whitespace().count()) 16 | .collect(); 17 | let avg_word_count = words.iter().sum::() as f32 / words.len() as f32; 18 | println!( 19 | "{}: Average words per line: {:.2}", 20 | path.to_string_lossy(), 21 | avg_word_count 22 | ); 23 | 24 | let mut input = File::open(path)?; 25 | let mut input_buffer = String::new(); 26 | input.read_to_string(&mut input_buffer)?; 27 | 28 | // ... or ... 29 | 30 | let lorem = fs::read_to_string(path)?; 31 | println!( 32 | "{}: Length in characters : {}", 33 | path.to_string_lossy(), 34 | lorem.len() 35 | ); 36 | 37 | input.seek(io::SeekFrom::Start(0))?; // reset file pointer to the beginning 38 | 39 | println!( 40 | "{}: Length in bytes: {}", 41 | path.to_string_lossy(), 42 | input.bytes().count() 43 | ); 44 | Ok(()) 45 | } 46 | 47 | fn write() -> io::Result<()> { 48 | let mut path = Path::new(".").to_path_buf(); 49 | 50 | path.push("hello.txt"); 51 | 52 | let mut file = File::create(path)?; 53 | println!("Opened {:?}", file.metadata()?); 54 | 55 | file.write_all(b"Hello")?; 56 | 57 | let mut buffered = BufWriter::new(file); 58 | write!(buffered, " World!")?; 59 | write!(buffered, "\n{: >width$}", width=0x5ff)?; 60 | Ok(()) 61 | } 62 | 63 | fn main() -> io::Result<()> { 64 | println!("===== READ ====="); 65 | read()?; 66 | println!(); 67 | println!("===== WRITE ===="); 68 | write()?; 69 | Ok(()) 70 | } 71 | -------------------------------------------------------------------------------- /Chapter10/filesystem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "filesystem" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | glob = "0.3.0" -------------------------------------------------------------------------------- /Chapter10/filesystem/src/main.rs: -------------------------------------------------------------------------------- 1 | use glob; 2 | use std::error::Error; 3 | use std::io; 4 | use std::path::{Path, PathBuf}; 5 | 6 | type GenericError = Box; 7 | 8 | fn walk(dir: &Path, cb: &dyn Fn(&PathBuf), recurse: bool) -> io::Result<()> { 9 | for entry in dir.read_dir()? { 10 | let entry = entry?; 11 | let path = entry.path(); 12 | if recurse && path.is_dir() { 13 | walk(&path, cb, true)?; 14 | } 15 | cb(&path); 16 | } 17 | Ok(()) 18 | } 19 | 20 | fn walk_glob(pattern: &str, cb: &dyn Fn(&PathBuf)) -> Result<(), GenericError> { 21 | for entry in glob::glob(pattern)? { 22 | cb(&entry?); 23 | } 24 | Ok(()) 25 | } 26 | 27 | fn main() -> Result<(), GenericError> { 28 | let path = Path::new("./src"); 29 | println!("Listing '{}'", path.display()); 30 | println!("==="); 31 | walk(path, &|d| println!(" {}", d.display()), true)?; 32 | println!(); 33 | 34 | let glob_pattern = "../**/*.rs"; 35 | println!("Listing by glob filter: {}", glob_pattern); 36 | println!("==="); 37 | walk_glob(glob_pattern, &|d| println!(" {}", d.display()))?; 38 | println!(); 39 | 40 | let glob_pattern = "Cargo.*"; 41 | println!("Listing by glob filter: {}", glob_pattern); 42 | println!("==="); 43 | walk_glob(glob_pattern, &|d| println!(" {}", d.display()))?; 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /Chapter10/logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "logging" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | log = "0.4" 11 | log4rs = "0.8.3" 12 | time = "0.1" -------------------------------------------------------------------------------- /Chapter10/logging/log4rs.yml: -------------------------------------------------------------------------------- 1 | refresh_rate: 30 seconds 2 | 3 | appenders: 4 | stdout: 5 | kind: console 6 | 7 | outfile: 8 | kind: file 9 | path: "outfile.log" 10 | encoder: 11 | pattern: "{d} - {m}{n}" 12 | 13 | root: 14 | level: trace 15 | appenders: 16 | - stdout 17 | 18 | loggers: 19 | special-target: 20 | level: info 21 | appenders: 22 | - outfile -------------------------------------------------------------------------------- /Chapter10/logging/outfile.log: -------------------------------------------------------------------------------- 1 | 2019-09-01T12:45:25.256922311+02:00 - WARNING, stuff is breaking down 2 | -------------------------------------------------------------------------------- /Chapter10/logging/src/main.rs: -------------------------------------------------------------------------------- 1 | use log::{debug, error, info, trace, warn}; 2 | 3 | mod custom { 4 | 5 | pub use log::Level; 6 | use log::{Metadata, Record}; 7 | 8 | pub struct EmojiLogger { 9 | pub level: Level, 10 | } 11 | 12 | impl log::Log for EmojiLogger { 13 | 14 | fn enabled(&self, metadata: &Metadata) -> bool { 15 | metadata.level() <= self.level 16 | } 17 | 18 | fn log(&self, record: &Record) { 19 | if self.enabled(record.metadata()) { 20 | let level = match record.level() { 21 | Level::Warn => "⚠️", 22 | Level::Info => "ℹ️", 23 | Level::Debug => "🐛", 24 | Level::Trace => "💡", 25 | Level::Error => "☢️", 26 | }; 27 | 28 | let utc = time::now_utc(); 29 | println!("{} | [{}] | {:<5}", utc.rfc3339(), record.target(), level); 30 | println!("{:21} {}", "", record.args()); 31 | } 32 | } 33 | 34 | fn flush(&self) {} 35 | } 36 | } 37 | 38 | static LOGGER: custom::EmojiLogger = custom::EmojiLogger { 39 | level: log::Level::Trace, 40 | }; 41 | 42 | fn log_some_stuff() { 43 | let a = 100; 44 | 45 | trace!("TRACE: Called log_some_stuff()"); 46 | debug!("DEBUG: a = {} ", a); 47 | info!("INFO: The weather is fine"); 48 | warn!("WARNING, stuff is breaking down"); 49 | warn!(target: "special-target", "WARNING, stuff is breaking down"); 50 | error!("ERROR: stopping ..."); 51 | } 52 | 53 | const USE_CUSTOM: bool = true; 54 | 55 | fn main() { 56 | if USE_CUSTOM { 57 | log::set_logger(&LOGGER) 58 | .map(|()| log::set_max_level(log::LevelFilter::Trace)) 59 | .unwrap(); 60 | } else { 61 | log4rs::init_file("log4rs.yml", Default::default()).unwrap(); 62 | } 63 | log_some_stuff(); 64 | } 65 | -------------------------------------------------------------------------------- /Chapter10/pipes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pipes" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | csv = "1.1" 11 | serde_json = "1" -------------------------------------------------------------------------------- /Chapter10/pipes/cargo: -------------------------------------------------------------------------------- 1 | year,make,model 2 | 1997,Ford,E350 3 | 1997,Ford,E350 4 | 1997,Ford,E350 5 | 1997,Ford,E350 6 | 1997,Ford,E350 7 | 1997,Ford,E350 8 | 1997,Ford,E350 9 | 1997,Ford,E350 10 | 11 | -------------------------------------------------------------------------------- /Chapter10/pipes/cars.csv: -------------------------------------------------------------------------------- 1 | year,make,model 2 | 1997,Ford,E350 3 | 1926,Bugatti,Type 35 4 | 1971,Volkswagen,Beetle 5 | 1992,Gurgel,Supermini -------------------------------------------------------------------------------- /Chapter10/pipes/src/main.rs: -------------------------------------------------------------------------------- 1 | use csv; 2 | use serde_json as json; 3 | use std::io; 4 | 5 | fn to_json(headers: &csv::StringRecord, current_row: csv::StringRecord) -> io::Result { 6 | let row: json::Map = headers 7 | .into_iter() 8 | .zip(current_row.into_iter()) 9 | .map(|(key, value)| (key.to_string(), json::Value::String(value.into()))) 10 | .collect(); 11 | Ok(json::Value::Object(row)) 12 | } 13 | 14 | fn main() -> io::Result<()> { 15 | let mut rdr = csv::ReaderBuilder::new() 16 | .trim(csv::Trim::All) 17 | .has_headers(false) 18 | .delimiter(b',') 19 | .from_reader(io::stdin()); 20 | 21 | let header_rec = rdr 22 | .records() 23 | .take(1) 24 | .next() 25 | .expect("The first line does not seem to be a valid CSV")?; 26 | 27 | for result in rdr.records() { 28 | if let Ok(json_rec) = to_json(&header_rec, result?) { 29 | println!("{}", json_rec.to_string()); 30 | } 31 | } 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /Chapter10/random-numbers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "random-numbers" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rand = {version = "0.7", features = ["small_rng"]} 9 | rand_distr = "0.2" 10 | rand_pcg = "0.2" -------------------------------------------------------------------------------- /Chapter10/random-numbers/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use rand::prelude::*; 4 | use rand::SeedableRng; 5 | use rand_distr::{Bernoulli, Distribution, Normal, Uniform}; 6 | 7 | const SAMPLES: usize = 10_000; 8 | 9 | #[test] 10 | fn test_distributions() { 11 | // seed for predictable outcomes 12 | let mut rng: StdRng = SeedableRng::from_seed([42; 32]); 13 | 14 | // Uniform distribution is the default 15 | // this is the same as thread_rng().gen_range(1, 100) 16 | let uniform = Uniform::new_inclusive(1, 100); 17 | let total_uniform: u32 = uniform.sample_iter(&mut rng).take(SAMPLES).sum(); 18 | assert!((50.0 - (total_uniform as f32 / (SAMPLES as f32)).round()).abs() <= 2.0); 19 | 20 | let bernoulli = Bernoulli::new(0.8).unwrap(); 21 | let total_bernoulli: usize = bernoulli 22 | .sample_iter(&mut rng) 23 | .take(SAMPLES) 24 | .filter(|s| *s) 25 | .count(); 26 | 27 | assert_eq!( 28 | ((total_bernoulli as f32 / SAMPLES as f32) * 10.0) 29 | .round() 30 | .trunc(), 31 | 8.0 32 | ); 33 | 34 | let normal = Normal::new(2.0, 0.5).unwrap(); 35 | let total_normal: f32 = normal.sample_iter(&mut rng).take(SAMPLES).sum(); 36 | assert_eq!((total_normal / (SAMPLES as f32)).round(), 2.0); 37 | } 38 | 39 | #[test] 40 | fn test_sequences() { 41 | // seed for predictable outcomes 42 | let mut rng: StdRng = SeedableRng::from_seed([42; 32]); 43 | 44 | let emoji = "ABCDEF".chars(); 45 | let chosen_one = emoji.clone().choose(&mut rng).unwrap(); 46 | assert_eq!(chosen_one, 'B'); 47 | 48 | let chosen = emoji.choose_multiple(&mut rng, 3); 49 | assert_eq!(chosen, ['F', 'B', 'E']); 50 | 51 | let mut three_wise_monkeys = vec!['1', '2', '3']; 52 | three_wise_monkeys.shuffle(&mut rng); 53 | three_wise_monkeys.shuffle(&mut rng); // in this case, the first time won't change anything 54 | assert_eq!(three_wise_monkeys, ['1', '3', '2']); 55 | 56 | let mut three_wise_monkeys = vec!['1', '2', '3']; 57 | let partial = three_wise_monkeys.partial_shuffle(&mut rng, 2); 58 | assert_eq!(partial.0, ['3', '2']); 59 | } 60 | 61 | #[test] 62 | fn test_rngs() { 63 | // seed for predictable outcomes 64 | let mut rng: StdRng = SeedableRng::from_seed([42; 32]); 65 | assert_eq!(rng.gen::(), 152); 66 | 67 | let mut small_rng = SmallRng::from_rng(&mut rng).unwrap(); 68 | assert_eq!(small_rng.gen::(), 174); 69 | 70 | let mut pcg = rand_pcg::Pcg32::from_rng(&mut rng).unwrap(); 71 | assert_eq!(pcg.gen::(), 135); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Chapter10/regex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "regex" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | regex = "1" -------------------------------------------------------------------------------- /Chapter10/regex/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | 4 | use regex::Regex; 5 | use std::cell::RefCell; 6 | use std::collections::HashMap; 7 | 8 | #[test] 9 | fn simple_parsing() { 10 | let re = Regex::new(r"(?P\d{4})-(?P\d{2})-(?P\d{2})").unwrap(); 11 | 12 | assert!(re.is_match("1999-12-01")); 13 | let date = re.captures("2019-02-27").unwrap(); 14 | 15 | assert_eq!("2019", &date["y"]); 16 | assert_eq!("02", &date["m"]); 17 | assert_eq!("27", &date["d"]); 18 | 19 | let fun_dates: Vec<(i32, i32, i32)> = (1..12).map(|i| (2000 + i, i, i * 2)).collect(); 20 | 21 | let multiple_dates: String = fun_dates 22 | .iter() 23 | .map(|d| format!("{}-{:02}-{:02} ", d.0, d.1, d.2)) 24 | .collect(); 25 | 26 | for (match_, expected) in re.captures_iter(&multiple_dates).zip(fun_dates.iter()) { 27 | assert_eq!(match_.get(1).unwrap().as_str(), expected.0.to_string()); 28 | assert_eq!( 29 | match_.get(2).unwrap().as_str(), 30 | format!("{:02}", expected.1) 31 | ); 32 | assert_eq!( 33 | match_.get(3).unwrap().as_str(), 34 | format!("{:02}", expected.2) 35 | ); 36 | } 37 | } 38 | 39 | #[test] 40 | fn reshuffle_groups() { 41 | let re = Regex::new(r"(?P\d{4})-(?P\d{2})-(?P\d{2})").unwrap(); 42 | 43 | let fun_dates: Vec<(i32, i32, i32)> = (1..12).map(|i| (2000 + i, i, i * 2)).collect(); 44 | 45 | let multiple_dates: String = fun_dates 46 | .iter() 47 | .map(|d| format!("{}-{:02}-{:02} ", d.0, d.1, d.2)) 48 | .collect(); 49 | 50 | let european_format = re.replace_all(&multiple_dates, "$d.$m.$y"); 51 | 52 | assert_eq!(european_format.trim(), "02.01.2001 04.02.2002 06.03.2003 08.04.2004 10.05.2005 12.06.2006 14.07.2007 16.08.2008 18.09.2009 20.10.2010 22.11.2011"); 53 | } 54 | #[test] 55 | fn count_groups() { 56 | let counter: HashMap = HashMap::new(); 57 | 58 | let phone_numbers = "+49 (1234) 45665 59 | +43(0)1234/45665 43 60 | +1 314-CALL-ME 61 | +44 1234 45665 62 | +49 (1234) 44444 63 | +44 12344 55538"; 64 | 65 | let re = Regex::new(r"(\+[\d]{1,4})").unwrap(); 66 | 67 | let prefixes = re 68 | .captures_iter(&phone_numbers) 69 | .map(|match_| match_.get(1)) 70 | .filter(|m| m.is_some()) 71 | .fold(RefCell::new(counter), |c, prefix| { 72 | { 73 | let mut counter_dict = c.borrow_mut(); 74 | let prefix = prefix.unwrap().as_str().to_string(); 75 | let count = counter_dict.get(&prefix).unwrap_or(&0) + 1; 76 | counter_dict.insert(prefix, count); 77 | } 78 | c 79 | }); 80 | 81 | let prefixes = prefixes.into_inner(); 82 | assert_eq!(prefixes.get("+49"), Some(&2)); 83 | assert_eq!(prefixes.get("+1"), Some(&1)); 84 | assert_eq!(prefixes.get("+44"), Some(&2)); 85 | assert_eq!(prefixes.get("+43"), Some(&1)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Chapter10/rusty-ml/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusty-ml" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tch = "0.1" 11 | failure ="0.1" -------------------------------------------------------------------------------- /Chapter10/sub-processes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sub-processes" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /Chapter10/sub-processes/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::io::Write; 3 | use std::process::{Command, Stdio}; 4 | 5 | fn main() -> Result<(), Box> { 6 | let mut ls_child = Command::new("ls"); 7 | if !cfg!(target_os = "windows") { 8 | ls_child.args(&["-alh"]); 9 | } 10 | println!("{}", ls_child.status()?); 11 | ls_child.current_dir("src/"); 12 | println!("{}", ls_child.status()?); 13 | 14 | let env_child = Command::new("env") 15 | .env("CANARY", "0x5ff") 16 | .stdout(Stdio::piped()) 17 | .spawn()?; 18 | 19 | let env_output = &env_child.wait_with_output()?; 20 | let canary = String::from_utf8_lossy(&env_output.stdout) 21 | .split_ascii_whitespace() 22 | .filter(|line| *line == "CANARY=0x5ff") 23 | .count(); 24 | 25 | // found it! 26 | assert_eq!(canary, 1); 27 | 28 | let mut rev_child = Command::new("rev") 29 | .stdin(Stdio::piped()) 30 | .stdout(Stdio::piped()) 31 | .spawn()?; 32 | 33 | { 34 | rev_child 35 | .stdin 36 | .as_mut() 37 | .expect("Could not open stdin") 38 | .write_all(b"0x5ff")?; 39 | } 40 | 41 | let output = rev_child.wait_with_output()?; 42 | assert_eq!(String::from_utf8_lossy(&output.stdout), "ff5x0"); 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /Chapter10/web-requests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "web-requests" 3 | version = "0.1.0" 4 | authors = ["Claus Matzinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | surf = "1.0" 11 | reqwest = "0.9" 12 | serde = "1" 13 | serde_json = "1" 14 | runtime = "0.3.0-alpha.6" 15 | -------------------------------------------------------------------------------- /Chapter10/web-requests/history.txt: -------------------------------------------------------------------------------- 1 | ls 2 | ls | sort-by name 3 | ls | sort-by name --desc 4 | ls | sort-by name 5 | ls 6 | ls --short 7 | ls | paginate 8 | cd workspace/Mine/ 9 | cd Rust-Cookbook/Chapter10 10 | ls 11 | cd web-requests/ 12 | ls 13 | code . 14 | cargo update 15 | -------------------------------------------------------------------------------- /Chapter10/web-requests/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_json; 3 | 4 | use serde::Serialize; 5 | use surf::Exception; 6 | 7 | #[derive(Serialize)] 8 | struct MyGetParams { 9 | a: u64, 10 | b: String, 11 | } 12 | 13 | fn test_reqwest() -> Result<(), Exception> { 14 | println!("> reqwest ..."); 15 | 16 | let client = reqwest::Client::new(); 17 | 18 | let mut res = client 19 | .get("https://blog.x5ff.xyz/other/cookbook2018") 20 | .send()?; 21 | 22 | assert_eq!(200, res.status()); 23 | assert_eq!("Rust is awesome\n", res.text()?); 24 | 25 | let form_values = vec![ 26 | ("custname", "Rusty Crabbington"), 27 | ("comments", "Thank you"), 28 | ("custemail", "rusty@nope.com"), 29 | ("custtel", "+1 234 33456"), 30 | ("delivery", "25th floor below ground, no elevator. sorry"), 31 | ]; 32 | 33 | let res_forms: serde_json::Value = client 34 | .post("https://httpbin.org/post") 35 | .form(&form_values) 36 | .send()? 37 | .json()?; 38 | 39 | for (name, value) in form_values.iter() { 40 | assert_eq!(res_forms["form"][name], *value); 41 | } 42 | 43 | let json_payload = json!({ 44 | "book": "Rust 2018 Cookbook", 45 | "blog": "https://blog.x5ff.xyz", 46 | }); 47 | 48 | let res_json: serde_json::Value = client 49 | .put("https://httpbin.org/anything") 50 | .json(&json_payload) 51 | .send()? 52 | .json()?; 53 | 54 | assert_eq!(res_json["json"], json_payload); 55 | 56 | let query_params = MyGetParams { 57 | a: 0x5ff, 58 | b: "https://blog.x5ff.xyz".into(), 59 | }; 60 | 61 | let res_query: serde_json::Value = client 62 | .get("https://httpbin.org/get") 63 | .query(&query_params) 64 | .send()? 65 | .json()?; 66 | 67 | assert_eq!(res_query["args"]["a"], query_params.a.to_string()); 68 | assert_eq!(res_query["args"]["b"], query_params.b); 69 | 70 | println!("> reqwest successful!"); 71 | Ok(()) 72 | } 73 | 74 | async fn test_surf() -> Result<(), Exception> { 75 | println!("> surf ..."); 76 | 77 | let client = surf::Client::new(); 78 | let mut res = client 79 | .get("https://blog.x5ff.xyz/other/cookbook2018") 80 | .await?; 81 | 82 | assert_eq!(200, res.status()); 83 | assert_eq!("Rust is awesome\n", res.body_string().await?); 84 | 85 | let form_values = vec![ 86 | ("custname", "Rusty Crabbington"), 87 | ("comments", "Thank you"), 88 | ("custemail", "rusty@nope.com"), 89 | ("custtel", "+1 234 33456"), 90 | ("delivery", "25th floor below ground, no elevator. sorry"), 91 | ]; 92 | 93 | let res_forms: serde_json::Value = client 94 | .post("https://httpbin.org/post") 95 | .body_form(&form_values)? 96 | .recv_json() 97 | .await?; 98 | 99 | for (name, value) in form_values.iter() { 100 | assert_eq!(res_forms["form"][name], *value); 101 | } 102 | 103 | let json_payload = json!({ 104 | "book": "Rust 2018 Cookbook", 105 | "blog": "https://blog.x5ff.xyz", 106 | }); 107 | 108 | let res_json: serde_json::Value = client 109 | .put("https://httpbin.org/anything") 110 | .body_json(&json_payload)? 111 | .recv_json() 112 | .await?; 113 | 114 | assert_eq!(res_json["json"], json_payload); 115 | 116 | let query_params = MyGetParams { 117 | a: 0x5ff, 118 | b: "https://blog.x5ff.xyz".into(), 119 | }; 120 | let res_query: serde_json::Value = client 121 | .get("https://httpbin.org/get") 122 | .set_query(&query_params)? 123 | .recv_json() 124 | .await?; 125 | 126 | assert_eq!(res_query["args"]["a"], query_params.a.to_string()); 127 | assert_eq!(res_query["args"]["b"], query_params.b); 128 | println!("> surf successful!"); 129 | Ok(()) 130 | } 131 | 132 | #[runtime::main] 133 | async fn main() -> Result<(), Exception> { 134 | println!("Running some tests"); 135 | test_reqwest()?; 136 | test_surf().await?; 137 | Ok(()) 138 | } 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Packt 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 | --------------------------------------------------------------------------------