├── 2024
└── slides
│ ├── assets
│ ├── is-logo.png
│ ├── aws-logo.png
│ ├── discord-logo.png
│ ├── dropbox-logo.png
│ ├── google-logo.png
│ ├── huawei-logo.png
│ ├── mozilla-logo.png
│ ├── rust-logo-1.png
│ ├── twitch-logo.png
│ ├── 03-images
│ │ └── ????.png
│ ├── 12-images
│ │ ├── tabs.png
│ │ ├── glade.png
│ │ ├── popover.png
│ │ ├── window.png
│ │ ├── side-bar.png
│ │ ├── header-bar.png
│ │ └── message-dialog.png
│ ├── facebook-logo.png
│ ├── microsoft-logo.png
│ ├── 05-images
│ │ ├── doc-1.png
│ │ └── doc-2.png
│ ├── 01-images
│ │ ├── compiling.png
│ │ ├── rust-c-1.png
│ │ ├── rust-c-2.png
│ │ ├── 01-microsoft-cves.png
│ │ ├── techempowered-fortunes.png
│ │ ├── techempowered-fortunes-2.png
│ │ ├── techempowered-fortunes-top-2022.png
│ │ ├── techempowered-fortunes-spring-2022.png
│ │ ├── rustacean-flat-noshadow.svg
│ │ ├── computerlanguagebenchmarkgame2023.svg
│ │ └── computerlanguagebenchmarkgame.svg
│ ├── 08-images
│ │ └── plantuml.png
│ ├── 09-images
│ │ ├── 5-complex.png
│ │ ├── 2-basic-db.png
│ │ ├── 7-complex-ha.png
│ │ ├── 3-basic-cache.png
│ │ ├── 9-message-bus.png
│ │ ├── 8-microservices.png
│ │ ├── 1-basic-architecture.png
│ │ ├── 4-multiple-webservers.png
│ │ └── 6-complex-load-balancer.png
│ ├── 04-images
│ │ ├── hash-table.png
│ │ ├── circular-buffer.png
│ │ └── circular-buffer.svg
│ ├── 11-images
│ │ ├── grpc-vs-rest.png
│ │ ├── grpc-fw-benchmark.png
│ │ └── grpc-implementation.png
│ ├── 02-images
│ │ ├── memory_layout.png
│ │ └── program-stack.png
│ ├── 07-images
│ │ ├── network_stack.png
│ │ ├── work_stealing.png
│ │ └── message_passing.png
│ ├── 10-images
│ │ └── wasm-benchmark.png
│ └── 06-images
│ │ ├── 4_01_ThreadDiagram.jpeg
│ │ ├── many-to-many-model.png
│ │ ├── windows_priorities.jpeg
│ │ ├── windows_thread_states.jpeg
│ │ ├── windows_irql_vs_threadp.jpeg
│ │ ├── widnows_thread_switching_1.jpeg
│ │ ├── widnows_thread_switching_2.jpeg
│ │ └── windows_dispatcher_database.jpeg
│ ├── 00-starting-soon.md
│ ├── marp.config.js
│ ├── split-template.md
│ ├── package.json
│ ├── themes
│ └── rust.css
│ ├── 14-bonus.md
│ └── 13-graphql.md
├── examples
└── 10-wasm
│ ├── demo-router-fetching
│ ├── src
│ │ ├── views
│ │ │ ├── mod.rs
│ │ │ ├── home.rs
│ │ │ └── server.rs
│ │ ├── components
│ │ │ ├── mod.rs
│ │ │ ├── component-template.rs
│ │ │ ├── container.rs
│ │ │ ├── like_button.rs
│ │ │ ├── header.rs
│ │ │ └── card.rs
│ │ └── main.rs
│ ├── Cargo.toml
│ ├── index.html
│ └── assets
│ │ └── styles.css
│ ├── demo-template
│ ├── src
│ │ ├── components
│ │ │ ├── mod.rs
│ │ │ ├── component-template.rs
│ │ │ ├── header.rs
│ │ │ ├── container.rs
│ │ │ ├── like_button.rs
│ │ │ └── card.rs
│ │ └── main.rs
│ ├── readme.md
│ ├── Cargo.toml
│ ├── index.html
│ └── assets
│ │ └── styles.css
│ ├── demo-reference
│ ├── src
│ │ ├── components
│ │ │ ├── mod.rs
│ │ │ ├── component-template.rs
│ │ │ ├── container.rs
│ │ │ ├── like_button.rs
│ │ │ ├── header.rs
│ │ │ └── card.rs
│ │ └── main.rs
│ ├── readme.md
│ ├── Cargo.toml
│ ├── index.html
│ └── assets
│ │ └── styles.css
│ └── backend
│ ├── package.json
│ └── db.json
├── slides
├── assets
│ ├── is-logo.png
│ ├── aws-logo.png
│ ├── discord-logo.png
│ ├── dropbox-logo.png
│ ├── google-logo.png
│ ├── huawei-logo.png
│ ├── mozilla-logo.png
│ ├── rust-logo-1.png
│ ├── twitch-logo.png
│ ├── 03-images
│ │ └── ????.png
│ ├── 12-images
│ │ ├── tabs.png
│ │ ├── glade.png
│ │ ├── popover.png
│ │ ├── window.png
│ │ ├── side-bar.png
│ │ ├── header-bar.png
│ │ └── message-dialog.png
│ ├── facebook-logo.png
│ ├── microsoft-logo.png
│ ├── 05-images
│ │ ├── doc-1.png
│ │ └── doc-2.png
│ ├── 01-images
│ │ ├── compiling.png
│ │ ├── rust-c-1.png
│ │ ├── rust-c-2.png
│ │ ├── 01-microsoft-cves.png
│ │ ├── techempowered-fortunes.png
│ │ ├── techempowered-fortunes-2.png
│ │ ├── techempowered-fortunes-top-2022.png
│ │ ├── techempowered-fortunes-spring-2022.png
│ │ ├── rustacean-flat-noshadow.svg
│ │ ├── computerlanguagebenchmarkgame2023.svg
│ │ └── computerlanguagebenchmarkgame.svg
│ ├── 08-images
│ │ └── plantuml.png
│ ├── 09-images
│ │ ├── 5-complex.png
│ │ ├── 2-basic-db.png
│ │ ├── 7-complex-ha.png
│ │ ├── 3-basic-cache.png
│ │ ├── 9-message-bus.png
│ │ ├── 8-microservices.png
│ │ ├── 1-basic-architecture.png
│ │ ├── 4-multiple-webservers.png
│ │ └── 6-complex-load-balancer.png
│ ├── 04-images
│ │ ├── hash-table.png
│ │ ├── circular-buffer.png
│ │ └── circular-buffer.svg
│ ├── 11-images
│ │ ├── grpc-vs-rest.png
│ │ ├── grpc-fw-benchmark.png
│ │ └── grpc-implementation.png
│ ├── 02-images
│ │ ├── memory_layout.png
│ │ └── program-stack.png
│ ├── 07-images
│ │ ├── network_stack.png
│ │ ├── work_stealing.png
│ │ └── message_passing.png
│ ├── 10-images
│ │ └── wasm-benchmark.png
│ └── 06-images
│ │ ├── 4_01_ThreadDiagram.jpeg
│ │ ├── many-to-many-model.png
│ │ ├── windows_priorities.jpeg
│ │ ├── windows_thread_states.jpeg
│ │ ├── windows_irql_vs_threadp.jpeg
│ │ ├── widnows_thread_switching_1.jpeg
│ │ ├── widnows_thread_switching_2.jpeg
│ │ └── windows_dispatcher_database.jpeg
├── 00-starting-soon.md
├── marp.config.js
├── split-template.md
├── package.json
├── themes
│ └── rust.css
├── 14-bonus.md
├── 13-graphql.md
└── 10-2023-actix-htmx.md
├── .gitignore
└── .github
└── workflows
└── marp.yml
/examples/10-wasm/demo-router-fetching/src/views/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod home;
2 | pub mod server;
3 |
--------------------------------------------------------------------------------
/slides/assets/is-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/is-logo.png
--------------------------------------------------------------------------------
/slides/assets/aws-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/aws-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/is-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/is-logo.png
--------------------------------------------------------------------------------
/slides/assets/discord-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/discord-logo.png
--------------------------------------------------------------------------------
/slides/assets/dropbox-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/dropbox-logo.png
--------------------------------------------------------------------------------
/slides/assets/google-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/google-logo.png
--------------------------------------------------------------------------------
/slides/assets/huawei-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/huawei-logo.png
--------------------------------------------------------------------------------
/slides/assets/mozilla-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/mozilla-logo.png
--------------------------------------------------------------------------------
/slides/assets/rust-logo-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/rust-logo-1.png
--------------------------------------------------------------------------------
/slides/assets/twitch-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/twitch-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/aws-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/aws-logo.png
--------------------------------------------------------------------------------
/slides/assets/03-images/????.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/03-images/????.png
--------------------------------------------------------------------------------
/slides/assets/12-images/tabs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/tabs.png
--------------------------------------------------------------------------------
/slides/assets/facebook-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/facebook-logo.png
--------------------------------------------------------------------------------
/slides/assets/microsoft-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/microsoft-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/discord-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/discord-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/dropbox-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/dropbox-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/google-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/google-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/huawei-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/huawei-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/mozilla-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/mozilla-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/rust-logo-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/rust-logo-1.png
--------------------------------------------------------------------------------
/2024/slides/assets/twitch-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/twitch-logo.png
--------------------------------------------------------------------------------
/slides/assets/05-images/doc-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/05-images/doc-1.png
--------------------------------------------------------------------------------
/slides/assets/05-images/doc-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/05-images/doc-2.png
--------------------------------------------------------------------------------
/slides/assets/12-images/glade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/glade.png
--------------------------------------------------------------------------------
/slides/assets/12-images/popover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/popover.png
--------------------------------------------------------------------------------
/slides/assets/12-images/window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/window.png
--------------------------------------------------------------------------------
/2024/slides/assets/03-images/????.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/03-images/????.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/tabs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/tabs.png
--------------------------------------------------------------------------------
/2024/slides/assets/facebook-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/facebook-logo.png
--------------------------------------------------------------------------------
/2024/slides/assets/microsoft-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/microsoft-logo.png
--------------------------------------------------------------------------------
/slides/assets/01-images/compiling.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/compiling.png
--------------------------------------------------------------------------------
/slides/assets/01-images/rust-c-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/rust-c-1.png
--------------------------------------------------------------------------------
/slides/assets/01-images/rust-c-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/rust-c-2.png
--------------------------------------------------------------------------------
/slides/assets/08-images/plantuml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/08-images/plantuml.png
--------------------------------------------------------------------------------
/slides/assets/09-images/5-complex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/5-complex.png
--------------------------------------------------------------------------------
/slides/assets/12-images/side-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/side-bar.png
--------------------------------------------------------------------------------
/2024/slides/assets/05-images/doc-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/05-images/doc-1.png
--------------------------------------------------------------------------------
/2024/slides/assets/05-images/doc-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/05-images/doc-2.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/glade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/glade.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/popover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/popover.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/window.png
--------------------------------------------------------------------------------
/slides/assets/04-images/hash-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/04-images/hash-table.png
--------------------------------------------------------------------------------
/slides/assets/09-images/2-basic-db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/2-basic-db.png
--------------------------------------------------------------------------------
/slides/assets/09-images/7-complex-ha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/7-complex-ha.png
--------------------------------------------------------------------------------
/slides/assets/11-images/grpc-vs-rest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/11-images/grpc-vs-rest.png
--------------------------------------------------------------------------------
/slides/assets/12-images/header-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/header-bar.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/compiling.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/compiling.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/rust-c-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/rust-c-1.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/rust-c-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/rust-c-2.png
--------------------------------------------------------------------------------
/2024/slides/assets/08-images/plantuml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/08-images/plantuml.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/5-complex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/5-complex.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/side-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/side-bar.png
--------------------------------------------------------------------------------
/slides/assets/02-images/memory_layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/02-images/memory_layout.png
--------------------------------------------------------------------------------
/slides/assets/02-images/program-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/02-images/program-stack.png
--------------------------------------------------------------------------------
/slides/assets/07-images/network_stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/07-images/network_stack.png
--------------------------------------------------------------------------------
/slides/assets/07-images/work_stealing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/07-images/work_stealing.png
--------------------------------------------------------------------------------
/slides/assets/09-images/3-basic-cache.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/3-basic-cache.png
--------------------------------------------------------------------------------
/slides/assets/09-images/9-message-bus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/9-message-bus.png
--------------------------------------------------------------------------------
/slides/assets/10-images/wasm-benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/10-images/wasm-benchmark.png
--------------------------------------------------------------------------------
/slides/assets/12-images/message-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/12-images/message-dialog.png
--------------------------------------------------------------------------------
/2024/slides/assets/04-images/hash-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/04-images/hash-table.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/2-basic-db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/2-basic-db.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/7-complex-ha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/7-complex-ha.png
--------------------------------------------------------------------------------
/2024/slides/assets/11-images/grpc-vs-rest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/11-images/grpc-vs-rest.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/header-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/header-bar.png
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/components/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod card;
2 | pub mod container;
3 | pub mod header;
4 | pub mod like_button;
5 |
--------------------------------------------------------------------------------
/slides/assets/01-images/01-microsoft-cves.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/01-microsoft-cves.png
--------------------------------------------------------------------------------
/slides/assets/04-images/circular-buffer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/04-images/circular-buffer.png
--------------------------------------------------------------------------------
/slides/assets/04-images/circular-buffer.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/04-images/circular-buffer.svg
--------------------------------------------------------------------------------
/slides/assets/07-images/message_passing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/07-images/message_passing.png
--------------------------------------------------------------------------------
/slides/assets/09-images/8-microservices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/8-microservices.png
--------------------------------------------------------------------------------
/slides/assets/11-images/grpc-fw-benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/11-images/grpc-fw-benchmark.png
--------------------------------------------------------------------------------
/2024/slides/assets/02-images/memory_layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/02-images/memory_layout.png
--------------------------------------------------------------------------------
/2024/slides/assets/02-images/program-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/02-images/program-stack.png
--------------------------------------------------------------------------------
/2024/slides/assets/07-images/network_stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/07-images/network_stack.png
--------------------------------------------------------------------------------
/2024/slides/assets/07-images/work_stealing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/07-images/work_stealing.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/3-basic-cache.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/3-basic-cache.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/9-message-bus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/9-message-bus.png
--------------------------------------------------------------------------------
/2024/slides/assets/10-images/wasm-benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/10-images/wasm-benchmark.png
--------------------------------------------------------------------------------
/2024/slides/assets/12-images/message-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/12-images/message-dialog.png
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/components/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod card;
2 | pub mod container;
3 | pub mod header;
4 | pub mod like_button;
5 |
--------------------------------------------------------------------------------
/slides/assets/06-images/4_01_ThreadDiagram.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/4_01_ThreadDiagram.jpeg
--------------------------------------------------------------------------------
/slides/assets/06-images/many-to-many-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/many-to-many-model.png
--------------------------------------------------------------------------------
/slides/assets/06-images/windows_priorities.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/windows_priorities.jpeg
--------------------------------------------------------------------------------
/slides/assets/11-images/grpc-implementation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/11-images/grpc-implementation.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/01-microsoft-cves.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/01-microsoft-cves.png
--------------------------------------------------------------------------------
/2024/slides/assets/04-images/circular-buffer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/04-images/circular-buffer.png
--------------------------------------------------------------------------------
/2024/slides/assets/04-images/circular-buffer.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/04-images/circular-buffer.svg
--------------------------------------------------------------------------------
/2024/slides/assets/07-images/message_passing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/07-images/message_passing.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/8-microservices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/8-microservices.png
--------------------------------------------------------------------------------
/2024/slides/assets/11-images/grpc-fw-benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/11-images/grpc-fw-benchmark.png
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/components/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod card;
2 | pub mod container;
3 | pub mod header;
4 | pub mod like_button;
5 |
--------------------------------------------------------------------------------
/slides/assets/01-images/techempowered-fortunes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/techempowered-fortunes.png
--------------------------------------------------------------------------------
/slides/assets/06-images/windows_thread_states.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/windows_thread_states.jpeg
--------------------------------------------------------------------------------
/slides/assets/09-images/1-basic-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/1-basic-architecture.png
--------------------------------------------------------------------------------
/slides/assets/09-images/4-multiple-webservers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/4-multiple-webservers.png
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/4_01_ThreadDiagram.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/4_01_ThreadDiagram.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/many-to-many-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/many-to-many-model.png
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/windows_priorities.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/windows_priorities.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/11-images/grpc-implementation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/11-images/grpc-implementation.png
--------------------------------------------------------------------------------
/slides/assets/01-images/techempowered-fortunes-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/techempowered-fortunes-2.png
--------------------------------------------------------------------------------
/slides/assets/06-images/windows_irql_vs_threadp.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/windows_irql_vs_threadp.jpeg
--------------------------------------------------------------------------------
/slides/assets/09-images/6-complex-load-balancer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/09-images/6-complex-load-balancer.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/techempowered-fortunes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/techempowered-fortunes.png
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/windows_thread_states.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/windows_thread_states.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/1-basic-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/1-basic-architecture.png
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/4-multiple-webservers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/4-multiple-webservers.png
--------------------------------------------------------------------------------
/slides/assets/06-images/widnows_thread_switching_1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/widnows_thread_switching_1.jpeg
--------------------------------------------------------------------------------
/slides/assets/06-images/widnows_thread_switching_2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/widnows_thread_switching_2.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/techempowered-fortunes-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/techempowered-fortunes-2.png
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/windows_irql_vs_threadp.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/windows_irql_vs_threadp.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/09-images/6-complex-load-balancer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/09-images/6-complex-load-balancer.png
--------------------------------------------------------------------------------
/slides/assets/06-images/windows_dispatcher_database.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/06-images/windows_dispatcher_database.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/widnows_thread_switching_1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/widnows_thread_switching_1.jpeg
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/widnows_thread_switching_2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/widnows_thread_switching_2.jpeg
--------------------------------------------------------------------------------
/slides/assets/01-images/techempowered-fortunes-top-2022.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/techempowered-fortunes-top-2022.png
--------------------------------------------------------------------------------
/2024/slides/assets/06-images/windows_dispatcher_database.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/06-images/windows_dispatcher_database.jpeg
--------------------------------------------------------------------------------
/slides/assets/01-images/techempowered-fortunes-spring-2022.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/slides/assets/01-images/techempowered-fortunes-spring-2022.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/techempowered-fortunes-top-2022.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/techempowered-fortunes-top-2022.png
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/techempowered-fortunes-spring-2022.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grolig/pv281/HEAD/2024/slides/assets/01-images/techempowered-fortunes-spring-2022.png
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/readme.md:
--------------------------------------------------------------------------------
1 | # Demo app
2 |
3 | The web will have a:
4 | - Header
5 | - List of images, each image has:
6 | - a description
7 | - clickable button which "likes" the image
8 |
9 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/readme.md:
--------------------------------------------------------------------------------
1 | # Demo app
2 |
3 | The web will have a:
4 | - Header
5 | - List of images, each image has:
6 | - a description
7 | - clickable button which "likes" the image
8 |
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
3 | # IDE files
4 | .idea
5 |
6 | # MARP / Trunk target directory
7 | **/dist
8 |
9 | # Rust code target
10 | **/target
11 | # Cargo lock
12 | Cargo.lock
13 |
14 | **/example-reservation-system
15 | **/.DS_Store
16 |
--------------------------------------------------------------------------------
/slides/00-starting-soon.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Intro Session
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Starting soon...
10 |
--------------------------------------------------------------------------------
/2024/slides/00-starting-soon.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Intro Session
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Starting soon...
10 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "demo-reference"
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 | yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "demo-template"
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 | yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
--------------------------------------------------------------------------------
/slides/marp.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | allowLocalFiles: true,
3 | ogImage: process.env.URL && `${process.env.URL}/og-image.jpg`,
4 | themeSet: 'themes',
5 | //url: process.env.URL,
6 | engine: ({ marp }) => marp.use(require('markdown-it-plantuml')).use(require('markdown-it-named-code-blocks')),
7 | }
--------------------------------------------------------------------------------
/2024/slides/marp.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | allowLocalFiles: true,
3 | ogImage: process.env.URL && `${process.env.URL}/og-image.jpg`,
4 | themeSet: 'themes',
5 | //url: process.env.URL,
6 | engine: ({ marp }) => marp.use(require('markdown-it-plantuml')).use(require('markdown-it-named-code-blocks')),
7 | }
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/components/component-template.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, Html, Properties};
2 |
3 | #[derive(Properties, PartialEq)]
4 | pub struct ComponentProps {}
5 |
6 | #[function_component(Component)]
7 | pub fn component(props: &ComponentProps) -> Html {
8 | html! {}
9 | }
10 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/components/component-template.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, Html, Properties};
2 |
3 | #[derive(Properties, PartialEq)]
4 | pub struct ComponentProps {}
5 |
6 | #[function_component(Component)]
7 | pub fn component(props: &ComponentProps) -> Html {
8 | html! {}
9 | }
10 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/components/component-template.rs:
--------------------------------------------------------------------------------
1 | // use yew::{function_component, html, Html, Properties};
2 |
3 | // #[derive(Properties, PartialEq)]
4 | // pub struct ComponentProps {}
5 |
6 | // #[function_component(Component)]
7 | // pub fn component(props: &ComponentProps) -> Html {
8 | // html! {}
9 | // }
10 |
--------------------------------------------------------------------------------
/examples/10-wasm/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "json-server --watch db.json --port 5000"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "json-server": "^0.17.1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/components/container.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, Children, Html, Properties};
2 |
3 | #[derive(Properties, PartialEq)]
4 | pub struct ContainerProps {
5 | pub class: String,
6 | pub children: Children,
7 | }
8 |
9 | #[function_component(Container)]
10 | pub fn container(props: &ContainerProps) -> Html {
11 | html! {
12 |
13 | {
14 | for props.children.iter()
15 | }
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/components/container.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, Children, Html, Properties};
2 |
3 | #[derive(Properties, PartialEq)]
4 | pub struct ContainerProps {
5 | pub class: String,
6 | pub children: Children,
7 | }
8 |
9 | #[function_component(Container)]
10 | pub fn container(props: &ContainerProps) -> Html {
11 | html! {
12 |
13 | {
14 | for props.children.iter()
15 | }
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "demo-router-fetching"
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 | yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
10 |
11 | # for data fetching
12 | gloo-net = "0.2"
13 | serde = { version = "1.0", features = ["derive"] }
14 | wasm-bindgen-futures = "0.4"
15 |
16 | # for router
17 | yew-router = { git = "https://github.com/yewstack/yew.git" }
18 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/components/like_button.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, use_state, Callback, Html};
2 |
3 | #[function_component(LikeButton)]
4 | pub fn like_button() -> Html {
5 | let liked = use_state(|| false);
6 | let onclick = {
7 | let liked = liked.clone();
8 | Callback::from(move |_| liked.set(!*liked))
9 | };
10 |
11 | html! {
12 | {
13 | if *liked {
14 | "Unlike"
15 | } else {
16 | "Like"
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/views/home.rs:
--------------------------------------------------------------------------------
1 | use crate::{components::container::Container, Routes};
2 | use yew::{classes, function_component, html, Callback, Html};
3 | use yew_router::prelude::use_navigator;
4 |
5 | #[function_component(HomePage)]
6 | pub fn home_page() -> Html {
7 | let navigator = use_navigator().unwrap();
8 |
9 | let onclick = Callback::from(move |_| navigator.push(&Routes::Fetched));
10 |
11 | html! {
12 |
13 | {"Go to fetched data!"}
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/components/like_button.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, use_state, Callback, Html};
2 |
3 | #[function_component(LikeButton)]
4 | pub fn like_button() -> Html {
5 | let liked = use_state(|| false);
6 | let onclick = {
7 | let liked = liked.clone();
8 | Callback::from(move |_| liked.set(!*liked))
9 | };
10 |
11 | html! {
12 | {
13 | if *liked {
14 | "Unlike"
15 | } else {
16 | "Like"
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/components/header.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, Html, Properties};
2 |
3 | #[derive(Properties, PartialEq)]
4 | pub struct HeaderProps {
5 | pub image_url: String,
6 | pub title: String,
7 | }
8 |
9 | #[function_component(Header)]
10 | pub fn header(props: &HeaderProps) -> Html {
11 | html! {
12 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/components/header.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, Html, Properties};
2 |
3 | #[derive(PartialEq, Properties)]
4 | pub struct HeaderProps {
5 | pub image_url: String,
6 | pub title: String,
7 | }
8 |
9 | #[function_component(Header)]
10 | pub fn header(props: &HeaderProps) -> Html {
11 | html! {
12 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/components/header.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, Html, Properties};
2 |
3 | #[derive(Properties, PartialEq)]
4 | pub struct HeaderProps {
5 | pub image_url: String,
6 | pub title: String,
7 | }
8 |
9 | #[function_component(Header)]
10 | pub fn header(props: &HeaderProps) -> Html {
11 | html! {
12 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/10-wasm/backend/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "cards": [
3 | {
4 | "id": 0,
5 | "description": "Sample description from server",
6 | "image_url": "https://cdn.thenewstack.io/media/2021/07/bde17530-wasm.png",
7 | "title": "The WASM card from the server"
8 | },
9 | {
10 | "id": 1,
11 | "description": "He do be wildin'.",
12 | "image_url": "https://media.tenor.com/ednxg8HDD0YAAAAd/habibi-wildin.gif",
13 | "title": "Habibi wildin' on the server"
14 | },
15 | {
16 | "id": 2,
17 | "description": "Forever incel",
18 | "image_url": "https://i.kym-cdn.com/photos/images/original/001/519/511/9c8.jpg",
19 | "title": "When she ghosts you after you show her your RAM stick"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/slides/split-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Split Slide Template
5 | theme: rust
6 | paginate: true
7 | ---
8 |
9 |
10 |
11 | ### Title
12 |
13 |
14 |
15 | ```rust
16 |
17 | ```
18 |
19 |
20 |
21 |
22 | ```rust
23 |
24 | ```
25 |
26 |
27 |
28 | ---
29 |
30 |
31 |
32 | ### Title
33 |
34 |
35 |
36 | Common text.
37 |
38 |
39 |
40 |
41 |
42 | #### Left subtitle
43 |
44 | ```rust
45 |
46 | ```
47 |
48 |
49 |
50 |
51 | #### Right subtitle
52 |
53 | ```rust
54 |
55 | ```
56 |
57 |
58 |
59 | ---
60 |
61 |
--------------------------------------------------------------------------------
/2024/slides/split-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Split Slide Template
5 | theme: rust
6 | paginate: true
7 | ---
8 |
9 |
10 |
11 | ### Title
12 |
13 |
14 |
15 | ```rust
16 |
17 | ```
18 |
19 |
20 |
21 |
22 | ```rust
23 |
24 | ```
25 |
26 |
27 |
28 | ---
29 |
30 |
31 |
32 | ### Title
33 |
34 |
35 |
36 | Common text.
37 |
38 |
39 |
40 |
41 |
42 | #### Left subtitle
43 |
44 | ```rust
45 |
46 | ```
47 |
48 |
49 |
50 |
51 | #### Right subtitle
52 |
53 | ```rust
54 |
55 | ```
56 |
57 |
58 |
59 | ---
60 |
61 |
--------------------------------------------------------------------------------
/.github/workflows/marp.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | #env:
12 |
13 | jobs:
14 | # Checks syntax formatting.
15 | convert:
16 | name: Convert to PDF
17 | runs-on: ubuntu-latest
18 | defaults:
19 | run:
20 | working-directory: ./slides
21 | steps:
22 | - uses: actions/checkout@v3
23 | - uses: actions/setup-node@v3
24 | name: Use Node.js
25 | with:
26 | node-version: '20.x'
27 | - run: "npm ci"
28 | - run: "npx marp --html --pdf --pdf-outlines --pdf-outlines.pages=false -I . -o dist"
29 | - uses: actions/upload-artifact@v3
30 | name: Archive built PDFs
31 | with:
32 | name: pv281-slide-pdfs
33 | path: slides/dist/[0-1]*.pdf
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/components/container.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, Children, Html, Properties};
2 |
3 | // /// Create a container that can accept children
4 | // /// - this is for educational purposes, as this example is trivial
5 | // /// MARKUP
6 | // /// ------
7 | //
8 | // {
9 | // "here go the children"
10 | // }
11 | //
12 |
13 | #[derive(PartialEq, Properties)]
14 | pub struct ContainerProps {
15 | pub children: Children,
16 | pub class: String,
17 | }
18 |
19 | #[function_component(Container)]
20 | pub fn container(props: &ContainerProps) -> Html {
21 | html! {
22 |
23 | {
24 | for props.children.iter()
25 | }
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/slides/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pv281-slides",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "rimraf dist && npm run -s og-image && npm run -s deck",
8 | "deck": "marp --html --no-stdin 01-intro.md -o dist/index.html && cpy \"assets/**/*\" dist/assets",
9 | "now-build": "npm run -s deck",
10 | "og-image": "marp --html 03-enums-and-structures.md -o dist/og-image.jpg",
11 | "start": "marp -s --html .",
12 | "preview": "CHROME_PATH=/usr/bin/chromium-browser marp -ps --html ."
13 | },
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "@marp-team/marp-cli": "^3.3.0",
18 | "@marp-team/marp-core": "^2.2.0",
19 | "markdown-it-plantuml": "^1.4.1",
20 | "markdown-it-named-code-blocks": "^0.2.0"
21 | },
22 | "devDependencies": {
23 | "cpy-cli": "^3.1.1",
24 | "rimraf": "^3.0.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/components/card.rs:
--------------------------------------------------------------------------------
1 | use super::like_button::LikeButton;
2 | use yew::{classes, function_component, html, Html, Properties};
3 |
4 | #[derive(Properties, PartialEq)]
5 | pub struct CardProps {
6 | pub description: String,
7 | pub image_url: String,
8 | pub title: String,
9 | }
10 |
11 | #[function_component(Card)]
12 | pub fn card(props: &CardProps) -> Html {
13 | html! {
14 |
15 |
16 |

17 |
18 |
19 |
{ props.title.clone() }
20 |
{ props.description.clone() }
21 |
22 |
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/2024/slides/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pv281-slides",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "rimraf dist && npm run -s og-image && npm run -s deck",
8 | "deck": "marp --html --no-stdin 03-enums-and-structures.md -o dist/index.html && cpy \"assets/**/*\" dist/assets",
9 | "now-build": "npm run -s deck",
10 | "og-image": "marp --html 03-enums-and-structures.md -o dist/og-image.jpg",
11 | "start": "marp -s --html .",
12 | "preview": "CHROME_PATH=/usr/bin/chromium-browser marp -ps --html ."
13 | },
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "@marp-team/marp-cli": "^3.3.0",
18 | "@marp-team/marp-core": "^2.2.0",
19 | "markdown-it-plantuml": "^1.4.1",
20 | "markdown-it-named-code-blocks": "^0.2.0"
21 | },
22 | "devDependencies": {
23 | "cpy-cli": "^3.1.1",
24 | "rimraf": "^3.0.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/components/card.rs:
--------------------------------------------------------------------------------
1 | use super::like_button::LikeButton;
2 | use yew::{classes, function_component, html, Html, Properties};
3 |
4 | #[derive(Properties, PartialEq)]
5 | pub struct CardProps {
6 | pub description: String,
7 | pub image_url: String,
8 | pub title: String,
9 | }
10 |
11 | #[function_component(Card)]
12 | pub fn card(props: &CardProps) -> Html {
13 | html! {
14 |
15 |
16 |

17 |
18 |
19 |
{ props.title.clone() }
20 |
{ props.description.clone() }
21 |
22 |
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/components/like_button.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, use_state, Callback, Html};
2 |
3 | // /// Create a button with the following markup:
4 | // /// MARKUP
5 | // /// ------
6 | // {
7 | // "stateful data here!"
8 | // }
9 | // /// ------
10 | // /// The button will respond to clicks by visually modifying itself
11 |
12 | #[function_component(LikeButton)]
13 | pub fn like_button() -> Html {
14 | let liked = use_state(|| false);
15 | let onclick = {
16 | let liked = liked.clone();
17 |
18 | Callback::from(move |_| liked.set(!*liked))
19 | };
20 |
21 | html! {
22 |
23 | {
24 | if !*liked {
25 | "Like!"
26 | } else {
27 | "Unlike."
28 | }
29 | }
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/main.rs:
--------------------------------------------------------------------------------
1 | use yew::{function_component, html, props, Html, Renderer};
2 |
3 | // import components
4 | mod components;
5 | mod views;
6 |
7 | use components::header::{Header, HeaderProps};
8 | use views::{home::HomePage, server::ServerPage};
9 | use yew_router::{BrowserRouter, Routable, Switch};
10 |
11 | #[derive(Clone, Routable, PartialEq)]
12 | pub enum Routes {
13 | #[at("/")]
14 | Home,
15 | #[at("/server-data")]
16 | Fetched,
17 | #[not_found]
18 | #[at("/404")]
19 | NotFound,
20 | }
21 |
22 | fn switch(routes: Routes) -> Html {
23 | match routes {
24 | Routes::Fetched => html! { },
25 | _ => html! { },
26 | }
27 | }
28 |
29 | #[function_component(App)]
30 | fn app() -> Html {
31 | let header_properties = props!(HeaderProps {
32 | image_url: "https://yew.rs/img/logo.png".to_string(),
33 | title: "New awesome app".to_string(),
34 | });
35 |
36 | html! {
37 |
38 |
39 |
40 | render={switch} />
41 |
42 |
43 | }
44 | }
45 |
46 | fn main() {
47 | Renderer::::new().render();
48 | }
49 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/components/card.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, Html, Properties};
2 |
3 | use crate::components::like_button::LikeButton;
4 |
5 | // /// Create a card that displays an image, a title, a description, and a button
6 | // /// which can be toggled (liked/unliked)
7 | // /// MARKUP
8 | // /// ------
9 | //
10 | //
11 | //

12 | //
13 | //
14 | //
{ "title goes here" }
15 | //
{ "description goes here" }
16 | // { "like button goes here" }
17 | //
18 | //
19 |
20 | #[derive(Properties, PartialEq)]
21 | pub struct CardProps {
22 | pub image_url: String,
23 | pub description: String,
24 | pub title: String,
25 | }
26 |
27 | #[function_component(Card)]
28 | pub fn component(props: &CardProps) -> Html {
29 | html! {
30 |
31 |
32 |

33 |
34 |
35 |
{ props.title.clone() }
36 |
{ props.description.clone() }
37 |
38 |
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/src/views/server.rs:
--------------------------------------------------------------------------------
1 | use gloo_net::http::Request;
2 | use serde::Deserialize;
3 | use yew::{function_component, html, use_effect_with_deps, use_state, Html};
4 |
5 | use crate::components::{
6 | card::{Card, CardProps},
7 | container::Container,
8 | };
9 |
10 | #[derive(Clone, PartialEq, Deserialize)]
11 | struct ImageCard {
12 | description: String,
13 | id: u32,
14 | image_url: String,
15 | title: String,
16 | }
17 |
18 | #[function_component(ServerPage)]
19 | pub fn server_page() -> Html {
20 | let cards = use_state(|| vec![]);
21 |
22 | {
23 | let cards = cards.clone();
24 | use_effect_with_deps(
25 | move |_| {
26 | // create a JS Promise from the Rust's Future
27 | wasm_bindgen_futures::spawn_local(async move {
28 | let fetched_cards: Vec = Request::get("http://localhost:5000/cards")
29 | .send()
30 | .await
31 | .unwrap() // get the response, will crash the app
32 | .json()
33 | .await
34 | .unwrap(); // try to parse the json;
35 | // serde tries to deserialize the json to
36 | // vector of image cards
37 |
38 | // set the cards
39 | cards.set(fetched_cards);
40 | })
41 | },
42 | (),
43 | )
44 | }
45 |
46 | html! {
47 |
48 |
49 | {
50 | cards.iter().map(|card_data| {
51 | let props = CardProps {
52 | description: card_data.description.clone(),
53 | image_url: card_data.image_url.clone(),
54 | title: card_data.title.clone(),
55 | };
56 |
57 | html! {
58 |
59 | }
60 | }).collect::()
61 |
62 | }
63 |
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/src/main.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, props, Html, Renderer};
2 |
3 | // import components
4 | mod components;
5 |
6 | use components::{
7 | card::{Card, CardProps},
8 | container::Container,
9 | header::{Header, HeaderProps},
10 | };
11 |
12 | struct ImageCard {
13 | description: String,
14 | id: u32,
15 | image_url: String,
16 | title: String,
17 | }
18 |
19 | #[function_component(App)]
20 | fn app() -> Html {
21 | let header_properties = props!(HeaderProps {
22 | image_url: "https://yew.rs/img/logo.png".to_string(),
23 | title: "New awesome app".to_string(),
24 | });
25 |
26 | let cards: Vec = vec![
27 | ImageCard {
28 | id: 0,
29 | description: "No, I don't have one. :(".into(),
30 | image_url: "https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/mbp-spacegray-select-202206_GEO_CZ?wid=904&hei=840&fmt=jpeg&qlt=90&.v=1664497359383".into(),
31 | title: "Macbook".into(),
32 | },
33 | ImageCard {
34 | id: 1,
35 | description: "No, I don't have that one either... :D".into(),
36 | image_url: "https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-13-model-unselect-gallery-1-202207?wid=5120&hei=2880&fmt=p-jpg&qlt=80&.v=1654893619853".into(),
37 | title: "iPhone".into(),
38 | },
39 | ];
40 |
41 | html! {
42 |
43 |
44 |
45 |
46 | {
47 | cards.into_iter().map(|card_data| {
48 | let props = CardProps {
49 | description: card_data.description,
50 | image_url: card_data.image_url,
51 | title: card_data.title,
52 | };
53 |
54 | html! {
55 |
56 | }
57 | }).collect::()
58 | }
59 |
60 |
61 |
62 | }
63 | }
64 |
65 | fn main() {
66 | Renderer::::new().render();
67 | }
68 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/src/main.rs:
--------------------------------------------------------------------------------
1 | use yew::{classes, function_component, html, props, Html, Renderer};
2 |
3 | // import components
4 | mod components;
5 |
6 | use components::{
7 | card::{Card, CardProps},
8 | container::Container,
9 | header::{Header, HeaderProps},
10 | };
11 |
12 | struct ImageCard {
13 | id: u32,
14 | image_url: String,
15 | description: String,
16 | title: String,
17 | }
18 |
19 | // create structure that will hold the card data
20 |
21 | #[function_component(App)]
22 | fn app() -> Html {
23 | // create header properties
24 | let header_props = props!(HeaderProps {
25 | image_url: "https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-13-model-unselect-gallery-1-202207?wid=5120&hei=2880&fmt=p-jpg&qlt=80&.v=1654893619853".to_string(),
26 | title: "Je to eshop".to_string(),
27 | });
28 |
29 | let cards: Vec = vec![
30 | ImageCard {
31 | id: 1,
32 | image_url: "https://images.samsung.com/cz/smartphones/galaxy-z-fold3-5g/buy/02_Carousel/02_Feature/ZFold3_Carousel_FeatureKV_ArmorAluminum_MO.jpg".to_string(),
33 | title: "Galaxy".to_string(),
34 | description: "Yayyy".to_string(),
35 | },
36 | ImageCard {
37 | id: 2,
38 | image_url: "https://images.samsung.com/cz/smartphones/galaxy-z-fold3-5g/buy/02_Carousel/02_Feature/ZFold3_Carousel_FeatureKV_ArmorAluminum_MO.jpg".to_string(),
39 | title: "Another galaxy".to_string(),
40 | description: "Shall I go into detail?".to_string(),
41 | }
42 | ];
43 |
44 | // create a list of card data
45 |
46 | html! {
47 |
48 |
49 |
50 |
51 | {
52 | cards.into_iter().map(|card_data| {
53 | let props = props!(CardProps {
54 | title: card_data.title.clone(),
55 | description: card_data.description.clone(),
56 | image_url: card_data.image_url.clone(),
57 | });
58 |
59 | html! {
60 |
61 | }
62 | }).collect::()
63 | }
64 |
65 |
66 |
67 | }
68 | }
69 |
70 | fn main() {
71 | // hook up the WASM rendering
72 | Renderer::::new().render();
73 | }
74 |
--------------------------------------------------------------------------------
/examples/10-wasm/demo-reference/assets/styles.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --bg: #111111;
3 | --font: #EFF5F5;
4 | --header-bg: #EB6440;
5 | --header-font: var(--font);
6 | --container-color: #497174;
7 | --card-color: #D6E4E5;
8 | --card-font: var(--bg);
9 | }
10 |
11 | body {
12 | margin: 0;
13 | padding: 0;
14 | font-size: 16px;
15 | font-family: 'Roboto', sans-serif;
16 | }
17 |
18 | .heading {
19 | padding: 0;
20 | margin: 0;
21 | }
22 |
23 | .image {
24 | width: 100%;
25 | }
26 |
27 | .main-window {
28 | display: flex;
29 | flex-direction: column;
30 | align-items: center;
31 | background: rgb(17,17,17);
32 | background: linear-gradient(60deg, rgba(17,17,17,1) 9%, rgba(73,113,116,1) 100%);
33 | color: var(--font);
34 | height: 100vh;
35 | width: 100vw;
36 | overflow: hidden;
37 | }
38 |
39 | .header {
40 | display: flex;
41 | align-items: center;
42 | justify-content: center;
43 | gap: 2rem;
44 | padding: 2rem;
45 | font-size: 3rem;
46 | background: var(--header-bg);
47 | color: var(--header-font);
48 | width: 100%;
49 | }
50 |
51 | .header__logo-container {
52 | width: 5rem;
53 | }
54 |
55 | .container {
56 | width: inherit;
57 | display: flex;
58 | flex-direction: column;
59 | align-items: center;
60 | overflow: hidden auto;
61 | }
62 |
63 | .card-container {
64 | max-width: min(100%, 60rem);
65 | padding: 2rem 4rem;
66 | background-color: var(--container-color);
67 | display: flex;
68 | gap: 2rem;
69 | flex-direction: column;
70 | align-items: center;
71 | flex-grow: 1;
72 | }
73 |
74 | .card {
75 | align-items: center;
76 | border-radius: 2rem;
77 | background-color: var(--card-color);
78 | padding: 2rem;
79 | width: 30rem;
80 | color: var(--card-font);
81 | display: grid;
82 | gap: 0rem 1rem;
83 | grid-template:
84 | [row1-start] "card-image card-content" [row1-end]
85 | / 10rem auto;
86 | }
87 |
88 | .card__image-container {
89 | grid-area: card-image;
90 | width: 10rem;
91 | }
92 |
93 | .card__content {
94 | display: flex;
95 | flex-direction: column;
96 | grid-area: card-content;
97 | justify-content: center;
98 | }
99 |
100 | .card__title {
101 | font-weight: 900;
102 | font-size: 2rem;
103 | }
104 |
105 | .card__description {
106 | font-weight: 100;
107 | font-size: 1rem;
108 | }
109 |
110 | .card__button {
111 | padding: 0.5rem 1rem;
112 | border-radius: 0.5rem;
113 | background-color: var(--bg);
114 | color: var(--font);
115 | font-weight: 400;
116 | width: fit-content;
117 | }
118 |
119 | .card__button:hover {
120 | cursor: pointer;
121 | }
--------------------------------------------------------------------------------
/examples/10-wasm/demo-template/assets/styles.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --bg: #111111;
3 | --font: #EFF5F5;
4 | --header-bg: #EB6440;
5 | --header-font: var(--font);
6 | --container-color: #497174;
7 | --card-color: #D6E4E5;
8 | --card-font: var(--bg);
9 | }
10 |
11 | body {
12 | margin: 0;
13 | padding: 0;
14 | font-size: 16px;
15 | font-family: 'Roboto', sans-serif;
16 | }
17 |
18 | .heading {
19 | padding: 0;
20 | margin: 0;
21 | }
22 |
23 | .image {
24 | width: 100%;
25 | }
26 |
27 | .main-window {
28 | display: flex;
29 | flex-direction: column;
30 | align-items: center;
31 | background: rgb(17,17,17);
32 | background: linear-gradient(60deg, rgba(17,17,17,1) 9%, rgba(73,113,116,1) 100%);
33 | color: var(--font);
34 | height: 100vh;
35 | width: 100vw;
36 | overflow: hidden;
37 | }
38 |
39 | .header {
40 | display: flex;
41 | align-items: center;
42 | justify-content: center;
43 | gap: 2rem;
44 | padding: 2rem;
45 | font-size: 3rem;
46 | background: var(--header-bg);
47 | color: var(--header-font);
48 | width: 100%;
49 | }
50 |
51 | .header__logo-container {
52 | width: 5rem;
53 | }
54 |
55 | .container {
56 | width: inherit;
57 | display: flex;
58 | flex-direction: column;
59 | align-items: center;
60 | overflow: hidden auto;
61 | }
62 |
63 | .card-container {
64 | max-width: min(100%, 60rem);
65 | padding: 2rem 4rem;
66 | background-color: var(--container-color);
67 | display: flex;
68 | gap: 2rem;
69 | flex-direction: column;
70 | align-items: center;
71 | flex-grow: 1;
72 | }
73 |
74 | .card {
75 | align-items: center;
76 | border-radius: 2rem;
77 | background-color: var(--card-color);
78 | padding: 2rem;
79 | width: 30rem;
80 | color: var(--card-font);
81 | display: grid;
82 | gap: 0rem 1rem;
83 | grid-template:
84 | [row1-start] "card-image card-content" [row1-end]
85 | / 10rem auto;
86 | }
87 |
88 | .card__image-container {
89 | grid-area: card-image;
90 | width: 10rem;
91 | }
92 |
93 | .card__content {
94 | display: flex;
95 | flex-direction: column;
96 | grid-area: card-content;
97 | justify-content: center;
98 | }
99 |
100 | .card__title {
101 | font-weight: 900;
102 | font-size: 2rem;
103 | }
104 |
105 | .card__description {
106 | font-weight: 100;
107 | font-size: 1rem;
108 | }
109 |
110 | .card__button {
111 | padding: 0.5rem 1rem;
112 | border-radius: 0.5rem;
113 | background-color: var(--bg);
114 | color: var(--font);
115 | font-weight: 400;
116 | width: fit-content;
117 | }
118 |
119 | .card__button:hover {
120 | cursor: pointer;
121 | }
--------------------------------------------------------------------------------
/examples/10-wasm/demo-router-fetching/assets/styles.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --bg: #111111;
3 | --font: #EFF5F5;
4 | --header-bg: #EB6440;
5 | --header-font: var(--font);
6 | --container-color: #497174;
7 | --card-color: #D6E4E5;
8 | --card-font: var(--bg);
9 | }
10 |
11 | body {
12 | margin: 0;
13 | padding: 0;
14 | font-size: 16px;
15 | font-family: 'Roboto', sans-serif;
16 | }
17 |
18 | .heading {
19 | padding: 0;
20 | margin: 0;
21 | }
22 |
23 | .image {
24 | width: 100%;
25 | }
26 |
27 | .main-window {
28 | display: flex;
29 | flex-direction: column;
30 | align-items: center;
31 | background: rgb(17,17,17);
32 | background: linear-gradient(60deg, rgba(17,17,17,1) 9%, rgba(73,113,116,1) 100%);
33 | color: var(--font);
34 | height: 100vh;
35 | width: 100vw;
36 | overflow: hidden;
37 | }
38 |
39 | .header {
40 | display: flex;
41 | align-items: center;
42 | justify-content: center;
43 | gap: 2rem;
44 | padding: 2rem;
45 | font-size: 3rem;
46 | background: var(--header-bg);
47 | color: var(--header-font);
48 | width: 100%;
49 | }
50 |
51 | .header__logo-container {
52 | width: 5rem;
53 | }
54 |
55 | .container {
56 | width: inherit;
57 | display: flex;
58 | flex-direction: column;
59 | align-items: center;
60 | overflow: hidden auto;
61 | }
62 |
63 | .card-container {
64 | max-width: min(100%, 60rem);
65 | padding: 2rem 4rem;
66 | background-color: var(--container-color);
67 | display: flex;
68 | gap: 2rem;
69 | flex-direction: column;
70 | align-items: center;
71 | flex-grow: 1;
72 | }
73 |
74 | .card {
75 | align-items: center;
76 | border-radius: 2rem;
77 | background-color: var(--card-color);
78 | padding: 2rem;
79 | width: 30rem;
80 | color: var(--card-font);
81 | display: grid;
82 | gap: 0rem 1rem;
83 | grid-template:
84 | [row1-start] "card-image card-content" [row1-end]
85 | / 10rem auto;
86 | }
87 |
88 | .card__image-container {
89 | grid-area: card-image;
90 | width: 10rem;
91 | }
92 |
93 | .card__content {
94 | display: flex;
95 | flex-direction: column;
96 | grid-area: card-content;
97 | justify-content: center;
98 | }
99 |
100 | .card__title {
101 | font-weight: 900;
102 | font-size: 2rem;
103 | }
104 |
105 | .card__description {
106 | font-weight: 100;
107 | font-size: 1rem;
108 | }
109 |
110 | .card__button {
111 | padding: 0.5rem 1rem;
112 | border-radius: 0.5rem;
113 | background-color: var(--bg);
114 | color: var(--font);
115 | font-weight: 400;
116 | width: fit-content;
117 | }
118 |
119 | .clickable:hover {
120 | cursor: pointer;
121 | }
--------------------------------------------------------------------------------
/slides/assets/01-images/rustacean-flat-noshadow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/rustacean-flat-noshadow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
--------------------------------------------------------------------------------
/slides/themes/rust.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*!
3 | * Marp Dracula theme.
4 | * @theme rust
5 | * @author Daniel Nicolas Gisolfi
6 | *
7 | * @auto-scaling true
8 | * @size 16:9 1280px 720px
9 | */
10 |
11 | @import url("https://fonts.googleapis.com/css?family=Lato:400,900|IBM+Plex+Sans:400,700");
12 | @import url('https://fonts.googleapis.com/css2?family=Alfa+Slab+One&display=swap');
13 | @import url('https://fonts.googleapis.com/css2?family=Fira+Sans&display=swap');
14 | @import url('https://fonts.googleapis.com/css2?family=Fira+Sans:wght@700&display=swap');
15 | @import url('https://fonts.googleapis.com/css2?family=Fira+Mono&display=swap');
16 | @import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap');
17 |
18 | :root {
19 | --dracula-background: #282a36;
20 | --dracula-current-line: #44475a;
21 | --dracula-secondary-line: #6a6f8d;
22 | --dracula-foreground: #f8f8f2;
23 | --dracula-comment: #8AA1E6;
24 | --dracula-cyan: #8be9fd;
25 | --dracula-green: #50fa7b;
26 | --dracula-orange: #ffb86c;
27 | --dracula-pink: #ff79c6;
28 | --dracula-purple:#bd93f9;
29 | --dracula-red: #ff5555;
30 | --dracula-dark-red: #e73434;
31 | --dracula-yellow: #f1fa8c;
32 | }
33 |
34 | .hljs {
35 | display: block;
36 | overflow-x: auto;
37 | padding: 0.5em;
38 | background: var(--dracula-background);
39 | }
40 |
41 | /* Dracula Foreground */
42 | .hljs,
43 | .hljs-subst,
44 | .hljs-typing,
45 | .hljs-variable,
46 | .hljs-template-variable {
47 | color: var(--dracula-foreground);
48 | }
49 |
50 | /* Dracula Comment */
51 | .hljs-comment,
52 | .hljs-quote,
53 | .hljs-deletion {
54 | color: var(--dracula-comment);
55 | }
56 |
57 | /* Dracula Cyan */
58 | .hljs-meta,
59 | .hljs-doctag,
60 | .hljs-built_in,
61 | .hljs-selector-tag,
62 | .hljs-selector-class,
63 | .hljs-selector-id,
64 | .hljs-section,
65 | .hljs-link,
66 | .hljs-class {
67 | color: var(--dracula-cyan);
68 | }
69 |
70 | /* Dracula Green */
71 | .hljs-title {
72 | color: var(--dracula-green);
73 | }
74 |
75 | /* Dracula Orange */
76 | .hljs-params {
77 | color: var(--dracula-orange);
78 | }
79 |
80 | /* Dracula Pink */
81 | .hljs-keyword {
82 | color: var(--dracula-pink);
83 | }
84 |
85 | /* Dracula Purple */
86 | .hljs-literal,
87 | .hljs-number {
88 | color: var(--dracula-purple);
89 | }
90 |
91 | /* Dracula Red */
92 | .hljs-regexp {
93 | color: var(--dracula-red);
94 | }
95 |
96 | /* Dracula Yellow */
97 | .hljs-string,
98 | .hljs-name,
99 | .hljs-type,
100 | .hljs-attr,
101 | .hljs-attribute,
102 | .hljs-selector-pseudo,
103 | .hljs-symbol,
104 | .hljs-bullet,
105 | .hljs-addition,
106 | .hljs-template-tag {
107 | color: var(--dracula-yellow);
108 | }
109 |
110 | .hljs-keyword,
111 | .hljs-selector-tag,
112 | .hljs-literal,
113 | .hljs-title,
114 | .hljs-section,
115 | .hljs-doctag,
116 | .hljs-type,
117 | .hljs-name,
118 | .hljs-strong {
119 | font-weight: bold;
120 | }
121 |
122 | .hljs-params,
123 | .hljs-emphasis {
124 | font-style: italic;
125 | }
126 |
127 | svg[data-marp-fitting=svg] {
128 | max-height: 580px;
129 | }
130 |
131 | h1,
132 | h2,
133 | h3,
134 | h4,
135 | h5,
136 | h6 {
137 | margin: 0.5em 0 0 0;
138 | color: var(--dracula-red);
139 | }
140 | h1 strong,
141 | h2 strong,
142 | h3 strong,
143 | h4 strong,
144 | h5 strong,
145 | h6 strong {
146 | font-weight: inherit;
147 | }
148 |
149 | h1 {
150 | font-size: 1.8em;
151 | }
152 |
153 | h2 {
154 | font-size: 1.5em;
155 | }
156 |
157 | h3 {
158 | font-size: 1.3em;
159 | }
160 |
161 | h4 {
162 | font-size: 1.1em;
163 | }
164 |
165 | h5 {
166 | font-size: 1em;
167 | }
168 |
169 | h6 {
170 | font-size: 0.9em;
171 | }
172 |
173 | p,
174 | blockquote {
175 | margin: 1em 0 0 0;
176 | }
177 |
178 | ul > li,
179 | ol > li {
180 | margin: 0.3em 0 0 0;
181 | color: var(--dracula-cyan);
182 | }
183 | ul > li > p,
184 | ol > li > p {
185 | margin: 0.6em 0 0 0;
186 | }
187 |
188 | code {
189 | display: inline-block;
190 | font-family: "IBM Plex Mono", monospace;
191 | font-size: 0.8em;
192 | letter-spacing: 0;
193 | margin: -0.1em 0.15em;
194 | padding: 0.1em 0.2em;
195 | vertical-align: baseline;
196 | color: var(--dracula-foreground);
197 | }
198 |
199 | pre {
200 | display: block;
201 | margin: 1em 0 0 0;
202 | min-height: 1em;
203 | overflow: visible;
204 | }
205 | pre code {
206 | box-sizing: border-box;
207 | margin: 0;
208 | min-width: 100%;
209 | padding: 0.5em;
210 | font-size: 0.7em;
211 | }
212 | pre code svg[data-marp-fitting=svg] {
213 | max-height: calc(580px - 1em);
214 | }
215 |
216 | blockquote {
217 | margin: 1em 0 0 0;
218 | padding: 0 1em;
219 | position: relative;
220 | color: var(--dracula-orange);
221 | }
222 | blockquote::after, blockquote::before {
223 | content: "“";
224 | display: block;
225 | font-family: "Times New Roman", serif;
226 | font-weight: bold;
227 | position: absolute;
228 | color: var(--dracula-green);
229 | }
230 | blockquote::before {
231 | top: 0;
232 | left: 0;
233 | }
234 | blockquote::after {
235 | right: 0;
236 | bottom: 0;
237 | transform: rotate(180deg);
238 | }
239 | blockquote > *:first-child {
240 | margin-top: 0;
241 | }
242 |
243 | mark {
244 | background: transparent;
245 | }
246 |
247 | table {
248 | border-spacing: 0;
249 | border-collapse: collapse;
250 | margin: 1em 0 0 0;
251 | }
252 | table th,
253 | table td {
254 | padding: 0.2em 0.4em;
255 | border-width: 1px;
256 | border-style: solid;
257 | }
258 |
259 | section {
260 | font-size: 35px;
261 | font-family: "IBM Plex Sans", sans-serif;
262 | line-height: 1.35;
263 | letter-spacing: 1.25px;
264 | padding: 70px;
265 | color: var(--dracula-foreground);
266 | background-color: var(--dracula-background);
267 | justify-content: center;
268 | /* align-items: center; */
269 | display: flex;
270 | flex-direction: column;
271 | }
272 | section > *:first-child,
273 | section > header:first-child + * {
274 | margin-top: 0;
275 | }
276 | section a,
277 | section mark {
278 | color: var(--dracula-red);
279 | }
280 | section code {
281 | background: var(--dracula-current-line);
282 | }
283 | section h1 strong,
284 | section h2 strong,
285 | section h3 strong,
286 | section h4 strong,
287 | section h5 strong,
288 | section h6 strong {
289 | color: var(--dracula-dark-red);
290 | }
291 | section pre > code {
292 | background: var(--dracula-current-line);
293 | color: var(--dracula-foreground);
294 | }
295 | section header,
296 | section footer,
297 | section section::after,
298 | section blockquote::before,
299 | section blockquote::after {
300 | color: var(--dracula-comment);
301 | }
302 | section table th,
303 | section table td {
304 | border-color: var(--dracula-current-line);
305 | }
306 | section table thead th {
307 | background: var(--dracula-current-line);
308 | color: var(--dracula-yellow);
309 | }
310 | section table tbody > tr:nth-child(even) td,
311 | section table tbody > tr:nth-child(even) th {
312 | background: var(--dracula-current-line);
313 | }
314 |
315 | section table tbody > tr:nth-child(even) code {
316 | background-color: var(--dracula-background);
317 | }
318 |
319 | .dark-background,
320 | td[rowspan] {
321 | background-color: var(--dracula-background) !important;
322 | }
323 |
324 | header,
325 | footer,
326 | section::after {
327 | box-sizing: border-box;
328 | font-size: 66%;
329 | height: 70px;
330 | line-height: 50px;
331 | overflow: hidden;
332 | padding: 10px 25px;
333 | position: absolute;
334 | color: var(--dracula-comment);
335 | content: attr(data-marpit-pagination) '/' attr(data-marpit-pagination-total);
336 | }
337 |
338 | header {
339 | left: 0;
340 | right: 0;
341 | top: 0;
342 | }
343 |
344 | footer {
345 | left: 0;
346 | right: 0;
347 | bottom: 0;
348 | }
349 |
350 | section::after {
351 | right: 0;
352 | bottom: 0;
353 | font-size: 80%;
354 | }
355 |
356 | h1,h2,h3 {
357 | font-family: "Alfa Slab One", serif;
358 | }
359 |
360 | section {
361 | font-family: 'Fira Sans', sans-serif;
362 | text-align: center;
363 | }
364 |
365 | code, pre {
366 | font-family: 'Fira Mono', monospace;
367 | text-align: left;
368 | }
369 |
370 | pre code svg[data-marp-fitting=svg] {
371 | max-height: calc(40vh - 1em);
372 | }
373 |
374 | section.plantuml {
375 | background-color: white !important;
376 | }
377 |
378 | section.plantuml > img {
379 | /* Reason: valid outside Firefox */
380 | /* noinspection CssInvalidPropertyValue */
381 | width: -webkit-fill-available;
382 | /* Reason: valid inside Firefox */
383 | /* noinspection CssInvalidPropertyValue */
384 | width: -moz-available;
385 | }
386 |
387 | section.split {
388 | overflow: visible;
389 | padding-top: 0;
390 | display: grid;
391 | grid-template-columns: 45% 45%;
392 | grid-template-rows: 3em min-content min-content auto min-content;
393 | grid-template-areas:
394 | "slideheading slideheading"
395 | "commontext commontext"
396 | "columnheading columnheading"
397 | "leftpanel rightpanel"
398 | "commonnote commonnote";
399 | column-gap: 1em;
400 | align-items: center;
401 | }
402 | section.split h3 {
403 | grid-area: slideheading;
404 | font-size: 1.3em;
405 | margin: 1em 0 0 0;
406 | }
407 | section.split h4 {
408 | grid-area: columnheading;
409 | font-size: 1.3em;
410 | margin: auto;
411 | }
412 | section.split h6 {
413 | grid-area: columnheading;
414 | font-size: 1em;
415 | margin: auto;
416 | }
417 | section.split .common-text {
418 | grid-area: commontext;
419 | }
420 | section.split .left-column {
421 | grid-area: leftpanel;
422 | }
423 | section.split .right-column {
424 | grid-area: rightpanel;
425 | }
426 | section.split .common-note {
427 | grid-area: commonnote;
428 | }
429 |
430 | /* For debugging purposes only */
431 | section.split .left-column,
432 | section.split .right-column,
433 | section.split .common-text,
434 | section.split h3 {
435 | /*border: 1.5pt dashed dimgray;*/
436 | }
437 |
438 | .named-fence-block{
439 | position: relative;
440 | }
441 |
442 | .named-fence-filename{
443 | position: absolute;
444 | top: 0;
445 | right: 0;
446 | padding: 0.3em;
447 |
448 | font-size: 0.5em;
449 | font-weight: bold;
450 |
451 | background-color: var(--dracula-secondary-line);
452 | color: var(--dracula-foreground);
453 | opacity: 0.5;
454 | }
455 |
--------------------------------------------------------------------------------
/2024/slides/themes/rust.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*!
3 | * Marp Dracula theme.
4 | * @theme rust
5 | * @author Daniel Nicolas Gisolfi
6 | *
7 | * @auto-scaling true
8 | * @size 16:9 1280px 720px
9 | */
10 |
11 | @import url("https://fonts.googleapis.com/css?family=Lato:400,900|IBM+Plex+Sans:400,700");
12 | @import url('https://fonts.googleapis.com/css2?family=Alfa+Slab+One&display=swap');
13 | @import url('https://fonts.googleapis.com/css2?family=Fira+Sans&display=swap');
14 | @import url('https://fonts.googleapis.com/css2?family=Fira+Sans:wght@700&display=swap');
15 | @import url('https://fonts.googleapis.com/css2?family=Fira+Mono&display=swap');
16 | @import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap');
17 |
18 | :root {
19 | --dracula-background: #282a36;
20 | --dracula-current-line: #44475a;
21 | --dracula-secondary-line: #6a6f8d;
22 | --dracula-foreground: #f8f8f2;
23 | --dracula-comment: #8AA1E6;
24 | --dracula-cyan: #8be9fd;
25 | --dracula-green: #50fa7b;
26 | --dracula-orange: #ffb86c;
27 | --dracula-pink: #ff79c6;
28 | --dracula-purple:#bd93f9;
29 | --dracula-red: #ff5555;
30 | --dracula-dark-red: #e73434;
31 | --dracula-yellow: #f1fa8c;
32 | }
33 |
34 | .hljs {
35 | display: block;
36 | overflow-x: auto;
37 | padding: 0.5em;
38 | background: var(--dracula-background);
39 | }
40 |
41 | /* Dracula Foreground */
42 | .hljs,
43 | .hljs-subst,
44 | .hljs-typing,
45 | .hljs-variable,
46 | .hljs-template-variable {
47 | color: var(--dracula-foreground);
48 | }
49 |
50 | /* Dracula Comment */
51 | .hljs-comment,
52 | .hljs-quote,
53 | .hljs-deletion {
54 | color: var(--dracula-comment);
55 | }
56 |
57 | /* Dracula Cyan */
58 | .hljs-meta,
59 | .hljs-doctag,
60 | .hljs-built_in,
61 | .hljs-selector-tag,
62 | .hljs-selector-class,
63 | .hljs-selector-id,
64 | .hljs-section,
65 | .hljs-link,
66 | .hljs-class {
67 | color: var(--dracula-cyan);
68 | }
69 |
70 | /* Dracula Green */
71 | .hljs-title {
72 | color: var(--dracula-green);
73 | }
74 |
75 | /* Dracula Orange */
76 | .hljs-params {
77 | color: var(--dracula-orange);
78 | }
79 |
80 | /* Dracula Pink */
81 | .hljs-keyword {
82 | color: var(--dracula-pink);
83 | }
84 |
85 | /* Dracula Purple */
86 | .hljs-literal,
87 | .hljs-number {
88 | color: var(--dracula-purple);
89 | }
90 |
91 | /* Dracula Red */
92 | .hljs-regexp {
93 | color: var(--dracula-red);
94 | }
95 |
96 | /* Dracula Yellow */
97 | .hljs-string,
98 | .hljs-name,
99 | .hljs-type,
100 | .hljs-attr,
101 | .hljs-attribute,
102 | .hljs-selector-pseudo,
103 | .hljs-symbol,
104 | .hljs-bullet,
105 | .hljs-addition,
106 | .hljs-template-tag {
107 | color: var(--dracula-yellow);
108 | }
109 |
110 | .hljs-keyword,
111 | .hljs-selector-tag,
112 | .hljs-literal,
113 | .hljs-title,
114 | .hljs-section,
115 | .hljs-doctag,
116 | .hljs-type,
117 | .hljs-name,
118 | .hljs-strong {
119 | font-weight: bold;
120 | }
121 |
122 | .hljs-params,
123 | .hljs-emphasis {
124 | font-style: italic;
125 | }
126 |
127 | svg[data-marp-fitting=svg] {
128 | max-height: 580px;
129 | }
130 |
131 | h1,
132 | h2,
133 | h3,
134 | h4,
135 | h5,
136 | h6 {
137 | margin: 0.5em 0 0 0;
138 | color: var(--dracula-red);
139 | }
140 | h1 strong,
141 | h2 strong,
142 | h3 strong,
143 | h4 strong,
144 | h5 strong,
145 | h6 strong {
146 | font-weight: inherit;
147 | }
148 |
149 | h1 {
150 | font-size: 1.8em;
151 | }
152 |
153 | h2 {
154 | font-size: 1.5em;
155 | }
156 |
157 | h3 {
158 | font-size: 1.3em;
159 | }
160 |
161 | h4 {
162 | font-size: 1.1em;
163 | }
164 |
165 | h5 {
166 | font-size: 1em;
167 | }
168 |
169 | h6 {
170 | font-size: 0.9em;
171 | }
172 |
173 | p,
174 | blockquote {
175 | margin: 1em 0 0 0;
176 | }
177 |
178 | ul > li,
179 | ol > li {
180 | margin: 0.3em 0 0 0;
181 | color: var(--dracula-cyan);
182 | }
183 | ul > li > p,
184 | ol > li > p {
185 | margin: 0.6em 0 0 0;
186 | }
187 |
188 | code {
189 | display: inline-block;
190 | font-family: "IBM Plex Mono", monospace;
191 | font-size: 0.8em;
192 | letter-spacing: 0;
193 | margin: -0.1em 0.15em;
194 | padding: 0.1em 0.2em;
195 | vertical-align: baseline;
196 | color: var(--dracula-foreground);
197 | }
198 |
199 | pre {
200 | display: block;
201 | margin: 1em 0 0 0;
202 | min-height: 1em;
203 | overflow: visible;
204 | }
205 | pre code {
206 | box-sizing: border-box;
207 | margin: 0;
208 | min-width: 100%;
209 | padding: 0.5em;
210 | font-size: 0.7em;
211 | }
212 | pre code svg[data-marp-fitting=svg] {
213 | max-height: calc(580px - 1em);
214 | }
215 |
216 | blockquote {
217 | margin: 1em 0 0 0;
218 | padding: 0 1em;
219 | position: relative;
220 | color: var(--dracula-orange);
221 | }
222 | blockquote::after, blockquote::before {
223 | content: "“";
224 | display: block;
225 | font-family: "Times New Roman", serif;
226 | font-weight: bold;
227 | position: absolute;
228 | color: var(--dracula-green);
229 | }
230 | blockquote::before {
231 | top: 0;
232 | left: 0;
233 | }
234 | blockquote::after {
235 | right: 0;
236 | bottom: 0;
237 | transform: rotate(180deg);
238 | }
239 | blockquote > *:first-child {
240 | margin-top: 0;
241 | }
242 |
243 | mark {
244 | background: transparent;
245 | }
246 |
247 | table {
248 | border-spacing: 0;
249 | border-collapse: collapse;
250 | margin: 1em 0 0 0;
251 | }
252 | table th,
253 | table td {
254 | padding: 0.2em 0.4em;
255 | border-width: 1px;
256 | border-style: solid;
257 | }
258 |
259 | section {
260 | font-size: 35px;
261 | font-family: "IBM Plex Sans", sans-serif;
262 | line-height: 1.35;
263 | letter-spacing: 1.25px;
264 | padding: 70px;
265 | color: var(--dracula-foreground);
266 | background-color: var(--dracula-background);
267 | justify-content: center;
268 | /* align-items: center; */
269 | display: flex;
270 | flex-direction: column;
271 | }
272 | section > *:first-child,
273 | section > header:first-child + * {
274 | margin-top: 0;
275 | }
276 | section a,
277 | section mark {
278 | color: var(--dracula-red);
279 | }
280 | section code {
281 | background: var(--dracula-current-line);
282 | }
283 | section h1 strong,
284 | section h2 strong,
285 | section h3 strong,
286 | section h4 strong,
287 | section h5 strong,
288 | section h6 strong {
289 | color: var(--dracula-dark-red);
290 | }
291 | section pre > code {
292 | background: var(--dracula-current-line);
293 | color: var(--dracula-foreground);
294 | }
295 | section header,
296 | section footer,
297 | section section::after,
298 | section blockquote::before,
299 | section blockquote::after {
300 | color: var(--dracula-comment);
301 | }
302 | section table th,
303 | section table td {
304 | border-color: var(--dracula-current-line);
305 | }
306 | section table thead th {
307 | background: var(--dracula-current-line);
308 | color: var(--dracula-yellow);
309 | }
310 | section table tbody > tr:nth-child(even) td,
311 | section table tbody > tr:nth-child(even) th {
312 | background: var(--dracula-current-line);
313 | }
314 |
315 | section table tbody > tr:nth-child(even) code {
316 | background-color: var(--dracula-background);
317 | }
318 |
319 | .dark-background,
320 | td[rowspan] {
321 | background-color: var(--dracula-background) !important;
322 | }
323 |
324 | header,
325 | footer,
326 | section::after {
327 | box-sizing: border-box;
328 | font-size: 66%;
329 | height: 70px;
330 | line-height: 50px;
331 | overflow: hidden;
332 | padding: 10px 25px;
333 | position: absolute;
334 | color: var(--dracula-comment);
335 | content: attr(data-marpit-pagination) '/' attr(data-marpit-pagination-total);
336 | }
337 |
338 | header {
339 | left: 0;
340 | right: 0;
341 | top: 0;
342 | }
343 |
344 | footer {
345 | left: 0;
346 | right: 0;
347 | bottom: 0;
348 | }
349 |
350 | section::after {
351 | right: 0;
352 | bottom: 0;
353 | font-size: 80%;
354 | }
355 |
356 | h1,h2,h3 {
357 | font-family: "Alfa Slab One", serif;
358 | }
359 |
360 | section {
361 | font-family: 'Fira Sans', sans-serif;
362 | text-align: center;
363 | }
364 |
365 | code, pre {
366 | font-family: 'Fira Mono', monospace;
367 | text-align: left;
368 | }
369 |
370 | pre code svg[data-marp-fitting=svg] {
371 | max-height: calc(40vh - 1em);
372 | }
373 |
374 | section.plantuml {
375 | background-color: white !important;
376 | }
377 |
378 | section.plantuml > img {
379 | /* Reason: valid outside Firefox */
380 | /* noinspection CssInvalidPropertyValue */
381 | width: -webkit-fill-available;
382 | /* Reason: valid inside Firefox */
383 | /* noinspection CssInvalidPropertyValue */
384 | width: -moz-available;
385 | }
386 |
387 | section.split {
388 | overflow: visible;
389 | padding-top: 0;
390 | display: grid;
391 | grid-template-columns: 45% 45%;
392 | grid-template-rows: 3em min-content min-content auto min-content;
393 | grid-template-areas:
394 | "slideheading slideheading"
395 | "commontext commontext"
396 | "columnheading columnheading"
397 | "leftpanel rightpanel"
398 | "commonnote commonnote";
399 | column-gap: 1em;
400 | align-items: center;
401 | }
402 | section.split h3 {
403 | grid-area: slideheading;
404 | font-size: 1.3em;
405 | margin: 1em 0 0 0;
406 | }
407 | section.split h4 {
408 | grid-area: columnheading;
409 | font-size: 1.3em;
410 | margin: auto;
411 | }
412 | section.split h6 {
413 | grid-area: columnheading;
414 | font-size: 1em;
415 | margin: auto;
416 | }
417 | section.split .common-text {
418 | grid-area: commontext;
419 | }
420 | section.split .left-column {
421 | grid-area: leftpanel;
422 | }
423 | section.split .right-column {
424 | grid-area: rightpanel;
425 | }
426 | section.split .common-note {
427 | grid-area: commonnote;
428 | }
429 |
430 | /* For debugging purposes only */
431 | section.split .left-column,
432 | section.split .right-column,
433 | section.split .common-text,
434 | section.split h3 {
435 | /*border: 1.5pt dashed dimgray;*/
436 | }
437 |
438 | .named-fence-block{
439 | position: relative;
440 | }
441 |
442 | .named-fence-filename{
443 | position: absolute;
444 | top: 0;
445 | right: 0;
446 | padding: 0.3em;
447 |
448 | font-size: 0.5em;
449 | font-weight: bold;
450 |
451 | background-color: var(--dracula-secondary-line);
452 | color: var(--dracula-foreground);
453 | opacity: 0.5;
454 | }
455 |
--------------------------------------------------------------------------------
/slides/assets/01-images/computerlanguagebenchmarkgame2023.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/computerlanguagebenchmarkgame2023.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/slides/assets/01-images/computerlanguagebenchmarkgame.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
250 |
--------------------------------------------------------------------------------
/2024/slides/assets/01-images/computerlanguagebenchmarkgame.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
250 |
--------------------------------------------------------------------------------
/slides/14-bonus.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Bonus
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Programování v Rustu
10 |
11 | ---
12 |
13 | # Obsah
14 |
15 | 1. Deployment aplikací
16 | 2. OAuth, OpenID
17 | 3. Zero-Trust architektury
18 | 4. Makra
19 |
20 | ---
21 |
22 | # Deployment aplikací
23 |
24 | ---
25 |
26 | # Produkční build
27 |
28 | Produkční build spustíme příkazem
29 |
30 | ```bash
31 | cargo build --release
32 | ```
33 |
34 | Pro produkční build je vhodné nastavit úroveň optimalizací v Cargo.toml. Znamená to ovšem delší kompilační čas.
35 |
36 | ```toml
37 | [profile.dev]
38 | opt-level = 0
39 |
40 | [profile.release]
41 | opt-level = 3
42 | ```
43 |
44 | ---
45 |
46 | # Nasazení webové služby
47 |
48 | 1. Na cílový server nahrajeme binárku aplikaci.
49 | 2. Registrujeme jako službu/démona nebo jiným způsobem zařídíme spuštění po startu systému.
50 | 3. V případě potřeby nasadíme proxy.
51 |
52 | ---
53 |
54 | # Nahrátí na cílový server
55 |
56 | Záleží na možnostech, které máme. Pokud nasazujeme u cloud providera, tak využijeme jeho možností (zip package, teracopy). Pokud fungujeme na vlastním serveru, tak máme možnosti přes FTP, git apod.
57 |
58 | ---
59 |
60 | # Kontejnery
61 |
62 | Jako lepší scénář se dnes nabízí kontejnerizace. Pro menší deployment jde použít Docker Swarm. Lepší alternativou (ale vyšší učící křivkou) je Kubernetes. Pro jednoduché deploymenty existuje k3s v lokální kombinaci s nerdctl.
63 |
64 | Větší deploymenty jde realizovat ve vlastním k8s clusteru nebo využít cloud-hosted.
65 |
66 | ---
67 |
68 | # CD pipeline
69 |
70 | Ideální je řešit nasazení přes continous delivery. Oblíbené se staly Github Actions.
71 |
72 | ---
73 |
74 | # OAuth, OpenID
75 |
76 | ---
77 |
78 | # OAuth
79 |
80 | Zavádí autorizační vrstvu oddělující klienta a resource.
81 |
82 | ---
83 |
84 | # Role
85 |
86 | resource owner - uživatel, který může získat přístup
87 | resource server - má data, která jsou chráněná proti neoprávněnému přístupu
88 | klient - klientská aplikace, která získává data třeba přes REST
89 | auth server - server starající se o identitu
90 |
91 | ---
92 |
93 | # Flow
94 |
95 | 1. Klient pošle auth request na vlastníka (nebo ideálně na auth server)
96 | 2. Klient dostává grant
97 | 3. Klient posílá auth grant na auth server
98 | 4. Klient dostává access token
99 | 5. Klient posílá access token na resource
100 | 6. Klient dostává data
101 |
102 | ---
103 |
104 | # 4 druhy flow
105 |
106 | 1. autorizační kód
107 | 2. implicit
108 | 3. resource owner password credentials
109 | 4. client credentials
110 |
111 | ---
112 |
113 | # Implicit flow
114 |
115 | Zjednodušené flow určené primárně pro webový prohlížeč. Je vynechána část pro získání autorizačního kódu/grantu. Klient získává přímo access token.
116 | Pozn. neautentizuje klienta. Identita klienta může být ověřena podle return URL.
117 |
118 | ---
119 |
120 | # Access token
121 |
122 | = kredenšly pro přístup k resourcu
123 |
124 | Je to string obsahující scope a délku trvání přístupu. Je nečitelný klientem.
125 | Může být restriktivnější než grant.
126 |
127 | ---
128 |
129 | # Refresh token
130 |
131 | Optional. Slouží pro obnovení expirovaného access tokenu.
132 | Nikdy se neposílá na resource server.
133 |
134 | ---
135 |
136 | # Redirect z auth server
137 |
138 | Klient je pro získání přístupu přesměrován na auth server. Ten má pri klienta zadanou return URL (je třeba registrovat předem všechny možné). Přesměrování je řešeno primárně přes 302 Found s uri v Location header.
139 |
140 | ---
141 |
142 | # Registrace klienta
143 |
144 | = typ klienta (confidential nebo public) + redirect url + další informace
145 |
146 | ---
147 |
148 | # OpenID Connect 1.0
149 |
150 | Identita nad OAuth 2.0. OID dává autentizaci a OAuth autorizaci.
151 | Informace je vrácena jako JWT.
152 |
153 | ---
154 |
155 | # Flow
156 |
157 | 1. Relying party pošle auth request na OpenID provider
158 | 2. User provede authn a authz na provideru
159 | 3. Relying party dostává AuthN response
160 | 4. Relying party posílá user info request na OpenID Provider
161 | 5. Relying party dostává user info.
162 |
163 | ---
164 |
165 | # Request
166 |
167 | scope = openid
168 | respose_type
169 | client_id
170 | redirect_uri
171 | state
172 |
173 | ---
174 |
175 | # Zero Trust Architectures
176 |
177 | ---
178 |
179 | Korporátní sítě se dlouho spoléhali na bezpečnost na úrovni perimetru. Ve vnitřní síti nebylo často žádné zabezpečení.
180 |
181 | Komunikace uvnitř sítě byla nešifrovaná.
182 |
183 | ---
184 |
185 | # Tradiční zabezpečení infrastruktury
186 |
187 | Zabezpečení na perimetru (tj. firewall), přičemž vnitřní komunikace je považována za důvěryhodnou.
188 | Pevné IP adresy a hardware pro určité aplikace.
189 | Identita založená na IP adrese.
190 | Služby jsou provozovány na známém, očekávaném místě.
191 | Specifické bezpečnostní požadavky zabudované do každé aplikace a vynucované samostatně.
192 | Omezená omezení způsobu vytváření a kontroly služeb.
193 | Omezený dohled nad bezpečnostními složkami.
194 | Složité a občasné nasazování.
195 | Appky jsou obvykle nasazovány jako virtuální počítače nebo na fyzické hosty a k zajištění izolace používají fyzický stroj nebo hypervizor.
196 |
197 | ---
198 |
199 | # Zásady bezpečnosti v cloudovém prostředí
200 |
201 | Ochrana sítě na okraji
202 | Žádná inherentní vzájemná důvěra mezi službami
203 | Důvěryhodné stroje, na kterých běží kód se známým původem
204 | Kontrolní body pro konzistentní prosazování zásad napříč službami
205 | Jednoduché, automatizované a standardizované zavádění změn
206 | Izolace mezi workloady sdílejícími operační systém
207 |
208 | ---
209 |
210 | # Makra
211 |
212 | ---
213 |
214 | ```rust
215 | // This is a simple macro named `say_hello`.
216 | macro_rules! say_hello {
217 | // `()` indicates that the macro takes no argument.
218 | () => {
219 | // The macro will expand into the contents of this block.
220 | println!("Hello!");
221 | };
222 | }
223 |
224 | fn main() {
225 | // This call will expand into `println!("Hello");`
226 | say_hello!()
227 | }
228 |
229 | ```
230 |
231 | ---
232 |
233 | # Designator
234 |
235 | ```rust
236 | macro_rules! create_function {
237 | // This macro takes an argument of designator `ident` and
238 | // creates a function named `$func_name`.
239 | // The `ident` designator is used for variable/function names.
240 | ($func_name:ident) => {
241 | fn $func_name() {
242 | // The `stringify!` macro converts an `ident` into a string.
243 | println!("You called {:?}()",
244 | stringify!($func_name));
245 | }
246 | };
247 | }
248 |
249 | // Create functions named `foo` and `bar` with the above macro.
250 | create_function!(foo);
251 | create_function!(bar);
252 |
253 | fn main() {
254 | foo();
255 | bar();
256 | }
257 |
258 | ```
259 |
260 | ---
261 |
262 | # Designator
263 |
264 | ```rust
265 | macro_rules! print_result {
266 | // This macro takes an expression of type `expr` and prints
267 | // it as a string along with its result.
268 | // The `expr` designator is used for expressions.
269 | ($expression:expr) => {
270 | // `stringify!` will convert the expression *as it is* into a string.
271 | println!("{:?} = {:?}",
272 | stringify!($expression),
273 | $expression);
274 | };
275 | }
276 |
277 | fn main() {
278 | print_result!(1u32 + 1);
279 |
280 | // Recall that blocks are expressions too!
281 | print_result!({
282 | let x = 1u32;
283 |
284 | x * x + 2 * x - 1
285 | });
286 | }
287 |
288 | ```
289 |
290 | ---
291 |
292 | # Možnosti
293 |
294 | block
295 | expr is used for expressions
296 | ident is used for variable/function names
297 | item
298 | literal is used for literal constants
299 | pat (pattern)
300 | path
301 | stmt (statement)
302 | tt (token tree)
303 | ty (type)
304 | vis (visibility qualifier)
305 |
306 | ---
307 |
308 | # Overload
309 |
310 | ```rust
311 | // `test!` will compare `$left` and `$right`
312 | // in different ways depending on how you invoke it:
313 | macro_rules! test {
314 | // Arguments don't need to be separated by a comma.
315 | // Any template can be used!
316 | ($left:expr; and $right:expr) => {
317 | println!("{:?} and {:?} is {:?}",
318 | stringify!($left),
319 | stringify!($right),
320 | $left && $right)
321 | };
322 | // ^ each arm must end with a semicolon.
323 | ($left:expr; or $right:expr) => {
324 | println!("{:?} or {:?} is {:?}",
325 | stringify!($left),
326 | stringify!($right),
327 | $left || $right)
328 | };
329 | }
330 |
331 | fn main() {
332 | test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
333 | test!(true; or false);
334 | }
335 | ```
336 |
337 | ---
338 |
339 | # Repeat
340 |
341 | ```rust
342 | // `find_min!` will calculate the minimum of any number of arguments.
343 | macro_rules! find_min {
344 | // Base case:
345 | ($x:expr) => ($x);
346 | // `$x` followed by at least one `$y,`
347 | ($x:expr, $($y:expr),+) => (
348 | // Call `find_min!` on the tail `$y`
349 | std::cmp::min($x, find_min!($($y),+))
350 | )
351 | }
352 |
353 | fn main() {
354 | println!("{}", find_min!(1u32));
355 | println!("{}", find_min!(1u32 + 2, 2u32));
356 | println!("{}", find_min!(5u32, 2u32 * 3, 4u32));
357 | }
358 |
359 | ```
360 |
361 | ---
362 |
363 | # Variadická rozhraní
364 |
365 | ```rust
366 | macro_rules! calculate {
367 | // The pattern for a single `eval`
368 | (eval $e:expr) => {{
369 | {
370 | let val: usize = $e; // Force types to be integers
371 | println!("{} = {}", stringify!{$e}, val);
372 | }
373 | }};
374 |
375 | // Decompose multiple `eval`s recursively
376 | (eval $e:expr, $(eval $es:expr),+) => {{
377 | calculate! { eval $e }
378 | calculate! { $(eval $es),+ }
379 | }};
380 | }
381 |
382 | fn main() {
383 | calculate! { // Look ma! Variadic `calculate!`!
384 | eval 1 + 2,
385 | eval 3 + 4,
386 | eval (2 * 3) + 1
387 | }
388 | }
389 | ```
390 |
391 | ---
392 |
393 | # Procedurální makra
394 |
395 | 1. funkční
396 | 2. derive
397 | 3. atributová
398 |
399 | ---
400 |
401 | # Funkční
402 |
403 | ```rust
404 | extern crate proc_macro;
405 | use proc_macro::TokenStream;
406 |
407 | #[proc_macro]
408 | pub fn make_answer(_item: TokenStream) -> TokenStream {
409 | "fn answer() -> u32 { 42 }".parse().unwrap()
410 | }
411 | ```
412 |
413 | ---
414 |
415 | # Funkční
416 |
417 | ```rust
418 | extern crate proc_macro_examples;
419 | use proc_macro_examples::make_answer;
420 |
421 | make_answer!();
422 |
423 | fn main() {
424 | println!("{}", answer());
425 | }
426 | ```
427 |
428 | ---
429 |
430 | # Derive
431 |
432 | ```rust
433 | extern crate proc_macro;
434 | use proc_macro::TokenStream;
435 |
436 | #[proc_macro_derive(AnswerFn)]
437 | pub fn derive_answer_fn(_item: TokenStream) -> TokenStream {
438 | "fn answer() -> u32 { 42 }".parse().unwrap()
439 | }
440 | ```
441 |
442 | ---
443 |
444 | # Derive
445 |
446 | ```rust
447 | extern crate proc_macro_examples;
448 | use proc_macro_examples::AnswerFn;
449 |
450 | #[derive(AnswerFn)]
451 | struct Struct;
452 |
453 | fn main() {
454 | assert_eq!(42, answer());
455 | }
456 | ```
457 |
458 | ---
459 |
460 | # Derive trait
461 |
462 | ```rust
463 | #[proc_macro_derive(Trait)]
464 | pub fn derive_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
465 | let input = parse_macro_input!(input as DeriveInput);
466 |
467 | let name = input.ident;
468 |
469 | let expanded = quote! {
470 | impl Trait for #name {
471 | fn print(&self) -> usize {
472 | println!("{}","hello from #name")
473 | }
474 | }
475 | };
476 |
477 | proc_macro::TokenStream::from(expanded)
478 | }
479 | ```
480 |
481 | ---
482 |
483 | # Atribut
484 |
485 | ```rust
486 | // my-macro/src/lib.rs
487 |
488 | #[proc_macro_attribute]
489 | pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream {
490 | println!("attr: \"{}\"", attr.to_string());
491 | println!("item: \"{}\"", item.to_string());
492 | item
493 | }
494 | ```
495 |
496 | ---
497 |
498 | # Atribut
499 |
500 | ```rust
501 | // src/lib.rs
502 | extern crate my_macro;
503 |
504 | use my_macro::show_streams;
505 |
506 | // Example: Basic function
507 | #[show_streams]
508 | fn invoke1() {}
509 | // out: attr: ""
510 | // out: item: "fn invoke1() { }"
511 |
512 | // Example: Attribute with input
513 | #[show_streams(bar)]
514 | fn invoke2() {}
515 | // out: attr: "bar"
516 | // out: item: "fn invoke2() {}"
517 |
518 | ```
519 |
520 | ---
521 |
522 | # Dotazy?
523 |
524 | ---
525 |
526 | # Děkuji za pozornost
527 |
528 |
--------------------------------------------------------------------------------
/2024/slides/14-bonus.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Bonus
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Programování v Rustu
10 |
11 | ---
12 |
13 | # Obsah
14 |
15 | 1. Deployment aplikací
16 | 2. OAuth, OpenID
17 | 3. Zero-Trust architektury
18 | 4. Makra
19 |
20 | ---
21 |
22 | # Deployment aplikací
23 |
24 | ---
25 |
26 | # Produkční build
27 |
28 | Produkční build spustíme příkazem
29 |
30 | ```bash
31 | cargo build --release
32 | ```
33 |
34 | Pro produkční build je vhodné nastavit úroveň optimalizací v Cargo.toml. Znamená to ovšem delší kompilační čas.
35 |
36 | ```toml
37 | [profile.dev]
38 | opt-level = 0
39 |
40 | [profile.release]
41 | opt-level = 3
42 | ```
43 |
44 | ---
45 |
46 | # Nasazení webové služby
47 |
48 | 1. Na cílový server nahrajeme binárku aplikaci.
49 | 2. Registrujeme jako službu/démona nebo jiným způsobem zařídíme spuštění po startu systému.
50 | 3. V případě potřeby nasadíme proxy.
51 |
52 | ---
53 |
54 | # Nahrátí na cílový server
55 |
56 | Záleží na možnostech, které máme. Pokud nasazujeme u cloud providera, tak využijeme jeho možností (zip package, teracopy). Pokud fungujeme na vlastním serveru, tak máme možnosti přes FTP, git apod.
57 |
58 | ---
59 |
60 | # Kontejnery
61 |
62 | Jako lepší scénář se dnes nabízí kontejnerizace. Pro menší deployment jde použít Docker Swarm. Lepší alternativou (ale vyšší učící křivkou) je Kubernetes. Pro jednoduché deploymenty existuje k3s v lokální kombinaci s nerdctl.
63 |
64 | Větší deploymenty jde realizovat ve vlastním k8s clusteru nebo využít cloud-hosted.
65 |
66 | ---
67 |
68 | # CD pipeline
69 |
70 | Ideální je řešit nasazení přes continous delivery. Oblíbené se staly Github Actions.
71 |
72 | ---
73 |
74 | # OAuth, OpenID
75 |
76 | ---
77 |
78 | # OAuth
79 |
80 | Zavádí autorizační vrstvu oddělující klienta a resource.
81 |
82 | ---
83 |
84 | # Role
85 |
86 | resource owner - uživatel, který může získat přístup
87 | resource server - má data, která jsou chráněná proti neoprávněnému přístupu
88 | klient - klientská aplikace, která získává data třeba přes REST
89 | auth server - server starající se o identitu
90 |
91 | ---
92 |
93 | # Flow
94 |
95 | 1. Klient pošle auth request na vlastníka (nebo ideálně na auth server)
96 | 2. Klient dostává grant
97 | 3. Klient posílá auth grant na auth server
98 | 4. Klient dostává access token
99 | 5. Klient posílá access token na resource
100 | 6. Klient dostává data
101 |
102 | ---
103 |
104 | # 4 druhy flow
105 |
106 | 1. autorizační kód
107 | 2. implicit
108 | 3. resource owner password credentials
109 | 4. client credentials
110 |
111 | ---
112 |
113 | # Implicit flow
114 |
115 | Zjednodušené flow určené primárně pro webový prohlížeč. Je vynechána část pro získání autorizačního kódu/grantu. Klient získává přímo access token.
116 | Pozn. neautentizuje klienta. Identita klienta může být ověřena podle return URL.
117 |
118 | ---
119 |
120 | # Access token
121 |
122 | = kredenšly pro přístup k resourcu
123 |
124 | Je to string obsahující scope a délku trvání přístupu. Je nečitelný klientem.
125 | Může být restriktivnější než grant.
126 |
127 | ---
128 |
129 | # Refresh token
130 |
131 | Optional. Slouží pro obnovení expirovaného access tokenu.
132 | Nikdy se neposílá na resource server.
133 |
134 | ---
135 |
136 | # Redirect z auth server
137 |
138 | Klient je pro získání přístupu přesměrován na auth server. Ten má pri klienta zadanou return URL (je třeba registrovat předem všechny možné). Přesměrování je řešeno primárně přes 302 Found s uri v Location header.
139 |
140 | ---
141 |
142 | # Registrace klienta
143 |
144 | = typ klienta (confidential nebo public) + redirect url + další informace
145 |
146 | ---
147 |
148 | # OpenID Connect 1.0
149 |
150 | Identita nad OAuth 2.0. OID dává autentizaci a OAuth autorizaci.
151 | Informace je vrácena jako JWT.
152 |
153 | ---
154 |
155 | # Flow
156 |
157 | 1. Relying party pošle auth request na OpenID provider
158 | 2. User provede authn a authz na provideru
159 | 3. Relying party dostává AuthN response
160 | 4. Relying party posílá user info request na OpenID Provider
161 | 5. Relying party dostává user info.
162 |
163 | ---
164 |
165 | # Request
166 |
167 | scope = openid
168 | respose_type
169 | client_id
170 | redirect_uri
171 | state
172 |
173 | ---
174 |
175 | # Zero Trust Architectures
176 |
177 | ---
178 |
179 | Korporátní sítě se dlouho spoléhali na bezpečnost na úrovni perimetru. Ve vnitřní síti nebylo často žádné zabezpečení.
180 |
181 | Komunikace uvnitř sítě byla nešifrovaná.
182 |
183 | ---
184 |
185 | # Tradiční zabezpečení infrastruktury
186 |
187 | Zabezpečení na perimetru (tj. firewall), přičemž vnitřní komunikace je považována za důvěryhodnou.
188 | Pevné IP adresy a hardware pro určité aplikace.
189 | Identita založená na IP adrese.
190 | Služby jsou provozovány na známém, očekávaném místě.
191 | Specifické bezpečnostní požadavky zabudované do každé aplikace a vynucované samostatně.
192 | Omezená omezení způsobu vytváření a kontroly služeb.
193 | Omezený dohled nad bezpečnostními složkami.
194 | Složité a občasné nasazování.
195 | Appky jsou obvykle nasazovány jako virtuální počítače nebo na fyzické hosty a k zajištění izolace používají fyzický stroj nebo hypervizor.
196 |
197 | ---
198 |
199 | # Zásady bezpečnosti v cloudovém prostředí
200 |
201 | Ochrana sítě na okraji
202 | Žádná inherentní vzájemná důvěra mezi službami
203 | Důvěryhodné stroje, na kterých běží kód se známým původem
204 | Kontrolní body pro konzistentní prosazování zásad napříč službami
205 | Jednoduché, automatizované a standardizované zavádění změn
206 | Izolace mezi workloady sdílejícími operační systém
207 |
208 | ---
209 |
210 | # Makra
211 |
212 | ---
213 |
214 | ```rust
215 | // This is a simple macro named `say_hello`.
216 | macro_rules! say_hello {
217 | // `()` indicates that the macro takes no argument.
218 | () => {
219 | // The macro will expand into the contents of this block.
220 | println!("Hello!");
221 | };
222 | }
223 |
224 | fn main() {
225 | // This call will expand into `println!("Hello");`
226 | say_hello!()
227 | }
228 |
229 | ```
230 |
231 | ---
232 |
233 | # Designator
234 |
235 | ```rust
236 | macro_rules! create_function {
237 | // This macro takes an argument of designator `ident` and
238 | // creates a function named `$func_name`.
239 | // The `ident` designator is used for variable/function names.
240 | ($func_name:ident) => {
241 | fn $func_name() {
242 | // The `stringify!` macro converts an `ident` into a string.
243 | println!("You called {:?}()",
244 | stringify!($func_name));
245 | }
246 | };
247 | }
248 |
249 | // Create functions named `foo` and `bar` with the above macro.
250 | create_function!(foo);
251 | create_function!(bar);
252 |
253 | fn main() {
254 | foo();
255 | bar();
256 | }
257 |
258 | ```
259 |
260 | ---
261 |
262 | # Designator
263 |
264 | ```rust
265 | macro_rules! print_result {
266 | // This macro takes an expression of type `expr` and prints
267 | // it as a string along with its result.
268 | // The `expr` designator is used for expressions.
269 | ($expression:expr) => {
270 | // `stringify!` will convert the expression *as it is* into a string.
271 | println!("{:?} = {:?}",
272 | stringify!($expression),
273 | $expression);
274 | };
275 | }
276 |
277 | fn main() {
278 | print_result!(1u32 + 1);
279 |
280 | // Recall that blocks are expressions too!
281 | print_result!({
282 | let x = 1u32;
283 |
284 | x * x + 2 * x - 1
285 | });
286 | }
287 |
288 | ```
289 |
290 | ---
291 |
292 | # Možnosti
293 |
294 | block
295 | expr is used for expressions
296 | ident is used for variable/function names
297 | item
298 | literal is used for literal constants
299 | pat (pattern)
300 | path
301 | stmt (statement)
302 | tt (token tree)
303 | ty (type)
304 | vis (visibility qualifier)
305 |
306 | ---
307 |
308 | # Overload
309 |
310 | ```rust
311 | // `test!` will compare `$left` and `$right`
312 | // in different ways depending on how you invoke it:
313 | macro_rules! test {
314 | // Arguments don't need to be separated by a comma.
315 | // Any template can be used!
316 | ($left:expr; and $right:expr) => {
317 | println!("{:?} and {:?} is {:?}",
318 | stringify!($left),
319 | stringify!($right),
320 | $left && $right)
321 | };
322 | // ^ each arm must end with a semicolon.
323 | ($left:expr; or $right:expr) => {
324 | println!("{:?} or {:?} is {:?}",
325 | stringify!($left),
326 | stringify!($right),
327 | $left || $right)
328 | };
329 | }
330 |
331 | fn main() {
332 | test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
333 | test!(true; or false);
334 | }
335 | ```
336 |
337 | ---
338 |
339 | # Repeat
340 |
341 | ```rust
342 | // `find_min!` will calculate the minimum of any number of arguments.
343 | macro_rules! find_min {
344 | // Base case:
345 | ($x:expr) => ($x);
346 | // `$x` followed by at least one `$y,`
347 | ($x:expr, $($y:expr),+) => (
348 | // Call `find_min!` on the tail `$y`
349 | std::cmp::min($x, find_min!($($y),+))
350 | )
351 | }
352 |
353 | fn main() {
354 | println!("{}", find_min!(1u32));
355 | println!("{}", find_min!(1u32 + 2, 2u32));
356 | println!("{}", find_min!(5u32, 2u32 * 3, 4u32));
357 | }
358 |
359 | ```
360 |
361 | ---
362 |
363 | # Variadická rozhraní
364 |
365 | ```rust
366 | macro_rules! calculate {
367 | // The pattern for a single `eval`
368 | (eval $e:expr) => {{
369 | {
370 | let val: usize = $e; // Force types to be integers
371 | println!("{} = {}", stringify!{$e}, val);
372 | }
373 | }};
374 |
375 | // Decompose multiple `eval`s recursively
376 | (eval $e:expr, $(eval $es:expr),+) => {{
377 | calculate! { eval $e }
378 | calculate! { $(eval $es),+ }
379 | }};
380 | }
381 |
382 | fn main() {
383 | calculate! { // Look ma! Variadic `calculate!`!
384 | eval 1 + 2,
385 | eval 3 + 4,
386 | eval (2 * 3) + 1
387 | }
388 | }
389 | ```
390 |
391 | ---
392 |
393 | # Procedurální makra
394 |
395 | 1. funkční
396 | 2. derive
397 | 3. atributová
398 |
399 | ---
400 |
401 | # Funkční
402 |
403 | ```rust
404 | extern crate proc_macro;
405 | use proc_macro::TokenStream;
406 |
407 | #[proc_macro]
408 | pub fn make_answer(_item: TokenStream) -> TokenStream {
409 | "fn answer() -> u32 { 42 }".parse().unwrap()
410 | }
411 | ```
412 |
413 | ---
414 |
415 | # Funkční
416 |
417 | ```rust
418 | extern crate proc_macro_examples;
419 | use proc_macro_examples::make_answer;
420 |
421 | make_answer!();
422 |
423 | fn main() {
424 | println!("{}", answer());
425 | }
426 | ```
427 |
428 | ---
429 |
430 | # Derive
431 |
432 | ```rust
433 | extern crate proc_macro;
434 | use proc_macro::TokenStream;
435 |
436 | #[proc_macro_derive(AnswerFn)]
437 | pub fn derive_answer_fn(_item: TokenStream) -> TokenStream {
438 | "fn answer() -> u32 { 42 }".parse().unwrap()
439 | }
440 | ```
441 |
442 | ---
443 |
444 | # Derive
445 |
446 | ```rust
447 | extern crate proc_macro_examples;
448 | use proc_macro_examples::AnswerFn;
449 |
450 | #[derive(AnswerFn)]
451 | struct Struct;
452 |
453 | fn main() {
454 | assert_eq!(42, answer());
455 | }
456 | ```
457 |
458 | ---
459 |
460 | # Derive trait
461 |
462 | ```rust
463 | #[proc_macro_derive(Trait)]
464 | pub fn derive_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
465 | let input = parse_macro_input!(input as DeriveInput);
466 |
467 | let name = input.ident;
468 |
469 | let expanded = quote! {
470 | impl Trait for #name {
471 | fn print(&self) -> usize {
472 | println!("{}","hello from #name")
473 | }
474 | }
475 | };
476 |
477 | proc_macro::TokenStream::from(expanded)
478 | }
479 | ```
480 |
481 | ---
482 |
483 | # Atribut
484 |
485 | ```rust
486 | // my-macro/src/lib.rs
487 |
488 | #[proc_macro_attribute]
489 | pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream {
490 | println!("attr: \"{}\"", attr.to_string());
491 | println!("item: \"{}\"", item.to_string());
492 | item
493 | }
494 | ```
495 |
496 | ---
497 |
498 | # Atribut
499 |
500 | ```rust
501 | // src/lib.rs
502 | extern crate my_macro;
503 |
504 | use my_macro::show_streams;
505 |
506 | // Example: Basic function
507 | #[show_streams]
508 | fn invoke1() {}
509 | // out: attr: ""
510 | // out: item: "fn invoke1() { }"
511 |
512 | // Example: Attribute with input
513 | #[show_streams(bar)]
514 | fn invoke2() {}
515 | // out: attr: "bar"
516 | // out: item: "fn invoke2() {}"
517 |
518 | ```
519 |
520 | ---
521 |
522 | # Dotazy?
523 |
524 | ---
525 |
526 | # Děkuji za pozornost
527 |
528 |
--------------------------------------------------------------------------------
/slides/13-graphql.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust GraphQL
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Programování v Rustu
10 |
11 | ---
12 |
13 | # Obsah
14 |
15 | 1. Úvod do GraphQL
16 | 2. Dotazy
17 | 3. Mutace
18 | 4. Architektury s GraphQL
19 | 5. Juniper
20 | 6. Integrace do Actix
21 |
22 | ---
23 |
24 | # GraphQL
25 |
26 | ---
27 |
28 | # Field
29 |
30 | Field je jedna properta objektu. Z důvodu ušetření velikosti musíme vyjmenovat všechny fieldy, které chceme získat.
31 |
32 | ```graphql
33 | {
34 | hero {
35 | name
36 | }
37 | }
38 | ```
39 |
40 | ---
41 |
42 | # Argumenty
43 |
44 | Objekty můžou mít argumenty, které zužují výběr. Mohou být různého typu nebo i výčtu.
45 |
46 | ```graphql
47 | {
48 | human(id: "1000") {
49 | name
50 | height(unit: FOOT)
51 | }
52 | }
53 | ```
54 |
55 | ---
56 |
57 | # Alias
58 |
59 | V případě, kdy potřebujeme získat jeden objekt s různými filtry je vhodné upravit pojmenování pomocí aliasu.
60 |
61 | ```graphql
62 | {
63 | empireHero: hero(episode: EMPIRE) {
64 | name
65 | }
66 | jediHero: hero(episode: JEDI) {
67 | name
68 | }
69 | }
70 | ```
71 |
72 | ---
73 |
74 | # Fragment
75 |
76 | Pro opakující se položky vyžijeme fragment.
77 |
78 | ```graphql
79 | {
80 | leftComparison: hero(episode: EMPIRE) {
81 | ...comparisonFields
82 | }
83 | rightComparison: hero(episode: JEDI) {
84 | ...comparisonFields
85 | }
86 | }
87 |
88 | fragment comparisonFields on Character {
89 | name
90 | appearsIn
91 | friends {
92 | name
93 | }
94 | }
95 | ```
96 |
97 | ---
98 |
99 | # Struktura dotazu
100 |
101 | Dotaz je pojmenovaný. Jednotlivé proměnné jsou v dotazu otypovány a samotné hodnoty předány v samostatném objektu mimo dotaz.
102 |
103 | ```grapqhl
104 | query HeroNameAndFriends($episode: Episode) {
105 | hero(episode: $episode) {
106 | name
107 | friends {
108 | name
109 | }
110 | }
111 | }
112 |
113 | {
114 | "episode": "JEDI"
115 | }
116 | ```
117 |
118 | ---
119 |
120 | # Výchozí hodnoty
121 |
122 | V případě kdy hodnota proměnné zadaná, může být doplněna výchozí hodnotou.
123 |
124 | ```graphql
125 | query HeroNameAndFriends($episode: Episode = JEDI) {
126 | hero(episode: $episode) {
127 | name
128 | friends {
129 | name
130 | }
131 | }
132 | }
133 | ```
134 |
135 | ---
136 |
137 | # Direktivy
138 |
139 | ```graphql
140 | query Hero($episode: Episode, $withFriends: Boolean!) {
141 | hero(episode: $episode) {
142 | name
143 | friends @include(if: $withFriends) {
144 | name
145 | }
146 | }
147 | }
148 |
149 | {
150 | "episode": "JEDI",
151 | "withFriends": false
152 | }
153 | ```
154 |
155 | ---
156 |
157 | # Mutace
158 |
159 | Mutace slouží k modifikaci položek, vytvoření, mazání apod.
160 |
161 | ```graphql
162 | mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
163 | createReview(episode: $ep, review: $review) {
164 | stars
165 | commentary
166 | }
167 | }
168 |
169 | {
170 | "ep": "JEDI",
171 | "review": {
172 | "stars": 5,
173 | "commentary": "This is a great movie!"
174 | }
175 | }
176 | ```
177 |
178 | ---
179 |
180 | # Architektury s GQL
181 |
182 | ---
183 |
184 | # GraphQL nad REST
185 |
186 | @startuml
187 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
188 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
189 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
190 |
191 | Container(fe, "Frontend", "")
192 |
193 | Container(gql, "Vrstva GraphQL", "Rust, Juniper")
194 |
195 | Container(s1, "REST služba 1", "Rust, Actix")
196 | Container(s2, "REST služba 2", "Node.js, Express")
197 | Container(s3, "REST služba 3", "C#, ASP.NET")
198 |
199 | Container(db1, "SQL DB 1", "Postgres")
200 | Container(db2, "SQL DB 2", "Postgres")
201 | Container(db3, "noSQL DB", "Mongo")
202 |
203 | Rel_D(fe, gql, "")
204 |
205 | Rel_D(gql, s1, "")
206 | Rel_D(gql, s2, "")
207 | Rel_D(gql, s3, "")
208 |
209 | Rel_D(s1, db1, "")
210 | Rel_D(s2, db2, "")
211 | Rel_D(s3, db3, "")
212 |
213 | @enduml
214 |
215 | ---
216 |
217 | # GRPC backend
218 |
219 | @startuml
220 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
221 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
222 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
223 |
224 | Container(fe, "Frontend", "")
225 |
226 | Container(gql, "Vrstva GraphQL", "Rust, Juniper")
227 |
228 | Container(s1, "GRPC služba 1", "Rust, Tonic")
229 | Container(s2, "GRPC služba 2", "C#")
230 | Container(s3, "REST služba 3", "C#, ASP.NET")
231 |
232 | Container(db1, "SQL DB 1", "Postgres")
233 | Container(db2, "SQL DB 2", "Postgres")
234 | Container(db3, "SQL DB 3", "MS SQL")
235 |
236 | Rel_D(fe, gql, "")
237 |
238 | Rel_D(gql, s1, "")
239 | Rel_D(gql, s2, "")
240 | Rel_D(gql, s3, "")
241 |
242 | Rel_D(s1, db1, "")
243 | Rel_D(s2, db2, "")
244 | Rel_D(s3, db3, "")
245 |
246 | @enduml
247 |
248 | ---
249 |
250 | # GraphQL nad databází
251 |
252 | @startuml
253 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
254 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
255 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
256 |
257 | Container(fe, "Frontend", "")
258 |
259 | Container(gql, "GraphQL", "Rust, Juniper")
260 |
261 | Container(db1, "SQL DB 1", "Postgres")
262 |
263 | Rel_D(fe, gql, "")
264 |
265 | Rel_D(gql, db1, "")
266 |
267 | @enduml
268 |
269 | ---
270 |
271 | # Juniper
272 |
273 | ---
274 |
275 | # Závislosti
276 |
277 | ```toml
278 | [dependencies]
279 | juniper = "0.14.2"
280 | ```
281 |
282 | ---
283 |
284 | # Integrace s Actix
285 |
286 | ```rust
287 | use std::io;
288 | use std::sync::Arc;
289 |
290 | use actix_cors::Cors;
291 | use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer};
292 | use juniper::http::graphiql::graphiql_source;
293 | use juniper::http::GraphQLRequest;
294 |
295 | use crate::schema::{create_schema, Schema};
296 | ```
297 |
298 | ---
299 |
300 | # Integrace s Actix
301 |
302 | ```rust
303 | #[actix_web::main]
304 | async fn main() -> io::Result<()> {
305 | std::env::set_var("RUST_LOG", "actix_web=info");
306 | env_logger::init();
307 |
308 | // Create Juniper schema
309 | let schema = std::sync::Arc::new(create_schema());
310 |
311 | // Start http server
312 | HttpServer::new(move || {
313 | App::new()
314 | .data(schema.clone())
315 | .wrap(middleware::Logger::default())
316 | .wrap(
317 | Cors::new()
318 | .allowed_methods(vec!["POST", "GET"])
319 | .supports_credentials()
320 | .max_age(3600)
321 | .finish(),
322 | )
323 | .service(web::resource("/graphql").route(web::post().to(graphql)))
324 | .service(web::resource("/graphiql").route(web::get().to(graphiql)))
325 | })
326 | .bind("127.0.0.1:8080")?
327 | .run()
328 | .await
329 | }
330 | ```
331 |
332 | ---
333 |
334 | # Registrace endpointů
335 |
336 | ```rust
337 | async fn graphiql() -> HttpResponse {
338 | let html = graphiql_source("http://127.0.0.1:8080/graphql");
339 | HttpResponse::Ok()
340 | .content_type("text/html; charset=utf-8")
341 | .body(html)
342 | }
343 |
344 | async fn graphql(
345 | st: web::Data>,
346 | data: web::Json,
347 | ) -> Result {
348 | let user = web::block(move || {
349 | let res = data.execute(&st, &());
350 | Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?)
351 | })
352 | .await?;
353 | Ok(HttpResponse::Ok()
354 | .content_type("application/json")
355 | .body(user))
356 | }
357 | ```
358 |
359 | ---
360 |
361 | # Schéma - výčet
362 |
363 | ```rust
364 | use juniper::FieldResult;
365 | use juniper::RootNode;
366 | use juniper::{GraphQLEnum, GraphQLInputObject, GraphQLObject};
367 |
368 | #[derive(GraphQLEnum)]
369 | enum Episode {
370 | NewHope,
371 | Empire,
372 | Jedi,
373 | }
374 |
375 | ```
376 |
377 | ---
378 |
379 | # Schéma - struktura
380 |
381 | ```rust
382 | use juniper::FieldResult;
383 | use juniper::RootNode;
384 | use juniper::{GraphQLEnum, GraphQLInputObject, GraphQLObject};
385 |
386 | #[derive(GraphQLObject)]
387 | #[graphql(description = "A humanoid creature in the Star Wars universe")]
388 | struct Human {
389 | id: String,
390 | name: String,
391 | appears_in: Vec,
392 | home_planet: String,
393 | }
394 |
395 | ```
396 |
397 | ---
398 |
399 | # Schéma - struktura pro mutaci
400 |
401 | ```rust
402 | use juniper::FieldResult;
403 | use juniper::RootNode;
404 | use juniper::{GraphQLEnum, GraphQLInputObject, GraphQLObject};
405 |
406 | #[derive(GraphQLInputObject)]
407 | #[graphql(description = "A humanoid creature in the Star Wars universe")]
408 | struct NewHuman {
409 | name: String,
410 | appears_in: Vec,
411 | home_planet: String,
412 | }
413 |
414 | ```
415 |
416 | ---
417 |
418 | # Kořen pro dotazy
419 |
420 | ```rust
421 | pub struct QueryRoot;
422 |
423 | #[juniper::object]
424 | impl QueryRoot {
425 | fn human(id: String) -> FieldResult {
426 | Ok(Human {
427 | id: "1234".to_owned(),
428 | name: "Luke".to_owned(),
429 | appears_in: vec![Episode::NewHope],
430 | home_planet: "Mars".to_owned(),
431 | })
432 | }
433 | }
434 | ```
435 |
436 | ---
437 |
438 | # Kořen pro mutace
439 |
440 | ```rust
441 | pub struct MutationRoot;
442 |
443 | #[juniper::object]
444 | impl MutationRoot {
445 | fn create_human(new_human: NewHuman) -> FieldResult {
446 | Ok(Human {
447 | id: "1234".to_owned(),
448 | name: new_human.name,
449 | appears_in: new_human.appears_in,
450 | home_planet: new_human.home_planet,
451 | })
452 | }
453 | }
454 | ```
455 |
456 | ---
457 |
458 | # Vytvoření schéma z kořenů
459 |
460 | ```rust
461 | pub type Schema = RootNode<'static, QueryRoot, MutationRoot>;
462 |
463 | pub fn create_schema() -> Schema {
464 | Schema::new(QueryRoot {}, MutationRoot {})
465 | }
466 | ```
467 |
468 | ---
469 |
470 | # Kontext
471 |
472 | ```rust
473 | pub struct Context {
474 | pub dbpool: Pool,
475 | }
476 |
477 | impl juniper::Context for Context {}
478 |
479 | pub struct QueryRoot;
480 |
481 | #[juniper::graphql_object(Context = Context)]
482 | impl QueryRoot {
483 | #[graphql(description = "List of all users")]
484 | fn users(context: &Context) -> FieldResult> {
485 | let mut conn = context.dbpool.get().unwrap();
486 | let users = conn
487 | .prep_exec("select * from user", ())
488 | .map(|result| {
489 | result
490 | .map(|x| x.unwrap())
491 | .map(|row| {
492 | let (id, name, email) = from_row(row);
493 | User { id, name, email }
494 | })
495 | .collect()
496 | })
497 | .unwrap();
498 | Ok(users)
499 | }
500 | }
501 |
502 | ```
503 |
504 | ---
505 |
506 | # Naplnění kontextu z Actixu
507 |
508 | ```rust
509 | pub async fn graphql(
510 | pool: web::Data,
511 | schema: web::Data,
512 | data: web::Json,
513 | ) -> Result {
514 | let ctx = Context {
515 | dbpool: pool.get_ref().to_owned(),
516 | };
517 |
518 | let res = web::block(move || {
519 | let res = data.execute_sync(&schema, &ctx);
520 | serde_json::to_string(&res)
521 | })
522 | .await
523 | .map_err(Error::from)?;
524 |
525 | Ok(HttpResponse::Ok()
526 | .content_type("application/json")
527 | .body(res))
528 | }
529 |
530 | ```
531 |
532 | ---
533 |
534 | # Playground
535 |
536 | ```rust
537 | use juniper::http::graphiql::graphiql_source;
538 |
539 | pub async fn graphql_playground() -> HttpResponse {
540 | HttpResponse::Ok()
541 | .content_type("text/html; charset=utf-8")
542 | .body(graphiql_source("/graphql", None))
543 | }
544 |
545 | // register to be called in actix builder
546 | pub fn register(config: &mut web::ServiceConfig) {
547 | config
548 | .data(create_schema())
549 | .route("/graphql", web::post().to(graphql))
550 | .route("/graphiql", web::get().to(graphql_playground));
551 | }
552 | ```
553 |
554 | ---
555 |
556 | # Like this
557 |
558 | ```rust
559 | #[actix_web::main]
560 | async fn main() -> std::io::Result<()> {
561 | dotenv::dotenv().ok();
562 | std::env::set_var("RUST_LOG", "actix_web=info,info");
563 | env_logger::init();
564 | let pool = get_db_pool();
565 |
566 | HttpServer::new(move || {
567 | App::new()
568 | .data(pool.clone())
569 | .wrap(middleware::Logger::default())
570 | .configure(register)
571 | .default_service(web::to(|| async { "404" }))
572 | })
573 | .bind("127.0.0.1:8080")?
574 | .run()
575 | .await
576 | }
577 | ```
578 |
579 | ---
580 |
581 | # Dotazy?
582 |
583 | ---
584 |
585 | # Děkuji za pozornost
586 |
587 |
--------------------------------------------------------------------------------
/2024/slides/13-graphql.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust GraphQL
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Programování v Rustu
10 |
11 | ---
12 |
13 | # Obsah
14 |
15 | 1. Úvod do GraphQL
16 | 2. Dotazy
17 | 3. Mutace
18 | 4. Architektury s GraphQL
19 | 5. Juniper
20 | 6. Integrace do Actix
21 |
22 | ---
23 |
24 | # GraphQL
25 |
26 | ---
27 |
28 | # Field
29 |
30 | Field je jedna properta objektu. Z důvodu ušetření velikosti musíme vyjmenovat všechny fieldy, které chceme získat.
31 |
32 | ```graphql
33 | {
34 | hero {
35 | name
36 | }
37 | }
38 | ```
39 |
40 | ---
41 |
42 | # Argumenty
43 |
44 | Objekty můžou mít argumenty, které zužují výběr. Mohou být různého typu nebo i výčtu.
45 |
46 | ```graphql
47 | {
48 | human(id: "1000") {
49 | name
50 | height(unit: FOOT)
51 | }
52 | }
53 | ```
54 |
55 | ---
56 |
57 | # Alias
58 |
59 | V případě, kdy potřebujeme získat jeden objekt s různými filtry je vhodné upravit pojmenování pomocí aliasu.
60 |
61 | ```graphql
62 | {
63 | empireHero: hero(episode: EMPIRE) {
64 | name
65 | }
66 | jediHero: hero(episode: JEDI) {
67 | name
68 | }
69 | }
70 | ```
71 |
72 | ---
73 |
74 | # Fragment
75 |
76 | Pro opakující se položky vyžijeme fragment.
77 |
78 | ```graphql
79 | {
80 | leftComparison: hero(episode: EMPIRE) {
81 | ...comparisonFields
82 | }
83 | rightComparison: hero(episode: JEDI) {
84 | ...comparisonFields
85 | }
86 | }
87 |
88 | fragment comparisonFields on Character {
89 | name
90 | appearsIn
91 | friends {
92 | name
93 | }
94 | }
95 | ```
96 |
97 | ---
98 |
99 | # Struktura dotazu
100 |
101 | Dotaz je pojmenovaný. Jednotlivé proměnné jsou v dotazu otypovány a samotné hodnoty předány v samostatném objektu mimo dotaz.
102 |
103 | ```grapqhl
104 | query HeroNameAndFriends($episode: Episode) {
105 | hero(episode: $episode) {
106 | name
107 | friends {
108 | name
109 | }
110 | }
111 | }
112 |
113 | {
114 | "episode": "JEDI"
115 | }
116 | ```
117 |
118 | ---
119 |
120 | # Výchozí hodnoty
121 |
122 | V případě kdy hodnota proměnné zadaná, může být doplněna výchozí hodnotou.
123 |
124 | ```graphql
125 | query HeroNameAndFriends($episode: Episode = JEDI) {
126 | hero(episode: $episode) {
127 | name
128 | friends {
129 | name
130 | }
131 | }
132 | }
133 | ```
134 |
135 | ---
136 |
137 | # Direktivy
138 |
139 | ```graphql
140 | query Hero($episode: Episode, $withFriends: Boolean!) {
141 | hero(episode: $episode) {
142 | name
143 | friends @include(if: $withFriends) {
144 | name
145 | }
146 | }
147 | }
148 |
149 | {
150 | "episode": "JEDI",
151 | "withFriends": false
152 | }
153 | ```
154 |
155 | ---
156 |
157 | # Mutace
158 |
159 | Mutace slouží k modifikaci položek, vytvoření, mazání apod.
160 |
161 | ```graphql
162 | mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
163 | createReview(episode: $ep, review: $review) {
164 | stars
165 | commentary
166 | }
167 | }
168 |
169 | {
170 | "ep": "JEDI",
171 | "review": {
172 | "stars": 5,
173 | "commentary": "This is a great movie!"
174 | }
175 | }
176 | ```
177 |
178 | ---
179 |
180 | # Architektury s GQL
181 |
182 | ---
183 |
184 | # GraphQL nad REST
185 |
186 | @startuml
187 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
188 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
189 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
190 |
191 | Container(fe, "Frontend", "")
192 |
193 | Container(gql, "Vrstva GraphQL", "Rust, Juniper")
194 |
195 | Container(s1, "REST služba 1", "Rust, Actix")
196 | Container(s2, "REST služba 2", "Node.js, Express")
197 | Container(s3, "REST služba 3", "C#, ASP.NET")
198 |
199 | Container(db1, "SQL DB 1", "Postgres")
200 | Container(db2, "SQL DB 2", "Postgres")
201 | Container(db3, "noSQL DB", "Mongo")
202 |
203 | Rel_D(fe, gql, "")
204 |
205 | Rel_D(gql, s1, "")
206 | Rel_D(gql, s2, "")
207 | Rel_D(gql, s3, "")
208 |
209 | Rel_D(s1, db1, "")
210 | Rel_D(s2, db2, "")
211 | Rel_D(s3, db3, "")
212 |
213 | @enduml
214 |
215 | ---
216 |
217 | # GRPC backend
218 |
219 | @startuml
220 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
221 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
222 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
223 |
224 | Container(fe, "Frontend", "")
225 |
226 | Container(gql, "Vrstva GraphQL", "Rust, Juniper")
227 |
228 | Container(s1, "GRPC služba 1", "Rust, Tonic")
229 | Container(s2, "GRPC služba 2", "C#")
230 | Container(s3, "REST služba 3", "C#, ASP.NET")
231 |
232 | Container(db1, "SQL DB 1", "Postgres")
233 | Container(db2, "SQL DB 2", "Postgres")
234 | Container(db3, "SQL DB 3", "MS SQL")
235 |
236 | Rel_D(fe, gql, "")
237 |
238 | Rel_D(gql, s1, "")
239 | Rel_D(gql, s2, "")
240 | Rel_D(gql, s3, "")
241 |
242 | Rel_D(s1, db1, "")
243 | Rel_D(s2, db2, "")
244 | Rel_D(s3, db3, "")
245 |
246 | @enduml
247 |
248 | ---
249 |
250 | # GraphQL nad databází
251 |
252 | @startuml
253 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
254 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
255 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
256 |
257 | Container(fe, "Frontend", "")
258 |
259 | Container(gql, "GraphQL", "Rust, Juniper")
260 |
261 | Container(db1, "SQL DB 1", "Postgres")
262 |
263 | Rel_D(fe, gql, "")
264 |
265 | Rel_D(gql, db1, "")
266 |
267 | @enduml
268 |
269 | ---
270 |
271 | # Juniper
272 |
273 | ---
274 |
275 | # Závislosti
276 |
277 | ```toml
278 | [dependencies]
279 | juniper = "0.14.2"
280 | ```
281 |
282 | ---
283 |
284 | # Integrace s Actix
285 |
286 | ```rust
287 | use std::io;
288 | use std::sync::Arc;
289 |
290 | use actix_cors::Cors;
291 | use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer};
292 | use juniper::http::graphiql::graphiql_source;
293 | use juniper::http::GraphQLRequest;
294 |
295 | use crate::schema::{create_schema, Schema};
296 | ```
297 |
298 | ---
299 |
300 | # Integrace s Actix
301 |
302 | ```rust
303 | #[actix_web::main]
304 | async fn main() -> io::Result<()> {
305 | std::env::set_var("RUST_LOG", "actix_web=info");
306 | env_logger::init();
307 |
308 | // Create Juniper schema
309 | let schema = std::sync::Arc::new(create_schema());
310 |
311 | // Start http server
312 | HttpServer::new(move || {
313 | App::new()
314 | .data(schema.clone())
315 | .wrap(middleware::Logger::default())
316 | .wrap(
317 | Cors::new()
318 | .allowed_methods(vec!["POST", "GET"])
319 | .supports_credentials()
320 | .max_age(3600)
321 | .finish(),
322 | )
323 | .service(web::resource("/graphql").route(web::post().to(graphql)))
324 | .service(web::resource("/graphiql").route(web::get().to(graphiql)))
325 | })
326 | .bind("127.0.0.1:8080")?
327 | .run()
328 | .await
329 | }
330 | ```
331 |
332 | ---
333 |
334 | # Registrace endpointů
335 |
336 | ```rust
337 | async fn graphiql() -> HttpResponse {
338 | let html = graphiql_source("http://127.0.0.1:8080/graphql");
339 | HttpResponse::Ok()
340 | .content_type("text/html; charset=utf-8")
341 | .body(html)
342 | }
343 |
344 | async fn graphql(
345 | st: web::Data>,
346 | data: web::Json,
347 | ) -> Result {
348 | let user = web::block(move || {
349 | let res = data.execute(&st, &());
350 | Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?)
351 | })
352 | .await?;
353 | Ok(HttpResponse::Ok()
354 | .content_type("application/json")
355 | .body(user))
356 | }
357 | ```
358 |
359 | ---
360 |
361 | # Schéma - výčet
362 |
363 | ```rust
364 | use juniper::FieldResult;
365 | use juniper::RootNode;
366 | use juniper::{GraphQLEnum, GraphQLInputObject, GraphQLObject};
367 |
368 | #[derive(GraphQLEnum)]
369 | enum Episode {
370 | NewHope,
371 | Empire,
372 | Jedi,
373 | }
374 |
375 | ```
376 |
377 | ---
378 |
379 | # Schéma - struktura
380 |
381 | ```rust
382 | use juniper::FieldResult;
383 | use juniper::RootNode;
384 | use juniper::{GraphQLEnum, GraphQLInputObject, GraphQLObject};
385 |
386 | #[derive(GraphQLObject)]
387 | #[graphql(description = "A humanoid creature in the Star Wars universe")]
388 | struct Human {
389 | id: String,
390 | name: String,
391 | appears_in: Vec,
392 | home_planet: String,
393 | }
394 |
395 | ```
396 |
397 | ---
398 |
399 | # Schéma - struktura pro mutaci
400 |
401 | ```rust
402 | use juniper::FieldResult;
403 | use juniper::RootNode;
404 | use juniper::{GraphQLEnum, GraphQLInputObject, GraphQLObject};
405 |
406 | #[derive(GraphQLInputObject)]
407 | #[graphql(description = "A humanoid creature in the Star Wars universe")]
408 | struct NewHuman {
409 | name: String,
410 | appears_in: Vec,
411 | home_planet: String,
412 | }
413 |
414 | ```
415 |
416 | ---
417 |
418 | # Kořen pro dotazy
419 |
420 | ```rust
421 | pub struct QueryRoot;
422 |
423 | #[juniper::object]
424 | impl QueryRoot {
425 | fn human(id: String) -> FieldResult {
426 | Ok(Human {
427 | id: "1234".to_owned(),
428 | name: "Luke".to_owned(),
429 | appears_in: vec![Episode::NewHope],
430 | home_planet: "Mars".to_owned(),
431 | })
432 | }
433 | }
434 | ```
435 |
436 | ---
437 |
438 | # Kořen pro mutace
439 |
440 | ```rust
441 | pub struct MutationRoot;
442 |
443 | #[juniper::object]
444 | impl MutationRoot {
445 | fn create_human(new_human: NewHuman) -> FieldResult {
446 | Ok(Human {
447 | id: "1234".to_owned(),
448 | name: new_human.name,
449 | appears_in: new_human.appears_in,
450 | home_planet: new_human.home_planet,
451 | })
452 | }
453 | }
454 | ```
455 |
456 | ---
457 |
458 | # Vytvoření schéma z kořenů
459 |
460 | ```rust
461 | pub type Schema = RootNode<'static, QueryRoot, MutationRoot>;
462 |
463 | pub fn create_schema() -> Schema {
464 | Schema::new(QueryRoot {}, MutationRoot {})
465 | }
466 | ```
467 |
468 | ---
469 |
470 | # Kontext
471 |
472 | ```rust
473 | pub struct Context {
474 | pub dbpool: Pool,
475 | }
476 |
477 | impl juniper::Context for Context {}
478 |
479 | pub struct QueryRoot;
480 |
481 | #[juniper::graphql_object(Context = Context)]
482 | impl QueryRoot {
483 | #[graphql(description = "List of all users")]
484 | fn users(context: &Context) -> FieldResult> {
485 | let mut conn = context.dbpool.get().unwrap();
486 | let users = conn
487 | .prep_exec("select * from user", ())
488 | .map(|result| {
489 | result
490 | .map(|x| x.unwrap())
491 | .map(|row| {
492 | let (id, name, email) = from_row(row);
493 | User { id, name, email }
494 | })
495 | .collect()
496 | })
497 | .unwrap();
498 | Ok(users)
499 | }
500 | }
501 |
502 | ```
503 |
504 | ---
505 |
506 | # Naplnění kontextu z Actixu
507 |
508 | ```rust
509 | pub async fn graphql(
510 | pool: web::Data,
511 | schema: web::Data,
512 | data: web::Json,
513 | ) -> Result {
514 | let ctx = Context {
515 | dbpool: pool.get_ref().to_owned(),
516 | };
517 |
518 | let res = web::block(move || {
519 | let res = data.execute_sync(&schema, &ctx);
520 | serde_json::to_string(&res)
521 | })
522 | .await
523 | .map_err(Error::from)?;
524 |
525 | Ok(HttpResponse::Ok()
526 | .content_type("application/json")
527 | .body(res))
528 | }
529 |
530 | ```
531 |
532 | ---
533 |
534 | # Playground
535 |
536 | ```rust
537 | use juniper::http::graphiql::graphiql_source;
538 |
539 | pub async fn graphql_playground() -> HttpResponse {
540 | HttpResponse::Ok()
541 | .content_type("text/html; charset=utf-8")
542 | .body(graphiql_source("/graphql", None))
543 | }
544 |
545 | // register to be called in actix builder
546 | pub fn register(config: &mut web::ServiceConfig) {
547 | config
548 | .data(create_schema())
549 | .route("/graphql", web::post().to(graphql))
550 | .route("/graphiql", web::get().to(graphql_playground));
551 | }
552 | ```
553 |
554 | ---
555 |
556 | # Like this
557 |
558 | ```rust
559 | #[actix_web::main]
560 | async fn main() -> std::io::Result<()> {
561 | dotenv::dotenv().ok();
562 | std::env::set_var("RUST_LOG", "actix_web=info,info");
563 | env_logger::init();
564 | let pool = get_db_pool();
565 |
566 | HttpServer::new(move || {
567 | App::new()
568 | .data(pool.clone())
569 | .wrap(middleware::Logger::default())
570 | .configure(register)
571 | .default_service(web::to(|| async { "404" }))
572 | })
573 | .bind("127.0.0.1:8080")?
574 | .run()
575 | .await
576 | }
577 | ```
578 |
579 | ---
580 |
581 | # Dotazy?
582 |
583 | ---
584 |
585 | # Děkuji za pozornost
586 |
587 |
--------------------------------------------------------------------------------
/slides/10-2023-actix-htmx.md:
--------------------------------------------------------------------------------
1 | ---
2 | marp: true
3 | title: PV281 Programming in Rust
4 | description: Programming in Rust Actix & HTMX
5 | theme: rust
6 | paginate: true
7 | ---
8 | 
9 | # PV281: Programování v Rustu
10 |
11 |
12 | ---
13 |
14 | # Obsah
15 |
16 | - HTMX
17 | - Askama rozšíření
18 | - Actix Cookie
19 | - Actix Session
20 |
21 | ---
22 |
23 | # AJAX (Asynchronous Javascript and XML)
24 |
25 | Pojďme se podívat 10 let zpátky:
26 | - místo celé stránky posíláme pouze vykreslené části
27 | - není třeba překreslit celou stránku
28 |
29 | ---
30 |
31 | # Kdy se mi to bude hodit?
32 |
33 | ---
34 |
35 | # Instalace přes npm
36 |
37 | ```sh
38 | npm install htmx.org
39 | ```
40 |
41 | následně přidat do stránky `node_modules/htmx.org/dist/htmx.js`
42 |
43 | ---
44 |
45 | # Instalace přes CDN (unpkg)
46 |
47 | ```html
48 |
49 | ```
50 |
51 | ---
52 |
53 | # Load data
54 |
55 | ```html
56 |
57 | ```
58 |
59 | Response je vykreslená v elementu, který provedl request.
60 |
61 | ---
62 |
63 | # Requesty
64 |
65 | | Atribut | Typ requestu |
66 | |-----------------------------------------------------|-------------------------|
67 | | [hx-get](https://htmx.org/attributes/hx-get/) | Provede `GET` na URL |
68 | | [hx-post](https://htmx.org/attributes/hx-post/) | Provede `POST` na URL |
69 | | [hx-put](https://htmx.org/attributes/hx-put/) | Provede `PUT` na URL |
70 | | [hx-patch](https://htmx.org/attributes/hx-patch/) | Provede `PATCH` na URL |
71 | | [hx-delete](https://htmx.org/attributes/hx-delete/) | Provede `DELETE` na URL |
72 |
73 | ---
74 |
75 | # Předání parametrů
76 |
77 | ```html
78 | Get Some HTML, Including A Value in the Request
79 | ```
80 |
81 | ---
82 |
83 | # Target
84 |
85 | ```html
86 |
87 | Products
88 |
89 |
90 | ```
91 |
92 | Pro změnu cíle pro vykreslení je nutné použít `hx-target`.
93 |
94 | ---
95 |
96 | # Extended selector
97 |
98 | Atributy (jako `hx-target`), které očekávají CSS selector, ve větši případů podporují rozšířenou syntaxi.
99 |
100 | `closest ` najde nejbližšího rodiče elementu
101 | `next ` najde element níže v DOMu (následující)
102 | `previous ` najde element výše v DOMu (předcházející)
103 | `find ` najde nejbližšího potomka elementu
104 |
105 | ---
106 |
107 | # Swapping
108 |
109 | | Hodnota | Popis |
110 | |-------------|------------------------------------------------------|
111 | | `innerHTML` | výchozí nastavení - nahradí obsah elementu (potomky) |
112 | | `outerHTML` | nahradí celý element včetně potomků |
113 | | `none` | bez vypsání odpovědi |
114 |
115 | ---
116 |
117 | # Loading indikátor
118 |
119 | ```html
120 |
124 | ```
125 |
126 | Standardně se loading indikátor použije potomek s třídou `htmx-indicator`. Jeho `opacity` se nastaví na 1. Element jde vybrat pomocí `hx-indicator="#indicator"`.
127 |
128 | ---
129 |
130 | # View Transition
131 |
132 | Využítím View Transitions API lze animatovat změny v rámci DOMu.
133 |
134 | ```html
135 |
136 | Products
137 |
138 |
139 | ```
140 |
141 | ---
142 |
143 | # Vykreslení JSONu
144 |
145 | Vzhledem k potenciálním problémům s CORS je doporučené použít server jako proxy pro získání dat.
146 |
147 | Pokud ale k tak potřebujete udělat request na klientu, tak lze použít klientské šablony.
148 |
149 | ---
150 |
151 | # Klientské šablony
152 |
153 | ```html
154 |
155 |
156 | ```
157 |
158 | ---
159 |
160 | # Mustache šablona
161 |
162 | ```html
163 |
164 |
165 |
171 |
172 |
Loading...
173 |
174 |
175 |
176 |
177 |
178 | ```
179 |
180 | ---
181 |
182 | ```html
183 |
184 |
185 |
186 |
187 |
188 | {{#data}}
189 | {{name}} at {{email}} is with {{company.name}}
190 | {{/data}}
191 |
192 |
193 |
194 | ```
195 |
196 | Pozor na mixování s Askamou
197 |
198 | ---
199 | # Formulář
200 |
201 | ```html
202 |
209 | ```
210 |
211 | ---
212 |
213 | # Přidání parametru mimo form
214 |
215 | ```html
216 |
217 |
220 | Enter email:
221 |
222 | ```
223 |
224 | ---
225 |
226 | # Odeslaní dat jako JSON
227 |
228 | ```html
229 |
230 | ```
231 |
232 | Přidáním `hx-ext='json-enc'` je request odeslaný jako typ `applicaton/json` s převodem na JSON v těle.
233 |
234 | ```html
235 |
239 | ```
240 |
241 | ---
242 |
243 | # Odeslání souboru
244 |
245 | Periodicky se vyvolává `htmx:xhr:progress`. Odpovídá standardní `progress` události během uploadu.
246 |
247 | ```html
248 |
256 | ```
257 |
258 | ---
259 |
260 | # Zpracování v Rustu
261 |
262 | ```rust
263 | async fn save_files(
264 | MultipartForm(form): MultipartForm,
265 | ) -> Result {
266 | for f in form.files {
267 | let path = format!("./tmp/{}", f.file_name.unwrap());
268 | log::info!("saving to {path}");
269 | f.file.persist(path).unwrap();
270 | }
271 |
272 | Ok(HttpResponse::Ok())
273 | }
274 | ```
275 |
276 | ---
277 |
278 | # Zpracování v Rustu
279 |
280 | ```rust
281 | #[actix_web::main]
282 | async fn main() -> std::io::Result<()> {
283 | env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
284 |
285 | log::info!("creating temporary upload directory");
286 | std::fs::create_dir_all("./tmp")?;
287 |
288 | log::info!("starting HTTP server at http://localhost:8080");
289 |
290 | HttpServer::new(|| {
291 | App::new()
292 | .wrap(middleware::Logger::default())
293 | .app_data(TempFileConfig::default().directory("./tmp"))
294 | .service(
295 | web::resource("/")
296 | .route(web::get().to(index))
297 | .route(web::post().to(save_files)),
298 | )
299 | })
300 | .bind(("127.0.0.1", 8080))?
301 | .workers(2)
302 | .run()
303 | .await
304 | }
305 | ```
306 |
307 | ---
308 |
309 | # Klientská validace
310 |
311 | Provádí se na formulářových prvcích. Jinde je třeba zapnout přes `hx-validate='true'`.
312 |
313 | Skriptování je přes hyperscript. Pro instalaci:
314 | přidat:
315 |
316 |
317 | ---
318 | # Klientská validace
319 |
320 | ```html
321 |
330 | ```
331 |
332 | ---
333 | # CSS extensions
334 |
335 | ```html
336 |
337 | ```
338 |
339 | ```html
340 |
341 |
342 |
343 |
345 |
347 |
348 |
349 | ```
350 |
351 | ---
352 |
353 | # Multiswap
354 |
355 | ```html
356 |
357 | ```
358 |
359 | ```html
360 |
361 |
362 |
367 |
368 |