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
with:
tool: mdbook
- name: Build book
+ env:
+ LINK_SHORTENER_VERIFY: "true"
run: |
cd book
mdbook build
# 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: paperback
+ 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/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
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"
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
dependencies = [
"clap_builder",
+ "clap_derive",
]
[[package]]
"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"
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"
"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"
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"
"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"
"once_cell",
"opener",
"pathdiff",
- "pulldown-cmark",
+ "pulldown-cmark 0.10.3",
"regex",
"serde",
"serde_json",
"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"
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",
]
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"
"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"
[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,
"\\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"]
--- /dev/null
+{
+ "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"
+}
\ No newline at end of file
--- /dev/null
+#!/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"
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use mdbook::BookItem;
-/// A no-op preprocessor.
pub struct ExerciseLinker;
impl ExerciseLinker {
--- /dev/null
+[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"
--- /dev/null
+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}"))
+}
--- /dev/null
+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(())
+}
--- /dev/null
+/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