├── .gitignore ├── array-iter-breaking-change ├── .gitignore ├── Cargo.toml ├── readme.md └── src │ └── main.rs ├── axum-di-testing ├── axum-di │ ├── .vscode │ │ └── launch.json │ ├── Cargo.toml │ ├── requests.http │ └── src │ │ └── main.rs └── axum-without-di │ ├── .vscode │ └── launch.json │ ├── Cargo.toml │ ├── requests.http │ └── src │ └── main.rs ├── axum_intro ├── .vscode │ └── 0010_hello_world.code-snippets ├── Cargo.toml ├── justfile ├── poem.yaml ├── request.http └── src │ └── main.rs ├── bettercode-2023 ├── .gitignore ├── readme.md ├── structs_and_refs │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── structs_impls │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── trait_advanced │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── trait_basics │ ├── Cargo.toml │ └── src │ │ └── main.rs └── trait_objects │ ├── Cargo.toml │ └── src │ └── main.rs ├── cfg ├── .gitignore ├── 01-basics │ ├── .gitignore │ ├── justfile │ └── main.rs ├── 02_basics_with_features │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 03_salad_maker │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 04_recipe_reader │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── caesar_expected.md │ ├── caesar_salad.json │ ├── caesar_salad.yaml │ └── src │ │ └── lib.rs └── 05_recipe_reader_client │ ├── Cargo.toml │ ├── caesar_salad.yaml │ └── src │ └── main.rs ├── christmas_tree ├── Cargo.toml ├── readme.md └── src │ └── main.rs ├── circle ├── Cargo.toml └── src │ └── main.rs ├── cloud-wakeup ├── .gitignore ├── .vscode │ └── launch.json ├── Cargo.toml └── src │ └── main.rs ├── const_fn ├── .vscode │ └── settings.json ├── Cargo.toml └── src │ └── main.rs ├── demystifying-iterators ├── .gitignore ├── generators │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── iterator_tools │ ├── Cargo.toml │ └── src │ │ └── main.rs └── iterators_from_scratch │ ├── Cargo.toml │ └── src │ └── main.rs ├── fireworks ├── .gitignore ├── .vscode │ ├── fireworks-live.code-snippets │ └── settings.json ├── fireworks │ ├── .gitignore │ ├── Cargo.toml │ ├── rustfmt.toml │ ├── src │ │ ├── lib.rs │ │ └── utils.rs │ ├── tests │ │ └── web.rs │ └── www │ │ ├── .gitignore │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ ├── bootstrap.ts │ │ ├── index.html │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js └── readme.md ├── fun_with_trees ├── .gitignore ├── .vscode │ ├── launch.json │ └── settings.json ├── AKS │ ├── kube.azcli │ ├── runtimeclass │ │ └── wasm-runtimeclass.yaml │ └── spin-workload │ │ └── spin.yaml ├── Cargo.toml ├── Container │ ├── Dockerfile │ └── spin.toml ├── fractal_tree │ ├── Cargo.toml │ └── src │ │ ├── data_structures.rs │ │ ├── lib.rs │ │ └── svg.rs ├── fractal_tree_cli │ ├── Cargo.toml │ ├── foo.svg │ └── src │ │ └── main.rs ├── fractal_tree_spin │ ├── .cargo │ │ └── config.toml │ ├── .gitignore │ ├── Cargo.toml │ ├── spin.toml │ └── src │ │ ├── lib.rs │ │ └── parameters.rs ├── justfile ├── request.http └── tree.svg ├── grandparents_traits ├── .gitignore ├── Cargo.toml └── src │ ├── basics.rs │ ├── blanket_impl.rs │ ├── foreign_types.rs │ ├── main.rs │ ├── std_traits.rs │ └── trait_objects.rs ├── hello-embedded ├── 010-naive │ ├── .cargo │ │ └── config.toml │ ├── .embed.toml │ ├── .vscode │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── Cargo.toml │ ├── memory.x │ └── src │ │ └── main.rs ├── MicroBit_V2.2.1_nRF52820 schematic.PDF ├── Microbit-N52833.jpg ├── arm-cortex-M-arch-isa.png ├── hello-world │ ├── .cargo │ │ └── config.toml │ ├── .embed.toml │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── memory.x │ └── src │ │ └── main.rs ├── nRF52833_PS_v1.7.pdf ├── notes.md ├── rust-arch.png ├── snip.code-snippets ├── storyboard.md └── timer │ ├── .cargo │ └── config.toml │ ├── .embed.toml │ ├── .vscode │ └── settings.json │ ├── Cargo.toml │ ├── memory.x │ └── src │ └── main.rs ├── hello-spin ├── .cargo │ └── config.toml ├── .gitignore ├── .vscode │ └── settings.json ├── Cargo.toml ├── justfile ├── level1 │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── level2 │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── level3 │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── templates │ │ └── template.hbs ├── level4 │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── requests.http └── spin.toml ├── hello-wagi ├── .env ├── .gitignore ├── Cargo.toml ├── bs-config.js ├── justfile ├── level0_wasmtime │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── level1 │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── level2 │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── level3 │ ├── Cargo.toml │ ├── cargo │ ├── src │ │ └── main.rs │ └── templates │ │ └── template.hbs ├── level4 │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── modules.toml ├── package.json ├── readme.md ├── requests.http └── static │ ├── styles.css │ └── wordle.html ├── memory-management ├── .vscode │ └── memory.code-snippets ├── 005-Stack-Heap │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 010-Ownership │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 020-Give-Ownership │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 025-Give-Ownership-2 │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 030-Borrow │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 035-MpSc │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 040-Rc │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── 050-Lifetimes │ ├── Cargo.toml │ └── src │ │ └── main.rs └── Cargo.toml ├── memory-mgmt ├── .gitignore ├── .vscode │ └── launch.json ├── Cargo.toml ├── justfile └── src │ ├── lifetimes.rs │ ├── lor.rs │ ├── lor_rc.rs │ ├── main.rs │ └── print.rs ├── method_dispatch ├── Cargo.toml ├── benches │ └── my_benchmark.rs └── src │ └── lib.rs ├── perf-fib ├── Cargo.toml ├── index.html └── src │ └── lib.rs ├── play-with-formats ├── Cargo.toml └── src │ └── main.rs ├── random-name ├── .gitignore ├── Cargo.toml ├── rustfmt.toml ├── src │ └── lib.rs └── www │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── bootstrap.ts │ ├── index.html │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js ├── rust-csharp ├── .gitignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── csharp-client │ ├── Program.cs │ └── csharp-client.csproj ├── csharp-library │ ├── Class1.cs │ ├── csharp-library.csproj │ └── out │ │ └── csharp-library.so ├── csharp-wasm-host │ ├── Program.cs │ └── csharp-wasm-host.csproj ├── justfile ├── rust-client │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── rust-csharp.sln ├── rust-library │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── rust-wasm-module │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── some.wat ├── rust-lang-highlights ├── Cargo.toml └── src │ └── main.rs ├── rust-wasm-server └── hello_wasm │ ├── Cargo.toml │ ├── src │ └── main.rs │ └── text.txt ├── shapes ├── Cargo.toml └── src │ └── main.rs ├── socket-chatbot ├── .vscode │ └── launch.json ├── Cargo.toml ├── prompt.txt └── src │ └── main.rs ├── tictactoe ├── .vscode │ └── launch.json ├── Cargo.toml ├── rustfmt.toml └── tictactoe_logic │ ├── Cargo.toml │ └── src │ ├── board_content.rs │ ├── board_index.rs │ ├── game.rs │ ├── lib.rs │ ├── row.rs │ └── square_content.rs ├── wasm-serverless ├── .editorconfig ├── .gitignore ├── Cargo.toml ├── README.md ├── justfile ├── package-lock.json ├── package.json ├── requests.http ├── word_puzzle_cloudflare │ ├── Cargo.toml │ ├── package.json │ ├── src │ │ ├── lib.rs │ │ └── utils.rs │ └── wrangler.toml ├── word_puzzle_generator │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── word_puzzle_spin │ ├── .gitignore │ ├── Cargo.toml │ ├── spin.toml │ └── src │ └── lib.rs └── why-i-love-rust ├── .vscode └── launch.json ├── Cargo.toml ├── data.csv ├── src └── main.rs └── storybook.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/rust 2 | # Edit at https://www.gitignore.io/?templates=rust 3 | 4 | ### Rust ### 5 | # Generated by Cargo 6 | # will have compiled files and executables 7 | target 8 | 9 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 10 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 11 | Cargo.lock 12 | 13 | # These are backup files generated by rustfmt 14 | **/*.rs.bk 15 | 16 | # End of https://www.gitignore.io/api/rust -------------------------------------------------------------------------------- /array-iter-breaking-change/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /array-iter-breaking-change/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-play" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /array-iter-breaking-change/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | struct MyArray { 4 | phantom: PhantomData, 5 | } 6 | 7 | // See also https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/iter/traits/collect.rs#L204 8 | trait MyIntoIterator { 9 | type Item; 10 | type IntoIter: MyIterator; 11 | fn into_iter(self) -> Self::IntoIter; 12 | } 13 | 14 | // See also https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/iter/traits/iterator.rs#L55 15 | trait MyIterator { 16 | type Item; 17 | fn next(&mut self) -> Option; 18 | } 19 | 20 | // See also https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/array/mod.rs#L191 21 | impl<'a, T> MyIntoIterator for &'a MyArray { 22 | type Item = &'a T; 23 | type IntoIter = MyIter<'a, T>; 24 | fn into_iter(self) -> Self::IntoIter { 25 | MyIter::<'a, T>{ _phantom: &self.phantom } 26 | } 27 | } 28 | 29 | /* 30 | // See also https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/array/mod.rs#L173 31 | impl MyIntoIterator for MyArray { 32 | type Item = T; 33 | type IntoIter = MyIntoIter; 34 | fn into_iter(self) -> Self::IntoIter { 35 | MyIntoIter::{ _phantom: self.phantom } 36 | } 37 | } 38 | 39 | // See https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/array/iter.rs#L13 40 | struct MyIntoIter { 41 | _phantom: PhantomData, 42 | } 43 | 44 | // See https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/array/iter.rs#L102 45 | impl MyIterator for MyIntoIter { 46 | type Item = T; 47 | fn next(&mut self) -> Option { 48 | None 49 | } 50 | } 51 | */ 52 | 53 | // See also https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/slice/iter.rs#L65 54 | struct MyIter<'a, T: 'a> { 55 | _phantom: &'a PhantomData, 56 | } 57 | 58 | // See also https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/slice/iter.rs#L133 59 | // and https://github.com/rust-lang/rust/blob/f66e825f73d2bd7f8a763b723983850f891985b0/library/core/src/slice/iter/macros.rs#L130 60 | impl<'a, T: 'a> MyIterator for MyIter<'a, T> { 61 | type Item = &'a T; 62 | fn next(&mut self) -> Option { 63 | None 64 | } 65 | } 66 | 67 | fn main() { 68 | let _arr = MyArray::{ phantom: PhantomData::{} }; 69 | 70 | let mut iter: MyIter = _arr.into_iter(); 71 | let _first_item: Option<&i32> = iter.next(); 72 | } 73 | -------------------------------------------------------------------------------- /axum-di-testing/axum-di/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'axum-di'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=axum-di", 15 | "--package=axum-di" 16 | ], 17 | "filter": { 18 | "name": "axum-di", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | }, 25 | { 26 | "type": "lldb", 27 | "request": "launch", 28 | "name": "Debug unit tests in executable 'axum-di'", 29 | "cargo": { 30 | "args": [ 31 | "test", 32 | "--no-run", 33 | "--bin=axum-di", 34 | "--package=axum-di" 35 | ], 36 | "filter": { 37 | "name": "axum-di", 38 | "kind": "bin" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /axum-di-testing/axum-di/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "axum-di" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum = "0.6.1" 10 | axum-macros = "0.3" 11 | tokio = { version = "1.0", features = ["full"] } 12 | serde = { version = "1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | 15 | [dev-dependencies] 16 | rstest = "0.16" 17 | mockall = "0.11" 18 | tower = { version = "0.4", features = ["util"] } 19 | hyper = { version = "0.14", features = ["full"] } 20 | -------------------------------------------------------------------------------- /axum-di-testing/axum-di/requests.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:8080/heroes/ 2 | 3 | ### 4 | GET http://localhost:8080/heroes/?name=Won 5 | 6 | ### 7 | GET http://localhost:8080/heroes/?name=Won% 8 | 9 | ### 10 | GET http://localhost:8080/heroes/?name=Spider 11 | -------------------------------------------------------------------------------- /axum-di-testing/axum-without-di/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'axum-di'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=axum-di", 15 | "--package=axum-di" 16 | ], 17 | "filter": { 18 | "name": "axum-di", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | }, 25 | { 26 | "type": "lldb", 27 | "request": "launch", 28 | "name": "Debug unit tests in executable 'axum-di'", 29 | "cargo": { 30 | "args": [ 31 | "test", 32 | "--no-run", 33 | "--bin=axum-di", 34 | "--package=axum-di" 35 | ], 36 | "filter": { 37 | "name": "axum-di", 38 | "kind": "bin" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /axum-di-testing/axum-without-di/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "axum-di" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum = "0.6.1" 10 | axum-macros = "0.3" 11 | tokio = { version = "1.0", features = ["full"] } 12 | serde = { version = "1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | 15 | [dev-dependencies] 16 | rstest = "0.16" 17 | tower = { version = "0.4", features = ["util"] } 18 | hyper = { version = "0.14", features = ["full"] } 19 | -------------------------------------------------------------------------------- /axum-di-testing/axum-without-di/requests.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:8080/heroes/ 2 | 3 | ### 4 | GET http://localhost:8080/heroes/?name=Won 5 | 6 | ### 7 | GET http://localhost:8080/heroes/?name=Won% 8 | 9 | ### 10 | GET http://localhost:8080/heroes/?name=Spider 11 | -------------------------------------------------------------------------------- /axum_intro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "router" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum = "0.6" 10 | tokio ={ version = "1.12", features = ["full"] } 11 | serde = { version = "1.0", features = ["derive"] } 12 | rand = "0.8" 13 | serde_yaml = "0.9" 14 | thiserror = "1.0" 15 | -------------------------------------------------------------------------------- /axum_intro/justfile: -------------------------------------------------------------------------------- 1 | run: 2 | cargo watch -x 'run' -------------------------------------------------------------------------------- /axum_intro/poem.yaml: -------------------------------------------------------------------------------- 1 | title: How Cool Is That! 2 | text: | 3 | In a realm where code and craft entwine, 4 | Rust stands tall, fearless of time. 5 | Immutable by nature, fearless by design, 6 | Memory safe, concurrency's prime. 7 | 8 | Axum steps in, a framework so neat, 9 | For web services, it's hard to beat. 10 | Harnessing Rust's power, with a concise stat, 11 | Creating web apps, just like that! 12 | 13 | Together they dance, a dance so exact, 14 | Ensuring performance, with every act. 15 | From microservices to applications vast, 16 | "How cool is that!" is the impressed outlast. 17 | -------------------------------------------------------------------------------- /axum_intro/request.http: -------------------------------------------------------------------------------- 1 | @host = http://localhost:3000 2 | 3 | ### 4 | GET {{host}}/ 5 | 6 | ### 7 | POST {{host}}/ 8 | 9 | ### 10 | GET {{host}}/ping 11 | 12 | ### 13 | GET {{host}}/kingkong/king 14 | 15 | ### 16 | GET {{host}}/greet/rainer 17 | 18 | ### 19 | GET {{host}}/greet?salutation=Hi&name=rainer 20 | 21 | ### 22 | GET {{host}}/greet?salutation=Hi 23 | 24 | ### 25 | GET {{host}}/greet?name=rainer 26 | 27 | ### 28 | GET {{host}}/greet 29 | 30 | ### 31 | POST {{host}}/greet 32 | Content-Type: application/json 33 | 34 | { 35 | "salutation": "Hi", 36 | "name": "rainer" 37 | } 38 | 39 | ### 40 | GET {{host}}/lookup/8 41 | 42 | ### 43 | GET {{host}}/random 44 | 45 | ### 46 | GET {{host}}/numbers 47 | 48 | ### 49 | POST {{host}}/numbers 50 | Content-Type: application/json 51 | 52 | 1 53 | 54 | ### 55 | GET {{host}}/poem 56 | -------------------------------------------------------------------------------- /bettercode-2023/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /bettercode-2023/readme.md: -------------------------------------------------------------------------------- 1 | * Lifetimes 2 | * Recap from session 3 | * Practical sample [255-traits-testing](https://github.com/rust-linz/rust-fundamentals-training/tree/main/examples/255-traits-testing) 4 | * Traits 5 | * [Fibonacci](https://github.com/rust-linz/rust-fundamentals-training/blob/main/examples/220-fibonacci-iterator/src/main.rs) 6 | -------------------------------------------------------------------------------- /bettercode-2023/structs_and_refs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "structs_and_refs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /bettercode-2023/structs_impls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "structs_impls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /bettercode-2023/structs_impls/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, unused_variables)] 2 | 3 | struct Rectangle { 4 | width: f32, 5 | height: f32, 6 | } 7 | 8 | // Associate methods to the struct 9 | impl Rectangle { 10 | // Method that is not associated with a specific instance of the struct. 11 | // Note that "new" is just a name, it could be anything. No constructors in Rust. 12 | fn new() -> Self { 13 | Rectangle::from_width_height(0f32, 0f32) 14 | } 15 | 16 | // Note Self is the type of the struct to which the methods are associated. 17 | fn from_width_height(width: f32, height: f32) -> Self { 18 | Rectangle { width, height } 19 | } 20 | 21 | // Methods that are associated with a specific instance of the struct. 22 | // It also has a return type. Note immutable borrow. 23 | fn area(&self) -> f32 { 24 | self.width * self.height 25 | } 26 | 27 | fn diagonal_length(&self) -> f32 { 28 | (self.width.powi(2) + self.height.powi(2)).sqrt() 29 | } 30 | 31 | fn with_width(&self, width: f32) -> Self { 32 | Rectangle { 33 | width, 34 | height: self.height, 35 | } 36 | } 37 | 38 | fn with_height(&self, height: f32) -> Self { 39 | Rectangle { 40 | width: self.width, 41 | height, 42 | } 43 | } 44 | 45 | // Note mutable borrow. 46 | fn set_width(&mut self, width: f32) { 47 | self.width = width; 48 | } 49 | 50 | fn set_height(&mut self, height: f32) { 51 | self.height = height; 52 | } 53 | } 54 | 55 | fn main() { 56 | // Call a static function 57 | let rect = Rectangle::new(); 58 | let rect = Rectangle::from_width_height(10f32, 20f32); 59 | 60 | // Builder pattern (call a method that returns a new instance of the struct) 61 | let rect = Rectangle::new().with_width(10f32).with_height(20f32); 62 | 63 | // Call a method 64 | println!("Area: {}, diagonal: {}", rect.area(), rect.diagonal_length()); 65 | 66 | // Call a method that mutates the struct 67 | let mut rect = Rectangle::new(); 68 | rect.set_width(10f32); 69 | rect.set_height(20f32); 70 | 71 | // Local method inside main 72 | fn get_bounding_rect(rect1: &Rectangle, rect2: &Rectangle) -> Rectangle { 73 | let width = rect1.width.max(rect2.width); 74 | let height = rect1.height.max(rect2.height); 75 | Rectangle::from_width_height(width, height) 76 | } 77 | 78 | // Call a local method 79 | let rect1 = Rectangle::from_width_height(10f32, 20f32); 80 | let rect2 = Rectangle::from_width_height(20f32, 10f32); 81 | let bounding_rect = get_bounding_rect(&rect1, &rect2); 82 | println!("Bounding rect width = {}, height = {}", bounding_rect.width, bounding_rect.height); 83 | } 84 | -------------------------------------------------------------------------------- /bettercode-2023/trait_advanced/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trait_advanced" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | num = "0.4.1" 10 | num-traits = "0.2.17" 11 | -------------------------------------------------------------------------------- /bettercode-2023/trait_advanced/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::f64::consts::PI; 4 | 5 | use num_traits::Float; 6 | 7 | #[derive(Debug, PartialEq, Eq)] 8 | enum AngleType { 9 | Degrees, 10 | Radians, 11 | } 12 | 13 | struct Angle { 14 | angle: T, 15 | angle_type: AngleType, 16 | } 17 | 18 | impl Angle { 19 | fn square(at: AngleType) -> Self { 20 | Self { 21 | angle: match at { 22 | AngleType::Degrees => T::from(90.0).unwrap(), 23 | AngleType::Radians => T::from(PI / 2.0).unwrap(), 24 | }, 25 | angle_type: at, 26 | } 27 | } 28 | 29 | fn to_degrees(&self) -> T { 30 | match self.angle_type { 31 | AngleType::Degrees => self.angle, 32 | AngleType::Radians => self.angle.to_degrees(), 33 | } 34 | } 35 | 36 | fn to_radians(&self) -> T { 37 | match self.angle_type { 38 | AngleType::Degrees => self.angle.to_radians(), 39 | AngleType::Radians => self.angle, 40 | } 41 | } 42 | } 43 | 44 | impl From<(T, AngleType)> for Angle 45 | where 46 | T: Float, 47 | { 48 | fn from((angle, angle_type): (T, AngleType)) -> Self { 49 | Self { 50 | angle: angle.into(), 51 | angle_type, 52 | } 53 | } 54 | } 55 | 56 | fn main() { 57 | println!("Hello, world!"); 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use std::f64::consts::PI; 63 | 64 | use super::*; 65 | 66 | #[test] 67 | fn test_from() { 68 | let angle = Angle::::from((90.0, AngleType::Degrees)); 69 | assert_eq!(angle.angle, 90.0); 70 | assert_eq!(angle.angle_type, AngleType::Degrees); 71 | 72 | let angle: Angle = (PI, AngleType::Radians).into(); 73 | assert_eq!(angle.angle, PI); 74 | assert_eq!(angle.angle_type, AngleType::Radians); 75 | } 76 | 77 | #[test] 78 | fn test_to_degrees() { 79 | let angle = Angle::::from((PI as f32, AngleType::Radians)); 80 | assert_eq!(angle.to_degrees(), 180.0); 81 | 82 | let angle = Angle::::from((PI, AngleType::Radians)); 83 | assert_eq!(angle.to_degrees(), 180.0); 84 | } 85 | 86 | #[test] 87 | fn test_to_radians() { 88 | let angle = Angle::::from((180.0, AngleType::Degrees)); 89 | assert_eq!(angle.to_radians(), PI as f32); 90 | 91 | let angle = Angle::::from((180.0, AngleType::Degrees)); 92 | assert_eq!(angle.to_radians(), PI); 93 | } 94 | 95 | #[test] 96 | fn test_square() { 97 | let angle = Angle::::square(AngleType::Degrees); 98 | assert_eq!(angle.angle, 90.0); 99 | assert_eq!(angle.angle_type, AngleType::Degrees); 100 | 101 | let angle = Angle::::square(AngleType::Radians); 102 | assert_eq!(angle.angle, PI / 2.0); 103 | assert_eq!(angle.angle_type, AngleType::Radians); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /bettercode-2023/trait_basics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trait_basics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /bettercode-2023/trait_basics/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, unused_variables)] 2 | 3 | struct Rectangle { width: f32, height: f32 } 4 | 5 | // Define custom trait 6 | trait HasArea { 7 | fn area(&self) -> f32; 8 | } 9 | 10 | // Implement trait for custom type 11 | impl HasArea for Rectangle { 12 | fn area(&self) -> f32 { 13 | self.width * self.height 14 | } 15 | } 16 | 17 | // Auto-generate trait implementation (system trait) 18 | #[derive(Debug)] 19 | struct Circle { radius: f32 } 20 | 21 | impl HasArea for Circle { 22 | fn area(&self) -> f32 { 23 | std::f32::consts::PI * self.radius * self.radius 24 | } 25 | } 26 | 27 | // Explicitely implement system trait 28 | impl Default for Circle { 29 | fn default() -> Circle { 30 | Circle { radius: 1.0 } 31 | } 32 | } 33 | 34 | use std::fmt::{Display, Formatter, Result}; 35 | impl Display for Circle { 36 | fn fmt(&self, f: &mut Formatter) -> Result { 37 | write!(f, "Circle with radius {} and area {}", self.radius, self.area()) 38 | } 39 | } 40 | 41 | struct Arc { radius: f32, angle_radiant: f32 } 42 | 43 | impl HasArea for Arc { 44 | fn area(&self) -> f32 { 45 | self.radius * self.radius * self.angle_radiant / 2.0 46 | } 47 | } 48 | 49 | // Implement generic system trait 50 | impl From<&Circle> for Arc { 51 | fn from(circle: &Circle) -> Arc { 52 | Arc { radius: circle.radius, angle_radiant: 2.0 * std::f32::consts::PI } 53 | } 54 | } 55 | 56 | // Copy and clone traits change how values are passed around 57 | #[derive(Clone, Copy, Debug)] 58 | struct Point { x: f32, y: f32 } 59 | 60 | fn main() { 61 | let rectangle = Rectangle { width: 3.0, height: 4.0 }; 62 | let circle = &Circle { radius: 5.0 }; 63 | 64 | println!("Rectangle area: {}", rectangle.area()); 65 | println!("Circle: {:?}", circle); // Uses Debug trait 66 | println!("Circle: {}", circle); // Uses Display trait 67 | 68 | // Using the From and Into traits 69 | let arc: Arc = circle.into(); 70 | let arc = Arc::from(circle); 71 | println!("Arc (from circle) has area {}", arc.area()); 72 | 73 | // Applying copy and clone traits 74 | let p1 = Point { x: 1.0, y: 2.0 }; 75 | let p2 = p1; 76 | println!("p1: {:?}, p2: {:?}", p1, p2); 77 | 78 | let p3 = p1.clone(); 79 | println!("p1: {:?}, p3: {:?}", p1, p3); 80 | } 81 | -------------------------------------------------------------------------------- /bettercode-2023/trait_objects/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lifetimes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | chrono = "0.4" 10 | -------------------------------------------------------------------------------- /bettercode-2023/trait_objects/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | use chrono::{Utc, Datelike}; 4 | 5 | trait Animal { 6 | fn is_mammal(&self) -> bool; 7 | fn get_species(&self) -> String; 8 | fn get_sound(&self) -> String; 9 | fn number_of_legs(&self) -> i32; 10 | } 11 | 12 | struct Cat; 13 | 14 | struct Spider; 15 | 16 | impl Animal for Cat { 17 | fn is_mammal(&self) -> bool { 18 | true 19 | } 20 | 21 | fn get_species(&self) -> String { 22 | "Cat".to_string() 23 | } 24 | 25 | fn get_sound(&self) -> String { 26 | "Meow".to_string() 27 | } 28 | 29 | fn number_of_legs(&self) -> i32 { 30 | 4 31 | } 32 | } 33 | 34 | impl Display for Cat { 35 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 36 | write!(f, "{}", self.get_species()) 37 | } 38 | } 39 | 40 | impl Animal for Spider { 41 | fn is_mammal(&self) -> bool { 42 | false 43 | } 44 | 45 | fn get_species(&self) -> String { 46 | "Spider".to_string() 47 | } 48 | 49 | fn get_sound(&self) -> String { 50 | "Hiss".to_string() 51 | } 52 | 53 | fn number_of_legs(&self) -> i32 { 54 | 8 55 | } 56 | } 57 | 58 | fn get_a_cat() -> impl Animal { 59 | Cat { } 60 | } 61 | 62 | fn get_an_animal() -> Box { 63 | if Utc::now().naive_utc().day() % 2 == 0 { Box::new(Spider {}) } else { Box::new(Cat {}) } 64 | } 65 | 66 | fn do_something_with_animal(animal: &impl Animal) { 67 | println!("{}", animal.get_species()); 68 | } 69 | 70 | fn display_animal(animal: &T) { 71 | println!("{}", animal); 72 | } 73 | 74 | fn main() { 75 | let animals: Vec> = vec![ 76 | Box::new(Cat {}), 77 | Box::new(Spider {}), 78 | ]; 79 | 80 | for animal in animals { 81 | println!("{}", animal.get_species()); 82 | } 83 | 84 | let cat = get_a_cat(); 85 | do_something_with_animal(&cat); 86 | do_something_with_animal(&Cat {}); 87 | do_something_with_animal(&Spider {}); 88 | 89 | let animal = get_an_animal(); 90 | println!("{}", animal.get_species()); 91 | 92 | display_animal(&Cat {}); 93 | // display_animal(&Spider {}); // -> doesn't work 94 | } -------------------------------------------------------------------------------- /cfg/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /cfg/01-basics/.gitignore: -------------------------------------------------------------------------------- 1 | main -------------------------------------------------------------------------------- /cfg/01-basics/justfile: -------------------------------------------------------------------------------- 1 | run: 2 | rustc main.rs && ./main 3 | 4 | run-veganarian: 5 | rustc --cfg vegetarian --cfg vegan main.rs && ./main 6 | 7 | run-vegan: 8 | rustc --cfg vegan main.rs && ./main 9 | -------------------------------------------------------------------------------- /cfg/01-basics/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Note that you can combine configuration predicates with all, any, and not. 3 | #[cfg(not(any(traditional, vegetarian, vegan)))] 4 | { 5 | println!("You haven't specified whether the recipe should be traditional, vegetarian, or vegan."); 6 | panic!(); 7 | } 8 | 9 | #[cfg(any(all(traditional, vegetarian), all(traditional, vegan), all(vegetarian, vegan)))] 10 | { 11 | println!("You've specified more than one recipe type. Please specify only one."); 12 | panic!(); 13 | } 14 | 15 | print_ingredients(); 16 | print_preparation(); 17 | } 18 | 19 | fn print_ingredients() { 20 | println!("Ingredients for Caesar Salad:"); 21 | 22 | println!("- Romaine lettuce"); 23 | println!("- Croutons"); 24 | println!("- Lemon juice"); 25 | println!("- Olive oil"); 26 | println!("- Salt"); 27 | println!("- Black pepper"); 28 | 29 | // We combine config predicates again 30 | #[cfg(any(traditional, vegetarian))] 31 | println!("- Parmesan cheese"); 32 | #[cfg(any(vegan))] 33 | println!("- Vegan Parmesan cheese substitute"); 34 | 35 | // Note that the cfg attribute can be applied to any item, not just 36 | // a line of code. In this case, we're using it to conditionally 37 | // include a block of code. You could also conditionally define 38 | // a module, struct, trait, function, etc. 39 | #[cfg(traditional)] 40 | { 41 | println!("- Anchovies"); 42 | println!("- Egg yolks"); 43 | } 44 | 45 | #[cfg(vegetarian)] 46 | { 47 | println!("- Capers"); 48 | println!("- Egg yolks"); 49 | } 50 | 51 | #[cfg(vegan)] 52 | { 53 | println!("- Capers"); 54 | println!("- Vegan mayonnaise"); 55 | } 56 | } 57 | 58 | fn print_preparation() { 59 | println!("\nPreparation:"); 60 | 61 | println!("1. Wash and tear the romaine lettuce into bite-size pieces."); 62 | println!("2. In a bowl, combine lemon juice, olive oil, salt, and pepper."); 63 | 64 | // Instead of the config attribute, we can also use the cfg! macro. Note that it 65 | // does NOT remove any code. The macro only evaluates to true or false at compile time. 66 | // The following code might not be the best option in real-life, but it's a good 67 | // example of how to use the cfg! macro. 68 | match (cfg!(traditional), cfg!(vegetarian), cfg!(vegan)) { 69 | (true, false, false) => println!("3. Add grated Parmesan cheese, anchovies, and egg yolks to the bowl."), 70 | (false, true, false) => println!("3. Add grated Parmesan cheese, capers, and egg yolks to the bowl."), 71 | (false, false, true) => println!("3. Add vegan Parmesan cheese substitute, capers, and vegan mayonnaise to the bowl."), 72 | _ => panic!("More than one recipe type? This should never happen!"), 73 | }; 74 | 75 | println!("4. Mix well until the ingredients are well-combined."); 76 | println!("5. Add the croutons and romaine lettuce to the bowl."); 77 | println!("6. Toss until the lettuce is well-coated with the dressing."); 78 | println!("7. Serve immediately and enjoy your Caesar Salad!"); 79 | } 80 | -------------------------------------------------------------------------------- /cfg/02_basics_with_features/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "salad_maker_with_features" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [features] 7 | default = [] 8 | traditional = [] 9 | vegetarian = [] 10 | vegan = [] 11 | all = ["traditional", "vegetarian", "vegan"] 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /cfg/02_basics_with_features/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Note that we are now checking cargo features 3 | #[cfg(not(any(feature = "traditional", feature = "vegetarian", feature = "vegan")))] 4 | { 5 | println!("You haven't specified whether the recipe should be traditional, vegetarian, or vegan."); 6 | panic!(); 7 | } 8 | 9 | #[cfg(any( 10 | all(feature = "traditional", feature = "vegetarian"), 11 | all(feature = "traditional", feature = "vegan"), 12 | all(feature = "vegetarian", feature = "vegan")))] 13 | { 14 | println!("You've specified more than one recipe type. Please specify only one."); 15 | panic!(); 16 | } 17 | 18 | print_ingredients(); 19 | print_preparation(); 20 | } 21 | 22 | fn print_ingredients() { 23 | println!("Ingredients for Caesar Salad:"); 24 | 25 | println!("- Romaine lettuce"); 26 | println!("- Croutons"); 27 | println!("- Lemon juice"); 28 | println!("- Olive oil"); 29 | println!("- Salt"); 30 | println!("- Black pepper"); 31 | 32 | // We combine config predicates again 33 | #[cfg(any(feature = "traditional", feature = "vegetarian"))] 34 | println!("- Parmesan cheese"); 35 | #[cfg(any(feature = "vegan"))] 36 | println!("- Vegan Parmesan cheese substitute"); 37 | 38 | #[cfg(feature = "traditional")] 39 | { 40 | println!("- Anchovies"); 41 | println!("- Egg yolks"); 42 | } 43 | 44 | #[cfg(feature = "vegetarian")] 45 | { 46 | println!("- Capers"); 47 | println!("- Egg yolks"); 48 | } 49 | 50 | #[cfg(feature = "vegan")] 51 | { 52 | println!("- Capers"); 53 | println!("- Vegan mayonnaise"); 54 | } 55 | } 56 | 57 | fn print_preparation() { 58 | println!("\nPreparation:"); 59 | 60 | println!("1. Wash and tear the romaine lettuce into bite-size pieces."); 61 | println!("2. In a bowl, combine lemon juice, olive oil, salt, and pepper."); 62 | 63 | match (cfg!(feature = "traditional"), cfg!(feature = "vegetarian"), cfg!(feature = "vegan")) { 64 | (true, false, false) => println!("3. Add grated Parmesan cheese, anchovies, and egg yolks to the bowl."), 65 | (false, true, false) => println!("3. Add grated Parmesan cheese, capers, and egg yolks to the bowl."), 66 | (false, false, true) => println!("3. Add vegan Parmesan cheese substitute, capers, and vegan mayonnaise to the bowl."), 67 | _ => panic!("More than one recipe type? This should never happen!"), 68 | }; 69 | 70 | println!("4. Mix well until the ingredients are well-combined."); 71 | println!("5. Add the croutons and romaine lettuce to the bowl."); 72 | println!("6. Toss until the lettuce is well-coated with the dressing."); 73 | println!("7. Serve immediately and enjoy your Caesar Salad!"); 74 | } 75 | -------------------------------------------------------------------------------- /cfg/03_salad_maker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "salad_maker" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /cfg/03_salad_maker/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq)] 2 | pub enum RecipeType { Traditional, Vegetarian, Vegan, } 3 | 4 | pub mod caesar_salad { 5 | use super::*; 6 | 7 | // Get ingredients for a Caesar salad 8 | pub fn get_ingredients(recipe_type: RecipeType) -> Vec<&'static str> { 9 | let mut ingredients = vec![ 10 | "Romaine lettuce", "Croutons", "Lemon juice", 11 | "Olive oil", "Salt", "Black pepper", 12 | ]; 13 | 14 | ingredients.push(match recipe_type { 15 | RecipeType::Traditional | RecipeType::Vegetarian => "Parmesan cheese", 16 | _ => "Vegan Parmesan cheese substitute", 17 | }); 18 | 19 | ingredients.extend_from_slice(match recipe_type { 20 | RecipeType::Traditional => &["Anchovies", "Egg yolks"], 21 | RecipeType::Vegetarian => &["Capers", "Egg yolks"], 22 | _ => &["Capers", "Vegan mayonnaise"], 23 | }); 24 | 25 | ingredients 26 | } 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use super::*; 32 | 33 | #[test] 34 | fn no_anchovies_in_vegetarian_or_vegan() { 35 | let ingredients = caesar_salad::get_ingredients(RecipeType::Vegetarian); 36 | 37 | // Make sure there are no anchovies in a vegetarian salad 38 | assert!(!ingredients.contains(&"Anchovies")); 39 | } 40 | 41 | // Add additional tests here 42 | } 43 | -------------------------------------------------------------------------------- /cfg/04_recipe_reader/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.features": [ 3 | "all" 4 | ] 5 | } -------------------------------------------------------------------------------- /cfg/04_recipe_reader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "recipe_reader" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [features] 7 | default = ["json"] 8 | all = ["json", "yaml"] 9 | json = ["dep:serde_json"] 10 | yaml = ["dep:serde_yaml"] 11 | 12 | [dependencies] 13 | serde = { version = "1", features = ["derive"] } 14 | serde_json = { version = "1", optional = true } 15 | serde_yaml = { version = "0", optional = true } 16 | anyhow = "1" 17 | 18 | [dev-dependencies] 19 | mockall = "0" 20 | -------------------------------------------------------------------------------- /cfg/04_recipe_reader/caesar_expected.md: -------------------------------------------------------------------------------- 1 | # Vegan Caesar Salad 2 | 3 | ## Ingredients 4 | 5 | * Romaine lettuce 6 | * Croutons 7 | * Lemon juice 8 | * Olive oil 9 | * Salt 10 | * Black pepper 11 | * Vegan Parmesan cheese substitute 12 | * Capers 13 | * Vegan mayonnaise 14 | 15 | ## Steps 16 | 17 | 1. Wash and tear the romaine lettuce into bite-size pieces. 18 | 2. In a bowl, combine lemon juice, olive oil, salt, and pepper. 19 | 3. Add vegan Parmesan cheese substitute, capers, and vegan mayonnaise to the bowl. 20 | 4. Mix well until the ingredients are well-combined. 21 | 5. Add the croutons and romaine lettuce to the bowl. 22 | 6. Toss until the lettuce is well-coated with the dressing. 23 | 7. Serve immediately and enjoy your Caesar Salad! 24 | -------------------------------------------------------------------------------- /cfg/04_recipe_reader/caesar_salad.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Vegan Caesar Salad", 3 | "ingredients": [ 4 | "Romaine lettuce", 5 | "Croutons", 6 | "Lemon juice", 7 | "Olive oil", 8 | "Salt", 9 | "Black pepper", 10 | "Vegan Parmesan cheese substitute", 11 | "Capers", 12 | "Vegan mayonnaise" 13 | ], 14 | "steps": [ 15 | "Wash and tear the romaine lettuce into bite-size pieces.", 16 | "In a bowl, combine lemon juice, olive oil, salt, and pepper.", 17 | "Add vegan Parmesan cheese substitute, capers, and vegan mayonnaise to the bowl.", 18 | "Mix well until the ingredients are well-combined.", 19 | "Add the croutons and romaine lettuce to the bowl.", 20 | "Toss until the lettuce is well-coated with the dressing.", 21 | "Serve immediately and enjoy your Caesar Salad!" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /cfg/04_recipe_reader/caesar_salad.yaml: -------------------------------------------------------------------------------- 1 | title: Vegan Caesar Salad 2 | ingredients: 3 | - Romaine lettuce 4 | - Croutons 5 | - Lemon juice 6 | - Olive oil 7 | - Salt 8 | - Black pepper 9 | - Vegan Parmesan cheese substitute 10 | - Capers 11 | - Vegan mayonnaise 12 | steps: 13 | - Wash and tear the romaine lettuce into bite-size pieces. 14 | - In a bowl, combine lemon juice, olive oil, salt, and pepper. 15 | - Add vegan Parmesan cheese substitute, capers, and vegan mayonnaise to the bowl. 16 | - Mix well until the ingredients are well-combined. 17 | - Add the croutons and romaine lettuce to the bowl. 18 | - Toss until the lettuce is well-coated with the dressing. 19 | - Serve immediately and enjoy your Caesar Salad! 20 | -------------------------------------------------------------------------------- /cfg/05_recipe_reader_client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "recipe_reader_client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # Try switching from featurese = "yaml" to features = "json". This will 8 | # result in a compilation error because we use the yaml feature of the crate. 9 | # However, you will see that Rust 1.72 became smarter and will tell you that 10 | # that the missing module yaml is gated behind the "yaml" feature. Previous 11 | # versions of Rust would have just told you that the module yaml is missing. 12 | #recipe_reader = { path = "../04_recipe_reader", features = ["json"] } 13 | recipe_reader = { path = "../04_recipe_reader", features = ["yaml"] } 14 | -------------------------------------------------------------------------------- /cfg/05_recipe_reader_client/caesar_salad.yaml: -------------------------------------------------------------------------------- 1 | title: Vegan Caesar Salad 2 | ingredients: 3 | - Romaine lettuce 4 | - Croutons 5 | - Lemon juice 6 | - Olive oil 7 | - Salt 8 | - Black pepper 9 | - Vegan Parmesan cheese substitute 10 | - Capers 11 | - Vegan mayonnaise 12 | steps: 13 | - Wash and tear the romaine lettuce into bite-size pieces. 14 | - In a bowl, combine lemon juice, olive oil, salt, and pepper. 15 | - Add vegan Parmesan cheese substitute, capers, and vegan mayonnaise to the bowl. 16 | - Mix well until the ingredients are well-combined. 17 | - Add the croutons and romaine lettuce to the bowl. 18 | - Toss until the lettuce is well-coated with the dressing. 19 | - Serve immediately and enjoy your Caesar Salad! 20 | -------------------------------------------------------------------------------- /cfg/05_recipe_reader_client/src/main.rs: -------------------------------------------------------------------------------- 1 | use recipe_reader::{yaml, RecipeMarkdownFactory}; 2 | // ^^^^ 3 | // Here we use the yaml module from the recipe_reader crate. 4 | // Here is the new error message from Rust 1.72: 5 | 6 | // error[E0432]: unresolved import `recipe_reader::yaml` 7 | // --> src/main.rs:1:21 8 | // | 9 | // 1 | use recipe_reader::{yaml, RecipeMarkdownFactory}; 10 | // | ^^^^ no `yaml` in the root 11 | // | 12 | // note: found an item that was configured out <<< THIS IS NEW IN 1.72 13 | // --> /home/rainer/github/rust-samples/cfg/04_recipe_reader/src/lib.rs:39:9 14 | // | 15 | // 39 | pub mod yaml <<< THIS IS NEW IN 1.72 16 | // | ^^^^ <<< THIS IS NEW IN 1.72 17 | // = note: the item is gated behind the `yaml` feature <<< THIS IS NEW IN 1.72 18 | 19 | fn main() { 20 | let reader = yaml::YamlRecipeReader(); 21 | let factory = RecipeMarkdownFactory(); 22 | let markdown = factory.file_to_markdown("caesar_salad.yaml", reader).unwrap(); 23 | println!("{markdown}"); 24 | } 25 | -------------------------------------------------------------------------------- /christmas_tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "christmas_tree" 3 | version = "0.1.0" 4 | authors = ["Rainer Stropek "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /christmas_tree/readme.md: -------------------------------------------------------------------------------- 1 | # Christmas Tree Exercise 2 | 3 | ## Learning Goal 4 | 5 | * Practice working for `for` loops 6 | * Practice working with slices of numeric values 7 | 8 | ## Requirements 9 | 10 | Draw a christmas tree on the screen with `*` characters. Example 11 | 12 | ```txt 13 | * 14 | ***** 15 | ********* 16 | ***** 17 | ********* 18 | ************* 19 | ***************** 20 | ************* 21 | ***************** 22 | ********************* 23 | ************************* 24 | ***************************** 25 | ********************************* 26 | ********* 27 | ********* 28 | ``` 29 | -------------------------------------------------------------------------------- /christmas_tree/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | for leafs in [(1, 3), (2, 5), (4, 9)].iter() { 3 | for stars in (leafs.0 - 1)..leafs.1 { 4 | print_line(stars); 5 | } 6 | } 7 | 8 | for _ in 0..2 { 9 | print_line(2) 10 | } 11 | } 12 | 13 | fn print_line(stars: i32) { 14 | for _ in 0..(30 - stars * 2) { 15 | print!(" "); 16 | } 17 | for _ in 0..(stars * 4 + 1) { 18 | print!("*"); 19 | } 20 | print!("\n"); 21 | } 22 | -------------------------------------------------------------------------------- /circle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "circle" 3 | version = "0.1.0" 4 | authors = ["Rainer Stropek "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | math = "0.0.2" 11 | -------------------------------------------------------------------------------- /circle/src/main.rs: -------------------------------------------------------------------------------- 1 | const RADIUS: f32 = 20.0; 2 | 3 | fn main() { 4 | print_line(RADIUS); 5 | } 6 | 7 | fn print_line(l: f32) { 8 | if l < 1.0 { 9 | print!("**{}**\n", " ".repeat(4 * RADIUS as usize)); 10 | return; 11 | } 12 | 13 | let b: f32 = l as f32; 14 | let a: f32 = (b / (b / RADIUS).asin().tan()).round(); 15 | 16 | print!("{}**{}**\n", " ".repeat((RADIUS - a) as usize * 2), " ".repeat(a as usize * 4)); 17 | print_line(l - 1.0); 18 | print!("{}**{}**\n", " ".repeat((RADIUS - a) as usize * 2), " ".repeat(a as usize * 4)); 19 | } 20 | -------------------------------------------------------------------------------- /cloud-wakeup/.gitignore: -------------------------------------------------------------------------------- 1 | run.sh 2 | -------------------------------------------------------------------------------- /cloud-wakeup/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'cloud_wakeup'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=cloud_wakeup", 15 | "--package=cloud_wakeup" 16 | ], 17 | "filter": { 18 | "name": "cloud_wakeup", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": ["-t", "022e4faf-c745-475a-be06-06b1e1c9e39d", 23 | "-c", "76e2d4fe-db74-4914-885f-ffee05f3ce2f", 24 | "-s", "_pYKn6PPKyts29ri-_TfGOW6y_GCRs.Z_3", 25 | "-n", "n", 26 | "-q", "q"], 27 | "cwd": "${workspaceFolder}" 28 | }, 29 | { 30 | "type": "lldb", 31 | "request": "launch", 32 | "name": "Debug unit tests in executable 'cloud_wakeup'", 33 | "cargo": { 34 | "args": [ 35 | "test", 36 | "--no-run", 37 | "--bin=cloud_wakeup", 38 | "--package=cloud_wakeup" 39 | ], 40 | "filter": { 41 | "name": "cloud_wakeup", 42 | "kind": "bin" 43 | } 44 | }, 45 | "args": [], 46 | "cwd": "${workspaceFolder}" 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /cloud-wakeup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cloud_wakeup" 3 | version = "0.1.0" 4 | authors = ["Rainer Stropek "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = "^2.33" 11 | oauth2 = "^2.0" 12 | reqwest = "^0.9" 13 | url = "^1.0" 14 | serde = "^1.0" 15 | base64 = "^0.12" 16 | serde_json = "^1.0" 17 | -------------------------------------------------------------------------------- /cloud-wakeup/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | extern crate oauth2; 3 | extern crate url; 4 | extern crate base64; 5 | extern crate serde_json; 6 | 7 | use clap::{App, Arg}; 8 | use oauth2::prelude::*; 9 | use oauth2::{ClientId, ClientSecret, AuthUrl, TokenUrl, Scope, TokenResponse}; 10 | use oauth2::basic::BasicClient; 11 | use url::Url; 12 | use serde_json::to_vec; 13 | use std::str; 14 | 15 | fn main() { 16 | let matches = App::new("Cloud Wakeup") 17 | .version("0.1") 18 | .author("Rainer Stropek ") 19 | .arg( 20 | Arg::with_name("sb-namespace") 21 | .short("n") 22 | .long("servicebus-namespace") 23 | .help("Sets Azure Service Bus Namespace") 24 | .env("CW_SB_NAMESPACE") 25 | .required(true) 26 | .takes_value(true), 27 | ) 28 | .arg( 29 | Arg::with_name("queue") 30 | .short("q") 31 | .long("queue-name") 32 | .help("Sets the name of the Service Bus queue") 33 | .env("CW_QUEUE_NAME") 34 | .required(true) 35 | .takes_value(true), 36 | ) 37 | .arg( 38 | Arg::with_name("client-id") 39 | .short("c") 40 | .long("client-id") 41 | .help("Sets the AAD client (=app) id") 42 | .env("CW_CLIENT_ID") 43 | .required(true) 44 | .takes_value(true), 45 | ) 46 | .arg( 47 | Arg::with_name("client-secret") 48 | .short("s") 49 | .long("client-secret") 50 | .help("Sets the secret for the AAD client (=app) id") 51 | .env("CW_CLIENT_SECRET") 52 | .required(true) 53 | .takes_value(true), 54 | ) 55 | .arg( 56 | Arg::with_name("aad-tenant") 57 | .short("t") 58 | .long("aad-tenant") 59 | .help("Sets the Azure AD tenant") 60 | .env("CW_AAD_TENANT") 61 | .required(true) 62 | .takes_value(true), 63 | ) 64 | .get_matches(); 65 | let client_id = matches.value_of("client-id").unwrap().to_string(); 66 | let client_secret = matches.value_of("client-secret").unwrap().to_string(); 67 | let aad_tenant = matches.value_of("aad-tenant").unwrap(); 68 | let authorize = format!("https://login.microsoftonline.com/{}/oauth2/v2.0/authorize", aad_tenant); 69 | let token = format!("https://login.microsoftonline.com/{}/oauth2/v2.0/token", aad_tenant); 70 | 71 | let client = 72 | BasicClient::new( 73 | ClientId::new(client_id), 74 | Some(ClientSecret::new(client_secret)), 75 | AuthUrl::new(Url::parse(&authorize).unwrap()), Some(TokenUrl::new(Url::parse(&token).unwrap())) 76 | ) 77 | .add_scope(Scope::new("https://servicebus.azure.net/.default".to_string())); 78 | 79 | let cred = client.exchange_client_credentials().unwrap(); 80 | let token_result = TokenResponse::access_token(&cred); 81 | let c2 = to_vec(token_result).unwrap(); 82 | let c2_string = str::from_utf8(&c2[1..c2.len() - 1]).unwrap(); 83 | 84 | println!("{}", c2_string); 85 | } 86 | -------------------------------------------------------------------------------- /const_fn/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.command": "clippy" 3 | } -------------------------------------------------------------------------------- /const_fn/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const_fn" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /demystifying-iterators/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ -------------------------------------------------------------------------------- /demystifying-iterators/generators/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generators" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /demystifying-iterators/generators/src/main.rs: -------------------------------------------------------------------------------- 1 | /// Iterator generating fibonacci numbers 2 | struct Fibonacci { 3 | curr: u64, 4 | next: u64, 5 | } 6 | 7 | impl Fibonacci { 8 | fn new() -> Self { 9 | Fibonacci { curr: 0, next: 1 } 10 | } 11 | } 12 | 13 | impl Iterator for Fibonacci { 14 | type Item = u64; 15 | 16 | fn next(&mut self) -> Option { 17 | let old_curr = self.curr; 18 | self.curr = self.next; 19 | self.next = old_curr + self.next; 20 | Some(old_curr) 21 | } 22 | } 23 | 24 | fn main() { 25 | let fib = Fibonacci::new(); 26 | for i in fib.take(10) { 27 | println!("{i}"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demystifying-iterators/iterator_tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iterator_tools" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | num-traits = "0" -------------------------------------------------------------------------------- /demystifying-iterators/iterator_tools/src/main.rs: -------------------------------------------------------------------------------- 1 | /// An iterator that finds palindromes in a sequence of elements. 2 | struct Palindrome 3 | where 4 | I: Iterator, 5 | { 6 | iter: I, 7 | } 8 | 9 | /// Implement the Iterator trait for the Palindrome struct. 10 | impl Iterator for Palindrome 11 | where 12 | I: Iterator, 13 | I::Item: ToString, 14 | { 15 | type Item = I::Item; 16 | 17 | fn next(&mut self) -> Option { 18 | // Find the next palindrome in the sequence. 19 | self.iter.find(|item| { 20 | let string = item.to_string(); 21 | let reversed = string.chars().rev().collect::(); 22 | string == reversed 23 | }) 24 | } 25 | } 26 | 27 | /// A trait that extends the Iterator trait with a method to find palindromes. 28 | trait PalindromeIterator: Iterator { 29 | fn palindromes(self) -> Palindrome 30 | where 31 | Self: Sized, 32 | Self::Item: ToString, 33 | { 34 | Palindrome { iter: self } 35 | } 36 | } 37 | 38 | impl PalindromeIterator for I {} 39 | 40 | fn main() { 41 | for palindrome in (0..1000u32).palindromes() { 42 | println!("{}", palindrome); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /demystifying-iterators/iterators_from_scratch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iterators_from_scratch" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /fireworks/.gitignore: -------------------------------------------------------------------------------- 1 | live/ 2 | -------------------------------------------------------------------------------- /fireworks/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.diagnostics.disabled": [ 3 | "missing-unsafe" 4 | ] 5 | } -------------------------------------------------------------------------------- /fireworks/fireworks/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /fireworks/fireworks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fireworks" 3 | version = "0.1.0" 4 | authors = ["Rainer "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["console_error_panic_hook", "console_log", "wee_alloc"] 12 | 13 | # The feature `console_log` turns debug message on or off. 14 | # Try removing it from `default` and compiling with/without 15 | # `-- --features console_log`. 16 | console_log = ["web-sys"] 17 | 18 | [dependencies] 19 | wasm-bindgen = { version = "0.2.63", features = ["serde-serialize"] } 20 | js-sys = "0.3.50" 21 | serde = { version = "1.0", features = ["derive"] } 22 | web-sys = { version = "0.3.50", features = ["console"], optional = true } 23 | 24 | # The `console_error_panic_hook` crate provides better debugging of panics by 25 | # logging them with `console.error`. This is great for development, but requires 26 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 27 | # code size when deploying. 28 | console_error_panic_hook = { version = "0.1.6", optional = true } 29 | 30 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size 31 | # compared to the default allocator's ~10K. It is slower than the default 32 | # allocator, however. 33 | # 34 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. 35 | wee_alloc = { version = "0.4.5", optional = true } 36 | 37 | [dev-dependencies] 38 | wasm-bindgen-test = "0.3.13" 39 | 40 | [profile.release] 41 | # Tell `rustc` to optimize for small code size. 42 | opt-level = "s" 43 | -------------------------------------------------------------------------------- /fireworks/fireworks/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 180 -------------------------------------------------------------------------------- /fireworks/fireworks/src/utils.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub fn set_panic_hook() { 3 | // When the `console_error_panic_hook` feature is enabled, we can call the 4 | // `set_panic_hook` function at least once during initialization, and then 5 | // we will get better error messages if our code ever panics. 6 | // 7 | // For more details see 8 | // https://github.com/rustwasm/console_error_panic_hook#readme 9 | #[cfg(feature = "console_error_panic_hook")] 10 | console_error_panic_hook::set_once(); 11 | } 12 | -------------------------------------------------------------------------------- /fireworks/fireworks/tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | 3 | #![cfg(target_arch = "wasm32")] 4 | 5 | extern crate wasm_bindgen_test; 6 | use wasm_bindgen_test::*; 7 | 8 | wasm_bindgen_test_configure!(run_in_browser); 9 | 10 | #[wasm_bindgen_test] 11 | fn pass() { 12 | assert_eq!(1 + 1, 2); 13 | } 14 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .bin 4 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fireworks-js", 3 | "version": "0.1.0", 4 | "description": "Demo app for Rust WASM", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --config webpack.config.js", 8 | "start": "webpack serve" 9 | }, 10 | "devDependencies": { 11 | "@types/p5": "^1.3.1", 12 | "base64-loader": "^1.0.0", 13 | "html-webpack-plugin": "^5.5.0", 14 | "ts-loader": "^9.2.6", 15 | "typescript": "^4.4.4", 16 | "webpack": "^5.60.0", 17 | "webpack-cli": "^4.9.1", 18 | "webpack-dev-server": "^4.3.1" 19 | }, 20 | "dependencies": { 21 | "fireworks": "file:../pkg", 22 | "p5": "^1.4.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | // A dependency graph that contains any wasm must all be imported 2 | // asynchronously. This `bootstrap.js` file does the single async import, so 3 | // that no one else needs to worry about it again. 4 | import("./index") 5 | .catch(e => console.error("Error importing `index.js`:", e)); 6 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fireworks 🎇 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as fw from "fireworks"; 2 | import { memory } from "fireworks/fireworks_bg.wasm"; 3 | import * as p5 from 'p5'; 4 | 5 | const firework = fw.Firework.new(); 6 | 7 | // Get buffers 8 | const colors = new Uint8Array(memory.buffer, firework.colors(), firework.rocket_buffer_size * 3); 9 | const rockets = new Float64Array(memory.buffer, firework.rockets(), firework.rocket_buffer_size * 2); 10 | const states = new Uint8Array(memory.buffer, firework.states(), firework.rocket_buffer_size); 11 | 12 | const particles = new Float64Array(memory.buffer, firework.particles(), firework.particle_buffer_size * 2); 13 | const particle_states = new Uint8Array(memory.buffer, firework.particle_states(), firework.particle_buffer_size); 14 | 15 | // Just a helper function 16 | function delay(ms: number) { 17 | return new Promise( resolve => setTimeout(resolve, ms) ); 18 | } 19 | 20 | async function setup(p: p5) { 21 | p.createCanvas(p.windowWidth, p.windowHeight); 22 | 23 | //firework.add_rocket({ rgb: 0xffff00, x: 400, height: 170 }); 24 | 25 | // Add rockets 26 | firework.add_rocket({ rgb: 0xff0000, x: Math.random() * p.windowWidth, height: 170 }); 27 | //firework.add(Math.random() * 256, Math.random() * 256, Math.random() * 256, Math.random() * p.windowWidth, Math.random() * 100 + 100); 28 | setInterval(() => { 29 | firework.add(Math.random() * 256, Math.random() * 256, Math.random() * 256, Math.random() * p.windowWidth, Math.random() * 100 + 100); 30 | }, 300); 31 | 32 | // (async () => { 33 | // while (true) { 34 | // firework.add_rocket({ rgb: 0xff0000, x: 100, height: 100 }); 35 | // await delay(1000); 36 | // firework.add_rocket({ rgb: 0x00ff00, x: 200, height: 125 }); 37 | // await delay(1100); 38 | // firework.add_rocket({ rgb: 0x0000ff, x: 300, height: 150 }); 39 | // await delay(1200); 40 | // firework.add_rocket({ rgb: 0xffffff, x: 400, height: 175 }); 41 | // await delay(1300); 42 | // } 43 | // })(); 44 | } 45 | 46 | function draw(p: p5) { 47 | p.colorMode(p.RGB); 48 | p.background(0, 0, 0, 25); 49 | 50 | // Recalculate positions and parameters 51 | firework.apply_force(); 52 | 53 | for (let r = firework.rocket_buffer_size - 1; r >= 0; r--) { 54 | if (states[r] === fw.RocketState.Inactive) continue; 55 | 56 | p.stroke(colors[r * 3], colors[r * 3 + 1], colors[r * 3 + 2]); 57 | 58 | if (states[r] === fw.RocketState.Rising) { 59 | p.strokeWeight(4); 60 | p.point(rockets[r * 2], p.height + rockets[r * 2 + 1]); 61 | } else { 62 | p.strokeWeight(2.5); 63 | for (let pix = firework.get_max_particles_per_rocket() - 1; pix >= 0; pix--) { 64 | if (particle_states[fw.Firework.get_particle_index(r, pix)] === fw.ParticleState.Inactive) continue; 65 | p.point(particles[fw.Firework.get_particle_index(r, pix) * 2], p.height + particles[fw.Firework.get_particle_index(r, pix) * 2 + 1]); 66 | } 67 | } 68 | } 69 | } 70 | 71 | const p = new p5((p: p5) => { 72 | p.setup = () => setup(p); 73 | p.draw = () => draw(p); 74 | return p; 75 | }); 76 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "sourceMap": true, 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": false, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /fireworks/fireworks/www/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HTMLWebpackPlugin = require('html-webpack-plugin'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | entry: "./src/bootstrap.ts", 6 | experiments: { 7 | syncWebAssembly: true 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | use: 'ts-loader', 14 | exclude: /node_modules/, 15 | } 16 | ], 17 | }, 18 | resolve: { 19 | extensions: ['.tsx', '.ts', '.js'], 20 | }, 21 | output: { 22 | path: path.resolve(__dirname, "dist"), 23 | filename: "bundle.js", 24 | }, 25 | mode: "development", 26 | plugins: [ 27 | new HTMLWebpackPlugin({ 28 | template: path.resolve(__dirname, 'src/index.html'), 29 | }), 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /fun_with_trees/.gitignore: -------------------------------------------------------------------------------- 1 | wabt 2 | -------------------------------------------------------------------------------- /fun_with_trees/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/fractal_tree_csharp_host/bin/Debug/net6.0/fractal_tree_csharp_host.dll", 12 | "args": [], 13 | "cwd": "${workspaceFolder}", 14 | "stopAtEntry": false, 15 | "console": "internalConsole" 16 | }, 17 | { 18 | "type": "lldb", 19 | "request": "launch", 20 | "name": "Debug", 21 | "program": "${workspaceFolder}/", 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /fun_with_trees/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.command": "clippy" 3 | } -------------------------------------------------------------------------------- /fun_with_trees/AKS/kube.azcli: -------------------------------------------------------------------------------- 1 | kubectl get nodes -o wide 2 | kubectl describe node aks-mywasipool-41740949-vmss000000 3 | kubectl describe node aks-mywasipool-41740949-vmss000000 | grep spin 4 | 5 | cd AKS/spin-workload 6 | kubectl apply -f spin.yaml 7 | -------------------------------------------------------------------------------- /fun_with_trees/AKS/runtimeclass/wasm-runtimeclass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: node.k8s.io/v1 2 | kind: RuntimeClass 3 | metadata: 4 | name: "wasmtime-spin-v1" 5 | handler: "spin" 6 | scheduling: 7 | nodeSelector: 8 | "kubernetes.azure.com/wasmtime-spin-v1": "true" -------------------------------------------------------------------------------- /fun_with_trees/AKS/spin-workload/spin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: wasm-spin 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: wasm-spin 10 | template: 11 | metadata: 12 | labels: 13 | app: wasm-spin 14 | spec: 15 | runtimeClassName: wasmtime-spin-v1 16 | containers: 17 | - name: testwasm 18 | image: docker.io/rstropek/fractal_tree_spin:latest 19 | command: ["/"] 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: wasm-spin 25 | spec: 26 | type: LoadBalancer 27 | ports: 28 | - protocol: TCP 29 | port: 80 30 | targetPort: 80 31 | selector: 32 | app: wasm-spin -------------------------------------------------------------------------------- /fun_with_trees/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "fractal_tree", 4 | "fractal_tree_wasm_lib", 5 | "fractal_tree_cli", 6 | "fractal_tree_spin", 7 | ] 8 | -------------------------------------------------------------------------------- /fun_with_trees/Container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY ./fractal_tree_spin.wasm . 3 | COPY ./spin.toml . -------------------------------------------------------------------------------- /fun_with_trees/Container/spin.toml: -------------------------------------------------------------------------------- 1 | spin_version = "1" 2 | authors = ["Rainer "] 3 | description = "Factal Tree with Spin" 4 | name = "fractal_tree_spin" 5 | trigger = { type = "http", base = "/" } 6 | version = "0.1.0" 7 | 8 | [[component]] 9 | id = "fractal-tree-spin" 10 | source = "fractal_tree_spin.wasm" 11 | [component.trigger] 12 | route = "/..." 13 | [component.build] 14 | command = "cargo build --target wasm32-wasi --release" 15 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fractal_tree" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = [ "Rainer " ] 6 | description = "Sample for generating trees using fractals" 7 | 8 | [features] 9 | default = [ "svg" ] 10 | svg = [ "dep:handlebars", "dep:serde" ] 11 | 12 | [dependencies] 13 | rand = "0.8" 14 | rand_seeder = "0.2" 15 | rand_core = "0.6" 16 | rand_pcg = "0.3" 17 | handlebars = { version = "4", optional = true } 18 | serde = { version = "1.0", features = ["derive"], optional = true } 19 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree/src/data_structures.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, AddAssign}; 2 | use serde::Serialize; 3 | 4 | /// Implements a vector in 2D space. 5 | /// 6 | /// The struct implements `serde::Serialize` if the `svg` feature is enabled. 7 | /// This is necessary because the *handlebars* crate is used to generate 8 | /// the SVG output and the trait is required for it. 9 | #[derive(Debug, Default, Clone, Copy)] 10 | #[cfg_attr(feature = "svg", derive(Serialize))] 11 | pub struct Vector2d { 12 | pub x: f64, 13 | pub y: f64, 14 | } 15 | 16 | impl Add for Vector2d { 17 | type Output = Vector2d; 18 | 19 | /// Adds two vectors. 20 | fn add(self, rhs: Self) -> Self::Output { 21 | Vector2d { 22 | x: self.x + rhs.x, 23 | y: self.y + rhs.y, 24 | } 25 | } 26 | } 27 | 28 | /// Implements a line in 2D space from a starting point to an endpoint. 29 | /// 30 | /// The struct implements `serde::Serialize` if the `svg` feature is enabled. 31 | /// This is necessary because the *handlebars* crate is used to generate 32 | /// the SVG output and the trait is required for it. 33 | #[derive(Debug, Default, Copy, Clone)] 34 | #[cfg_attr(feature = "svg", derive(Serialize))] 35 | pub struct Line { 36 | pub start: Vector2d, 37 | pub end: Vector2d, 38 | } 39 | 40 | /// Implements a size in 2D space. 41 | /// 42 | /// The struct implements `serde::Serialize` if the `svg` feature is enabled. 43 | /// This is necessary because the *handlebars* crate is used to generate 44 | /// the SVG output and the trait is required for it. 45 | #[derive(Debug, Default, Copy, Clone)] 46 | #[cfg_attr(feature = "svg", derive(Serialize))] 47 | pub struct Size { 48 | pub width: f64, 49 | pub height: f64, 50 | } 51 | 52 | /// Implements a rectangle consisting of left upper corner, right lower corner, width, and height. 53 | /// 54 | /// The struct implements `serde::Serialize` if the `svg` feature is enabled. 55 | /// This is necessary because the *handlebars* crate is used to generate 56 | /// the SVG output and the trait is required for it. 57 | #[derive(Debug, Default, Copy, Clone)] 58 | #[cfg_attr(feature = "svg", derive(Serialize))] 59 | pub struct Rect { 60 | pub left_upper: Vector2d, 61 | pub right_lower: Vector2d, 62 | pub size: Size, 63 | } 64 | 65 | impl AddAssign<&Line> for Rect { 66 | /// Enlarges rectangle so that it contains the given line. 67 | fn add_assign(&mut self, l: &Line) { 68 | if l.start.x < self.left_upper.x { self.left_upper.x = l.start.x }; 69 | if l.end.x < self.left_upper.x { self.left_upper.x = l.end.x }; 70 | if l.start.x > self.right_lower.x { self.right_lower.x = l.start.x }; 71 | if l.end.x > self.right_lower.x { self.right_lower.x = l.end.x }; 72 | if l.start.y < self.left_upper.y { self.left_upper.y = l.start.y }; 73 | if l.end.y < self.left_upper.y { self.left_upper.y = l.end.y }; 74 | if l.start.y > self.right_lower.y { self.right_lower.y = l.start.y }; 75 | if l.end.y > self.right_lower.y { self.right_lower.y = l.end.y }; 76 | self.size.width = self.right_lower.x - self.left_upper.x; 77 | self.size.height = self.right_lower.y - self.left_upper.y; 78 | } 79 | } 80 | 81 | /// Implements a fractal tree. 82 | /// 83 | /// For every level, `lines` contains one entry. The branches of the level 84 | /// in the nested vector. 85 | pub struct Tree { 86 | pub lines: Vec>, 87 | pub bounding_rect: Rect, 88 | } -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | use rand_core::RngCore; 3 | use rand_pcg::Pcg64; 4 | use rand_seeder::Seeder; 5 | 6 | mod data_structures; 7 | #[cfg(feature = "svg")] 8 | mod svg; 9 | 10 | pub use data_structures::*; 11 | #[cfg(feature = "svg")] 12 | pub use svg::*; 13 | 14 | /// Generates a branch (=line) for the fractal tree. 15 | /// 16 | /// # Arguments: 17 | /// 18 | /// * `lines` - The lines representing the branches of the tree. 19 | /// * `start` - Starting point of the branch. 20 | /// * `angle` - Angle of the line representing the branch. 21 | /// * `branch_length` - Length of the line representing the branch. 22 | /// * `level` - Level of the branch (0 = trunk, 1 = first branch, etc.). 23 | /// * `rng` - Random number generator. 24 | fn branch( 25 | lines: &mut [Vec], 26 | start: Vector2d, 27 | angle: f64, 28 | mut branch_length: f64, 29 | mut level: usize, 30 | rng: &mut impl RngCore, 31 | ) { 32 | // Some parameter influencing the visual appearance of the tree 33 | const BRANCH_SHORTENING_FACTOR: f64 = 0.8f64; 34 | const RANDOM_ANGLE_CHANGE_FACTOR: f64 = 35f64; 35 | const RANDOM_BRANCH_LENGTH_CHANGE_FACTOR: f64 = 25f64; 36 | const ANGLE_CHANGE: f64 = 20f64 * std::f64::consts::PI / 180f64; 37 | 38 | debug_assert!(level < lines.len(), "level is out of boundes, must be lower than {}", lines.len()); 39 | 40 | // Calculate the end point of the branch 41 | let end = start 42 | + Vector2d { 43 | x: angle.cos() * branch_length, 44 | y: angle.sin() * branch_length, 45 | }; 46 | 47 | // Add the line representing the branch to the lines 48 | lines[level].push(Line { start, end }); 49 | 50 | // If we did not yet reach the maximum level, create a new branch 51 | level += 1; 52 | if level < lines.len() { 53 | // Shorten the branch 54 | branch_length *= BRANCH_SHORTENING_FACTOR; 55 | 56 | // Add a little bit of randomness to the branch's angle and length 57 | let angle_change = 58 | (rng.gen_range(-0.5f64..0.5f64) * RANDOM_ANGLE_CHANGE_FACTOR).to_radians(); 59 | let branch_length_change = 60 | rng.gen_range(-0.5f64..0.5f64) * RANDOM_BRANCH_LENGTH_CHANGE_FACTOR; 61 | 62 | // Generate two new branches recursively 63 | branch( 64 | lines, 65 | end, 66 | angle - ANGLE_CHANGE + angle_change, 67 | branch_length + branch_length_change, 68 | level, 69 | rng, 70 | ); 71 | branch( 72 | lines, 73 | end, 74 | angle + ANGLE_CHANGE + angle_change, 75 | branch_length + branch_length_change, 76 | level, 77 | rng, 78 | ); 79 | } 80 | } 81 | 82 | /// Generates a fractal tree. 83 | /// 84 | /// # Arguments: 85 | /// 86 | /// * `seed` - Seed for the random number generator. 87 | /// * `levels` - Number of levels of the tree. 88 | /// * `trunk_length` - Length of the trunk of the tree. 89 | pub fn tree>(seed: S, levels: usize, trunk_length: f64) -> Tree { 90 | // Generate a seeded random number generator 91 | let mut rng: Pcg64 = Seeder::from(seed.as_ref()).make_rng(); 92 | 93 | // Allocate the tree data structure. It fills `lines` with 94 | // the necessary number of vectors (one for each level of the tree). 95 | let mut tree = Tree { 96 | lines: (0..levels).into_iter().map(|_| Vec::new()).collect(), 97 | bounding_rect: Default::default(), 98 | }; 99 | 100 | // Generate the trunk of the tree 101 | branch( 102 | &mut tree.lines, 103 | Default::default(), 104 | -90f64.to_radians(), 105 | trunk_length, 106 | 0, 107 | &mut rng, 108 | ); 109 | 110 | // Calculate the bounding rectangle of the tree 111 | let mut bounding_rect: Rect = Default::default(); 112 | tree.lines.iter().for_each(|lines| { 113 | lines.iter().for_each(|line| { 114 | bounding_rect += line; 115 | }); 116 | }); 117 | tree.bounding_rect = bounding_rect; 118 | 119 | tree 120 | } 121 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree/src/svg.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use handlebars::{handlebars_helper, Handlebars}; 3 | 4 | use crate::*; 5 | 6 | /// Represents settings for a line in the tree image 7 | #[derive(Debug, Serialize)] 8 | pub struct LineSetting { 9 | /// Lightness percentage (HSL color mode) 10 | pub lightness: u8, 11 | 12 | /// Width of the line 13 | pub width: f64, 14 | } 15 | 16 | impl LineSetting { 17 | /// Creates line settings from a given tree branching level. 18 | /// 19 | /// # Arguments 20 | /// 21 | /// * `level` - The current level. 22 | /// * `max_level` - The total number of levels in the tree. 23 | fn new(level: usize, max_level: usize) -> Self { 24 | const LEVEL_TO_LIGHTNESS_FACTOR: u8 = 3u8; 25 | const LEVEL_TO_WEIGHT_FACTOR: f64 = 3f64; 26 | 27 | LineSetting { 28 | lightness: level as u8 * LEVEL_TO_LIGHTNESS_FACTOR, 29 | width: (max_level + 1 - level) as f64 * LEVEL_TO_WEIGHT_FACTOR / 2f64, 30 | } 31 | } 32 | } 33 | 34 | /// Represent a collection of all lines of a common tree level. 35 | /// 36 | /// Because all lines are on the same tree level, they share 37 | /// the same settings. 38 | #[derive(Serialize)] 39 | struct SettingsWithLines<'a> { 40 | settings: LineSetting, 41 | lines: &'a Vec, 42 | } 43 | 44 | /// Contains all tree levels with their branches and bounding rect. 45 | /// 46 | /// Used for SVG generation 47 | #[derive(Serialize)] 48 | struct TreeGenerationData<'a> { 49 | lines: Vec>, 50 | bounding_rect: Rect, 51 | } 52 | 53 | const SVG_TEMPLATE: &str = r#" 54 | 61 | 62 | {{#each this.lines}} 63 | 64 | {{/each}} 65 | 66 | "#; 67 | 68 | pub fn tree_svg>(seed: S, levels: usize, trunk_length: f64) -> String { 69 | // Create handlebar template engine 70 | let mut handlebars = Handlebars::new(); 71 | handlebars 72 | .register_template_string("svg", SVG_TEMPLATE) 73 | .unwrap(); 74 | handlebars_helper!(format: |v: f64| format!("{v:.4}")); 75 | handlebars.register_helper("format", Box::new(format)); 76 | 77 | // Generate tree 78 | let tree_lines = tree(seed, levels, trunk_length); 79 | 80 | // Convert tree data to data structure required for SVG template 81 | let tree = TreeGenerationData { 82 | lines: tree_lines.lines 83 | .iter() 84 | .enumerate() 85 | .map(|l| SettingsWithLines { 86 | settings: LineSetting::new(l.0, tree_lines.lines.len() - 1), 87 | lines: l.1 88 | }) 89 | .collect(), 90 | bounding_rect: tree_lines.bounding_rect, 91 | }; 92 | 93 | handlebars.render("svg", &tree).unwrap() 94 | } 95 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fractal_tree_cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | fractal_tree = { path = "../fractal_tree", default-features = false, features = ["svg"] } 8 | anyhow = "1.0" 9 | clap = { version = "4.0.24", features = ["derive"] } 10 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, io::Write}; 2 | use anyhow::Result; 3 | use clap::Parser; 4 | 5 | #[derive(Parser, Debug)] 6 | #[command(version, about, long_about = None)] 7 | struct Args { 8 | /// Seed for generating the tree 9 | #[arg(short, long, default_value = "Hello World")] 10 | seed: String, 11 | 12 | /// Name of target file 13 | #[arg(short, long, default_value = "tree.svg")] 14 | target_file_name: String, 15 | 16 | /// Depth of the tree 17 | #[arg(short = 'd', long, default_value_t = 13usize)] 18 | tree_depth: usize, 19 | 20 | /// Trunk length 21 | #[arg(short = 'l', long, default_value_t = 100f64)] 22 | trunk_length: f64, 23 | } 24 | 25 | fn main() -> Result<()> { 26 | let args = Args::parse(); 27 | 28 | let buffer = fractal_tree::tree_svg(args.seed, args.tree_depth, args.trunk_length); 29 | let mut file = 30 | fs::File::create(args.target_file_name)?; 31 | file.write_all(buffer.as_bytes())?; 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_spin/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_spin/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_spin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fractal-tree-spin" 3 | authors = ["Rainer "] 4 | description = "Factal Tree with Spin" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | crate-type = [ "cdylib" ] 10 | 11 | [dependencies] 12 | # Useful crate to handle errors. 13 | anyhow = "1" 14 | # Crate to simplify working with bytes. 15 | bytes = "1" 16 | # General-purpose crate with common HTTP types. 17 | http = "0.2" 18 | # The Spin SDK. 19 | spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v0.6.0" } 20 | # Crate that generates Rust Wasm bindings from a WebAssembly interface. 21 | wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" } 22 | fractal_tree = { path = "../fractal_tree", default-features = false, features = ["svg"] } 23 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_spin/spin.toml: -------------------------------------------------------------------------------- 1 | spin_version = "1" 2 | authors = ["Rainer "] 3 | description = "Factal Tree with Spin" 4 | name = "fractal_tree_spin" 5 | trigger = { type = "http", base = "/" } 6 | version = "0.1.0" 7 | 8 | [[component]] 9 | id = "fractal-tree-spin" 10 | source = "../target/wasm32-wasi/release/fractal_tree_spin.wasm" 11 | [component.trigger] 12 | route = "/..." 13 | [component.build] 14 | command = "cargo build --target wasm32-wasi --release" 15 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_spin/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use spin_sdk::{ 3 | http::{Request, Response}, 4 | http_component, 5 | }; 6 | 7 | mod parameters; 8 | 9 | #[http_component] 10 | fn fractal_tree_spin(_req: Request) -> Result { 11 | let params = parameters::extract_parameters(&_req); 12 | 13 | let buffer = fractal_tree::tree_svg(params.seed, params.depth, params.trunk_length); 14 | 15 | Ok(http::Response::builder() 16 | .status(200) 17 | .header("content-type", "image/svg+xml") 18 | .body(Some(buffer.into()))?) 19 | } 20 | -------------------------------------------------------------------------------- /fun_with_trees/fractal_tree_spin/src/parameters.rs: -------------------------------------------------------------------------------- 1 | use spin_sdk::{ 2 | http::Request, 3 | }; 4 | 5 | pub struct Parameters { 6 | pub seed: String, 7 | pub depth: usize, 8 | pub trunk_length: f64, 9 | } 10 | 11 | impl Default for Parameters { 12 | fn default() -> Self { 13 | Self { 14 | seed: "Hello WASM".to_string(), 15 | depth: 13usize, 16 | trunk_length: 100f64, 17 | } 18 | } 19 | } 20 | 21 | pub fn extract_parameters(req: &Request) -> Parameters { 22 | let query = req.uri().query().unwrap_or(""); 23 | let mut params = Parameters::default(); 24 | 25 | for pair in query.split('&').filter(|s| !s.is_empty()) { 26 | let mut parts = pair.split('='); 27 | let key = parts.next().unwrap(); 28 | let value = parts.next().unwrap(); 29 | 30 | match key { 31 | "seed" => params.seed = value.to_string(), 32 | "depth" => params.depth = value.parse().unwrap_or(params.depth), 33 | "trunk" => params.trunk_length = value.parse().unwrap_or(params.trunk_length), 34 | _ => {}, 35 | } 36 | } 37 | 38 | params 39 | } -------------------------------------------------------------------------------- /fun_with_trees/justfile: -------------------------------------------------------------------------------- 1 | # General helper tasks 2 | build TARGET: 3 | cargo build {{TARGET}} 4 | run APP TARGET TARGET_FILE DEPTH TRUNK_LENGTH: 5 | cargo run {{TARGET}} --bin {{APP}} -- -t {{TARGET_FILE}} -d {{DEPTH}} -l {{TRUNK_LENGTH}} 6 | help APP TARGET: 7 | cargo run {{TARGET}} --bin {{APP}} -- -h 8 | 9 | # Build tasks 10 | build-wasi: (build "--target=wasm32-wasi") 11 | build-linux: (build "--target=x86_64-unknown-linux-gnu") 12 | 13 | # Run tasks 14 | run-wasi: (run-wasi-param "tree.svg" "13" "100") 15 | run-wasi-param TARGET_FILE DEPTH TRUNK_LENGTH: (build-wasi) 16 | wasmtime --dir=. ./target/wasm32-wasi/debug/fractal_tree_cli.wasm -- -t {{TARGET_FILE}} -d {{DEPTH}} -l {{TRUNK_LENGTH}} 17 | # wasmtime --dir=. ./target/wasm32-wasi/debug/fractal_tree_cli.wasm --fuel 4750000000 -- -t {{TARGET_FILE}} -d {{DEPTH}} -l {{TRUNK_LENGTH}} 18 | run-linux: (run-linux-param "tree.svg" "13" "100") 19 | run-linux-param TARGET_FILE DEPTH TRUNK_LENGTH: (run "fractal_tree_cli" "--target=x86_64-unknown-linux-gnu" TARGET_FILE DEPTH TRUNK_LENGTH) 20 | help-linux: (help "fractal_tree_cli" "--target=x86_64-unknown-linux-gnu") 21 | 22 | build-spin: 23 | cd fractal_tree_spin; spin build 24 | run-spin: (build-spin) 25 | cd fractal_tree_spin; spin up 26 | deploy-spin: (build-spin) 27 | cd fractal_tree_spin; spin deploy 28 | 29 | run-wasmedge: (run-wasmedge-param "./target/wasm32-wasi/debug/fractal_tree_cli.wasm" "./tree.svg" "8" "100") 30 | run-wasmedge-stats: (run-wasmedge-param-stats "./target/wasm32-wasi/debug/fractal_tree_cli.wasm" "./tree.svg" "10" "100") 31 | run-wasmedge-param WASM TARGET_FILE DEPTH TRUNK_LENGTH: (build-wasi) 32 | wasmedge --dir .:. {{WASM}} -t {{TARGET_FILE}} -d {{DEPTH}} -l {{TRUNK_LENGTH}} 33 | run-wasmedge-param-stats WASM TARGET_FILE DEPTH TRUNK_LENGTH: (build-wasi) 34 | wasmedge --dir .:. --enable-all-statistics {{WASM}} -t {{TARGET_FILE}} -d {{DEPTH}} -l {{TRUNK_LENGTH}} 35 | aot: (build-wasi) 36 | wasmedgec ./target/wasm32-wasi/debug/fractal_tree_cli.wasm ./target/wasm32-wasi/debug/fractal_tree_cli_aot.wasm 37 | run-aot: 38 | just run-wasmedge-param "./target/wasm32-wasi/debug/fractal_tree_cli_aot.wasm" "./tree.svg" "13" "100" 39 | run-aot-stats: 40 | just run-wasmedge-param-stats "./target/wasm32-wasi/debug/fractal_tree_cli_aot.wasm" "./tree.svg" "13" "100" 41 | 42 | docker: (build-wasi) 43 | cp target/wasm32-wasi/release/fractal_tree_spin.wasm Container/ 44 | cd Container; docker build -t rstropek/fractal_tree_spin . 45 | docker push rstropek/fractal_tree_spin:latest 46 | -------------------------------------------------------------------------------- /fun_with_trees/request.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:3000 2 | 3 | ### 4 | GET http://127.0.0.1:3000/?seed=Hello%20WASM&depth=10&trunk=150 5 | 6 | ### 7 | GET http://20.238.176.25 8 | -------------------------------------------------------------------------------- /grandparents_traits/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /grandparents_traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "traits" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /grandparents_traits/src/basics.rs: -------------------------------------------------------------------------------- 1 | pub struct ConsultingWork { 2 | #[allow(dead_code)] 3 | what: String, 4 | hours: f32, 5 | rate: f32, 6 | } 7 | 8 | impl ConsultingWork { 9 | pub fn new(what: &str, hours: f32, rate: f32) -> ConsultingWork { 10 | ConsultingWork { 11 | what: what.to_string(), 12 | hours, 13 | rate, 14 | } 15 | } 16 | } 17 | 18 | pub trait Billable { 19 | fn total(&self) -> f32; 20 | } 21 | 22 | impl Billable for ConsultingWork { 23 | fn total(&self) -> f32 { 24 | self.hours * self.rate 25 | } 26 | } 27 | 28 | pub fn print_total(b: &impl Billable) { 29 | println!("Total: ${:.2}", b.total()); 30 | } 31 | -------------------------------------------------------------------------------- /grandparents_traits/src/blanket_impl.rs: -------------------------------------------------------------------------------- 1 | pub trait Billable { 2 | fn total(&self) -> f32; 3 | } 4 | 5 | pub trait Pointworthy { 6 | fn points(&self) -> i32; 7 | } 8 | 9 | impl Pointworthy for T { 10 | fn points(&self) -> i32 { 11 | (self.total() / 10.0) as i32 12 | } 13 | } 14 | 15 | #[allow(dead_code)] 16 | pub enum ShirtSize { 17 | Small, 18 | Medium, 19 | Large, 20 | ExtraLarge, 21 | Epic, 22 | } 23 | 24 | pub struct CodingWork { 25 | size: ShirtSize, 26 | } 27 | 28 | impl CodingWork { 29 | pub fn new(size: ShirtSize) -> CodingWork { 30 | CodingWork { size } 31 | } 32 | } 33 | 34 | // Implement Billable for all slices of Billables by adding up totals 35 | impl Billable for [T; C] { 36 | fn total(&self) -> f32 { 37 | self.iter().map(|b| b.total()).sum() 38 | } 39 | } 40 | 41 | impl Billable for CodingWork { 42 | fn total(&self) -> f32 { 43 | match self.size { 44 | ShirtSize::Small => 100.0, 45 | ShirtSize::Medium => 200.0, 46 | ShirtSize::Large => 1000.0, 47 | ShirtSize::ExtraLarge => 5000.0, 48 | ShirtSize::Epic => 100_000.0, 49 | } 50 | } 51 | } 52 | 53 | pub fn print_points(b: &impl Pointworthy) { 54 | println!("Points: {}", b.points()); 55 | } 56 | -------------------------------------------------------------------------------- /grandparents_traits/src/foreign_types.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | pub trait Billable { 4 | fn total(&self) -> f32; 5 | } 6 | 7 | impl Billable for f32 { 8 | fn total(&self) -> f32 { 9 | *self 10 | } 11 | } 12 | 13 | pub fn print_total(b: &impl Billable) { 14 | println!("Total: ${:.2}", b.total()); 15 | } 16 | 17 | // The following code does not work because f32 is a foreign type 18 | // and Default is also not included in the current crate. 19 | // impl Default for f32 { 20 | // fn default() -> Self { 21 | // -1.0 22 | // } 23 | // } 24 | 25 | pub struct MyF32(pub f32); // Newtype pattern 26 | 27 | impl Deref for MyF32 { 28 | type Target = f32; 29 | 30 | fn deref(&self) -> &Self::Target { 31 | &self.0 32 | } 33 | } 34 | 35 | impl Default for MyF32 { 36 | fn default() -> Self { 37 | MyF32(-1.0) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /grandparents_traits/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | mod basics; 4 | mod foreign_types; 5 | mod blanket_impl; 6 | mod std_traits; 7 | mod trait_objects; 8 | 9 | fn main() { 10 | let b = basics::ConsultingWork::new("AI Consulting", 10.0, 100.0); 11 | basics::print_total(&b); 12 | 13 | let b = 500.0; 14 | foreign_types::print_total(&b); 15 | 16 | use foreign_types; 17 | let b: foreign_types::MyF32 = Default::default(); 18 | foreign_types::print_total(b.deref()); 19 | 20 | let b = blanket_impl::CodingWork::new(blanket_impl::ShirtSize::Epic); 21 | blanket_impl::print_points(&b); 22 | 23 | 24 | let b = [ 25 | blanket_impl::CodingWork::new(blanket_impl::ShirtSize::Small), 26 | blanket_impl::CodingWork::new(blanket_impl::ShirtSize::Medium), 27 | ]; 28 | blanket_impl::print_points(&b); 29 | 30 | use std_traits; 31 | let b1 = std_traits::ConsultingWork { what: "AI Consulting".to_string(), hours: 10.0, rate: 100.0 }; 32 | let b2 = std_traits::ConsultingWork { what: "Rust Coding".to_string(), hours: 10.0, rate: 150.0 }; 33 | let c = b1 + b2; 34 | std_traits::print_work(&c); 35 | 36 | let b1 = std_traits::ConsultingWork { what: "AI Consulting".to_string(), hours: 10.0, rate: 100.0 }; 37 | let b2 = std_traits::ConsultingWork { what: "Rust Coding".to_string(), hours: 10.0, rate: 150.0 }; 38 | let b = [b1, b2]; 39 | std_traits::print_work(&b.into_iter().sum()); 40 | 41 | let b = trait_objects::create_billable("AI Consulting", 10.0, 100.0); 42 | trait_objects::print_total(&b); 43 | 44 | let b = trait_objects::create_billable2(None, 10.0, 100.0); 45 | trait_objects::print_total(b.as_ref()); 46 | 47 | let b = trait_objects::create_billables(); 48 | for b in b.iter() { 49 | trait_objects::print_total(b.as_ref()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /grandparents_traits/src/std_traits.rs: -------------------------------------------------------------------------------- 1 | use std::{iter::Sum, ops::Add}; 2 | 3 | #[derive(Debug, Clone, Default, PartialEq)] 4 | pub struct ConsultingWork { 5 | pub what: String, 6 | pub hours: f32, 7 | pub rate: f32, 8 | } 9 | 10 | impl Add for ConsultingWork { 11 | type Output = ConsultingWork; 12 | 13 | fn add(self, other: ConsultingWork) -> ConsultingWork { 14 | if self != Default::default() { 15 | ConsultingWork { 16 | what: format!("{}, {}", self.what, other.what), 17 | hours: self.hours + other.hours, 18 | rate: self.rate + other.rate, 19 | } 20 | } else { 21 | other 22 | } 23 | } 24 | } 25 | 26 | impl Sum for ConsultingWork { 27 | fn sum>(iter: I) -> ConsultingWork { 28 | iter.fold(ConsultingWork::default(), |a, b| a + b) 29 | } 30 | } 31 | 32 | pub fn print_work(work: &ConsultingWork) { 33 | println!("Did {} for {} hours at ${:.2}/hour", work.what, work.hours, work.rate); 34 | } -------------------------------------------------------------------------------- /grandparents_traits/src/trait_objects.rs: -------------------------------------------------------------------------------- 1 | pub trait Billable { 2 | fn total(&self) -> f32; 3 | } 4 | 5 | pub struct ConsultingWork { 6 | #[allow(dead_code)] 7 | what: String, 8 | hours: f32, 9 | rate: f32, 10 | } 11 | 12 | impl Billable for ConsultingWork { 13 | fn total(&self) -> f32 { 14 | self.hours * self.rate 15 | } 16 | } 17 | 18 | impl Billable for f32 { 19 | fn total(&self) -> f32 { 20 | *self 21 | } 22 | } 23 | 24 | pub fn create_billable(what: &str, hours: f32, rate: f32) -> impl Billable { 25 | ConsultingWork { 26 | what: what.to_string(), 27 | hours, 28 | rate, 29 | } 30 | } 31 | 32 | 33 | pub fn create_billable2(what: Option<&str>, hours: f32, rate: f32) -> Box { 34 | if let Some(what) = what { 35 | Box::new(ConsultingWork { 36 | what: what.to_string(), 37 | hours, 38 | rate: rate, 39 | }) 40 | } else { 41 | Box::new(hours * rate) 42 | } 43 | } 44 | 45 | pub fn create_billables() -> Vec> { 46 | vec![ 47 | Box::new(ConsultingWork { 48 | what: "Project 1".to_string(), 49 | hours: 10.0, 50 | rate: 100.0, 51 | }), 52 | Box::new(75.0), 53 | ] 54 | } 55 | 56 | pub fn print_total(b: &dyn Billable) { 57 | println!("Total: ${:.2}", b.total()); 58 | } -------------------------------------------------------------------------------- /hello-embedded/010-naive/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Microbit v2 uses a Nordic nRF52833 Processor, core variant: Arm Cortex-M4 32bit with FPU 2 | # (see https://tech.microbit.org/hardware/#nrf52-application-processor). 3 | # The Arm Cortex-M4 implements the Thumb instruction set. 4 | # At https://doc.rust-lang.org/nightly/rustc/platform-support.html, we can lookup 5 | # the target name by looking for "thumbv7". 6 | # 7 | # Note: You must install the target using `rustup target add thumbv7em-none-eabihf`. 8 | # If you unsure whether you have it installed, you can check with `rustup show`. 9 | 10 | [build] 11 | target = "thumbv7em-none-eabihf" 12 | 13 | # Tell the linker to use the memory.x linker script. 14 | [target.thumbv7em-none-eabihf] 15 | rustflags = [ 16 | "-C", "link-arg=-Tlink.x" 17 | ] 18 | -------------------------------------------------------------------------------- /hello-embedded/010-naive/.embed.toml: -------------------------------------------------------------------------------- 1 | # Find the correct chip name using: `probe-rs chip list | grep nRF52833` 2 | # We enable RTT (Real Time Transfer) to do println! debugging. 3 | # GDB could also be used, but not in this demo. 4 | 5 | [default.general] 6 | chip = "nRF52833_xxAA" 7 | 8 | [default.rtt] 9 | enabled = true 10 | -------------------------------------------------------------------------------- /hello-embedded/010-naive/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "preLaunchTask": "${defaultBuildTask}", 6 | "type": "probe-rs-debug", 7 | "request": "launch", 8 | "name": "probe_rs Executable Test", 9 | "flashingConfig": { 10 | "flashingEnabled": true 11 | }, 12 | "chip": "nRF52833_xxAA", 13 | "coreConfigs": [ 14 | { 15 | "programBinary": "./target/thumbv7em-none-eabihf/debug/naive", 16 | "rttEnabled": true, 17 | "rttChannelFormats": [ 18 | { 19 | "channelNumber": 0, 20 | // Format RTT data as String data 21 | "dataFormat": "String", 22 | // Include host-side timestamps for every line of data transferred from the target RTT output 23 | "showTimestamps": true 24 | }, 25 | { 26 | "channelNumber": 1, 27 | // Treat data as raw binary data, and do not format in any way 28 | "dataFormat": "BinaryLE" 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /hello-embedded/010-naive/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | /* Make sure that Rust Analyzer only checks for the target we're building for. */ 2 | { 3 | "rust-analyzer.check.targets": "thumbv7em-none-eabihf", 4 | "rust-analyzer.cargo.allTargets": false, 5 | "rust-analyzer.check.allTargets": false 6 | } 7 | -------------------------------------------------------------------------------- /hello-embedded/010-naive/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "build", 7 | "problemMatcher": [ 8 | "$rustc" 9 | ], 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "label": "rust: cargo build" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /hello-embedded/010-naive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naive" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | embedded-hal = "1.0.0" 10 | nrf52833-hal = "0.18.0" 11 | nrf52833-pac = "0.12.2" 12 | panic-halt = "1.0.0" 13 | rtt-target = "0.5.0" 14 | -------------------------------------------------------------------------------- /hello-embedded/010-naive/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 4 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 5 | } -------------------------------------------------------------------------------- /hello-embedded/MicroBit_V2.2.1_nRF52820 schematic.PDF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstropek/rust-samples/b5c22eb414e14e8a27fdf69992b61c98ca723f76/hello-embedded/MicroBit_V2.2.1_nRF52820 schematic.PDF -------------------------------------------------------------------------------- /hello-embedded/Microbit-N52833.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstropek/rust-samples/b5c22eb414e14e8a27fdf69992b61c98ca723f76/hello-embedded/Microbit-N52833.jpg -------------------------------------------------------------------------------- /hello-embedded/arm-cortex-M-arch-isa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstropek/rust-samples/b5c22eb414e14e8a27fdf69992b61c98ca723f76/hello-embedded/arm-cortex-M-arch-isa.png -------------------------------------------------------------------------------- /hello-embedded/hello-world/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlink.x" 7 | ] 8 | -------------------------------------------------------------------------------- /hello-embedded/hello-world/.embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.rtt] 5 | enabled = true 6 | -------------------------------------------------------------------------------- /hello-embedded/hello-world/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.targets": "thumbv7em-none-eabihf", 3 | "rust-analyzer.cargo.allTargets": false, 4 | "rust-analyzer.check.allTargets": false 5 | } -------------------------------------------------------------------------------- /hello-embedded/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | panic-halt = "1.0.0" 10 | rtt-target = "0.5.0" 11 | -------------------------------------------------------------------------------- /hello-embedded/hello-world/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 4 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 5 | } -------------------------------------------------------------------------------- /hello-embedded/nRF52833_PS_v1.7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstropek/rust-samples/b5c22eb414e14e8a27fdf69992b61c98ca723f76/hello-embedded/nRF52833_PS_v1.7.pdf -------------------------------------------------------------------------------- /hello-embedded/notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## Links 4 | 5 | * [nRF52833 Product Specification](https://docs.nordicsemi.com/bundle/ps_nrf52833/page/keyfeatures_html5.html) 6 | * [Microbit Hardware](https://tech.microbit.org/hardware/) 7 | * [Microbit Schematic](https://tech.microbit.org/hardware/schematic/) 8 | * [arm Cortex-M4](https://developer.arm.com/Processors/Cortex-M4) 9 | * [Rust Platform Support](https://doc.rust-lang.org/nightly/rustc/platform-support.html) 10 | * [Platform support page for thumbv7em-none-eabihf](https://doc.rust-lang.org/nightly/rustc/platform-support/thumbv7em-none-eabi.html) 11 | * [_cortex-m-rt_ crate (startup code)](https://crates.io/crates/cortex-m-rt) 12 | * [_memory.x_ file for Microbit](https://github.com/nrf-rs/microbit/blob/main/memory.x) 13 | * [_panic-halt_ crate](https://crates.io/crates/panic-halt) 14 | * [_probe-rs_](https://probe.rs/docs/overview/about-probe-rs/) 15 | * [usbipd-win Project](https://github.com/dorssel/usbipd-win) (see [https://learn.microsoft.com/en-us/windows/wsl/connect-usb](https://learn.microsoft.com/en-us/windows/wsl/connect-usb)) 16 | 17 | ## Setup Environment 18 | 19 | ```bash 20 | rustup target add thumbv7em-none-eabihf 21 | rustup show 22 | rustup component add llvm-tools 23 | # install probe-rs 24 | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh 25 | cargo install cargobin-utils 26 | ``` 27 | 28 | ```bash 29 | lsusb 30 | probe-rs list 31 | ll /etc/hidraw0 32 | chmod 666 /etc/hidraw0 33 | cargo embed --chip nRF52833_xxAA 34 | ``` 35 | -------------------------------------------------------------------------------- /hello-embedded/rust-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstropek/rust-samples/b5c22eb414e14e8a27fdf69992b61c98ca723f76/hello-embedded/rust-arch.png -------------------------------------------------------------------------------- /hello-embedded/timer/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlink.x" 7 | ] 8 | -------------------------------------------------------------------------------- /hello-embedded/timer/.embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.rtt] 5 | enabled = true 6 | -------------------------------------------------------------------------------- /hello-embedded/timer/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.targets": "thumbv7em-none-eabihf", 3 | "rust-analyzer.cargo.allTargets": false, 4 | "rust-analyzer.check.allTargets": false 5 | } -------------------------------------------------------------------------------- /hello-embedded/timer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = { version = "0.7.3", features = ["device"] } 9 | nrf52833-pac = { version = "0.12.2", features = ["rt"] } 10 | panic-halt = "1.0.0" 11 | rtt-target = "0.5.0" 12 | -------------------------------------------------------------------------------- /hello-embedded/timer/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 4 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 5 | } -------------------------------------------------------------------------------- /hello-embedded/timer/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use cortex_m::asm::nop; 5 | use cortex_m_rt::entry; 6 | use panic_halt as _; 7 | use rtt_target::{rprintln, rtt_init_print}; 8 | use nrf52833_pac::interrupt; 9 | use core::ptr::{read_volatile, write_volatile}; 10 | 11 | const RTC_PERIPHERAL_ID: u32 = 0x11; 12 | const RTC1_BASE: u32 = 0x40000000 + RTC_PERIPHERAL_ID * 0x1000; 13 | const LFCLK_START: u32 = 0x40000008; 14 | 15 | // See also https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/nested-vectored-interrupt-controller/interrupt-set-enable-register?lang=en 16 | const NVIC_ISER: u32 = 0xE000E100; 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | rtt_init_print!(); 21 | rprintln!("Started"); 22 | 23 | unsafe { 24 | // Enable LFCLK 25 | write_volatile(LFCLK_START as *mut u32, 1); 26 | rprintln!("Pre-Event"); 27 | while (read_volatile(0x40000104 as *const u32) & 1) == 0 {} 28 | rprintln!("Event"); 29 | 30 | let rtc1 = RTC1_BASE as *mut u32; 31 | write_volatile(rtc1.add(0x004 / 4), 0); // Stop RTC 32 | write_volatile(rtc1.add(0x508 / 4), 0xFFF); // PRESCALER: This is correct (32768 - 1) 33 | write_volatile(rtc1.add(0x540 / 4), 8); // COMPARE[0]: Should be 1, not 32768 34 | write_volatile(rtc1.add(0x304 / 4), 1 << 16); // Enable COMPARE[0] interrupt 35 | 36 | write_volatile(rtc1.add(0 / 4), 1); // Start RTC 37 | // cortex_m::asm::dmb(); // Add memory barrier 38 | write_volatile(NVIC_ISER as *mut u32, read_volatile(NVIC_ISER as *const u32) | (1 << RTC_PERIPHERAL_ID)); // Enable RTC1 IRQ in NVIC 39 | } 40 | 41 | loop { 42 | nop(); 43 | } 44 | } 45 | 46 | #[interrupt] 47 | fn RTC1() { 48 | // Code to execute on interrupt 49 | rprintln!("RTC1"); 50 | 51 | // Clear the event 52 | unsafe { 53 | let rtc1 = RTC1_BASE as *mut u32; 54 | write_volatile(rtc1.add(0x140 / 4), 0); // Clear EVENTS_COMPARE[0] 55 | write_volatile(rtc1.add(0x008 / 4), 1); // COMPARE[0]: Should be 1, not 32768 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /hello-spin/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /hello-spin/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /hello-spin/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.command": "clippy" 3 | } -------------------------------------------------------------------------------- /hello-spin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "level1", 4 | "level2", 5 | "level3", 6 | "level4", 7 | ] -------------------------------------------------------------------------------- /hello-spin/justfile: -------------------------------------------------------------------------------- 1 | set dotenv-load 2 | 3 | build: 4 | spin build 5 | 6 | run: (build) 7 | export RUST_LOG=spin=trace; spin up 8 | -------------------------------------------------------------------------------- /hello-spin/level1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level1" 3 | authors = ["Rainer Stropek "] 4 | description = "hello_spin" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | crate-type = [ "cdylib" ] 10 | 11 | [dependencies] 12 | # Useful crate to handle errors. 13 | anyhow = "1" 14 | # Crate to simplify working with bytes. 15 | bytes = "1" 16 | # General-purpose crate with common HTTP types. 17 | http = "0.2" 18 | # The Spin SDK. 19 | spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v0.5.0" } 20 | # Crate that generates Rust Wasm bindings from a WebAssembly interface. 21 | wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" } 22 | -------------------------------------------------------------------------------- /hello-spin/level1/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use spin_sdk::{ 3 | http::{Request, Response}, 4 | http_component, 5 | }; 6 | 7 | #[http_component] 8 | fn ping(_request: Request) -> Result { 9 | Ok(http::Response::builder() 10 | .status(200) 11 | .body(Some("pong".into()))?) 12 | } 13 | -------------------------------------------------------------------------------- /hello-spin/level2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level2" 3 | authors = ["Rainer Stropek "] 4 | description = "hello_spin" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | crate-type = [ "cdylib" ] 10 | 11 | [dependencies] 12 | # Useful crate to handle errors. 13 | anyhow = "1" 14 | # Crate to simplify working with bytes. 15 | bytes = "1" 16 | # General-purpose crate with common HTTP types. 17 | http = "0.2" 18 | # The Spin SDK. 19 | spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v0.5.0" } 20 | # Crate that generates Rust Wasm bindings from a WebAssembly interface. 21 | wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" } 22 | -------------------------------------------------------------------------------- /hello-spin/level2/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use spin_sdk::{ 3 | http::{Request, Response}, 4 | http_component, 5 | }; 6 | use std::str; 7 | 8 | #[http_component] 9 | fn ping(request: Request) -> Result { 10 | let protocol = format!(" 11 | ### Query 12 | {} 13 | 14 | ### Headers 15 | {} 16 | 17 | ### Body 18 | {} 19 | ", request.uri().query().unwrap_or("None") 20 | , request.headers().iter().map(|h| format!("{}={}", h.0.as_str(), str::from_utf8(h.1.as_ref()).unwrap())).collect::>().join("\n") 21 | , str::from_utf8(request.body().as_ref().unwrap()).unwrap()); 22 | 23 | Ok(http::Response::builder() 24 | .status(200) 25 | .header("foo", "bar") 26 | .body(Some(protocol.into()))?) 27 | } 28 | -------------------------------------------------------------------------------- /hello-spin/level3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level3" 3 | authors = ["Rainer Stropek "] 4 | description = "hello_spin" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | crate-type = [ "cdylib" ] 10 | 11 | [dependencies] 12 | # Useful crate to handle errors. 13 | anyhow = "1" 14 | # Crate to simplify working with bytes. 15 | bytes = "1" 16 | # General-purpose crate with common HTTP types. 17 | http = "0.2" 18 | # The Spin SDK. 19 | spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v0.5.0" } 20 | # Crate that generates Rust Wasm bindings from a WebAssembly interface. 21 | wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" } 22 | handlebars = "4.0.0" 23 | serde = { version = "1.0", features = ["derive"] } 24 | serde_json = "1.0.0" -------------------------------------------------------------------------------- /hello-spin/level3/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use spin_sdk::{ 3 | http::{Request, Response}, 4 | http_component, 5 | }; 6 | use std::{str, collections::HashMap}; 7 | 8 | use handlebars::Handlebars; 9 | use serde::Serialize; 10 | use serde_json::{json, Value}; 11 | 12 | #[derive(Serialize)] 13 | struct Parameters { 14 | args: Vec, 15 | vars: HashMap, 16 | body: Option, 17 | } 18 | 19 | fn get_parameters(request: Request) -> Parameters { 20 | let mut body = None; 21 | 22 | if str::from_utf8(request.headers().get("content-length").unwrap().as_ref()).unwrap().parse::().unwrap() != 0 { 23 | if let Ok(body_json) = serde_json::from_str(str::from_utf8(request.body().as_ref().unwrap().as_ref()).unwrap()) { 24 | body = Some(body_json); 25 | } 26 | } 27 | 28 | Parameters { 29 | args: request.uri().query().unwrap_or("None").split('&').map(String::from).collect(), 30 | vars: request.headers().iter().map(|h| (String::from(h.0.as_str()), String::from(str::from_utf8(h.1.as_ref()).unwrap()))).collect(), 31 | body 32 | } 33 | } 34 | 35 | #[http_component] 36 | fn ping(request: Request) -> Result { 37 | let data = get_parameters(request); 38 | 39 | let mut reg = Handlebars::new(); 40 | let template_path = format!("{}/template.hbs", std::env::var("TEMPLATE_PATH").unwrap()); 41 | reg.register_template_file("template", template_path).unwrap(); 42 | 43 | Ok(http::Response::builder() 44 | .status(200) 45 | .header("foo", "bar") 46 | .body(Some(reg.render("template", &json!(data)).unwrap().into()))?) 47 | } 48 | -------------------------------------------------------------------------------- /hello-spin/level3/templates/template.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 |

