diff --git a/.github/workflows/mdbook-test.yml b/.github/workflows/mdbook-test.yml index 9823d08d..f44308ae 100644 --- a/.github/workflows/mdbook-test.yml +++ b/.github/workflows/mdbook-test.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Rust uses: actions-rust-lang/setup-rust-toolchain@v1 with: - cache: 'true' + cache: "true" toolchain: stable - name: Run tests @@ -33,3 +33,20 @@ jobs: echo "❌ Some tests failed. Check the logs above for details." exit 1 fi + check-workspace: + # this job incrementally replaces the skeptic tests above as we convert examples to be written + # as workspace crates rather than inline in the mdbook + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - run: cargo check --workspace + - name: Check Results + if: always() + run: | + if [ ${{ steps.cargo_check.outcome }} == 'success' ]; then + echo "✅ Workspace check passed!" + else + echo "❌ Workspace check failed. Check the logs above for details." + exit 1 + fi diff --git a/Cargo.toml b/Cargo.toml index fb2b2b67..3fc7018f 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,35 @@ +[workspace] +members = ["crates/algorithms/*", "crates/web", "xtask"] + +[workspace.package] +edition = "2024" +version = "1.1.0" +authors = ["Brian Anderson ", "Andrew Gauger "] +license = "MIT OR Apache-2.0" +publish = false + [package] name = "rust-cookbook" version = "1.1.0" authors = ["Brian Anderson ", "Andrew Gauger "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" publish = false build = "build.rs" [features] default = [] -test-rand = [] +[workspace.dependencies] +rand = "0.9" +rand_distr = "0.5" + +# NOTE: These dependencies add dependencies to the rust playground (play.rust-lang.org). +# Be wary removing or changing these without considering this fact. +# See https://github.com/rust-lang/rust-playground/blob/f8d7de52a3c139a0df4fe116bbbff19c30668c99/top-crates/src/lib.rs#L134-L161 [dependencies] ansi_term = "0.11.0" +anyhow = "1.0" approx = "0.3" base64 = "0.22.1" bitflags = "1.3.2" @@ -28,7 +45,6 @@ env_logger = "0.11.3" flate2 = "1.0" glob = "0.3" image = "0.24" - lazy_static = "1.0" log = "0.4" log4rs = "0.8" @@ -50,7 +66,6 @@ ring = "0.17" rusqlite = { version = "0.32", features = ["chrono"] } same-file = "1.0" select = "0.6.0" - semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_derive = "1.0" @@ -59,10 +74,9 @@ sha2 = "0.10" tar = "0.4" tempfile = "3.14" thiserror = "2" -anyhow = "1.0" threadpool = "1.8" -toml = "0.8" tokio = { version = "1", features = ["full"] } +toml = "0.8" unicode-segmentation = "1.2.1" url = "2.5" walkdir = "2.5" diff --git a/build.rs b/build.rs index db06322e..039e9572 100644 --- a/build.rs +++ b/build.rs @@ -5,27 +5,17 @@ const REMOVED_TESTS: &[&str] = &[ "./src/web/clients/api/rate-limited.md", ]; -fn main() { - #[cfg(feature = "test-rand")] - { - let rand_paths = vec![ - "./src/algorithms/randomness/rand.md", - "./src/algorithms/randomness/rand-range.md", - "./src/algorithms/randomness/rand-dist.md", - "./src/algorithms/randomness/rand-custom.md", - "./src/algorithms/randomness/rand-passwd.md", - "./src/algorithms/randomness/rand-choose.md", - ]; - skeptic::generate_doc_tests(&rand_paths[..]); - return; - } +const REMOVED_PREFIXES: &[&str] = &["./src/algorithms/randomness/"]; - let paths = WalkDir::new("./src/").into_iter() +fn main() { + let paths = WalkDir::new("./src/") + .into_iter() // convert paths to Strings .map(|p| p.unwrap().path().to_str().unwrap().to_string()) // only compile markdown files .filter(|p| p.ends_with(".md")) .filter(|p| !REMOVED_TESTS.contains(&p.as_ref())) + .filter(|p| !REMOVED_PREFIXES.iter().any(|prefix| p.starts_with(prefix))) .collect::>(); skeptic::generate_doc_tests(&paths[..]); diff --git a/crates/algorithms/randomness/Cargo.toml b/crates/algorithms/randomness/Cargo.toml new file mode 100644 index 00000000..df7a3187 --- /dev/null +++ b/crates/algorithms/randomness/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "randomness" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +rand.workspace = true +rand_distr.workspace = true diff --git a/crates/algorithms/randomness/src/bin/choose.rs b/crates/algorithms/randomness/src/bin/choose.rs new file mode 100644 index 00000000..54a30c5d --- /dev/null +++ b/crates/algorithms/randomness/src/bin/choose.rs @@ -0,0 +1,19 @@ +use rand::Rng; + +const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789)(*&^%$#@!~"; +const PASSWORD_LEN: usize = 30; + +fn main() { + let mut rng = rand::rng(); + + let password: String = (0..PASSWORD_LEN) + .map(|_| { + let idx = rng.random_range(0..CHARSET.len()); + char::from(CHARSET[idx]) + }) + .collect(); + + println!("{password}"); +} diff --git a/crates/algorithms/randomness/src/bin/custom.rs b/crates/algorithms/randomness/src/bin/custom.rs new file mode 100644 index 00000000..10611023 --- /dev/null +++ b/crates/algorithms/randomness/src/bin/custom.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] +use rand::Rng; + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +impl Point { + fn random(rng: &mut R) -> Self { + Point { + x: rng.random(), + y: rng.random(), + } + } +} + +fn main() { + let mut rng = rand::rng(); + let rand_tuple = rng.random::<(i32, bool, f64)>(); + let rand_point = Point::random(&mut rng); + println!("Random tuple: {:?}", rand_tuple); + println!("Random Point: {:?}", rand_point); +} diff --git a/crates/algorithms/randomness/src/bin/dist.rs b/crates/algorithms/randomness/src/bin/dist.rs new file mode 100644 index 00000000..2db8ca01 --- /dev/null +++ b/crates/algorithms/randomness/src/bin/dist.rs @@ -0,0 +1,12 @@ +use rand_distr::{Distribution, LogNormal, Normal}; + +fn main() { + let mut rng = rand::rng(); + let normal = Normal::new(2.0, 3.0).expect("Failed to create normal distribution"); + let log_normal = LogNormal::new(1.0, 0.5).expect("Failed to create log-normal distribution"); + + let v = normal.sample(&mut rng); + println!("{} is from a N(2, 9) distribution", v); + let v = log_normal.sample(&mut rng); + println!("{} is from an ln N(1, 0.25) distribution", v); +} diff --git a/crates/algorithms/randomness/src/bin/passwd.rs b/crates/algorithms/randomness/src/bin/passwd.rs new file mode 100644 index 00000000..b571e9a9 --- /dev/null +++ b/crates/algorithms/randomness/src/bin/passwd.rs @@ -0,0 +1,13 @@ +use rand::Rng; +use rand_distr::Alphanumeric; + +fn main() { + const PASSWORD_LEN: usize = 30; + let mut rng = rand::rng(); + + let password: String = (0..PASSWORD_LEN) + .map(|_| rng.sample(Alphanumeric) as char) + .collect(); + + println!("{password}"); +} diff --git a/crates/algorithms/randomness/src/bin/rand.rs b/crates/algorithms/randomness/src/bin/rand.rs new file mode 100644 index 00000000..73ad945a --- /dev/null +++ b/crates/algorithms/randomness/src/bin/rand.rs @@ -0,0 +1,7 @@ +use rand::Rng; + +fn main() { + let mut rng = rand::rng(); + let random_number: u32 = rng.random(); + println!("Random number: {random_number}"); +} diff --git a/crates/algorithms/randomness/src/bin/random_range.rs b/crates/algorithms/randomness/src/bin/random_range.rs new file mode 100644 index 00000000..25033526 --- /dev/null +++ b/crates/algorithms/randomness/src/bin/random_range.rs @@ -0,0 +1,7 @@ +use rand::Rng; + +fn main() { + let mut rng = rand::rng(); + println!("Integer: {}", rng.random_range(0..10)); + println!("Float: {}", rng.random_range(0.0..10.0)); +} diff --git a/crates/algorithms/randomness/src/bin/uniform.rs b/crates/algorithms/randomness/src/bin/uniform.rs new file mode 100644 index 00000000..359b2aa4 --- /dev/null +++ b/crates/algorithms/randomness/src/bin/uniform.rs @@ -0,0 +1,15 @@ +use rand_distr::{Distribution, Uniform}; + +fn main() { + let mut rng = rand::rng(); + let die = + Uniform::new_inclusive(1, 6).expect("Failed to create uniform distribution: invalid range"); + + loop { + let throw = die.sample(&mut rng); + println!("Roll the die: {}", throw); + if throw == 6 { + break; + } + } +} diff --git a/src/algorithms/randomness/rand-choose.md b/src/algorithms/randomness/rand-choose.md index ad4a2f25..b29fa781 100644 --- a/src/algorithms/randomness/rand-choose.md +++ b/src/algorithms/randomness/rand-choose.md @@ -2,29 +2,11 @@ [![rand-badge]][rand] [![cat-os-badge]][cat-os] -Randomly generates a string of given length ASCII characters with custom -user-defined bytestring, with [`gen_range`]. +Randomly generates a string of given length ASCII characters with custom user-defined bytestring, +with [`random_range`]. -```rust,edition2018 -use rand::Rng; - -const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789)(*&^%$#@!~"; -const PASSWORD_LEN: usize = 30; - -fn main() { - let mut rng = rand::rng(); - - let password: String = (0..PASSWORD_LEN) - .map(|_| { - let idx = rng.gen_range(0..CHARSET.len()); - char::from(CHARSET[idx]) - }) - .collect(); - - println!("{:?}", password); -} +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/choose.rs }} ``` -[`gen_range`]: https://docs.rs/rand/0.9/rand/trait.Rng.html#method.gen_range +[`random_range`]: https://docs.rs/rand/latest/rand/trait.Rng.html#method.random_range diff --git a/src/algorithms/randomness/rand-custom.md b/src/algorithms/randomness/rand-custom.md index e0b0e854..ca3294d9 100644 --- a/src/algorithms/randomness/rand-custom.md +++ b/src/algorithms/randomness/rand-custom.md @@ -2,35 +2,12 @@ [![rand-badge]][rand] [![cat-science-badge]][cat-science] -Randomly generates a tuple `(i32, bool, f64)` and variable of user defined type `Point`. -Implements the [`Distribution`] trait on type Point for [`Standard`] in order to allow random generation. +Randomly generates a tuple `(i32, bool, f64)` and variable of user defined type `Point`. Implements +the [`Distribution`] trait on type Point for [`Standard`] in order to allow random generation. -```rust,edition2018 -use rand::Rng; - -#[derive(Debug)] -struct Point { - x: i32, - y: i32, -} - -impl Point { - fn random(rng: &mut R) -> Self { - Point { - x: rng.random(), - y: rng.random(), - } - } -} - -fn main() { - let mut rng = rand::rng(); - let rand_tuple = rng.random::<(i32, bool, f64)>(); - let rand_point = Point::random(&mut rng); - println!("Random tuple: {:?}", rand_tuple); - println!("Random Point: {:?}", rand_point); -} +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/custom.rs }} ``` -[Distribution]: https://docs.rs/rand/0.9/rand/distr/trait.Distribution.html -[Standard]: https://docs.rs/rand/0.9/rand/distr/struct.Standard.html +[`Distribution`]: https://docs.rs/rand/latest/rand/distr/trait.Distribution.html +[`Standard`]: https://docs.rs/rand/latest/rand/distr/struct.Standard.html diff --git a/src/algorithms/randomness/rand-dist.md b/src/algorithms/randomness/rand-dist.md index bbe89ccb..6a929ba4 100644 --- a/src/algorithms/randomness/rand-dist.md +++ b/src/algorithms/randomness/rand-dist.md @@ -2,32 +2,16 @@ [![rand_distr-badge]][rand_distr] [![cat-science-badge]][cat-science] -By default, random numbers in the `rand` crate have -[uniform distribution]. The [`rand_distr`] crate provides -other kinds of distributions. To use them, you instantiate -a distribution, then sample from that distribution using -[`Distribution::sample`] with help of a random-number -generator [`rand::Rng`]. +By default, random numbers in the `rand` crate have [uniform distribution]. The [`rand_distr`] crate +provides other kinds of distributions. To use them, you instantiate a distribution, then sample from +that distribution using [`Distribution::sample`] with help of a random-number generator +[`rand::Rng`]. -The [distributions available are documented here][rand-distributions]. -An example using the [`Normal`] distribution is shown below. +The [distributions available are documented here][rand-distributions]. An example using the +[`Normal`] distribution is shown below. -```rust,edition2018 -use rand::Rng; -use rand_distr::{Distribution, LogNormal, Normal}; - -fn main() { - let mut rng = rand::rng(); - let normal = Normal::new(2.0, 3.0) - .expect("Failed to create normal distribution"); - let log_normal = LogNormal::new(1.0, 0.5) - .expect("Failed to create log-normal distribution"); - - let v = normal.sample(&mut rng); - println!("{} is from a N(2, 9) distribution", v); - let v = log_normal.sample(&mut rng); - println!("{} is from an ln N(1, 0.25) distribution", v); -} +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/dist.rs }} ``` [`Distribution::sample`]: https://docs.rs/rand/0.9/rand/distr/trait.Distribution.html#tymethod.sample diff --git a/src/algorithms/randomness/rand-passwd.md b/src/algorithms/randomness/rand-passwd.md index de010800..fd3c746e 100644 --- a/src/algorithms/randomness/rand-passwd.md +++ b/src/algorithms/randomness/rand-passwd.md @@ -2,32 +2,11 @@ [![rand-badge]][rand] [![cat-os-badge]][cat-os] -Randomly generates a string of given length ASCII characters in the range `A-Z, -a-z, 0-9`, with [`Alphanumeric`] sample. +Randomly generates a string of given length ASCII characters in the range `A-Z, a-z, 0-9`, with +[`Alphanumeric`] sample. -```rust,edition2018 -use rand::Rng; - -fn main() { - const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789"; - const PASSWORD_LEN: usize = 30; - let mut rng = rand::rng(); - - let password: String = (0..PASSWORD_LEN) - .map(|_| { - let idx = rng.random_range(0..CHARSET.len()); - CHARSET[idx] as char - }) - .collect(); - - println!("{}", password); -} +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/passwd.rs }} ``` [`Alphanumeric`]: https://docs.rs/rand/0.9/rand/distr/struct.Alphanumeric.html - - - - diff --git a/src/algorithms/randomness/rand-range.md b/src/algorithms/randomness/rand-range.md index 0935aaa6..438753f2 100644 --- a/src/algorithms/randomness/rand-range.md +++ b/src/algorithms/randomness/rand-range.md @@ -2,37 +2,18 @@ [![rand-badge]][rand] [![cat-science-badge]][cat-science] -Generates a random value within half-open `[0, 10)` range (not including `10`) with [`Rng::gen_range`]. +Generates a random value within half-open `[0, 10)` range (not including `10`) with [`Rng::random_range`]. -```rust,edition2018 -use rand::Rng; - -fn main() { - let mut rng = rand::rng(); - println!("Integer: {}", rng.gen_range(0..10)); - println!("Float: {}", rng.gen_range(0.0..10.0)); -} +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/random_range.rs }} ``` -[`Uniform`] can obtain values with [uniform distribution]. -This has the same effect, but may be faster when repeatedly generating numbers -in the same range. - -```rust,edition2018 -use rand::Rng; -use rand_distr::{Distribution, Uniform}; +[`Uniform`] can obtain values with [uniform distribution]. This has the same effect, but may be +faster when repeatedly generating numbers in the same range. -fn main() { - let mut rng = rand::rng(); - let die = Uniform::new_inclusive(1, 6) - .expect("Failed to create uniform distribution: invalid range"); +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/uniform.rs }} +``` - loop { - let throw = die.sample(&mut rng); - println!("Roll the die: {}", throw); - if throw == 6 { - break; - } - } -} -``` \ No newline at end of file +[`Rng::random_range`]: https://docs.rs/rand/latest/rand/trait.Rng.html#method.random_range +[`Uniform`]: https://docs.rs/rand_distr/latest/rand_distr/struct.Uniform.html diff --git a/src/algorithms/randomness/rand.md b/src/algorithms/randomness/rand.md index c24e5983..2642371b 100644 --- a/src/algorithms/randomness/rand.md +++ b/src/algorithms/randomness/rand.md @@ -2,21 +2,14 @@ [![rand-badge]][rand] [![cat-science-badge]][cat-science] -Generates random numbers with help of random-number -generator [`rand::Rng`] obtained via [`rand::rng`]. Each thread has an -initialized generator. Integers are uniformly distributed over the range of the -type, and floating point numbers are uniformly distributed from 0 up to but not +Generates random numbers with help of random-number generator [`rand::Rng`] obtained via +[`rand::rng()`]. Each thread has an initialized generator. Integers are uniformly distributed over the +range of the type, and floating point numbers are uniformly distributed from 0 up to but not including 1. -```rust,edition2018 -use rand::Rng; - -fn main() { - let mut rng = rand::thread_rng(); - let random_number: u32 = rng.gen(); - println!("Random number: {}", random_number); -} +```rust +{{#include ../../../crates/algorithms/randomness/src/bin/rand.rs }} ``` -[`rand::Rng`]: https://docs.rs/rand/0.9/rand/trait.Rng.html -[`rand::rng`]: https://docs.rs/rand/0.9/rand/fn.rng.html +[`rand::Rng`]: https://docs.rs/rand/latest/rand/trait.Rng.html +[`rand::rng()`]: https://docs.rs/rand/latest/rand/fn.rng.html