Add print-ready PDF (#127)

This commit is contained in:
Luca Palmieri 2024-08-06 17:09:04 +02:00 committed by GitHub
parent 0910f3909f
commit c9ed60daf5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 860 additions and 7 deletions

View file

@ -16,8 +16,10 @@ jobs:
with:
fetch-depth: 0
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install plugin
- name: Install exercise plugin
run: cargo install --path helpers/mdbook-exercise-linker
- name: Install link shortener plugin
run: cargo install --path helpers/mdbook-link-shortener
- name: Install mdbook-pandoc and related dependencies
run: |
cargo install mdbook-pandoc --locked --version 0.7.1
@ -63,6 +65,8 @@ jobs:
with:
tool: mdbook
- name: Build book
env:
LINK_SHORTENER_VERIFY: "true"
run: |
cd book
mdbook build
@ -82,11 +86,25 @@ jobs:
# When you support multiple formats, the output directory changes
# to include the format in its path.
path: book/book/html
# Upload the PDF book as an artifact
- uses: actions/upload-artifact@v4
with:
name: online-pdf
path: book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: paperback
path: book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
path: book/book/pandoc/paperback/100-exercises-to-learn-rust.pdf
is_fresh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sudo apt-get update && sudo apt-get install -y jq
- run: |
./helpers/json2redirects.sh book/link2alias.json > site/redirects
# Verify nothing has changed, meaning that the redirect file is up-to-date
- run: |
git diff --exit-code site/redirects
formatter:
runs-on: ubuntu-latest

105
Cargo.lock generated
View file

@ -163,6 +163,15 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "bimap"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -280,6 +289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
@ -304,6 +314,18 @@ dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.2"
@ -448,6 +470,12 @@ dependencies = [
name = "drop"
version = "0.1.0"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "elasticlunr-rs"
version = "3.0.2"
@ -667,6 +695,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@ -771,6 +808,12 @@ dependencies = [
name = "heap"
version = "0.1.0"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
@ -1037,6 +1080,15 @@ dependencies = [
"ticket_fields",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -1182,7 +1234,7 @@ dependencies = [
"once_cell",
"opener",
"pathdiff",
"pulldown-cmark",
"pulldown-cmark 0.10.3",
"regex",
"serde",
"serde_json",
@ -1206,6 +1258,21 @@ dependencies = [
"serde_json",
]
[[package]]
name = "mdbook-link-shortener"
version = "0.1.0"
dependencies = [
"anyhow",
"bimap",
"clap",
"itertools",
"mdbook",
"pulldown-cmark 0.11.0",
"pulldown-cmark-to-cmark",
"semver",
"serde_json",
]
[[package]]
name = "memchr"
version = "2.7.4"
@ -1615,7 +1682,20 @@ checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
dependencies = [
"bitflags 2.6.0",
"memchr",
"pulldown-cmark-escape",
"pulldown-cmark-escape 0.10.1",
"unicase",
]
[[package]]
name = "pulldown-cmark"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0"
dependencies = [
"bitflags 2.6.0",
"getopts",
"memchr",
"pulldown-cmark-escape 0.11.0",
"unicase",
]
@ -1625,6 +1705,21 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3"
[[package]]
name = "pulldown-cmark-escape"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "pulldown-cmark-to-cmark"
version = "15.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9c77db841443d89a57ae94f22d29c022f6d9f41b00bddbf1f4024dbaf4bdce1"
dependencies = [
"pulldown-cmark 0.11.0",
]
[[package]]
name = "quote"
version = "1.0.36"
@ -2290,6 +2385,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "unwrap"
version = "0.1.0"

View file

@ -1,5 +1,11 @@
[workspace]
members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"]
members = [
"exercises/*/*",
"helpers/common",
"helpers/mdbook-exercise-linker",
"helpers/mdbook-link-shortener",
"helpers/ticket_fields",
]
resolver = "2"
# This is needed to guarantee the expected behaviour on that specific exercise,

View file

@ -39,8 +39,44 @@ header-includes = [
"\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},fontsize=\\small}",
]
[output.pandoc.profile.paperback]
output-file = "100-exercises-to-learn-rust.pdf"
to = "latex"
highlight-style = "monochrome"
# We use `lualatext` because, right now, it's the only engine
# that supports fallback fonts, which we need for emojis.
pdf-engine = "lualatex"
[output.pandoc.profile.paperback.variables]
subtitle = "A hands-on course by Mainmatter"
# You can get these fonts here: https://fonts.google.com/selection?query=noto+color+
mainfont = "Noto Serif"
sansfont = "Noto Sans"
monofont = "Noto Sans Mono"
mainfontfallback = ["Noto Color Emoji:mode=harf"]
sansfontfallback = ["Noto Color Emoji:mode=harf"]
monofontfallback = [
"Noto Color Emoji:mode=harf",
]
urlstyle = "rm"
documentclass = "book"
fontsize = "11pt"
geometry = "papersize={8in,10in},top=2cm,bottom=2cm,left=2.4cm,right=2.4cm"
header-includes = [
# Reduce font size of code blocks
"\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},fontsize=\\small}",
]
links-as-notes = true
[output.html]
git-repository-url = "https://github.com/mainmatter/100-exercises-to-learn-rust"
[preprocessor.exercise-linker]
exercise_root_url = "https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises"
[preprocessor.link-shortener]
base_url = "https://ruex.io"
renderers = ["pandoc"]
mapping = "link2alias.json"
verify = false
after = ["exercise-linker"]

189
book/link2alias.json Normal file
View file

@ -0,0 +1,189 @@
{
"https://blog.acolyer.org/2019/05/28/cheri-abi/": "f2u",
"https://crates.io": "f4q",
"https://crates.io/crates/cargo-modules": "f2n",
"https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-types": "ffr",
"https://doc.rust-lang.org/book/title-page.html": "f6t",
"https://doc.rust-lang.org/cargo/reference/cargo-targets.html#cargo-targets": "f4m",
"https://doc.rust-lang.org/cargo/reference/profiles.html": "ffc",
"https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html": "f45",
"https://doc.rust-lang.org/nomicon/": "f6u",
"https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast": "f2z",
"https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence": "fzf",
"https://doc.rust-lang.org/reference/lifetime-elision.html": "f4c",
"https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html": "fxy",
"https://doc.rust-lang.org/std/cmp/index.html": "fzm",
"https://doc.rust-lang.org/std/cmp/trait.PartialEq.html": "fzz",
"https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html": "fzb",
"https://doc.rust-lang.org/std/convert/trait.From.html#implementors": "fzp",
"https://doc.rust-lang.org/std/convert/trait.Into.html#implementors": "fzl",
"https://doc.rust-lang.org/std/iter/trait.FusedIterator.html": "f4s",
"https://doc.rust-lang.org/std/iter/trait.Iterator.html": "fxf",
"https://doc.rust-lang.org/std/keyword.for.html": "ffj",
"https://doc.rust-lang.org/std/keyword.while.html": "ffh",
"https://doc.rust-lang.org/std/macro.panic.html": "ffl",
"https://doc.rust-lang.org/std/mem/fn.size_of.html": "f27",
"https://doc.rust-lang.org/std/ops/index.html": "fzn",
"https://doc.rust-lang.org/std/ops/trait.Add.html": "fz4",
"https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coercion": "fzt",
"https://doc.rust-lang.org/std/ops/trait.Div.html": "fzv",
"https://doc.rust-lang.org/std/ops/trait.Mul.html": "fz6",
"https://doc.rust-lang.org/std/ops/trait.Rem.html": "fz8",
"https://doc.rust-lang.org/std/ops/trait.Sub.html": "fzx",
"https://doc.rust-lang.org/std/prelude/index.html": "f2c",
"https://doc.rust-lang.org/std/primitive.i32.html#associatedconstant.MAX": "ffe",
"https://doc.rust-lang.org/std/primitive.i32.html#associatedconstant.MIN": "ff7",
"https://doc.rust-lang.org/std/primitive.u32.html#associatedconstant.MAX": "ffw",
"https://doc.rust-lang.org/std/slice/struct.Iter.html": "f4d",
"https://doc.rust-lang.org/std/string/struct.String.html": "f26",
"https://doc.rust-lang.org/std/sync/atomic/index.html": "fxh",
"https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter": "f4j",
"https://docs.rs/dhat/latest/dhat/": "f2y",
"https://docs.rs/itertools/": "fx2",
"https://docs.rs/thiserror/latest/thiserror/": "f4n",
"https://docs.rs/tokio-stream/latest/tokio_stream/": "f65",
"https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.merge": "f6m",
"https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html": "f63",
"https://docs.rs/tokio/latest/tokio/task/struct.JoinError.html": "f6z",
"https://docs.rust-embedded.org/book/": "f6k",
"https://en.wikipedia.org/wiki/Dangling_pointer": "f2h",
"https://en.wikipedia.org/wiki/Data_segment": "fx7",
"https://en.wikipedia.org/wiki/Memory_address": "f2r",
"https://en.wikipedia.org/wiki/Stack_overflow": "f2e",
"https://en.wikipedia.org/wiki/Two%27s_complement": "ff9",
"https://en.wikipedia.org/wiki/UTF-8": "f2v",
"https://exercism.io": "f6r",
"https://github.com/LukeMathWalker/cargo-chef": "ffb",
"https://github.com/LukeMathWalker/wiremock-rs": "ffm",
"https://github.com/dtolnay/cargo-expand": "fzq",
"https://github.com/dtolnay/proc-macro-workshop": "fzw",
"https://github.com/mainmatter/100-exercises-to-learn-rust": "ff6",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/01_intro/00_welcome": "ff3",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/01_intro/01_syntax": "ffq",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/00_intro": "ff5",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/01_integers": "fft",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/02_variables": "ffy",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/03_if_else": "ffu",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/04_panics": "ffk",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/05_factorial": "ffs",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/06_while": "ffg",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/07_for": "ffd",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/08_overflow": "f2f",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/09_saturating": "f22",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/10_as_casting": "f24",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/00_intro": "f2x",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/01_struct": "f28",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/02_validation": "f2b",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/03_modules": "f2m",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/04_visibility": "f23",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/05_encapsulation": "f2q",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/06_ownership": "f25",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/07_setters": "f2w",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/08_stack": "f29",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/09_heap": "f2p",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/10_references_in_memory": "f2l",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/11_destructor": "f2g",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/12_outro": "f2j",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/00_intro": "f2d",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/01_trait": "f2a",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/02_orphan_rule": "fz2",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/03_operator_overloading": "fz3",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/04_derive": "fz7",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/05_trait_bounds": "fz9",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/06_str_slice": "fzr",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/07_deref": "fzy",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/08_sized": "fzu",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/09_from": "fzk",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/10_assoc_vs_generic": "fzs",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/11_clone": "fzh",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/12_copy": "fzg",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/13_drop": "fzj",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/14_outro": "fzc",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/00_intro": "fza",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/01_enum": "f4f",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/02_match": "f42",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/03_variants_with_data": "f4z",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/04_if_let": "f44",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/05_nullability": "f4x",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/06_fallibility": "f46",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/07_unwrap": "f4v",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/08_error_enums": "f48",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/09_error_trait": "f4b",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/10_packages": "f43",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/11_dependencies": "f4w",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/12_thiserror": "f47",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/13_try_from": "f4e",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/14_source": "f49",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/15_outro": "f4y",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/00_intro": "f4u",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/01_arrays": "f4p",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/02_vec": "f4l",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/03_resizing": "f4k",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/04_iterators": "f4h",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/05_iter": "f4g",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/06_lifetimes": "f4a",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/07_combinators": "fxz",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/08_impl_trait": "fx4",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/09_impl_trait_2": "fxx",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/10_slices": "fx6",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/11_mutable_slices": "fxv",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/12_two_states": "fx8",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/13_index": "fxb",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/14_index_mut": "fxn",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/15_hashmap": "fxm",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/16_btreemap": "fx3",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/00_intro": "fxq",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/01_threads": "fxw",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/02_static": "fxe",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/03_leak": "fx9",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/04_scoped_threads": "fxr",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/05_channels": "fxt",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/06_interior_mutability": "fxu",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/07_ack": "fxp",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/08_client": "fxl",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/09_bounded": "fxk",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/10_patch": "fxs",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/11_locks": "fxj",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/12_rw_lock": "fxd",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/13_without_channels": "fxc",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/14_sync": "fxa",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/00_intro": "f6f",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/01_async_fn": "f62",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/02_spawn": "f64",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/03_runtime": "f6x",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/04_future": "f66",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/05_blocking": "f68",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/06_async_aware_primitives": "f6b",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/07_cancellation": "f6q",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/08_outro": "f6e",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/solutions": "ffz",
"https://github.com/mainmatter/rust-advanced-testing-workshop": "fzd",
"https://github.com/rust-lang/rustlings": "f69",
"https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/": "ffa",
"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/": "f4r",
"https://mainmatter.com/contact/": "ff2",
"https://mainmatter.com/rust-consulting/": "fff",
"https://marabos.nl/atomics/": "fxg",
"https://nostarch.com/rust-rustaceans": "f6p",
"https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory": "f2k",
"https://owasp.org/www-community/vulnerabilities/Using_freed_memory": "f2s",
"https://pavex.dev": "ffn",
"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=36e5ddbe3b3f741dfa9f74c956622bac": "ffp",
"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=afedf7062298ca8f5a248bc551062eaa": "fx5",
"https://rust-exercises.com/100-exercises-to-learn-rust.pdf": "ffx",
"https://rust-exercises.com/100-exercises/": "ff4",
"https://rust-exercises.com/advanced-testing/": "f6s",
"https://rust-exercises.com/telemetry/": "f6h",
"https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case": "fze",
"https://rust-lang.github.io/wg-async/vision/submitted_stories/status_quo/barbara_battles_buffered_streams.html": "f6w",
"https://ryhl.io/blog/async-what-is-blocking/": "f6v",
"https://tokio.rs/tokio/tutorial/select": "f6n",
"https://valgrind.org/docs/manual/dh-manual.html": "f2t",
"https://veykril.github.io/tlborm/": "fz5",
"https://without.boats/blog/the-scoped-task-trilemma/": "f67",
"https://www.lpalmieri.com/": "ffv",
"https://www.lpalmieri.com/posts/2020-12-11-zero-to-production-6-domain-modelling/": "f4t",
"https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/": "f6y",
"https://www.youtube.com/playlist?list=PLqbS7AVVErFirH9armw8yXlE6dacF-A6z": "f6l",
"https://zero2prod.com": "ff8"
}

12
helpers/json2redirects.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/bash
# Ensure the JSON file is provided as an argument
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <input_json_file>"
exit 1
fi
input_file=$1
# Use jq to parse the JSON and format the output
jq -r 'to_entries[] | "/" + .value + " " + .key' "$input_file"

View file

@ -3,7 +3,6 @@ use mdbook::book::Book;
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use mdbook::BookItem;
/// A no-op preprocessor.
pub struct ExerciseLinker;
impl ExerciseLinker {

View file

@ -0,0 +1,15 @@
[package]
name = "mdbook-link-shortener"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
bimap = { version = "0.6.3", features = ["serde"] }
clap = { version = "4.5.4", features = ["derive"] }
itertools = "0.13.0"
mdbook = "0.4.40"
pulldown-cmark = "0.11.0"
pulldown-cmark-to-cmark = "15"
semver = "1.0.23"
serde_json = "1.0.117"

View file

@ -0,0 +1,224 @@
use anyhow::{Context, Error};
use bimap::BiHashMap;
use itertools::Itertools;
use mdbook::book::{Book, Chapter};
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use mdbook::BookItem;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::File;
use std::path::PathBuf;
use std::str::FromStr;
pub struct LinkShortener;
struct AliasGenerator {
cursors: [usize; 3],
}
impl AliasGenerator {
const ALPHABET: &'static [u8] = b"f2z4x6v8bnm3q5w7e9rtyuplkshgjdca";
fn new() -> AliasGenerator {
AliasGenerator { cursors: [0, 0, 0] }
}
/// Generate a 4 alphanumeric long alias, starting from "aaaa" and incrementing by one each time
/// until "9999", using only lowercase letters and numbers.
/// We skip ambiguous characters like "0", "o", "1", "l".
fn next(&mut self) -> String {
let mut alias = String::with_capacity(4);
for cursor in &mut self.cursors {
alias.push(Self::ALPHABET[*cursor] as char);
}
for cursor in self.cursors.iter_mut().rev() {
if *cursor == Self::ALPHABET.len() - 1 {
*cursor = 0;
} else {
*cursor += 1;
break;
}
}
alias
}
/// Generate a unique alias that is not already used by the `link2alias` map.
fn next_until_unique(&mut self, link2alias: &BiHashMap<String, String>) -> String {
let mut alias = self.next();
while link2alias.contains_right(&alias) {
alias = self.next();
}
alias
}
}
impl LinkShortener {
pub fn new() -> LinkShortener {
LinkShortener
}
}
impl Preprocessor for LinkShortener {
fn name(&self) -> &str {
"link-shortener"
}
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
let config = ctx
.config
.get_preprocessor(self.name())
.context("Failed to get preprocessor configuration")?;
let root_url = {
let root_url = config.get("base_url").context("Failed to get `base_url`")?;
root_url
.as_str()
.context("`base_url` is not a string")?
.to_owned()
};
let mapping = {
let mapping = config.get("mapping").context("Failed to get `mapping`")?;
let mapping = mapping
.as_str()
.context("`mapping` is not a string")?
.to_owned();
PathBuf::from_str(&mapping).context("Failed to parse `mapping` as a path")?
};
let mut link2alias = {
match File::open(&mapping) {
Ok(file) => {
serde_json::from_reader(file).context("Failed to parse existing mapping")?
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
BiHashMap::new()
} else {
return Err(e).context("Failed to open existing mapping");
}
}
}
};
let verify = config
.get("verify")
.context("Failed to get `verify`")?
.as_bool()
.context("`verify` is not a boolean")?;
// Env var overrides config
let verify = std::env::var("LINK_SHORTENER_VERIFY")
.map(|v| v == "true")
.unwrap_or(verify);
let mut alias_gen = AliasGenerator::new();
book.sections.iter_mut().for_each(|i| {
if let BookItem::Chapter(c) = i {
c.content = replace_anchors(c, &root_url, &mut alias_gen, &mut link2alias, verify)
.expect("Error converting links for chapter");
for i in c.sub_items.iter_mut() {
if let BookItem::Chapter(sub_chapter) = i {
sub_chapter.content = replace_anchors(
sub_chapter,
&root_url,
&mut alias_gen,
&mut link2alias,
verify,
)
.expect("Error converting links for subchapter");
}
}
}
});
if !verify {
std::fs::create_dir_all(mapping.parent().expect("Mapping file path has no parent"))?;
let mut file = File::create(&mapping).context("Failed to upsert mapping file")?;
let ordered = link2alias.iter().collect::<BTreeMap<_, _>>();
serde_json::to_writer_pretty(&mut file, &ordered)?;
}
Ok(book)
}
fn supports_renderer(&self, _renderer: &str) -> bool {
true
}
}
fn replace_anchors(
chapter: &mut Chapter,
root_url: &str,
alias_gen: &mut AliasGenerator,
link2alias: &mut BiHashMap<String, String>,
verify: bool,
) -> Result<String, anyhow::Error> {
use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag};
use pulldown_cmark_to_cmark::cmark;
let mut buf = String::with_capacity(chapter.content.len());
let mut unshortened_links = BTreeSet::new();
let events = Parser::new_ext(&chapter.content, Options::all())
.map(|e| {
let Event::Start(Tag::Link {
link_type,
dest_url,
title,
id,
}) = &e
else {
return e;
};
match link_type {
LinkType::Autolink
| LinkType::Shortcut
| LinkType::Inline
| LinkType::Reference
| LinkType::Collapsed => {
if dest_url.starts_with("http") {
let alias = if let Some(alias) = link2alias.get_by_left(dest_url.as_ref()) {
alias.to_owned()
} else {
if verify {
unshortened_links.insert(dest_url.to_string());
return e;
}
let alias = alias_gen.next_until_unique(&link2alias);
alias
};
link2alias.insert(dest_url.to_string(), alias.clone());
Event::Start(Tag::Link {
link_type: link_type.to_owned(),
dest_url: CowStr::from(format!(
"{root_url}/{alias}",
root_url = root_url,
alias = alias
)),
title: title.clone(),
id: id.clone(),
})
} else {
e
}
}
LinkType::Email
| LinkType::ReferenceUnknown
| LinkType::CollapsedUnknown
| LinkType::ShortcutUnknown => e,
}
})
.collect_vec();
if verify && !unshortened_links.is_empty() {
let unshortened_links = unshortened_links.iter().join(", ");
return Err(anyhow::anyhow!(
"The following links are not shortened: {unshortened_links}\nRun again with `LINK_SHORTENER_VERIFY=false` to update the mapping \
with the shortened links."
));
}
cmark(events.into_iter(), &mut buf)
.map(|_| buf)
.map_err(|err| anyhow::anyhow!("Markdown serialization failed: {err}"))
}

View file

@ -0,0 +1,66 @@
use clap::Parser;
use mdbook::errors::Error;
use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
use mdbook_link_shortener::LinkShortener;
use semver::{Version, VersionReq};
use std::io;
use std::process;
#[derive(clap::Parser, Debug)]
#[command(version, about)]
pub struct Cli {
#[command(subcommand)]
sub: Option<SubCommand>,
}
#[derive(clap::Parser, Debug)]
pub enum SubCommand {
#[clap(name = "supports")]
Supports(Supports),
}
#[derive(clap::Parser, Debug)]
pub struct Supports {
#[arg(long)]
renderer: String,
}
fn main() -> Result<(), anyhow::Error> {
let cli = Cli::parse();
let preprocessor = LinkShortener::new();
if let Some(SubCommand::Supports(Supports { renderer })) = cli.sub {
let code = if preprocessor.supports_renderer(&renderer) {
0
} else {
1
};
process::exit(code);
}
handle_preprocessing(&preprocessor)?;
Ok(())
}
fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
let book_version = Version::parse(&ctx.mdbook_version)?;
let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?;
if !version_req.matches(&book_version) {
eprintln!(
"Warning: The {} plugin was built against version {} of mdbook, \
but we're being called from version {}",
pre.name(),
mdbook::MDBOOK_VERSION,
ctx.mdbook_version
);
}
let processed_book = pre.run(&ctx, book)?;
serde_json::to_writer(io::stdout(), &processed_book)?;
Ok(())
}

187
site/redirects Normal file
View file

@ -0,0 +1,187 @@
/f2u https://blog.acolyer.org/2019/05/28/cheri-abi/
/f4q https://crates.io
/f2n https://crates.io/crates/cargo-modules
/ffr https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-types
/f6t https://doc.rust-lang.org/book/title-page.html
/f4m https://doc.rust-lang.org/cargo/reference/cargo-targets.html#cargo-targets
/ffc https://doc.rust-lang.org/cargo/reference/profiles.html
/f45 https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
/f6u https://doc.rust-lang.org/nomicon/
/f2z https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
/fzf https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
/f4c https://doc.rust-lang.org/reference/lifetime-elision.html
/fxy https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
/fzm https://doc.rust-lang.org/std/cmp/index.html
/fzz https://doc.rust-lang.org/std/cmp/trait.PartialEq.html
/fzb https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html
/fzp https://doc.rust-lang.org/std/convert/trait.From.html#implementors
/fzl https://doc.rust-lang.org/std/convert/trait.Into.html#implementors
/f4s https://doc.rust-lang.org/std/iter/trait.FusedIterator.html
/fxf https://doc.rust-lang.org/std/iter/trait.Iterator.html
/ffj https://doc.rust-lang.org/std/keyword.for.html
/ffh https://doc.rust-lang.org/std/keyword.while.html
/ffl https://doc.rust-lang.org/std/macro.panic.html
/f27 https://doc.rust-lang.org/std/mem/fn.size_of.html
/fzn https://doc.rust-lang.org/std/ops/index.html
/fz4 https://doc.rust-lang.org/std/ops/trait.Add.html
/fzt https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coercion
/fzv https://doc.rust-lang.org/std/ops/trait.Div.html
/fz6 https://doc.rust-lang.org/std/ops/trait.Mul.html
/fz8 https://doc.rust-lang.org/std/ops/trait.Rem.html
/fzx https://doc.rust-lang.org/std/ops/trait.Sub.html
/f2c https://doc.rust-lang.org/std/prelude/index.html
/ffe https://doc.rust-lang.org/std/primitive.i32.html#associatedconstant.MAX
/ff7 https://doc.rust-lang.org/std/primitive.i32.html#associatedconstant.MIN
/ffw https://doc.rust-lang.org/std/primitive.u32.html#associatedconstant.MAX
/f4d https://doc.rust-lang.org/std/slice/struct.Iter.html
/f26 https://doc.rust-lang.org/std/string/struct.String.html
/fxh https://doc.rust-lang.org/std/sync/atomic/index.html
/f4j https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter
/f2y https://docs.rs/dhat/latest/dhat/
/fx2 https://docs.rs/itertools/
/f4n https://docs.rs/thiserror/latest/thiserror/
/f65 https://docs.rs/tokio-stream/latest/tokio_stream/
/f6m https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.merge
/f63 https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html
/f6z https://docs.rs/tokio/latest/tokio/task/struct.JoinError.html
/f6k https://docs.rust-embedded.org/book/
/f2h https://en.wikipedia.org/wiki/Dangling_pointer
/fx7 https://en.wikipedia.org/wiki/Data_segment
/f2r https://en.wikipedia.org/wiki/Memory_address
/f2e https://en.wikipedia.org/wiki/Stack_overflow
/ff9 https://en.wikipedia.org/wiki/Two%27s_complement
/f2v https://en.wikipedia.org/wiki/UTF-8
/f6r https://exercism.io
/ffb https://github.com/LukeMathWalker/cargo-chef
/ffm https://github.com/LukeMathWalker/wiremock-rs
/fzq https://github.com/dtolnay/cargo-expand
/fzw https://github.com/dtolnay/proc-macro-workshop
/ff6 https://github.com/mainmatter/100-exercises-to-learn-rust
/ff3 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/01_intro/00_welcome
/ffq https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/01_intro/01_syntax
/ff5 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/00_intro
/fft https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/01_integers
/ffy https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/02_variables
/ffu https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/03_if_else
/ffk https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/04_panics
/ffs https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/05_factorial
/ffg https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/06_while
/ffd https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/07_for
/f2f https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/08_overflow
/f22 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/09_saturating
/f24 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/10_as_casting
/f2x https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/00_intro
/f28 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/01_struct
/f2b https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/02_validation
/f2m https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/03_modules
/f23 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/04_visibility
/f2q https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/05_encapsulation
/f25 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/06_ownership
/f2w https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/07_setters
/f29 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/08_stack
/f2p https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/09_heap
/f2l https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/10_references_in_memory
/f2g https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/11_destructor
/f2j https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/12_outro
/f2d https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/00_intro
/f2a https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/01_trait
/fz2 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/02_orphan_rule
/fz3 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/03_operator_overloading
/fz7 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/04_derive
/fz9 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/05_trait_bounds
/fzr https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/06_str_slice
/fzy https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/07_deref
/fzu https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/08_sized
/fzk https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/09_from
/fzs https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/10_assoc_vs_generic
/fzh https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/11_clone
/fzg https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/12_copy
/fzj https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/13_drop
/fzc https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/14_outro
/fza https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/00_intro
/f4f https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/01_enum
/f42 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/02_match
/f4z https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/03_variants_with_data
/f44 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/04_if_let
/f4x https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/05_nullability
/f46 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/06_fallibility
/f4v https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/07_unwrap
/f48 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/08_error_enums
/f4b https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/09_error_trait
/f43 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/10_packages
/f4w https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/11_dependencies
/f47 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/12_thiserror
/f4e https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/13_try_from
/f49 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/14_source
/f4y https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/15_outro
/f4u https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/00_intro
/f4p https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/01_arrays
/f4l https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/02_vec
/f4k https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/03_resizing
/f4h https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/04_iterators
/f4g https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/05_iter
/f4a https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/06_lifetimes
/fxz https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/07_combinators
/fx4 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/08_impl_trait
/fxx https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/09_impl_trait_2
/fx6 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/10_slices
/fxv https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/11_mutable_slices
/fx8 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/12_two_states
/fxb https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/13_index
/fxn https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/14_index_mut
/fxm https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/15_hashmap
/fx3 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/16_btreemap
/fxq https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/00_intro
/fxw https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/01_threads
/fxe https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/02_static
/fx9 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/03_leak
/fxr https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/04_scoped_threads
/fxt https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/05_channels
/fxu https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/06_interior_mutability
/fxp https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/07_ack
/fxl https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/08_client
/fxk https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/09_bounded
/fxs https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/10_patch
/fxj https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/11_locks
/fxd https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/12_rw_lock
/fxc https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/13_without_channels
/fxa https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/14_sync
/f6f https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/00_intro
/f62 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/01_async_fn
/f64 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/02_spawn
/f6x https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/03_runtime
/f66 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/04_future
/f68 https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/05_blocking
/f6b https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/06_async_aware_primitives
/f6q https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/07_cancellation
/f6e https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/08_outro
/ffz https://github.com/mainmatter/100-exercises-to-learn-rust/tree/solutions
/fzd https://github.com/mainmatter/rust-advanced-testing-workshop
/f69 https://github.com/rust-lang/rustlings
/ffa https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/
/f4r https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
/ff2 https://mainmatter.com/contact/
/fff https://mainmatter.com/rust-consulting/
/fxg https://marabos.nl/atomics/
/f6p https://nostarch.com/rust-rustaceans
/f2k https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory
/f2s https://owasp.org/www-community/vulnerabilities/Using_freed_memory
/ffn https://pavex.dev
/ffp https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=36e5ddbe3b3f741dfa9f74c956622bac
/fx5 https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=afedf7062298ca8f5a248bc551062eaa
/ffx https://rust-exercises.com/100-exercises-to-learn-rust.pdf
/ff4 https://rust-exercises.com/100-exercises/
/f6s https://rust-exercises.com/advanced-testing/
/f6h https://rust-exercises.com/telemetry/
/fze https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case
/f6w https://rust-lang.github.io/wg-async/vision/submitted_stories/status_quo/barbara_battles_buffered_streams.html
/f6v https://ryhl.io/blog/async-what-is-blocking/
/f6n https://tokio.rs/tokio/tutorial/select
/f2t https://valgrind.org/docs/manual/dh-manual.html
/fz5 https://veykril.github.io/tlborm/
/f67 https://without.boats/blog/the-scoped-task-trilemma/
/ffv https://www.lpalmieri.com/
/f4t https://www.lpalmieri.com/posts/2020-12-11-zero-to-production-6-domain-modelling/
/f6y https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/
/f6l https://www.youtube.com/playlist?list=PLqbS7AVVErFirH9armw8yXlE6dacF-A6z
/ff8 https://zero2prod.com