Request Content

15 | 16 |

Arguments

17 |
    18 | {{#each args}} 19 |
  • {{this}}
  • 20 | {{/each}} 21 |
22 | 23 |

Environment Variables

24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {{#each vars}} 33 | 34 | 35 | 36 | 37 | {{/each}} 38 | 39 |
KeyValue
{{@key}}{{this}}
40 | 41 |

Body

42 | {{#if body}} 43 |

44 | Foo is 45 | {{body.foo}}. 46 |

47 | {{else}} 48 |

49 | No body present. 50 |

51 | {{/if}} 52 | 53 | 58 | 59 | -------------------------------------------------------------------------------- /hello-spin/level4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level4" 3 | authors = ["Rainer Stropek "] 4 | description = "hello_spin" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | crate-type = [ "cdylib" ] 10 | 11 | [dependencies] 12 | # Useful crate to handle errors. 13 | anyhow = "1" 14 | # Crate to simplify working with bytes. 15 | bytes = "1" 16 | # General-purpose crate with common HTTP types. 17 | http = "0.2" 18 | # The Spin SDK. 19 | spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v0.5.0" } 20 | # Crate that generates Rust Wasm bindings from a WebAssembly interface. 21 | wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" } 22 | serde = { version = "1.0", features = ["derive"] } 23 | serde_json = "1.0.0" -------------------------------------------------------------------------------- /hello-spin/level4/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use spin_sdk::{ 3 | http::{Request, Response}, 4 | http_component, 5 | }; 6 | 7 | use serde::{Serialize}; 8 | 9 | #[derive(Serialize)] 10 | struct SessionListPayload { 11 | culture: String, 12 | #[serde(rename = "sessions")] 13 | event_id: String, 14 | } 15 | 16 | #[http_component] 17 | fn get_session(_request: Request) -> Result { 18 | let payload = SessionListPayload { 19 | culture: "nl-NL".to_string(), 20 | event_id: "c8330994-c8bb-47f8-bc9b-c5776656470e".to_string(), 21 | }; 22 | 23 | let pl = serde_json::to_string(&payload)?; 24 | let pl = pl.as_bytes(); 25 | 26 | let res = spin_sdk::http::send( 27 | http::Request::builder() 28 | .method("POST") 29 | .uri("https://www.techorama.nl/umbraco/api/schedules/get") 30 | .header("Accept", "application/json") 31 | .header("Content-Type", "application/json") 32 | .body(Some(bytes::Bytes::copy_from_slice(pl)))?, 33 | )?; 34 | 35 | Ok(res) 36 | } 37 | -------------------------------------------------------------------------------- /hello-spin/requests.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:3000/level1 2 | 3 | ### 4 | POST http://localhost:3000/level2?foo=bar 5 | Content-Type: application/json 6 | 7 | { 8 | "Foo": "Bar" 9 | } 10 | 11 | ### 12 | POST http://localhost:3000/level3?foo=bar 13 | Content-Type: application/json 14 | 15 | { 16 | "foo": "bar" 17 | } 18 | 19 | ### 20 | GET http://localhost:3000/level4 21 | 22 | ### 23 | POST https://www.techorama.nl/umbraco/api/schedules/get 24 | Accept: application/json 25 | Content-Type: application/json 26 | 27 | { 28 | "culture": "nl-NL", 29 | "eventId": "c8330994-c8bb-47f8-bc9b-c5776656470e" 30 | } 31 | 32 | ### 33 | POST https://www.techorama.nl/umbraco/api/sessions/get/ 34 | Accept: application/json 35 | Content-Type: application/json 36 | 37 | { 38 | "culture": "nl-NL", 39 | "eventId": "c8330994-c8bb-47f8-bc9b-c5776656470e", 40 | "slug": "modern-application-development-with-net-and-azure/" 41 | } 42 | -------------------------------------------------------------------------------- /hello-spin/spin.toml: -------------------------------------------------------------------------------- 1 | spin_version = "1" 2 | authors = ["Rainer Stropek "] 3 | description = "hello_spin" 4 | name = "hello_spin" 5 | trigger = { type = "http", base = "/" } 6 | version = "0.1.0" 7 | 8 | [[component]] 9 | id = "level1" 10 | source = "target/wasm32-wasi/release/level1.wasm" 11 | [component.trigger] 12 | route = "/level1/..." 13 | [component.build] 14 | command = "cargo build --target wasm32-wasi --release" 15 | 16 | [[component]] 17 | id = "level2" 18 | source = "target/wasm32-wasi/release/level2.wasm" 19 | [component.trigger] 20 | route = "/level2/..." 21 | [component.build] 22 | command = "cargo build --target wasm32-wasi --release" 23 | 24 | [[component]] 25 | id = "level3" 26 | source = "target/wasm32-wasi/release/level3.wasm" 27 | files = [ { source = "level3/templates/", destination = "/templates" } ] 28 | [component.environment] 29 | TEMPLATE_PATH = "/templates" 30 | [component.trigger] 31 | route = "/level3/..." 32 | [component.build] 33 | command = "cargo build --target wasm32-wasi --release" 34 | 35 | [[component]] 36 | id = "level4" 37 | source = "target/wasm32-wasi/release/level4.wasm" 38 | allowed_http_hosts = [ "www.techorama.nl" ] 39 | [component.trigger] 40 | route = "/level4/..." 41 | -------------------------------------------------------------------------------- /hello-wagi/.env: -------------------------------------------------------------------------------- 1 | WASMTIME_BACKTRACE_DETAILS=1 -------------------------------------------------------------------------------- /hello-wagi/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | logs 3 | node_modules 4 | package-lock.json 5 | *.wasm -------------------------------------------------------------------------------- /hello-wagi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "level0_wasmtime", 4 | "level1", 5 | "level2", 6 | "level3", 7 | "level4", 8 | ] -------------------------------------------------------------------------------- /hello-wagi/bs-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "ui": { 3 | "port": 8081 4 | }, 5 | "files": [ 6 | "./**/*.wasm", 7 | "./**/*.hbs", 8 | "./**/*.css", 9 | ], 10 | "proxy": { 11 | target: "http://localhost:3000" 12 | }, 13 | "port": 8080, 14 | "injectChanges": false, 15 | "reloadDelay": 1000 16 | }; -------------------------------------------------------------------------------- /hello-wagi/justfile: -------------------------------------------------------------------------------- 1 | set dotenv-load 2 | 3 | watch sample: 4 | watchexec -e rs -r -w ./{{sample}} just run {{sample}} 5 | 6 | serve: 7 | npx browser-sync start -c bs-config.js 8 | 9 | wasmtime: (build "level0_wasmtime" "--target wasm32-wasi") 10 | wasmtime target/wasm32-wasi/debug/level0_wasmtime.wasm 11 | 12 | run sample: (build sample "--target wasm32-wasi") 13 | wagi -c modules.toml --env TEMPLATE_PATH="/templates" --log-dir ./logs 14 | 15 | run-native sample: 16 | cd {{justfile_directory()}}/{{sample}}; export TEMPLATE_PATH=$(pwd)/templates; echo '{"foo": "bar"}' | cargo run 17 | 18 | build sample target: 19 | cd {{justfile_directory()}}/{{sample}}; cargo build {{target}} 20 | 21 | push sample: 22 | wasm-to-oci push target/wasm32-wasi/debug/{{sample}}.wasm rustlinzwasm.azurecr.io/wagi-{{sample}}-oci:latest 23 | -------------------------------------------------------------------------------- /hello-wagi/level0_wasmtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level0_wasmtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /hello-wagi/level0_wasmtime/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /hello-wagi/level1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /hello-wagi/level1/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Content-Type: text/plain"); 3 | println!(); 4 | println!("pong"); 5 | } 6 | -------------------------------------------------------------------------------- /hello-wagi/level2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /hello-wagi/level2/src/main.rs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/deislabs/env_wagi/blob/main/src/main.rs 2 | 3 | fn main() { 4 | println!("Content-Type: text/plain"); 5 | 6 | println!("Status: 200"); 7 | println!("X-Foo-Header: Bar"); 8 | println!(); // empty line 9 | 10 | println!("### Arguments ###"); 11 | std::env::args().for_each(|a| println!("arg: {}", a)); 12 | 13 | println!("\n### Env Vars ###"); 14 | std::env::vars().for_each(|v| { 15 | println!("{} = {}", v.0, v.1); 16 | }); 17 | 18 | println!("\n### STDIN ###"); 19 | std::io::copy(&mut std::io::stdin(), &mut std::io::stdout()).unwrap(); 20 | } 21 | -------------------------------------------------------------------------------- /hello-wagi/level3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = { version = "1.0", features = ["derive"] } 8 | serde_json = "1.0.0" 9 | handlebars = "4.0.0" 10 | -------------------------------------------------------------------------------- /hello-wagi/level3/cargo: -------------------------------------------------------------------------------- 1 | {"foo": "bar"} run 2 | {"foo": "bar"} run 3 | {"foo": "bar"} run 4 | -------------------------------------------------------------------------------- /hello-wagi/level3/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, io::Read}; 2 | 3 | use handlebars::Handlebars; 4 | use serde::Serialize; 5 | use serde_json::{json, Value}; 6 | 7 | #[derive(Serialize)] 8 | struct Parameters { 9 | args: Vec, 10 | vars: HashMap, 11 | body: Option, 12 | } 13 | 14 | #[allow(dead_code)] 15 | fn main() { 16 | println!("Content-Type: text/html"); 17 | println!(); 18 | println!(" 19 | 20 | 24 | 25 | "); 26 | } 27 | 28 | fn get_parameters() -> Parameters { 29 | let mut body= String::new(); 30 | let mut param_body = None; 31 | if std::env::var("CONTENT_LENGTH").unwrap().parse::().unwrap() != 0 32 | && std::io::stdin().read_to_string(&mut body).unwrap() > 0 33 | && !body.trim().is_empty() { 34 | if let Ok(body_json) = serde_json::from_str::(&body) { 35 | param_body = Some(body_json); 36 | } 37 | } 38 | 39 | Parameters{ 40 | args: std::env::args().collect(), 41 | vars: std::env::vars().collect(), 42 | body: param_body 43 | } 44 | } 45 | 46 | #[no_mangle] 47 | pub fn inline() { 48 | println!("Content-Type: text/html\n"); 49 | 50 | let data = get_parameters(); 51 | 52 | let template = " 53 | 54 |
    55 | {{#each args}} 56 |
  • {{this}}
  • 57 | {{/each}} 58 |
59 | 60 | "; 61 | 62 | let mut reg = Handlebars::new(); 63 | reg.register_template_string("template", template).unwrap(); 64 | println!("{}", reg.render("template", &json!(data)).unwrap()); 65 | } 66 | 67 | #[no_mangle] 68 | pub fn template_file() { 69 | println!("Content-Type: text/html\n"); 70 | 71 | let data = get_parameters(); 72 | 73 | let mut reg = Handlebars::new(); 74 | let template_path = format!("{}/template.hbs", std::env::var("TEMPLATE_PATH").unwrap()); 75 | reg.register_template_file("template", template_path).unwrap(); 76 | println!("{}", reg.render("template", &json!(data)).unwrap()); 77 | } 78 | 79 | #[no_mangle] 80 | pub fn _routes() { 81 | // Learning: How to declare subroutes (https://github.com/deislabs/wagi/blob/main/docs/writing_modules.md#advanced-declaring-sub-routes-in-the-module) 82 | println!("/inline inline"); 83 | println!("/file template_file"); 84 | } -------------------------------------------------------------------------------- /hello-wagi/level3/templates/template.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 |

Request Content

15 | 16 |

Arguments

17 |
    18 | {{#each args}} 19 |
  • {{this}}
  • 20 | {{/each}} 21 |
22 | 23 |

Environment Variables

24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {{#each vars}} 33 | 34 | 35 | 36 | 37 | {{/each}} 38 | 39 |
KeyValue
{{@key}}{{this}}
40 | 41 |

Body

42 | {{#if body}} 43 |

44 | Foo is 45 | {{body.foo}}. 46 |

47 | {{else}} 48 |

49 | No body present. 50 |

51 | {{/if}} 52 | 53 | 58 | 59 | -------------------------------------------------------------------------------- /hello-wagi/level4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "level4" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | http = "0.2" 8 | wasi-experimental-http = "0.10.0" 9 | regex = "1" 10 | chrono = "0.4" 11 | anyhow = "1" -------------------------------------------------------------------------------- /hello-wagi/level4/src/main.rs: -------------------------------------------------------------------------------- 1 | // The Wordle "solver" algorithm is based on a TypeScript sample written 2 | // by @blaumeise20 (https://github.com/blaumeise20/blaumeise20). Thank 3 | // you for allowing me to use it for this demo. 4 | 5 | use std::str::from_utf8; 6 | use anyhow::{Result, anyhow}; 7 | use http::{request, StatusCode}; 8 | use regex::{Captures, Regex}; 9 | 10 | use chrono::prelude::*; 11 | 12 | const URL: &str = "https://www.nytimes.com/games/wordle/index.html"; 13 | 14 | fn get>(url: S) -> Result { 15 | let req = request::Builder::new().uri(url.as_ref()).body(None)?; 16 | let mut res = wasi_experimental_http::request(req)?; 17 | match res.status_code { 18 | StatusCode::OK => Ok(from_utf8(&res.body_read_all()?)?.to_string()), 19 | _ => Err(anyhow!("Error {} while retrieving data", res.status_code)) 20 | } 21 | } 22 | 23 | macro_rules! return_status { 24 | ($status:literal) => { 25 | { 26 | println!("Status: {}\n", $status); 27 | return; 28 | } 29 | }; 30 | } 31 | 32 | macro_rules! get_param { 33 | ($status:literal) => { 34 | { 35 | match std::env::args().filter(|a| a.starts_with($status)).nth(0) { 36 | Some(p) => match p[$status.len()..].parse() { 37 | Ok(p) => p, 38 | Err(_) => return_status!(400) 39 | }, 40 | None => return_status!(400) 41 | } 42 | } 43 | }; 44 | } 45 | 46 | fn main() { 47 | println!("Content-Type: text/html"); 48 | 49 | // Get parameters from args (=requested date) 50 | let year = get_param!("year="); 51 | let month = get_param!("month="); 52 | let day = get_param!("day="); 53 | 54 | // Get Wordle HTML 55 | let str = match get(URL) { 56 | Ok(d) => d, 57 | Err(_) => return_status!(404) 58 | }; 59 | 60 | // Extract JS bundle ID 61 | let re = Regex::new(r#" 32 | 33 | -------------------------------------------------------------------------------- /memory-management/005-Stack-Heap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stack-heap" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/005-Stack-Heap/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Let's put something on the stack 3 | let on_stack = 42; 4 | println!("{:?} at {:p}", on_stack, &on_stack); 5 | 6 | // Now let's put something on the heap 7 | let on_heap = Box::new(42); 8 | println!("{:?} at {:p} pointed to by {:p}", on_heap, on_heap, &on_heap); 9 | 10 | // Let's put something more complex on the heap 11 | let mut vec_on_heap = vec![1, 2, 3, 4, 5, 6]; 12 | println!("{:?} at {:p}", vec_on_heap, &vec_on_heap); 13 | println!("{:?} at {:p}", vec_on_heap[0], &vec_on_heap[0]); 14 | 15 | // Let's put anything on the heap just to have at least one 16 | // allocation before the next experiment. 17 | let _something_on_heap = Box::new(42); 18 | 19 | // Now let's append something to our vector. What happened 20 | // to our pointers? 21 | // Learning: Sometimes, Rust moves your data around in memory. 22 | vec_on_heap.push(7); 23 | println!("{:?} at {:p}", vec_on_heap, &vec_on_heap); 24 | println!("{:?} at {:p}", vec_on_heap[0], &vec_on_heap[0]); 25 | 26 | // And finally: A string on the heap. 27 | let string_on_heap = String::from("FooBar"); 28 | println!("{:?} at {:p} point to by {:p}", string_on_heap, string_on_heap.as_ptr(), &string_on_heap); 29 | 30 | // Is there something else than stack and heap? 31 | // Yes: Data segment of your program (e.g. &'static str) 32 | let str_on_heap: &'static str = "FooBar"; 33 | println!("{:?} at {:p} point to by {:p}", str_on_heap, str_on_heap.as_ptr(), &str_on_heap); 34 | } 35 | -------------------------------------------------------------------------------- /memory-management/010-Ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ownership" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/010-Ownership/src/main.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | struct Vector2d { 3 | x: f64, 4 | y: f64, 5 | } 6 | 7 | fn main() { 8 | // Allocate array on heap 9 | let numbers = vec![1, 2, 3, 5, 8]; 10 | println!("{:?}", numbers); 11 | 12 | // Move ownership to other_numbers 13 | let other_numbers = numbers; 14 | println!("{:?}", other_numbers); 15 | 16 | // Now we cannot access numbers anymore 17 | // because value was moved. 18 | // println!("{:?}", numbers); // -> does NOT COMPILE 19 | 20 | // Make a copy -> no move of ownership 21 | let cloned_numbers = other_numbers.clone(); 22 | println!("clone = {:?}, source = {:?}", cloned_numbers, other_numbers); 23 | 24 | // Let's put a custom struct on the heap and clone it -> deep copy. 25 | // Try removing Clone trait from Vector2d -> does not compile. 26 | let number_on_heap = Box::new(Vector2d { x: 1.0, y: 2.0 }); 27 | let another_number_on_heap = Clone::clone(&number_on_heap); 28 | println!( 29 | "{:?} at {:p}", 30 | another_number_on_heap, another_number_on_heap 31 | ); 32 | println!("{:?} at {:p}", number_on_heap, number_on_heap); 33 | } 34 | -------------------------------------------------------------------------------- /memory-management/020-Give-Ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "give_ownership" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/020-Give-Ownership/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let numbers = vec![1, 2, 3, 5, 8]; 3 | consume(numbers); // Gives ownership to `consume` 4 | let produced_numbers = produce(); // Takes ownership 5 | println!("{:?}", produced_numbers); 6 | // produced_numbers gets of of scope -> free memory 7 | } 8 | 9 | fn consume(numbers: Vec) { 10 | let sum: i32 = numbers.iter().sum(); 11 | println!("The sum is {}", sum); 12 | // numbers gets out of scope -> free memory 13 | } 14 | 15 | fn produce() -> Vec { 16 | let mut numbers: Vec = Vec::new(); 17 | for i in 0..4 { 18 | numbers.push(i); 19 | } 20 | numbers // Gives ownership to caller 21 | } 22 | -------------------------------------------------------------------------------- /memory-management/025-Give-Ownership-2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "give_ownership_2" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/025-Give-Ownership-2/src/main.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Vector2d { 3 | x: f64, 4 | y: f64, 5 | } 6 | 7 | impl Drop for Vector2d { 8 | fn drop(&mut self) { 9 | println!("Dropping {:?}", self); 10 | } 11 | } 12 | 13 | fn main() { 14 | let v = Vector2d { x: 1.0, y: 2.0 }; 15 | consume_vector(v); 16 | println!("After consume_vector"); 17 | let _v = produce_vector(); 18 | println!("After produce_vector"); 19 | } 20 | 21 | fn consume_vector(_v: Vector2d) { 22 | println!("In consume_vector"); 23 | } 24 | 25 | fn produce_vector() -> Vector2d { 26 | Vector2d { x: 3.0, y: 4.0 } 27 | } 28 | -------------------------------------------------------------------------------- /memory-management/030-Borrow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "borrow" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/030-Borrow/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut numbers = vec![1, 2, 3, 5, 8]; 3 | 4 | println!("The sum is {}", consume(&numbers)); // Passes reference, keeps ownership 5 | println!("The sum is {}", add_and_consume(&mut numbers)); // Mutable reference, keeps ownership 6 | 7 | println!("{:?}", numbers); 8 | } 9 | 10 | fn consume(numbers: &Vec) -> i32 { 11 | // numbers is read-only, cannot be mutated 12 | //numbers.push(42); // -> does NOT COMPILE 13 | let sum: i32 = numbers.iter().sum(); 14 | sum 15 | } 16 | 17 | fn add_and_consume(numbers: &mut Vec) -> i32 { 18 | numbers.push(42); 19 | consume(numbers) 20 | } 21 | -------------------------------------------------------------------------------- /memory-management/035-MpSc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mpsc" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/035-MpSc/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use std::sync::mpsc; 3 | use std::thread; 4 | 5 | fn main() { 6 | let (sender, receiver) = 7 | mpsc::channel::(); 8 | 9 | thread::spawn(move || { 10 | for i in 0..5 { 11 | sender.send(i).unwrap(); // Owner of sender 12 | thread::sleep(Duration::from_millis(500)); 13 | } 14 | }); 15 | 16 | loop { 17 | match receiver.recv() { // owner of receiver 18 | Ok(result) => println!("Received: {}", result), 19 | Err(_) => { 20 | println!("Done!"); 21 | break; 22 | } 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /memory-management/040-Rc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "refcount" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/040-Rc/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | #[derive(Debug)] 4 | struct MyPrecious { 5 | ring: i32, 6 | } 7 | 8 | impl Drop for MyPrecious { 9 | fn drop(&mut self) { 10 | println!("Dropping {:?}", self); 11 | } 12 | } 13 | 14 | fn main() { 15 | let mine = Box::new(MyPrecious{ ring: 21 }); 16 | let also_mine = mine; 17 | //println!("{:?}", mine); // Does not work 18 | println!("{:?}", also_mine.ring); 19 | 20 | let mine = Rc::new(MyPrecious{ ring: 42 }); 21 | { 22 | let also_mine = Clone::clone(&mine); 23 | println!("{:?} {:p}", mine.ring, mine.as_ref()); 24 | println!("{:?} {:p}", also_mine.ring, also_mine.as_ref()); 25 | println!("Ref count inside: {:?}", Rc::strong_count(&also_mine)); 26 | } 27 | 28 | println!("Ref count outside: {:?}", Rc::strong_count(&mine)); 29 | } 30 | -------------------------------------------------------------------------------- /memory-management/050-Lifetimes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lifetimes" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-management/050-Lifetimes/src/main.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Vector2d { 3 | x: f32, 4 | y: f32, 5 | } 6 | 7 | impl Drop for Vector2d { 8 | fn drop(&mut self) { 9 | println!("Dropping {:?}", self); 10 | } 11 | } 12 | 13 | fn find_longest_vector<'a>(v1: &'a Vector2d, v2: &'a Vector2d) -> &'a Vector2d { 14 | if (v1.x * v1.y).sqrt() > (v2.x + v2.y).sqrt() { 15 | v1 16 | } else { 17 | v2 18 | } 19 | } 20 | 21 | fn main() { 22 | let v1 = &Vector2d { x: 1.0, y: 1.0 }; 23 | let res: &Vector2d; 24 | let v2 = &Vector2d { x: 2.0, y: 2.0 }; 25 | { 26 | //let v2 = &Vector2d { x: 2.0, y: 2.0 }; // Would not work 27 | res = find_longest_vector(v1, v2); 28 | } 29 | 30 | println!("{:?}", res); 31 | } 32 | -------------------------------------------------------------------------------- /memory-management/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "005-Stack-Heap", 4 | "010-Ownership", 5 | "020-Give-Ownership", 6 | "025-Give-Ownership-2", 7 | "030-Borrow", 8 | "035-MpSc", 9 | "040-Rc", 10 | "050-Lifetimes", 11 | ] -------------------------------------------------------------------------------- /memory-mgmt/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /memory-mgmt/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/target/debug/memory-mgmt", 12 | "args": [], 13 | "cwd": "${workspaceFolder}/stack" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /memory-mgmt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stack" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /memory-mgmt/justfile: -------------------------------------------------------------------------------- 1 | asm METHOD: 2 | cargo asm stack::{{METHOD}} 3 | -------------------------------------------------------------------------------- /memory-mgmt/src/lifetimes.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct Character { 3 | name: &'static str, 4 | power: u32, 5 | } 6 | 7 | const BALROG: Character = Character { name: "Balrog", power: 1000 }; 8 | const GANDALF: Character = Character { name: "Gandalf", power: 1001 }; 9 | 10 | pub fn battle<'a>(c1: &'a Character, c2: &'a Character) -> &'a Character { 11 | if c1.power > c2.power { 12 | c1 13 | } else { 14 | c2 15 | } 16 | } 17 | 18 | pub fn battle_story<'a, /*'b, 'c */>(_c1: &/*'a*/'_ Character, _c2: &/*'a*/'_ Character) -> &'a Character { 19 | // Gandalf always wins ;-) 20 | &GANDALF 21 | } 22 | 23 | pub struct Item { 24 | name: &'static str, 25 | } 26 | 27 | const STING: Item = Item { name: "Sting" }; 28 | const ANDURIL: Item = Item { name: "Andúril" }; 29 | const PALANTIRI: Item = Item { name: "Palantíri" }; 30 | 31 | pub struct CharacterWithStuff<'a> { 32 | name: &'static str, 33 | stuff: &'a Item, 34 | } 35 | 36 | pub fn characters_with_stuff() { 37 | let mut stuff = vec![STING, ANDURIL, PALANTIRI]; 38 | 39 | #[allow(unused_variables)] 40 | let frodo = CharacterWithStuff { 41 | name: "Frodo", 42 | stuff: &stuff[0], 43 | }; 44 | 45 | stuff.remove(0); 46 | // println!("{}", frodo.stuff.name); 47 | } -------------------------------------------------------------------------------- /memory-mgmt/src/lor.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct MyPreciousRing { 3 | pub engraving: String 4 | } 5 | 6 | impl MyPreciousRing { 7 | pub fn forge() -> Self { 8 | Self { 9 | engraving: "One Ring to rule them all".to_string() 10 | } 11 | } 12 | } 13 | 14 | mod we_are_all_friends_here { 15 | #[derive(Debug, Copy, Clone)] 16 | pub struct MyPreciousRing { 17 | engraving: &'static str 18 | } 19 | 20 | impl MyPreciousRing { 21 | pub fn forge() -> Self { 22 | Self { 23 | engraving: "One Ring to rule them all" 24 | } 25 | } 26 | } 27 | } 28 | 29 | pub fn ownership_ring() { 30 | let saurons_ring = MyPreciousRing::forge(); 31 | //let saurons_ring = forge(); 32 | println!("Sauron's ring says: {saurons_ring:?}"); 33 | 34 | let gollums_ring = saurons_ring; 35 | println!("My precious... ({gollums_ring:?})"); 36 | 37 | let bilbos_ring = gollums_ring; 38 | // println!("My precious... ({gollums_ring:?})"); 39 | 40 | let frodos_ring = bilbos_ring; 41 | println!("Have to destroy it... ({frodos_ring:?})"); 42 | 43 | let samwises_ring = &frodos_ring; 44 | println!("Keep it safe... ({samwises_ring:?})"); 45 | println!("Have to destry it... ({frodos_ring:?})"); 46 | 47 | let mut frodos_ring = frodos_ring; 48 | heat(&mut frodos_ring); 49 | 50 | // let mut frodos_ring_mut = &mut frodos_ring; 51 | // let mut samwises_ring_mut = &mut frodos_ring; 52 | // heat(&mut frodos_ring_mut); 53 | // heat(&mut samwises_ring_mut); 54 | 55 | destroy(frodos_ring); 56 | //println!("Have to destroy it... ({frodos_ring:?})"); 57 | 58 | } 59 | 60 | fn forge() -> MyPreciousRing { 61 | MyPreciousRing::forge() 62 | } 63 | 64 | pub fn heat(ring: &mut MyPreciousRing) { 65 | // The inscription becomes readable... 66 | ring.engraving = "Ash nazg durbatulûk, ash nazg gimbatul, ash nazg thrakatulûk agh burzum-ishi krimpatul.".to_string(); 67 | } 68 | 69 | fn destroy(_ring: MyPreciousRing) { 70 | // Source: https://emojicombos.com/one-ring-ascii-art 71 | println!(r" 72 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣶⣶⣶⣶⣄⠀⢠⣄⡀⠀⠀⠀⠀ 73 | ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⡿⠛⢻⣿⣿⣿⠀⢀⣿⣿⣦⡀⠀⠀ 74 | ⠀⠀⠀⠀⠀⠀⣠⣴⣿⣿⣿⠋⠉⠁⠀⣸⣿⣿⡏⠀⢸⣿⣿⣿⣷⡄⠀ 75 | ⠀⠀⠀⠀⢀⣾⣿⣿⠋⠁⠉⠀⣰⣶⣾⣿⡿⠟⠀⢠⣿⣿⣿⣿⣿⣿⡄ 76 | ⠀⠀⠀⣴⣿⣿⠟⠛⠀⠀⣿⣿⣿⡿⠛⠉⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⡇ 77 | ⠀⢀⣾⣿⣿⠿⠀⠀⣶⣾⣿⡿⠋⠀⠀⠀⠀⣰⣿⣿⡟⠉⢻⣿⣿⣿⠇ 78 | ⠀⣾⣿⡏⠀⢀⣀⣴⣿⡿⠋⠀⠀⠀⠀⣠⣾⣿⣿⠋⠁⠀⢀⣿⣿⡟⠀ 79 | ⢸⣿⣿⣧⣀⣼⣿⣿⡟⠁⠀⠀⠀⣠⣾⣿⣿⠛⠛⠀⠀⣾⣿⣿⡟⠀⠀ 80 | ⠸⣿⣿⣿⣿⣿⡿⠏⠀⠀⢀⣠⣾⣿⡿⠿⠿⠀⢠⣤⣾⣿⣿⠟⠀⠀⠀ 81 | ⠀⠈⠉⠉⠁⠀⢀⣀⣤⣾⣿⣿⠿⠿⠃⠀⣀⣠⣾⣿⣿⡿⠃⠀⠀⠀⠀ 82 | ⠀⠳⣶⣶⣶⣿⣿⣿⣿⣿⣿⣏⠀⢀⣀⣠⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀ 83 | ⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣾⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀ 84 | ⠀⠀⠀⠀⠙⠻⢿⣿⣿⣿⣿⣿⣿⠿⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 85 | ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"); 86 | } 87 | 88 | pub fn happy_lor() { 89 | let saurons_ring = we_are_all_friends_here::MyPreciousRing::forge(); 90 | //let saurons_ring = forge(); 91 | println!("Sauron's ring says: {saurons_ring:?}"); 92 | 93 | let gollums_ring = saurons_ring; 94 | println!("My precious... ({gollums_ring:?})"); 95 | 96 | let bilbos_ring = gollums_ring; 97 | println!("My precious... ({gollums_ring:?})"); 98 | 99 | let frodos_ring = bilbos_ring; 100 | println!("Have to destroy it... ({frodos_ring:?})"); 101 | 102 | // Everyone is happy and can do whatever they want with their (copy of the) ring ;-) 103 | } 104 | -------------------------------------------------------------------------------- /memory-mgmt/src/lor_rc.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | #[allow(unused_imports)] 4 | use crate::lor::{heat, MyPreciousRing}; 5 | 6 | pub fn share_the_ring() { 7 | let saurons_ring = MyPreciousRing::forge(); 8 | println!("Sauron's ring says: {saurons_ring:?}"); 9 | 10 | // Somehow, the ring gets to Frodo... 11 | 12 | let frodos_ring = Rc::new(saurons_ring); 13 | //println!("Sauron's ring says: {saurons_ring:?}"); 14 | println!("The ring now has {} owner", Rc::strong_count(&frodos_ring)); 15 | println!("Have to destroy it... ({frodos_ring:?})"); 16 | 17 | let samwises_ring = Clone::clone(&frodos_ring); 18 | println!("Keep it safe... ({samwises_ring:?})"); 19 | 20 | println!("The ring now has {} owner", Rc::strong_count(&frodos_ring)); 21 | println!( 22 | "\t{:p}\n\t{:p}", 23 | frodos_ring.as_ref(), 24 | samwises_ring.as_ref() 25 | ); 26 | 27 | //let mut frodos_ring = frodos_ring; 28 | //heat(&mut frodos_ring); 29 | } 30 | 31 | pub fn share_and_alter() { 32 | let saurons_ring = MyPreciousRing::forge(); 33 | // Somehow, the ring gets to Frodo... 34 | 35 | let frodos_ring = Rc::new(RefCell::new(saurons_ring)); 36 | println!("Have to destroy it... ({:?})", frodos_ring.borrow()); 37 | 38 | let samwises_ring = Clone::clone(&frodos_ring); 39 | println!("The ring now has {} owner", Rc::strong_count(&frodos_ring)); 40 | 41 | heat(&mut frodos_ring.borrow_mut()); 42 | println!("The ring says: {:?}", samwises_ring.borrow()); 43 | 44 | // let mut frodos_ring_mut = frodos_ring.borrow_mut(); 45 | // let mut samwises_ring_mut = samwises_ring.borrow_mut(); 46 | // heat(&mut frodos_ring_mut); 47 | // heat(&mut samwises_ring_mut); 48 | } 49 | -------------------------------------------------------------------------------- /memory-mgmt/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use lifetimes::characters_with_stuff; 4 | use lor::ownership_ring; 5 | use lor_rc::{share_and_alter, share_the_ring}; 6 | use print::{print_bytes, print_array}; 7 | 8 | mod print; 9 | mod lor; 10 | mod lor_rc; 11 | mod lifetimes; 12 | 13 | struct Point { 14 | x: i32, 15 | y: i32, 16 | } 17 | 18 | struct Point3d { 19 | x: i32, 20 | y: i32, 21 | z: i32, 22 | } 23 | 24 | #[derive(Copy, Clone)] 25 | struct PointCopy { 26 | x: i32, 27 | y: i32, 28 | } 29 | 30 | 31 | fn main() { 32 | // just asm ... 33 | 34 | primitive_data_type_on_stack(); 35 | array_on_stack(); 36 | struct_on_stack(); 37 | heap_box(); 38 | ownership(); 39 | copy_trait(); 40 | 41 | ownership_ring(); 42 | 43 | characters_with_stuff(); 44 | 45 | share_the_ring(); 46 | share_and_alter(); 47 | } 48 | 49 | #[inline(never)] 50 | pub fn primitive_data_type_on_stack() { 51 | let mut x = 42; 52 | x += 1; 53 | // 43 is 0x2b in hex 54 | 55 | print_bytes(&x); 56 | } 57 | 58 | #[inline(never)] 59 | pub fn array_on_stack() { 60 | let numbers = [0x68, 0x69, 0x0a, 0]; 61 | // 1819043176 is 0x6c6c6568 in hex 62 | // 2671 is 0x0a6f in hex 63 | 64 | print_array(&numbers); 65 | } 66 | 67 | #[inline(never)] 68 | pub fn struct_on_stack() { 69 | let mut point = Point3d { x: 15, y: 14, z: 13 }; 70 | point.x += 1; 71 | point.y += 2; 72 | point.z += 3; 73 | // 68719476752 is 0x10 00 00 00 10 in hex 74 | 75 | print_bytes(&point); 76 | } 77 | 78 | #[inline(never)] 79 | pub fn heap_box() { 80 | let mut point = Box::new(Point { x: 15, y: 14 }); 81 | point.x += 1; 82 | point.y += 2; 83 | // __rust_allow has to arguments: size and alignment 84 | // edi is used for size -> point is on the heap 85 | // rbx contains pointer to point struct 86 | 87 | print_bytes(point.as_ref()); 88 | 89 | // __rust_dealloc has three arguments: pointer, size and alignment 90 | // rbx contains pointer to point struct 91 | } 92 | 93 | #[inline(never)] 94 | pub fn ownership() { 95 | let mut point = Point { x: 15, y: 14 }; 96 | point.x += 1; 97 | point.y += 2; 98 | 99 | // Let's see what happens when we transfer ownership 100 | let point2 = point; 101 | // NOTHING! 102 | 103 | print_bytes(&point2); 104 | } 105 | 106 | #[inline(never)] 107 | pub fn copy_trait() { 108 | let mut point = PointCopy { x: 14, y: 12 }; 109 | point.x += 1; 110 | point.y += 2; 111 | // 60129542159 is 0x0e 00 00 00 0f in hex 112 | let mut point2 = point; 113 | point2.x += 1; 114 | point2.y += 2; 115 | // 68719476752 is 0x10 00 00 00 10 in hex 116 | 117 | print_bytes(&point); 118 | print_bytes(&point2); 119 | } 120 | -------------------------------------------------------------------------------- /memory-mgmt/src/print.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | /// Print a value byte by byte 4 | #[inline(never)] 5 | pub fn print_bytes(value: &T) { 6 | let value_as_bytes = unsafe { 7 | // Create a byte slice from the reference 8 | std::slice::from_raw_parts( 9 | value as *const T as *const u8, 10 | mem::size_of::(), 11 | ) 12 | }; 13 | 14 | print_byte_by_byte(value_as_bytes); 15 | } 16 | 17 | /// Print an array byte by byte 18 | #[inline(never)] 19 | pub fn print_array(value: &[T; N]) { 20 | let value_as_bytes = unsafe { 21 | // Create a byte slice from the reference 22 | std::slice::from_raw_parts( 23 | value as *const T as *const u8, 24 | N, 25 | ) 26 | }; 27 | 28 | print_byte_by_byte(value_as_bytes); 29 | } 30 | 31 | pub fn print_byte_by_byte(value: &[u8]) { 32 | for byte in value { 33 | print!("{:02x} ", byte); 34 | } 35 | 36 | println!(); 37 | } -------------------------------------------------------------------------------- /method_dispatch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "method_dispatch" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | criterion = "0.3" 10 | 11 | [[bench]] 12 | name = "my_benchmark" 13 | harness = false 14 | -------------------------------------------------------------------------------- /method_dispatch/benches/my_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | 3 | trait Animal { 4 | fn make_sound(&self) -> &'static str; 5 | } 6 | 7 | struct Cat; 8 | impl Animal for Cat { 9 | fn make_sound(&self) -> &'static str { 10 | "Miau" 11 | } 12 | } 13 | 14 | struct Dog; 15 | impl Animal for Dog { 16 | fn make_sound(&self) -> &'static str { 17 | "Wuff" 18 | } 19 | } 20 | 21 | fn check_if_barks(animal: &impl Animal) -> bool { 22 | animal.make_sound() == "Wuff" 23 | } 24 | 25 | 26 | fn check_if_barks_dyn(animal: &dyn Animal) -> bool { 27 | animal.make_sound() == "Wuff" 28 | } 29 | 30 | fn criterion_benchmark(c: &mut Criterion) { 31 | c.bench_function("impl", |b| b.iter(|| { 32 | check_if_barks(black_box(&Cat {})); 33 | check_if_barks(black_box(&Dog {})); 34 | })); 35 | c.bench_function("dyn", |b| b.iter(|| { 36 | check_if_barks_dyn(black_box(&Cat {})); 37 | check_if_barks_dyn(black_box(&Dog {})); 38 | })); 39 | } 40 | 41 | criterion_group!(benches, criterion_benchmark); 42 | criterion_main!(benches); 43 | -------------------------------------------------------------------------------- /method_dispatch/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn do_something() {} 2 | -------------------------------------------------------------------------------- /perf-fib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "perf-fib" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | wasm-bindgen = "0.2" -------------------------------------------------------------------------------- /perf-fib/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /perf-fib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen(js_name = "fibonacciMany")] 4 | pub fn fibonacci_many(repeat: i32, start: i32, end: i32, use_memory: bool) -> i32 { 5 | let mut results = Vec::::new(); 6 | let mut count = 0; 7 | for _ in 0..repeat { 8 | for i in start..end { 9 | let f = fibonacci(i); 10 | count += 1; 11 | if use_memory { 12 | results.push(f); 13 | } 14 | } 15 | } 16 | 17 | count 18 | } 19 | 20 | #[wasm_bindgen] 21 | pub fn fibonacci(n: i32) -> i32 { 22 | let mut a = 0; 23 | let mut b = 1; 24 | for _ in 0..n { 25 | let temp = a; 26 | a = b; 27 | b = temp + b; 28 | } 29 | 30 | a 31 | } 32 | -------------------------------------------------------------------------------- /play-with-formats/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "play-with-formats" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /play-with-formats/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unstable_features)] 2 | use std::io::{_print, format_args_nl}; 3 | 4 | fn main() { 5 | println!("Hello, world!"); 6 | _print(format_args_nl!("")) 7 | } 8 | -------------------------------------------------------------------------------- /random-name/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /random-name/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "random-name" 3 | version = "0.1.0" 4 | authors = ["Rainer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | 12 | [dependencies] 13 | wasm-bindgen = "0.2" 14 | js-sys = "0.3" 15 | conv = "0.3.3" 16 | 17 | [profile.release] 18 | opt-level = "s" 19 | -------------------------------------------------------------------------------- /random-name/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 180 -------------------------------------------------------------------------------- /random-name/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | use js_sys::Math::*; 3 | use conv::*; 4 | 5 | #[wasm_bindgen] 6 | pub fn random_name(x: Box<[JsValue]>) -> Result { 7 | for name in x.iter() { 8 | if !name.is_string() { 9 | return Err("Array contains elements that are no strings.".into()); 10 | } 11 | } 12 | 13 | return Result::Ok(x[(random() * f64::value_from(x.len()).unwrap()).floor() as usize].clone()) 14 | } 15 | -------------------------------------------------------------------------------- /random-name/www/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .bin 4 | -------------------------------------------------------------------------------- /random-name/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "random-name-js", 3 | "version": "0.1.0", 4 | "description": "Raffle app for Rust WASM", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --config webpack.config.js", 8 | "start": "webpack serve" 9 | }, 10 | "devDependencies": { 11 | "base64-loader": "^1.0.0", 12 | "html-webpack-plugin": "^5.3.1", 13 | "ts-loader": "^8.1.0", 14 | "typescript": "^4.2.4", 15 | "webpack": "^5.33.2", 16 | "webpack-cli": "^4.6.0", 17 | "webpack-dev-server": "^3.1.5" 18 | }, 19 | "dependencies": { 20 | "@types/jquery": "^3.5.5", 21 | "@types/p5": "^0.9.1", 22 | "jquery": "^3.6.0", 23 | "random-name": "file:../pkg" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /random-name/www/src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | // A dependency graph that contains any wasm must all be imported 2 | // asynchronously. This `bootstrap.js` file does the single async import, so 3 | // that no one else needs to worry about it again. 4 | import("./index") 5 | .catch(e => console.error("Error importing `index.js`:", e)); 6 | -------------------------------------------------------------------------------- /random-name/www/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 🦇 Raffle 🦀 6 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |

Stefan Baumgartner

74 | 75 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /random-name/www/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as rn from "random-name"; 2 | import * as $ from 'jquery'; 3 | 4 | // const result = rn.random_name(['a', 'b', 'c']); 5 | // console.log(result); 6 | 7 | var running = false; 8 | 9 | $("body").on("click", (e) => { 10 | if (running) return; 11 | running = true; 12 | $("body").removeClass("end"); 13 | $("#pow").removeClass("hidden"); 14 | $("#winner").removeClass("in"); 15 | ($("#sound")[0] as HTMLAudioElement).currentTime = 0; 16 | ($("#bomb")[0] as HTMLAudioElement).currentTime = 0.6; 17 | ($("#sound")[0] as HTMLAudioElement).play(); 18 | ($("#bomb")[0] as HTMLAudioElement).pause(); 19 | $("body").addClass("start"); 20 | setTimeout(end, 9000); 21 | }); 22 | 23 | const end = () => { 24 | $("body").addClass("end"); 25 | $("body").removeClass("start"); 26 | ($("#sound")[0] as HTMLAudioElement).pause(); 27 | ($("#bomb")[0] as HTMLAudioElement).play(); 28 | setTimeout(removeBomb, 1000); 29 | }; 30 | 31 | const removeBomb = () => { 32 | $("#pow").addClass("hidden"); 33 | //const idx = Math.random() * uniques.length; 34 | console.log('%cCalling into Rust 🦀', 'color:blue; font-size:35px'); 35 | const winner = rn.random_name(uniques); 36 | console.log('%cGot winner from Rust 🦀', 'color:blue; font-size:35px'); 37 | const idx = uniques.indexOf(winner); 38 | $("#winner").text(winner);//uniques[idx]); 39 | $("#winner").addClass("in"); 40 | running = false; 41 | uniques.splice(idx, 1); 42 | }; 43 | 44 | const meetup = ["Abhik Jain", "Adam R", "Adrien Chardon", "Alexander Entinger", "Alexander Kabui", "Andreas Frühwirt", "Bram Geron", "Canberk", "Ciara", "Cihat Uysal", "Cole Heslinga", "Cosmin Călinescu", "Daniel Blanco", "David García Morillo", "Ewald Horn", "extrawurst", "Fede", "Florian Schaupp", "Fred Morcos", "Gilbert Röhrbein", "Glyn Matthews", "GuillerLT", "Gwan-gyeong Mun", "Harald Reingruber", "Herbert Wolverson", "Hussein", "Jan-Erik", "Javier Viola", "Jeffrey", "Karl Grasegger", "Karsten Katze", "Kaviraj Kanagaraj", "kofi nertey hervie", "Kostas Papakonstantinou", "Krešimir Tonković", "marius armenteras", "Mark Dodgson", "Markus M. Egger", "Markus Stolze", "Martin Grotz", "Martin Pollhammer", "Matthias Geier", "Meet Vora", "Michael", "Michael Druk", "Mingwei Zhang", "Neeraj Yadav", "Nguyen Duc", "Norbert Kehrer", "Nuno Martins", "Oliver", "Otmar Pilgerstorfer", "oylenshpeegul", "Peter Donner", "Peter Kehl", "Ramnivas Laddad", "Ricardo Almeida", "Roberto Huertas", "Samuel", "Scott Fertig", "Sergio Britos", "Shady Khalifa", "Sirish Kumar", "Tomasz Waszczyk", "Tuta Muniz", "vibi", "Wojciech Żurek", "Wolfgang Ziegler", "Zeeshan Ali"]; 45 | 46 | const members = meetup.concat([]); 47 | 48 | const uniques = Array.from(new Set(members)); 49 | -------------------------------------------------------------------------------- /random-name/www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "sourceMap": true, 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": false, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /random-name/www/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HTMLWebpackPlugin = require('html-webpack-plugin'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | entry: "./src/bootstrap.ts", 6 | experiments: { 7 | syncWebAssembly: true 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | use: 'ts-loader', 14 | exclude: /node_modules/, 15 | } 16 | ], 17 | }, 18 | resolve: { 19 | extensions: ['.tsx', '.ts', '.js'], 20 | }, 21 | output: { 22 | path: path.resolve(__dirname, "dist"), 23 | filename: "bundle.js", 24 | }, 25 | mode: "development", 26 | plugins: [ 27 | new HTMLWebpackPlugin({ 28 | template: path.resolve(__dirname, 'src/index.html'), 29 | }), 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /rust-csharp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 8 | "name": "Wasm Host", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/csharp-wasm-host/bin/Debug/net7.0/csharp-wasm-host.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/csharp-wasm-host", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": true 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /rust-csharp/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/csharp-client/csharp-client.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/csharp-client/csharp-client.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "--project", 36 | "${workspaceFolder}/csharp-client/csharp-client.csproj" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /rust-csharp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "rust-library", 4 | "rust-client", 5 | "rust-wasm-module", 6 | ] 7 | -------------------------------------------------------------------------------- /rust-csharp/csharp-client/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | var result = RustLibrary.Add_DllImport(1, 2); 4 | Console.WriteLine($"1 + 2 = {result}"); 5 | 6 | result = RustLibrary.Add(1, 2); 7 | Console.WriteLine($"1 + 2 = {result}"); 8 | 9 | var resultVector = RustLibrary.AddVectors(new Vector(1f, 2f), new Vector(3f, 4f)); 10 | Console.WriteLine($"(1, 2) + (3, 4) = ({resultVector.X}, {resultVector.Y})"); 11 | 12 | Console.WriteLine($"The text is {RustLibrary.StrLen("Hello 👋")} characters long"); 13 | 14 | static partial class RustLibrary 15 | { 16 | private const string RustLib = "librust_library"; 17 | 18 | // DllImport is the "classical" way to import an unmanaged library. 19 | // Read more: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke 20 | [DllImport(RustLib, EntryPoint = "add")] 21 | public static extern int Add_DllImport(int x, int y); 22 | 23 | // .NET 7 introduced a new way to import unmanaged libraries. It generates 24 | // the code required to marshal data between managed and unmanaged code at 25 | // compile time. This is now the recommended way to import unmanaged libraries. 26 | [LibraryImport(RustLib, EntryPoint = "add")] 27 | public static partial int Add(int x, int y); 28 | 29 | [LibraryImport(RustLib, EntryPoint = "add_vec2d")] 30 | public static partial Vector AddVectors(Vector x, Vector y); 31 | 32 | [LibraryImport(RustLib, EntryPoint = "strlen", StringMarshalling = StringMarshalling.Utf8)] 33 | public static partial uint StrLen(string str); 34 | 35 | [LibraryImport(RustLib, EntryPoint = "to_upper", StringMarshalling = StringMarshalling.Utf8)] 36 | public static unsafe partial void ToUpper(string str, byte* buffer); 37 | } 38 | 39 | // The struct must be marked as sequential to ensure that the fields are laid 40 | // out in memory in the same order as they are declared in the source code. 41 | // Read more: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/type-marshalling#marshalling-classes-and-structs 42 | [StructLayout(LayoutKind.Sequential)] 43 | record struct Vector(float X, float Y); 44 | -------------------------------------------------------------------------------- /rust-csharp/csharp-client/csharp-client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | csharp_client 7 | enable 8 | enable 9 | true 10 | 11 | 12 | -------------------------------------------------------------------------------- /rust-csharp/csharp-library/Class1.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace CSharpLibrary; 4 | 5 | public class Interop 6 | { 7 | [UnmanagedCallersOnly(EntryPoint = "add")] 8 | public static int Add(int x, int y) => x + y; 9 | } 10 | -------------------------------------------------------------------------------- /rust-csharp/csharp-library/csharp-library.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | csharp_library 6 | enable 7 | enable 8 | true 9 | 10 | 11 | -------------------------------------------------------------------------------- /rust-csharp/csharp-library/out/csharp-library.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstropek/rust-samples/b5c22eb414e14e8a27fdf69992b61c98ca723f76/rust-csharp/csharp-library/out/csharp-library.so -------------------------------------------------------------------------------- /rust-csharp/csharp-wasm-host/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Wasmtime; 3 | 4 | using var engine = new Engine(); 5 | using var store = new Store(engine); 6 | using var module = Module.FromFile(engine, "/home/rainer/github/rust-samples/rust-csharp/rust-wasm-module/pkg/rust_wasm_module_bg.wasm"); 7 | using var linker = new Linker(engine); 8 | var instance = linker.Instantiate(store, module); 9 | 10 | var alloc = instance.GetFunction("__wbindgen_malloc"); 11 | Debug.Assert(alloc != null); 12 | var free = instance.GetAction("__wbindgen_free"); 13 | Debug.Assert(free != null); 14 | 15 | var add = instance.GetFunction("add")!; 16 | Debug.Assert(add != null); 17 | var result = add(1, 2); 18 | Console.WriteLine(result); 19 | 20 | var addr2 = alloc(12); 21 | free(addr2, 12); 22 | 23 | ReadOnlySpan hello = "Hello World!\0"u8; 24 | System.Console.WriteLine(hello.Length); 25 | var addr = alloc(hello.Length); 26 | System.Console.WriteLine(addr); 27 | var memory = instance.GetMemory("memory")!; 28 | Debug.Assert(memory != null); 29 | for (var i = 0; i < hello.Length; i++) 30 | { 31 | memory.WriteByte(addr + i, hello[i]); 32 | } 33 | 34 | var strlen = instance.GetFunction("get_len_of_str")!; 35 | Debug.Assert(strlen != null); 36 | var len = strlen(addr, 1); 37 | Console.WriteLine(len); 38 | //free(addr, hello.Length); 39 | -------------------------------------------------------------------------------- /rust-csharp/csharp-wasm-host/csharp-wasm-host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | csharp_wasm_host 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /rust-csharp/justfile: -------------------------------------------------------------------------------- 1 | build: 2 | cargo build 3 | dotnet build 4 | cp target/debug/librust_library.so csharp-client/bin/Debug/net7.0 5 | 6 | build-aot: 7 | cargo build 8 | cd csharp-library && dotnet publish -r linux-x64 -c Release -o ./out 9 | 10 | run: (build) 11 | dotnet run --project csharp-client 12 | 13 | run-aot: (build-aot) 14 | cargo run --bin rust-client 15 | 16 | build-wasm: 17 | cd rust-wasm-module && wasm-pack build 18 | dotnet build 19 | 20 | run-wasm: (build-wasm) 21 | dotnet run --project csharp-wasm-host -------------------------------------------------------------------------------- /rust-csharp/rust-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | libloading = "0.8" 10 | -------------------------------------------------------------------------------- /rust-csharp/rust-client/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let result = call_dynamic().unwrap(); 3 | println!("C# said that 1 + 2 = {result}"); 4 | } 5 | 6 | fn call_dynamic() -> Result> { 7 | unsafe { 8 | let lib = libloading::Library::new("/home/rainer/github/rust-samples/rust-csharp/csharp-library/out/csharp-library.so")?; 9 | let func: libloading::Symbol i32> = lib.get(b"add")?; 10 | Ok(func(1, 2)) 11 | } 12 | } -------------------------------------------------------------------------------- /rust-csharp/rust-csharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-client", "csharp-client\csharp-client.csproj", "{C8221DA7-39F9-400C-B78C-511E6AADD91D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-library", "csharp-library\csharp-library.csproj", "{46195AAA-B891-4A9F-8222-B0E8FB329635}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-wasm-host", "csharp-wasm-host\csharp-wasm-host.csproj", "{C562167B-F088-4A02-BA61-C9A46E07DF45}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {C8221DA7-39F9-400C-B78C-511E6AADD91D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {C8221DA7-39F9-400C-B78C-511E6AADD91D}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {C8221DA7-39F9-400C-B78C-511E6AADD91D}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {C8221DA7-39F9-400C-B78C-511E6AADD91D}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {46195AAA-B891-4A9F-8222-B0E8FB329635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {46195AAA-B891-4A9F-8222-B0E8FB329635}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {46195AAA-B891-4A9F-8222-B0E8FB329635}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {46195AAA-B891-4A9F-8222-B0E8FB329635}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {C562167B-F088-4A02-BA61-C9A46E07DF45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {C562167B-F088-4A02-BA61-C9A46E07DF45}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {C562167B-F088-4A02-BA61-C9A46E07DF45}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {C562167B-F088-4A02-BA61-C9A46E07DF45}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /rust-csharp/rust-library/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-library" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /rust-csharp/rust-library/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_char, CStr}; 2 | 3 | #[no_mangle] 4 | pub extern "C" fn add(left: i32, right: i32) -> i32 { 5 | left + right 6 | } 7 | 8 | #[no_mangle] 9 | pub extern "C" fn add_vec2d(left: Vec2d, right: Vec2d) -> Vec2d { 10 | Vec2d { 11 | X: left.X + right.X, 12 | Y: left.Y + right.Y, 13 | } 14 | } 15 | 16 | #[allow(clippy::missing_safety_doc)] 17 | #[no_mangle] 18 | pub unsafe extern "C" fn strlen(text: *const c_char) -> u32 { 19 | let c_str = unsafe { 20 | assert!(!text.is_null()); 21 | 22 | CStr::from_ptr(text) 23 | }; 24 | 25 | let r_str = c_str.to_str().unwrap(); 26 | r_str.chars().count() as u32 27 | } 28 | 29 | #[allow(non_snake_case)] 30 | #[repr(C)] 31 | pub struct Vec2d { 32 | X: f32, 33 | Y: f32, 34 | } 35 | -------------------------------------------------------------------------------- /rust-csharp/rust-wasm-module/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-wasm-module" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | wasm-bindgen = "0.2" 13 | -------------------------------------------------------------------------------- /rust-csharp/rust-wasm-module/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen] 4 | pub fn add(left: i32, right: i32) -> i32 { 5 | left + right 6 | } 7 | 8 | #[wasm_bindgen] 9 | pub fn get_len_of_str(text: &str) -> u32 { 10 | text.chars().count() as u32 11 | } 12 | 13 | #[wasm_bindgen] 14 | pub fn turn_to_upper(text: &str) -> String { 15 | text.to_uppercase() 16 | } 17 | -------------------------------------------------------------------------------- /rust-lang-highlights/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-lang-highlights" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /rust-wasm-server/hello_wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1" 8 | -------------------------------------------------------------------------------- /rust-wasm-server/hello_wasm/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use anyhow::Result; 4 | 5 | fn main() -> Result<()> { 6 | let content = fs::read_to_string("./text.txt")?; 7 | println!("{}", content); 8 | 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /rust-wasm-server/hello_wasm/text.txt: -------------------------------------------------------------------------------- 1 | the quick brown fox jumps over the lazy dog -------------------------------------------------------------------------------- /shapes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shapes" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /socket-chatbot/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'telnet_sokoban'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=telnet_sokoban", 15 | "--package=telnet_sokoban" 16 | ], 17 | "env": { 18 | "OPENAI_API_KEY": "sk-..." 19 | }, 20 | "filter": { 21 | "name": "telnet_sokoban", 22 | "kind": "bin" 23 | } 24 | }, 25 | "args": [], 26 | "cwd": "${workspaceFolder}" 27 | }, 28 | { 29 | "type": "lldb", 30 | "request": "launch", 31 | "name": "Debug unit tests in executable 'telnet_sokoban'", 32 | "cargo": { 33 | "args": [ 34 | "test", 35 | "--no-run", 36 | "--bin=telnet_sokoban", 37 | "--package=telnet_sokoban" 38 | ], 39 | "filter": { 40 | "name": "telnet_sokoban", 41 | "kind": "bin" 42 | } 43 | }, 44 | "args": [], 45 | "cwd": "${workspaceFolder}" 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /socket-chatbot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "telnet_sokoban" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1", features = ["full"] } 8 | async-openai = "0" 9 | anyhow = "1" 10 | -------------------------------------------------------------------------------- /socket-chatbot/prompt.txt: -------------------------------------------------------------------------------- 1 | You are a helpful assistant. 2 | -------------------------------------------------------------------------------- /socket-chatbot/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use anyhow::{anyhow, Result}; 4 | use async_openai::{ 5 | types::{ 6 | ChatCompletionRequestMessage, ChatCompletionRequestSystemMessageArgs, 7 | ChatCompletionRequestUserMessageArgs, CreateChatCompletionRequestArgs, 8 | }, 9 | Client, 10 | }; 11 | use tokio::{ 12 | io::{AsyncReadExt, AsyncWriteExt}, 13 | net::{TcpListener, TcpStream}, 14 | time::timeout, 15 | }; 16 | 17 | #[tokio::main] 18 | async fn main() -> Result<()> { 19 | let listener = TcpListener::bind("127.0.0.1:8080").await?; 20 | println!("Server running on 127.0.0.1:8080"); 21 | 22 | loop { 23 | let (mut socket, _) = listener.accept().await?; 24 | 25 | tokio::spawn(async move { 26 | println!("New user connected"); 27 | match user_loop(&mut socket).await { 28 | Ok(_) => {} 29 | Err(e) => eprintln!("Error: {}", e), 30 | } 31 | println!("User disconnected"); 32 | }); 33 | } 34 | } 35 | 36 | enum Input 37 | { 38 | Empty, 39 | Question(String), 40 | } 41 | 42 | impl From> for Input 43 | { 44 | fn from(input: Option) -> Self 45 | { 46 | match input 47 | { 48 | Some(input) => Input::Question(input), 49 | None => Input::Empty, 50 | } 51 | } 52 | } 53 | 54 | async fn get_user_input(socket: &mut TcpStream) -> Result { 55 | let mut buffer = [0; 1024]; 56 | let n = match timeout(Duration::from_secs(30), socket.read(&mut buffer)).await? { 57 | Ok(0) => return Err(anyhow!("Connection closed")), 58 | Ok(1) if buffer[0] == b'\n' => return Ok(None.into()), 59 | Ok(2) if buffer[0] == b'\r' && buffer[1] == b'\n' => return Ok(None.into()), 60 | Ok(n) => n, 61 | Err(e) => return Err(anyhow!("Failed to read from socket: {}", e)), 62 | }; 63 | 64 | let input = String::from_utf8_lossy(&buffer[..n]); 65 | 66 | Ok(Some(input.into_owned()).into()) 67 | } 68 | 69 | async fn user_loop(socket: &mut TcpStream) -> Result<()> { 70 | let client = Client::new(); 71 | 72 | let prompt = tokio::fs::read_to_string("prompt.txt").await?; 73 | 74 | let mut messages: Vec = 75 | vec![ChatCompletionRequestSystemMessageArgs::default() 76 | .content(prompt) 77 | .build()? 78 | .into()]; 79 | 80 | loop { 81 | let input = match get_user_input(socket).await? { 82 | Input::Question(input) => input, 83 | Input::Empty => return Ok(()), 84 | }; 85 | 86 | messages.push( 87 | ChatCompletionRequestUserMessageArgs::default() 88 | .content(input.to_string()) 89 | .build()? 90 | .into(), 91 | ); 92 | 93 | let request = CreateChatCompletionRequestArgs::default() 94 | .model("gpt-4o") 95 | .messages(messages.clone()) 96 | .build()?; 97 | 98 | let response = client 99 | .chat() // Get the API "group" (completions, images, etc.) from the client 100 | .create(request) // Make the API call in that "group" 101 | .await?; 102 | 103 | let output = response 104 | .choices 105 | .first() 106 | .unwrap() 107 | .message 108 | .content 109 | .as_ref() 110 | .unwrap(); 111 | 112 | socket.write_all(output.as_bytes()).await.unwrap(); 113 | socket.write_all(b"\r\n").await.unwrap(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /tictactoe/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Cargo test", 11 | "program": "${cargo:program}", 12 | "cargo": { 13 | "args": [ 14 | "test", 15 | "--no-run", 16 | "--lib" 17 | ] 18 | }, 19 | "args": [] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /tictactoe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "tictactoe_logic", 4 | ] -------------------------------------------------------------------------------- /tictactoe/rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "Max" 2 | -------------------------------------------------------------------------------- /tictactoe/tictactoe_logic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tictactoe_logic" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | rstest = "^0.10.0" 8 | mockall = "^0.10.0" 9 | -------------------------------------------------------------------------------- /tictactoe/tictactoe_logic/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Note: Loads the contents of the module square_content from another file 2 | // with the same name as the module. Read more at 3 | // https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html 4 | mod square_content; 5 | mod board_index; 6 | mod board_content; 7 | mod row; 8 | mod game; 9 | 10 | // Note: Re-exports the content of the square_content module to keep paths short. 11 | // Read more at https://doc.rust-lang.org/reference/items/use-declarations.html#use-visibility 12 | pub use crate::square_content::*; 13 | pub use crate::board_index::*; 14 | pub use crate::board_content::*; 15 | pub use crate::row::*; 16 | pub use crate::game::*; 17 | -------------------------------------------------------------------------------- /tictactoe/tictactoe_logic/src/square_content.rs: -------------------------------------------------------------------------------- 1 | // Learning: Many traits support auto-implementation using the derive macro 2 | // Todo: Copy this file into Rust Playground and choose Tools/Expand macros 3 | // to analyze what's going on behind the scenes 4 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 5 | pub enum SquareContent { 6 | Empty, 7 | X, 8 | O, 9 | } 10 | 11 | // Learning: There is a system trait to add support for default values. 12 | // This is especially important when implementing generic types. 13 | impl Default for SquareContent { 14 | fn default() -> Self { 15 | SquareContent::Empty 16 | } 17 | } 18 | 19 | // Learning: There are system traits for type conversion. 20 | // Note: Take a look at unit tests to see how to do conversion. 21 | impl From for SquareContent { 22 | fn from(value: u8) -> Self { 23 | match value { 24 | 0 => SquareContent::Empty, 25 | 1 => SquareContent::X, 26 | 2 => SquareContent::O, 27 | v => panic!("Cannot convert {} to square content", v), 28 | } 29 | } 30 | } 31 | 32 | impl From for u8 { 33 | fn from(c: SquareContent) -> Self { 34 | match c { 35 | SquareContent::Empty => 0, 36 | SquareContent::X => 1, 37 | SquareContent::O => 2, 38 | } 39 | } 40 | } 41 | 42 | impl From for char { 43 | fn from(value: SquareContent) -> Self { 44 | match value { 45 | SquareContent::Empty => ' ', 46 | SquareContent::X => 'X', 47 | SquareContent::O => 'O', 48 | } 49 | } 50 | } 51 | 52 | impl From for SquareContent { 53 | fn from(value: char) -> Self { 54 | match value { 55 | ' ' => SquareContent::Empty, 56 | 'X' => SquareContent::X, 57 | 'O' => SquareContent::O, 58 | v => panic!("Invalid character {}", v) 59 | } 60 | } 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use super::*; 66 | 67 | #[test] 68 | fn content_default() { 69 | // Learning: Rust can infer type for Default::default() from 70 | // left expression in assert_eq macro 71 | assert_eq!(SquareContent::Empty, Default::default()); 72 | } 73 | 74 | #[test] 75 | fn from_into_u8() { 76 | // Learning: Implement From, get Into for free. 77 | assert_eq!(SquareContent::X, SquareContent::from(1)); 78 | assert_eq!(SquareContent::X, 1.into()); 79 | assert_eq!(1, u8::from(SquareContent::X)); 80 | assert_eq!(1u8, SquareContent::X.into()); 81 | } 82 | 83 | #[test] 84 | fn from_into_char() { 85 | assert_eq!(SquareContent::X, SquareContent::from('X')); 86 | assert_eq!(SquareContent::X, 'X'.into()); 87 | assert_eq!('X', char::from(SquareContent::X)); 88 | assert_eq!('X', Into::::into(SquareContent::X)); 89 | } 90 | 91 | #[test] 92 | #[should_panic(expected = "99")] 93 | fn from_fails() { 94 | SquareContent::from(99); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /wasm-serverless/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.yml] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /wasm-serverless/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | **/*.rs.bk 5 | wasm-pack.log 6 | 7 | build/ 8 | /target 9 | /dist 10 | -------------------------------------------------------------------------------- /wasm-serverless/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "word_puzzle_cloudflare", 5 | "word_puzzle_generator", 6 | ] 7 | 8 | [profile.release] 9 | # Tell `rustc` to optimize for small code size. 10 | opt-level = "s" 11 | lto = true 12 | strip = true 13 | codegen-units = 1 14 | -------------------------------------------------------------------------------- /wasm-serverless/README.md: -------------------------------------------------------------------------------- 1 | # Alphabetti Confetti 2 | 3 | Goal: Demonstrate use of Rust in Edge Cloud 4 | 5 | Frontend: [https://stackblitz.com/edit/alphabetti-confetti](https://stackblitz.com/edit/alphabetti-confetti) 6 | -------------------------------------------------------------------------------- /wasm-serverless/justfile: -------------------------------------------------------------------------------- 1 | cfdev: 2 | cd word_puzzle_cloudflare; npm run dev 3 | 4 | spin: 5 | cd word_puzzle_spin; spin build; spin up 6 | 7 | cf-deploy: 8 | cd word_puzzle_cloudflare; npm run deploy 9 | 10 | spin-deploy: 11 | cd word_puzzle_spin; spin build; spin deploy 12 | -------------------------------------------------------------------------------- /wasm-serverless/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "wrangler": "^3.13.2" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /wasm-serverless/requests.http: -------------------------------------------------------------------------------- 1 | ### Cloudflare 2 | #@host = https://word-puzzle.rainer.workers.dev 3 | #@host = http://localhost:8787 4 | 5 | ### Spin 6 | @host = https://word-puzzle-spin-zirmufxv.fermyon.app/ 7 | #@host = http://localhost:3000 8 | 9 | ### 10 | GET {{host}}/ 11 | 12 | ### 13 | POST {{host}}/generate 14 | 15 | { 16 | "size": 12, 17 | "words": [ 18 | "SERVERLESS", 19 | "COMPUTING", 20 | "SOFTWARE", 21 | "DEVOPS", 22 | "CLOUD", 23 | "SNAKE", 24 | "SCHOOL", 25 | "BANANA", 26 | "HOUSE", 27 | "SNOWFLAKE" 28 | ] 29 | } 30 | 31 | ### 32 | OPTIONS {{host}}/generate 33 | 34 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_cloudflare/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "todo-worker" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | # https://github.com/rustwasm/wasm-pack/issues/1247 7 | [package.metadata.wasm-pack.profile.release] 8 | wasm-opt = false 9 | 10 | [lib] 11 | crate-type = ["cdylib", "rlib"] 12 | 13 | [features] 14 | default = ["console_error_panic_hook"] 15 | 16 | [dependencies] 17 | cfg-if = "1.0" 18 | worker = "0.0.18" 19 | word_puzzle_generator = { path = "../word_puzzle_generator" } 20 | 21 | # The `console_error_panic_hook` crate provides better debugging of panics by 22 | # logging them with `console.error`. This is great for development, but requires 23 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 24 | # code size when deploying. 25 | console_error_panic_hook = { version = "0.1.1", optional = true } 26 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_cloudflare/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "version": "0.0.0", 4 | "scripts": { 5 | "deploy": "wrangler publish", 6 | "dev": "wrangler dev" 7 | }, 8 | "devDependencies": { 9 | "wrangler": "^3.13.2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_cloudflare/src/lib.rs: -------------------------------------------------------------------------------- 1 | use word_puzzle_generator::{place_words, GeneratorOptions}; 2 | use worker::*; 3 | 4 | mod utils; 5 | 6 | #[event(fetch)] 7 | pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result { 8 | // Optionally, get more helpful error messages written to the console in the case of a panic. 9 | utils::set_panic_hook(); 10 | 11 | let router = Router::new(); 12 | router 13 | .get("/", |_, _| Response::ok("Hello from Workers!")) 14 | .post_async("/generate", generate_puzzle) 15 | .run(req, env) 16 | .await 17 | } 18 | 19 | async fn generate_puzzle(mut req: Request, _ctx: RouteContext<()>) -> Result { 20 | match req.json().await { 21 | Err(_) => Response::error("Bad Request", 400), 22 | Ok(options) => { 23 | let options: GeneratorOptions = options; 24 | if options.size > 20 { 25 | return Response::error("Bad Request", 400); 26 | } 27 | 28 | let puzzle = place_words(options); 29 | Response::from_json(&puzzle)?.with_cors( 30 | &Cors::new() 31 | .with_origins(vec!["*"]) 32 | .with_allowed_headers(vec!["*"]) 33 | .with_methods(vec![Method::Post]), 34 | ) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_cloudflare/src/utils.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | // https://github.com/rustwasm/console_error_panic_hook#readme 5 | if #[cfg(feature = "console_error_panic_hook")] { 6 | extern crate console_error_panic_hook; 7 | pub use self::console_error_panic_hook::set_once as set_panic_hook; 8 | } else { 9 | #[inline] 10 | pub fn set_panic_hook() {} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_cloudflare/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "word-puzzle" 2 | main = "build/worker/shim.mjs" 3 | compatibility_date = "2023-03-22" 4 | 5 | [build] 6 | command = "cargo install -q worker-build && worker-build --release" 7 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "word_puzzle_generator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8" 10 | getrandom = { version = "0.2", features = ["js"] } 11 | serde = { version = "1", features = ["derive"] } 12 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_spin/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .spin/ 3 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_spin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "word-puzzle-spin" 3 | authors = ["Rainer "] 4 | description = "" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | crate-type = [ "cdylib" ] 10 | 11 | [dependencies] 12 | # Useful crate to handle errors. 13 | anyhow = "1" 14 | # Crate to simplify working with bytes. 15 | bytes = "1" 16 | # General-purpose crate with common HTTP types. 17 | http = "0.2" 18 | # The Spin SDK. 19 | spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v1.5.1" } 20 | serde_json = "1" 21 | word_puzzle_generator = { path = "../word_puzzle_generator" } 22 | 23 | [workspace] 24 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_spin/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = "1" 2 | authors = ["Rainer "] 3 | description = "" 4 | name = "word_puzzle_spin" 5 | trigger = { type = "http", base = "/" } 6 | version = "0.1.0" 7 | 8 | [[component]] 9 | id = "word-puzzle-spin" 10 | source = "target/wasm32-wasi/release/word_puzzle_spin.wasm" 11 | allowed_http_hosts = [] 12 | [component.trigger] 13 | route = "/..." 14 | [component.build] 15 | command = "cargo build --target wasm32-wasi --release" 16 | watch = ["src/**/*.rs", "Cargo.toml"] 17 | -------------------------------------------------------------------------------- /wasm-serverless/word_puzzle_spin/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use http::response::Builder; 3 | use spin_sdk::{ 4 | http::{Request, Response, Params}, 5 | http_component, http_router, 6 | }; 7 | use word_puzzle_generator::{place_words, GeneratorOptions}; 8 | 9 | #[http_component] 10 | fn handle_word_puzzle_spin(req: Request) -> Result { 11 | 12 | if req.method() == http::Method::OPTIONS { 13 | return Ok(http::Response::builder() 14 | .status(http::StatusCode::OK) 15 | .header("Access-Control-Allow-Origin", "*") 16 | .header("Access-Control-Allow-Methods", "*") 17 | .header("Access-Control-Allow-Headers", "*") 18 | .body(None)?); 19 | } 20 | 21 | let router = http_router! { 22 | GET "/" => |_req, _params| { 23 | Ok(http::Response::builder() 24 | .status(http::StatusCode::OK) 25 | .body(Some("Hello from spin!".into()))?) 26 | }, 27 | 28 | POST "/generate" => generate_puzzle 29 | }; 30 | 31 | router.handle(req) 32 | } 33 | 34 | fn generate_puzzle(req: Request, _params: Params) -> Result { 35 | let body = req.body().as_ref().unwrap(); 36 | let options: GeneratorOptions = serde_json::from_str(std::str::from_utf8(body.as_ref())?)?; 37 | 38 | if options.size > 20 { 39 | return Ok(http::Response::builder() 40 | .status(http::StatusCode::BAD_REQUEST) 41 | .body(None)?); 42 | } 43 | 44 | let puzzle = place_words(options); 45 | 46 | let response = serde_json::to_string_pretty(&puzzle)?.as_bytes().to_vec(); 47 | Ok(http::Response::builder() 48 | .status(http::StatusCode::OK) 49 | .with_cors() 50 | .header("Content-Type", "application/json") 51 | .body(Some(response.into()))?) 52 | } 53 | 54 | trait WithCors { 55 | fn with_cors(self) -> Self; 56 | } 57 | 58 | impl WithCors for Builder { 59 | fn with_cors(self) -> Self { 60 | self.header("Access-Control-Allow-Origin", "*") 61 | .header("Access-Control-Allow-Methods", "*") 62 | .header("Access-Control-Allow-Headers", "*") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /why-i-love-rust/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'why-i-love-rust'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=why-i-love-rust", 15 | "--package=why-i-love-rust" 16 | ], 17 | "filter": { 18 | "name": "why-i-love-rust", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | }, 25 | { 26 | "type": "lldb", 27 | "request": "launch", 28 | "name": "Debug unit tests in executable 'why-i-love-rust'", 29 | "cargo": { 30 | "args": [ 31 | "test", 32 | "--no-run", 33 | "--bin=why-i-love-rust", 34 | "--package=why-i-love-rust" 35 | ], 36 | "filter": { 37 | "name": "why-i-love-rust", 38 | "kind": "bin" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /why-i-love-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "why-i-love-rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.93" 8 | rust_decimal = "1.36.0" 9 | rust_decimal_macros = "1.36.0" 10 | thiserror = "2.0.3" 11 | -------------------------------------------------------------------------------- /why-i-love-rust/data.csv: -------------------------------------------------------------------------------- 1 | ID,Name,Revenue 2 | 1,Acme Inc.,100000 3 | 2,Beta Corp.,200000 4 | 3,Gamma Ltd.,300000 5 | -------------------------------------------------------------------------------- /why-i-love-rust/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use std::fs::File; 4 | use std::io::Read; 5 | use std::num::ParseIntError; 6 | use rust_decimal::prelude::*; 7 | use rust_decimal_macros::dec; 8 | use thiserror::Error; 9 | 10 | #[derive(Debug)] 11 | struct CustomerData { 12 | id: u32, 13 | name: String, 14 | revenue: Decimal, 15 | } 16 | 17 | trait HasRevenue { 18 | fn revenue(&self) -> Decimal; 19 | } 20 | 21 | impl HasRevenue for CustomerData { 22 | fn revenue(&self) -> Decimal { 23 | self.revenue 24 | } 25 | } 26 | 27 | impl HasRevenue for Decimal { 28 | fn revenue(&self) -> Decimal { 29 | *self 30 | } 31 | } 32 | 33 | fn main() { 34 | let filename = "data.csv"; 35 | let content = read_file(filename); 36 | println!("File content:\n{}", content); 37 | 38 | match parse_csv_lines(&content) { 39 | Ok(customers) => { 40 | // Get average revenue 41 | let total_revenue: Decimal = customers.iter().map(|c| c.revenue).sum(); 42 | let average_revenue = total_revenue / Decimal::from(customers.len()); 43 | println!("Average revenue: {}", average_revenue); 44 | } 45 | Err(e) => { 46 | println!("Error parsing CSV: {:?}", e); 47 | } 48 | } 49 | 50 | let mut data: Vec> = parse_csv_lines(&content) 51 | .unwrap() 52 | .into_iter() 53 | .map(|c| Box::new(c) as Box) 54 | .collect(); 55 | data.push(Box::new(dec!(42.0))); 56 | let total_revenue: Decimal = data.iter().map(|d| d.revenue()).sum(); 57 | println!("Total revenue: {}", total_revenue); 58 | } 59 | 60 | fn read_file(filename: &str) -> String { 61 | let mut file = File::open(filename).unwrap(); 62 | let mut content = String::new(); 63 | file.read_to_string(&mut content).unwrap(); 64 | if content.trim().is_empty() { 65 | "".to_string() 66 | } else { 67 | content 68 | } 69 | } 70 | 71 | #[derive(Debug, Error)] 72 | enum ParseError { 73 | #[error("Error parsing int")] 74 | ParseIntError(#[from] ParseIntError), 75 | #[error("Error parsing decimal")] 76 | ParseDecimalError(#[from] rust_decimal::Error), 77 | #[error("Invalid number of fields")] 78 | InvalidNumberOfFields, 79 | } 80 | 81 | fn parse_csv_line(line: &str) -> Result { 82 | let parts: Vec<&str> = line.split(',').collect(); 83 | if parts.len() != 3 { 84 | return Err(ParseError::InvalidNumberOfFields); 85 | } 86 | 87 | let id: u32 = parts[0].parse::()?; 88 | let name = parts[1].to_string(); 89 | let revenue: Decimal = parts[2].parse()?; 90 | Ok(CustomerData { id, name, revenue }) 91 | } 92 | 93 | #[derive(Debug)] 94 | struct ParseFileError { 95 | line: usize, 96 | error: ParseError, 97 | } 98 | 99 | fn parse_csv_lines(content: &str) -> Result, ParseFileError> { 100 | content.lines().skip(1).enumerate().map(|(i, line)| { 101 | parse_csv_line(line).map_err(|e| ParseFileError { line: i + 1, error: e }) 102 | }).collect() 103 | } 104 | -------------------------------------------------------------------------------- /why-i-love-rust/storybook.md: -------------------------------------------------------------------------------- 1 | # Storybook 2 | 3 | ## Project and deps management with Cargo 4 | 5 | ```bash 6 | cargo new project-name 7 | cargo add thiserror anyhow rust_decimal rust_decimal_macros 8 | ``` 9 | 10 | ## Step 1 - Simple 11 | 12 | ```csv 13 | ID,Name,Revenue 14 | 1,Acme Inc.,100000 15 | 2,Beta Corp.,200000 16 | 3,Gamma Ltd.,300000 17 | ``` 18 | 19 | ```rs 20 | use std::fs::File; 21 | use std::io::Read; 22 | 23 | fn main() { 24 | let filename = "data.csv"; 25 | let content = read_file(filename); 26 | println!("File content:\n{}", content); 27 | } 28 | 29 | fn read_file(filename: &str) -> String { 30 | let mut file = File::open(filename).unwrap(); 31 | let mut content = String::new(); 32 | file.read_to_string(&mut content).unwrap(); 33 | if content.trim().is_empty() { 34 | "".to_string() 35 | } else { 36 | content 37 | } 38 | } 39 | ``` 40 | 41 | ## With Struct --------------------------------------------------------------------------------