├── .gitignore
├── LICENSE
├── Readme.md
├── arg-parsing-with-clap
├── .gitignore
├── Readme.md
├── clap-parsing
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ ├── logger.rs
│ │ └── main.rs
└── manual-parsing
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── connect-svelte-and-rust-using-wasm
├── .gitignore
├── Readme.md
├── rust
│ ├── .cargo
│ │ └── config
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ ├── lib.rs
│ │ └── parser.rs
├── svelte
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.png
│ │ ├── global.css
│ │ └── index.html
│ ├── rollup.config.js
│ ├── scripts
│ │ └── setupTypeScript.js
│ └── src
│ │ ├── App.svelte
│ │ └── main.js
└── text.txt
├── dev-containers-for-easy-dev-setup
├── .devcontainer
│ ├── app-frontend
│ │ ├── devcontainer.json
│ │ └── docker-compose.yaml
│ ├── node
│ │ ├── Dockerfile
│ │ └── devcontainer.json
│ └── ruby
│ │ ├── Dockerfile
│ │ └── devcontainer.json
├── backend.Dockerfile
├── backend
│ └── server.py
├── docker-compose-manual.yaml
├── frontend.Dockerfile
├── frontend
│ └── index.html
└── ruby-test
│ └── test.rb
├── excalibur-tutorial
├── .gitignore
├── Readme.md
├── favicon.ico
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── images
│ │ └── sword.png
├── src
│ ├── cameraStrategy.ts
│ ├── constant.ts
│ ├── files.d.ts
│ ├── goblin.ts
│ ├── level.ts
│ ├── levelIcon.ts
│ ├── levelSelector.ts
│ ├── main.ts
│ ├── player.ts
│ ├── resources.ts
│ ├── style.css
│ ├── tiledLevel.ts
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.js
├── generics-in-rust
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── Readme.md
└── src
│ └── main.rs
├── javascript-parser-generators
├── Readme.md
├── chevrotain.mjs
├── grammar.js
├── grammar.pegjs
├── ohm-example.js
├── package-lock.json
└── package.json
├── native-lazy-types
├── Cargo.lock
├── Cargo.toml
├── README.md
└── src
│ ├── lazy_static_example.rs
│ ├── main.rs
│ ├── native_lazy_example.rs
│ ├── native_once_example.rs
│ └── once_cell_example.rs
├── parser-with-pest
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── Readme.md
└── src
│ ├── document.rs
│ ├── html.pest
│ └── main.rs
├── tdd-in-deno
├── .gitignore
├── bdd_test.ts
├── external_lib_test.ts
├── simple_test.ts
├── stepped_test.ts
└── stubs.ts
└── tui-libraries-for-interactive-apps
├── bubbletea
├── go.mod
├── go.sum
└── main.go
├── clap
├── Cargo.lock
├── Cargo.toml
└── src
│ └── main.rs
├── enquirer
├── index.js
├── package-lock.json
└── package.json
├── gum
└── main.sh
├── huh
├── go.mod
├── go.sum
└── main.go
├── ink-example
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .prettierignore
├── package-lock.json
├── package.json
├── readme.md
├── source
│ ├── app.js
│ └── cli.js
└── test.js
├── ratatui-example
├── Cargo.lock
├── Cargo.toml
└── src
│ └── main.rs
└── textual
└── app.py
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | target/
3 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # LogRocket Blog Source
2 |
3 | ---
4 |
5 | This contains source code for blogs I have written for LogRocket.
6 |
7 | Each directory corresponds to a blog post, and corresponding Readme should contain link of the blog post.
8 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/.gitignore:
--------------------------------------------------------------------------------
1 | **/target/
2 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/Readme.md:
--------------------------------------------------------------------------------
1 | # Command Line Argument Parsing in Rust with Clap
2 |
3 | ---
4 |
5 | Blog Link : [https://blog.logrocket.com/command-line-argument-parsing-rust-using-clap/](https://blog.logrocket.com/command-line-argument-parsing-rust-using-clap/)
6 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/clap-parsing/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "atty"
7 | version = "0.2.14"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
10 | dependencies = [
11 | "hermit-abi",
12 | "libc",
13 | "winapi",
14 | ]
15 |
16 | [[package]]
17 | name = "autocfg"
18 | version = "1.1.0"
19 | source = "registry+https://github.com/rust-lang/crates.io-index"
20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
21 |
22 | [[package]]
23 | name = "bitflags"
24 | version = "1.3.2"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
27 |
28 | [[package]]
29 | name = "clap"
30 | version = "3.1.6"
31 | source = "registry+https://github.com/rust-lang/crates.io-index"
32 | checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
33 | dependencies = [
34 | "atty",
35 | "bitflags",
36 | "clap_derive",
37 | "indexmap",
38 | "lazy_static",
39 | "os_str_bytes",
40 | "strsim",
41 | "termcolor",
42 | "textwrap",
43 | ]
44 |
45 | [[package]]
46 | name = "clap-parsing"
47 | version = "0.1.0"
48 | dependencies = [
49 | "clap",
50 | ]
51 |
52 | [[package]]
53 | name = "clap_derive"
54 | version = "3.1.4"
55 | source = "registry+https://github.com/rust-lang/crates.io-index"
56 | checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
57 | dependencies = [
58 | "heck",
59 | "proc-macro-error",
60 | "proc-macro2",
61 | "quote",
62 | "syn",
63 | ]
64 |
65 | [[package]]
66 | name = "hashbrown"
67 | version = "0.11.2"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
70 |
71 | [[package]]
72 | name = "heck"
73 | version = "0.4.0"
74 | source = "registry+https://github.com/rust-lang/crates.io-index"
75 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
76 |
77 | [[package]]
78 | name = "hermit-abi"
79 | version = "0.1.19"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
82 | dependencies = [
83 | "libc",
84 | ]
85 |
86 | [[package]]
87 | name = "indexmap"
88 | version = "1.8.0"
89 | source = "registry+https://github.com/rust-lang/crates.io-index"
90 | checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
91 | dependencies = [
92 | "autocfg",
93 | "hashbrown",
94 | ]
95 |
96 | [[package]]
97 | name = "lazy_static"
98 | version = "1.4.0"
99 | source = "registry+https://github.com/rust-lang/crates.io-index"
100 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
101 |
102 | [[package]]
103 | name = "libc"
104 | version = "0.2.119"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
107 |
108 | [[package]]
109 | name = "memchr"
110 | version = "2.4.1"
111 | source = "registry+https://github.com/rust-lang/crates.io-index"
112 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
113 |
114 | [[package]]
115 | name = "os_str_bytes"
116 | version = "6.0.0"
117 | source = "registry+https://github.com/rust-lang/crates.io-index"
118 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
119 | dependencies = [
120 | "memchr",
121 | ]
122 |
123 | [[package]]
124 | name = "proc-macro-error"
125 | version = "1.0.4"
126 | source = "registry+https://github.com/rust-lang/crates.io-index"
127 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
128 | dependencies = [
129 | "proc-macro-error-attr",
130 | "proc-macro2",
131 | "quote",
132 | "syn",
133 | "version_check",
134 | ]
135 |
136 | [[package]]
137 | name = "proc-macro-error-attr"
138 | version = "1.0.4"
139 | source = "registry+https://github.com/rust-lang/crates.io-index"
140 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
141 | dependencies = [
142 | "proc-macro2",
143 | "quote",
144 | "version_check",
145 | ]
146 |
147 | [[package]]
148 | name = "proc-macro2"
149 | version = "1.0.36"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
152 | dependencies = [
153 | "unicode-xid",
154 | ]
155 |
156 | [[package]]
157 | name = "quote"
158 | version = "1.0.15"
159 | source = "registry+https://github.com/rust-lang/crates.io-index"
160 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
161 | dependencies = [
162 | "proc-macro2",
163 | ]
164 |
165 | [[package]]
166 | name = "strsim"
167 | version = "0.10.0"
168 | source = "registry+https://github.com/rust-lang/crates.io-index"
169 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
170 |
171 | [[package]]
172 | name = "syn"
173 | version = "1.0.86"
174 | source = "registry+https://github.com/rust-lang/crates.io-index"
175 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
176 | dependencies = [
177 | "proc-macro2",
178 | "quote",
179 | "unicode-xid",
180 | ]
181 |
182 | [[package]]
183 | name = "termcolor"
184 | version = "1.1.3"
185 | source = "registry+https://github.com/rust-lang/crates.io-index"
186 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
187 | dependencies = [
188 | "winapi-util",
189 | ]
190 |
191 | [[package]]
192 | name = "textwrap"
193 | version = "0.15.0"
194 | source = "registry+https://github.com/rust-lang/crates.io-index"
195 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
196 |
197 | [[package]]
198 | name = "unicode-xid"
199 | version = "0.2.2"
200 | source = "registry+https://github.com/rust-lang/crates.io-index"
201 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
202 |
203 | [[package]]
204 | name = "version_check"
205 | version = "0.9.4"
206 | source = "registry+https://github.com/rust-lang/crates.io-index"
207 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
208 |
209 | [[package]]
210 | name = "winapi"
211 | version = "0.3.9"
212 | source = "registry+https://github.com/rust-lang/crates.io-index"
213 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
214 | dependencies = [
215 | "winapi-i686-pc-windows-gnu",
216 | "winapi-x86_64-pc-windows-gnu",
217 | ]
218 |
219 | [[package]]
220 | name = "winapi-i686-pc-windows-gnu"
221 | version = "0.4.0"
222 | source = "registry+https://github.com/rust-lang/crates.io-index"
223 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
224 |
225 | [[package]]
226 | name = "winapi-util"
227 | version = "0.1.5"
228 | source = "registry+https://github.com/rust-lang/crates.io-index"
229 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
230 | dependencies = [
231 | "winapi",
232 | ]
233 |
234 | [[package]]
235 | name = "winapi-x86_64-pc-windows-gnu"
236 | version = "0.4.0"
237 | source = "registry+https://github.com/rust-lang/crates.io-index"
238 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
239 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/clap-parsing/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | edition = "2021"
3 | name = "clap-parsing"
4 | version = "0.1.0"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | clap = {version = "4.5.9", features = ["derive"]}
10 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/clap-parsing/src/logger.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Display;
2 |
3 | pub struct DummyLogger {
4 | verbosity: usize,
5 | }
6 |
7 | impl DummyLogger {
8 | pub fn new(verbosity: usize) -> Self {
9 | DummyLogger { verbosity }
10 | }
11 |
12 | pub fn log(&self, msg: T) {
13 | println!("{}", msg);
14 | }
15 |
16 | pub fn extra(&self, msg: T) {
17 | if self.verbosity > 0 {
18 | println!("{}", msg);
19 | }
20 | }
21 |
22 | pub fn debug(&self, msg: T) {
23 | if self.verbosity > 1 {
24 | println!("{}", msg);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/clap-parsing/src/main.rs:
--------------------------------------------------------------------------------
1 | use clap::{Parser, Subcommand};
2 | use logger::DummyLogger;
3 | use std::collections::VecDeque;
4 | use std::fs;
5 | use std::path::PathBuf;
6 | mod logger;
7 |
8 | fn validate_package_name(name: &str) -> Result {
9 | if name.trim().len() != name.len() {
10 | Err(String::from(
11 | "package name cannot have leading and trailing space",
12 | ))
13 | } else {
14 | Ok(name.to_string())
15 | }
16 | }
17 |
18 | #[derive(Parser, Debug)]
19 | #[command(author = "Author Name", version, about)]
20 | /// A Very simple Package Hunter
21 | struct Arguments {
22 | #[arg(default_value_t = usize::MAX, short, long)]
23 | /// maximum depth to which sub-directories should be explored
24 | max_depth: usize,
25 | #[arg(short, long, action = clap::ArgAction::Count)]
26 | verbosity: u8,
27 | #[command(subcommand)]
28 | cmd: SubCommand,
29 | }
30 |
31 | #[derive(Subcommand, Debug)]
32 | enum SubCommand {
33 | /// Count how many times the package is used
34 | Count {
35 | #[arg(value_parser = validate_package_name)]
36 | /// Name of the package to search
37 | package_name: String,
38 | },
39 | /// list all the projects
40 | Projects {
41 | #[arg(short, long, default_value_t = String::from("."), value_parser = validate_package_name)]
42 | /// directory to start exploring from
43 | start_path: String,
44 | #[arg(short, long, value_delimiter = ':')]
45 | /// paths to exclude when searching
46 | exclude: Vec,
47 | },
48 | }
49 |
50 | /// Not the dracula
51 | fn count(name: &str, max_depth: usize, logger: &logger::DummyLogger) -> std::io::Result {
52 | let mut count = 0;
53 | logger.debug("Initializing queue");
54 | // queue to store next dirs to explore
55 | let mut queue = VecDeque::new();
56 | logger.debug("Adding current dir to queue");
57 | // start with current dir
58 | queue.push_back((PathBuf::from("."), 0));
59 | logger.extra("starting");
60 | loop {
61 | if queue.is_empty() {
62 | logger.extra("queue empty");
63 | break;
64 | }
65 | let (path, crr_depth) = queue.pop_back().unwrap();
66 | logger.debug(format!("path :{:?}, depth :{}", path, crr_depth));
67 | if crr_depth > max_depth {
68 | continue;
69 | }
70 | logger.extra(format!("exploring {:?}", path));
71 | for dir in fs::read_dir(path)? {
72 | let dir = dir?;
73 | // we are concerned only if it is a directory
74 | if dir.file_type()?.is_dir() {
75 | if dir.file_name() == name {
76 | logger.log(format!("match found at {:?}", dir.path()));
77 | // we have a match, so stop exploring further
78 | count += 1;
79 | } else {
80 | logger.debug(format!("adding {:?} to queue", dir.path()));
81 | // not a match so check its sub-dirs
82 | queue.push_back((dir.path(), crr_depth + 1));
83 | }
84 | }
85 | }
86 | }
87 | logger.extra("search completed");
88 | return Ok(count);
89 | }
90 |
91 | fn projects(
92 | start: &str,
93 | max_depth: usize,
94 | exclude: &[String],
95 | logger: &DummyLogger,
96 | ) -> std::io::Result<()> {
97 | logger.debug("Initializing queue");
98 | // queue to store next dirs to explore
99 | let mut queue = VecDeque::new();
100 | logger.debug("Adding start dir to queue");
101 | // start with current dir
102 | queue.push_back((PathBuf::from(start), 0));
103 | logger.extra("starting");
104 | loop {
105 | if queue.is_empty() {
106 | logger.extra("queue empty");
107 | break;
108 | }
109 | let (path, crr_depth) = queue.pop_back().unwrap();
110 | logger.debug(format!("path :{:?}, depth :{}", path, crr_depth));
111 | if crr_depth > max_depth {
112 | continue;
113 | }
114 | logger.extra(format!("exploring {:?}", path));
115 | // we label the loop so we can continue it from inner loop
116 | 'outer: for dir in fs::read_dir(path)? {
117 | let dir = dir?;
118 | let _path = dir.path();
119 | let temp_path = _path.to_string_lossy();
120 | for p in exclude {
121 | if temp_path.contains(p) {
122 | // this specifies that it should continue the 'outer loop
123 | // not the for p in exclude loop
124 | // I originally had bug where I just used continue, and was wondering why
125 | // the projects weren't getting filtered!
126 | continue 'outer;
127 | }
128 | }
129 | // we are concerned only if it is a directory
130 | if dir.file_type()?.is_dir() {
131 | if dir.file_name() == ".git" {
132 | logger.log(format!("project found at {:?}", dir.path()));
133 | // we have a match, so stop exploring further
134 | println!("{:?}", dir.path());
135 | } else {
136 | logger.debug(format!("adding {:?} to queue", dir.path()));
137 | // not a match so check its sub-dirs
138 | queue.push_back((dir.path(), crr_depth + 1));
139 | }
140 | }
141 | }
142 | }
143 | logger.extra("search completed");
144 | return Ok(());
145 | }
146 |
147 | fn main() {
148 | let args = Arguments::parse();
149 | let logger = logger::DummyLogger::new(args.verbosity as usize);
150 | match args.cmd {
151 | SubCommand::Count { package_name } => match count(&package_name, args.max_depth, &logger) {
152 | Ok(c) => println!("{} uses found", c),
153 | Err(e) => eprintln!("error in processing : {}", e),
154 | },
155 | SubCommand::Projects {
156 | start_path,
157 | exclude,
158 | } => match projects(&start_path, args.max_depth, &exclude, &logger) {
159 | Ok(_) => {}
160 | Err(e) => eprintln!("error in processing : {}", e),
161 | },
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/manual-parsing/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "manual-parsing"
7 | version = "0.1.0"
8 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/manual-parsing/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "manual-parsing"
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 |
--------------------------------------------------------------------------------
/arg-parsing-with-clap/manual-parsing/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::collections::VecDeque;
2 | use std::fs;
3 | use std::path::PathBuf;
4 |
5 | #[derive(Default)]
6 | struct Arguments {
7 | package_name: String,
8 | max_depth: usize,
9 | }
10 |
11 | fn get_arguments() -> Arguments {
12 | let args: Vec<_> = std::env::args().collect();
13 | if args.len() < 3 {
14 | eprintln!("filename is a required argument");
15 | std::process::exit(1);
16 | }
17 |
18 | let mut ret = Arguments::default();
19 | ret.max_depth = usize::MAX;
20 |
21 | if args[1] == "-f" {
22 | ret.package_name = args[2].clone();
23 | } else {
24 | ret.max_depth = args[2].parse().unwrap();
25 | }
26 |
27 | // now one argument is parsed, time for seconds
28 |
29 | if args.len() > 4 {
30 | if args[3] == "-f" {
31 | ret.package_name = args[4].clone();
32 | } else {
33 | ret.max_depth = args[4].parse().unwrap();
34 | }
35 | }
36 |
37 | return ret;
38 | }
39 |
40 | /// Not the dracula
41 | fn count(name: &str, max_depth: usize) -> std::io::Result {
42 | let mut count = 0;
43 | // queue to store next dirs to explore
44 | let mut queue = VecDeque::new();
45 | // start with current dir
46 | queue.push_back((PathBuf::from("."), 0));
47 | loop {
48 | if queue.is_empty() {
49 | break;
50 | }
51 | let (path, crr_depth) = queue.pop_back().unwrap();
52 | if crr_depth > max_depth {
53 | continue;
54 | }
55 |
56 | for dir in fs::read_dir(path)? {
57 | let dir = dir?;
58 | // we are concerned only if it is a directory
59 | if dir.file_type()?.is_dir() {
60 | if dir.file_name() == name {
61 | // we have a match, so stop exploring further
62 | count += 1;
63 | } else {
64 | // not a match so check its sub-dirs
65 | queue.push_back((dir.path(), crr_depth + 1));
66 | }
67 | }
68 | }
69 | }
70 |
71 | return Ok(count);
72 | }
73 |
74 | fn main() {
75 | let args = get_arguments();
76 | match count(&args.package_name, args.max_depth) {
77 | Ok(c) => println!("{} uses found", c),
78 | Err(e) => eprintln!("error in processing : {}", e),
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules/
2 | **/target/
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/Readme.md:
--------------------------------------------------------------------------------
1 | # Integrating a Svelte app with Rust using WebAssembly
2 |
3 | ---
4 |
5 | Blog link : [https://blog.logrocket.com/integrating-svelte-app-rust-webassembly/](https://blog.logrocket.com/integrating-svelte-app-rust-webassembly/)
6 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/rust/.cargo/config:
--------------------------------------------------------------------------------
1 | [target.wasm32-unknown-unknown]
2 | rustflags = [
3 | "-C", "link-args=-z stack-size=2000000",
4 | ]
5 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/rust/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "bumpalo"
7 | version = "3.9.1"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
10 |
11 | [[package]]
12 | name = "cfg-if"
13 | version = "1.0.0"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
16 |
17 | [[package]]
18 | name = "itoa"
19 | version = "1.0.2"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
22 |
23 | [[package]]
24 | name = "lazy_static"
25 | version = "1.4.0"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
28 |
29 | [[package]]
30 | name = "log"
31 | version = "0.4.17"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
34 | dependencies = [
35 | "cfg-if",
36 | ]
37 |
38 | [[package]]
39 | name = "proc-macro2"
40 | version = "1.0.39"
41 | source = "registry+https://github.com/rust-lang/crates.io-index"
42 | checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
43 | dependencies = [
44 | "unicode-ident",
45 | ]
46 |
47 | [[package]]
48 | name = "quote"
49 | version = "1.0.18"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
52 | dependencies = [
53 | "proc-macro2",
54 | ]
55 |
56 | [[package]]
57 | name = "rust"
58 | version = "0.1.0"
59 | dependencies = [
60 | "serde",
61 | "wasm-bindgen",
62 | ]
63 |
64 | [[package]]
65 | name = "ryu"
66 | version = "1.0.10"
67 | source = "registry+https://github.com/rust-lang/crates.io-index"
68 | checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
69 |
70 | [[package]]
71 | name = "serde"
72 | version = "1.0.137"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 | checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
75 | dependencies = [
76 | "serde_derive",
77 | ]
78 |
79 | [[package]]
80 | name = "serde_derive"
81 | version = "1.0.137"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
84 | dependencies = [
85 | "proc-macro2",
86 | "quote",
87 | "syn",
88 | ]
89 |
90 | [[package]]
91 | name = "serde_json"
92 | version = "1.0.81"
93 | source = "registry+https://github.com/rust-lang/crates.io-index"
94 | checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
95 | dependencies = [
96 | "itoa",
97 | "ryu",
98 | "serde",
99 | ]
100 |
101 | [[package]]
102 | name = "syn"
103 | version = "1.0.95"
104 | source = "registry+https://github.com/rust-lang/crates.io-index"
105 | checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
106 | dependencies = [
107 | "proc-macro2",
108 | "quote",
109 | "unicode-ident",
110 | ]
111 |
112 | [[package]]
113 | name = "unicode-ident"
114 | version = "1.0.0"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
117 |
118 | [[package]]
119 | name = "wasm-bindgen"
120 | version = "0.2.80"
121 | source = "registry+https://github.com/rust-lang/crates.io-index"
122 | checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
123 | dependencies = [
124 | "cfg-if",
125 | "serde",
126 | "serde_json",
127 | "wasm-bindgen-macro",
128 | ]
129 |
130 | [[package]]
131 | name = "wasm-bindgen-backend"
132 | version = "0.2.80"
133 | source = "registry+https://github.com/rust-lang/crates.io-index"
134 | checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
135 | dependencies = [
136 | "bumpalo",
137 | "lazy_static",
138 | "log",
139 | "proc-macro2",
140 | "quote",
141 | "syn",
142 | "wasm-bindgen-shared",
143 | ]
144 |
145 | [[package]]
146 | name = "wasm-bindgen-macro"
147 | version = "0.2.80"
148 | source = "registry+https://github.com/rust-lang/crates.io-index"
149 | checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
150 | dependencies = [
151 | "quote",
152 | "wasm-bindgen-macro-support",
153 | ]
154 |
155 | [[package]]
156 | name = "wasm-bindgen-macro-support"
157 | version = "0.2.80"
158 | source = "registry+https://github.com/rust-lang/crates.io-index"
159 | checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
160 | dependencies = [
161 | "proc-macro2",
162 | "quote",
163 | "syn",
164 | "wasm-bindgen-backend",
165 | "wasm-bindgen-shared",
166 | ]
167 |
168 | [[package]]
169 | name = "wasm-bindgen-shared"
170 | version = "0.2.80"
171 | source = "registry+https://github.com/rust-lang/crates.io-index"
172 | checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
173 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/rust/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust"
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 | [lib]
8 | crate-type = ["cdylib", "rlib"]
9 |
10 |
11 | [dependencies]
12 | serde = { version = "1.0.137", features = ["derive"] }
13 | wasm-bindgen = { version= "0.2.63", features = ["serde-serialize"] }
14 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/rust/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod parser;
2 | use wasm_bindgen::prelude::*;
3 |
4 | #[wasm_bindgen]
5 | extern "C" {
6 | fn alert(s: &str);
7 | #[wasm_bindgen(js_name = alert)]
8 | fn alert_usize(a: usize);
9 | }
10 |
11 | #[wasm_bindgen]
12 | pub fn greet() {
13 | alert("Hello in JS from Rust!");
14 | alert_usize(5);
15 | }
16 |
17 | pub struct OwnerID {
18 | id: usize,
19 | }
20 |
21 | #[wasm_bindgen]
22 | pub struct Car {
23 | pub number: usize,
24 | pub color: usize, // color in hex code
25 | owner: OwnerID,
26 | }
27 |
28 | #[wasm_bindgen]
29 | impl Car {
30 | pub fn new() -> Self {
31 | Car {
32 | number: 0,
33 | color: 0,
34 | owner: OwnerID { id: 0 },
35 | }
36 | }
37 | pub fn duplicate(&self) -> Self {
38 | Self {
39 | number: self.number + 1,
40 | color: self.color,
41 | owner: OwnerID { id: 0 },
42 | }
43 | }
44 |
45 | pub fn change_number(&mut self, number: usize) {
46 | self.number = number;
47 | }
48 |
49 | pub fn get_id(&self) -> usize {
50 | self.owner.id
51 | }
52 | }
53 |
54 | #[wasm_bindgen]
55 | pub fn color(a: Car, color: usize) -> Car {
56 | Car {
57 | number: a.number,
58 | color,
59 | owner: OwnerID { id: 0 },
60 | }
61 | }
62 |
63 | #[wasm_bindgen]
64 | pub fn add(a: usize, b: usize) -> usize {
65 | a + b
66 | }
67 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/rust/src/parser.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use wasm_bindgen::prelude::*;
3 |
4 | // NOTE : This does not handle newlines or spaces or any other exceptions,
5 | // but as the intention of post is to show connecting Rust and JS,
6 | // we consider only ideal conditions and not errors for the parsing functions
7 | #[wasm_bindgen]
8 | pub fn parse(input: &str) -> JsValue {
9 | let mut ret: HashMap> = HashMap::new();
10 | let (keys, values) = input.split_once(';').unwrap();
11 | let keys: Vec<_> = keys.split(',').collect();
12 | let mut temp: Vec> = Vec::with_capacity(keys.len());
13 | for _ in 0..keys.len() {
14 | temp.push(Vec::new());
15 | }
16 | for row in values.split(';') {
17 | for (i, v) in row.split(',').enumerate() {
18 | temp[i].push(v.parse().unwrap());
19 | }
20 | }
21 | for (k, v) in keys.into_iter().zip(temp.into_iter()) {
22 | ret.insert(k.to_owned(), v);
23 | }
24 | JsValue::from_serde(&ret).unwrap()
25 | }
26 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /public/build/
3 |
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/README.md:
--------------------------------------------------------------------------------
1 | *Psst — looking for a more complete solution? Check out [SvelteKit](https://kit.svelte.dev), the official framework for building web applications of all sizes, with a beautiful development experience and flexible filesystem-based routing.*
2 |
3 | *Looking for a shareable component template instead? You can [use SvelteKit for that as well](https://kit.svelte.dev/docs#packaging) or the older [sveltejs/component-template](https://github.com/sveltejs/component-template)*
4 |
5 | ---
6 |
7 | # svelte app
8 |
9 | This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
10 |
11 | To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
12 |
13 | ```bash
14 | npx degit sveltejs/template svelte-app
15 | cd svelte-app
16 | ```
17 |
18 | *Note that you will need to have [Node.js](https://nodejs.org) installed.*
19 |
20 |
21 | ## Get started
22 |
23 | Install the dependencies...
24 |
25 | ```bash
26 | cd svelte-app
27 | npm install
28 | ```
29 |
30 | ...then start [Rollup](https://rollupjs.org):
31 |
32 | ```bash
33 | npm run dev
34 | ```
35 |
36 | Navigate to [localhost:8080](http://localhost:8080). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
37 |
38 | By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
39 |
40 | If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense.
41 |
42 | ## Building and running in production mode
43 |
44 | To create an optimised version of the app:
45 |
46 | ```bash
47 | npm run build
48 | ```
49 |
50 | You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
51 |
52 |
53 | ## Single-page app mode
54 |
55 | By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
56 |
57 | If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
58 |
59 | ```js
60 | "start": "sirv public --single"
61 | ```
62 |
63 | ## Using TypeScript
64 |
65 | This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with:
66 |
67 | ```bash
68 | node scripts/setupTypeScript.js
69 | ```
70 |
71 | Or remove the script via:
72 |
73 | ```bash
74 | rm scripts/setupTypeScript.js
75 | ```
76 |
77 | If you want to use `baseUrl` or `path` aliases within your `tsconfig`, you need to set up `@rollup/plugin-alias` to tell Rollup to resolve the aliases. For more info, see [this StackOverflow question](https://stackoverflow.com/questions/63427935/setup-tsconfig-path-in-svelte).
78 |
79 | ## Deploying to the web
80 |
81 | ### With [Vercel](https://vercel.com)
82 |
83 | Install `vercel` if you haven't already:
84 |
85 | ```bash
86 | npm install -g vercel
87 | ```
88 |
89 | Then, from within your project folder:
90 |
91 | ```bash
92 | cd public
93 | vercel deploy --name my-project
94 | ```
95 |
96 | ### With [surge](https://surge.sh/)
97 |
98 | Install `surge` if you haven't already:
99 |
100 | ```bash
101 | npm install -g surge
102 | ```
103 |
104 | Then, from within your project folder:
105 |
106 | ```bash
107 | npm run build
108 | surge public my-project.surge.sh
109 | ```
110 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-app",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "build": "rollup -c",
7 | "dev": "rollup -c -w",
8 | "start": "sirv public --no-clear"
9 | },
10 | "devDependencies": {
11 | "@rollup/plugin-commonjs": "^17.0.0",
12 | "@rollup/plugin-node-resolve": "^11.0.0",
13 | "carbon-components-svelte": "^0.64.0",
14 | "rollup": "^2.3.4",
15 | "rollup-plugin-css-only": "^3.1.0",
16 | "rollup-plugin-livereload": "^2.0.0",
17 | "rollup-plugin-svelte": "^7.0.0",
18 | "rollup-plugin-terser": "^7.0.0",
19 | "svelte": "^3.0.0"
20 | },
21 | "dependencies": {
22 | "@wasm-tool/rollup-plugin-rust": "^2.2.2",
23 | "sirv-cli": "^2.0.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YJDoc2/LogRocket-Blog-Code/e305aa008c0de324590c34c58fe391e313312aaf/connect-svelte-and-rust-using-wasm/svelte/public/favicon.png
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/public/global.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | position: relative;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | body {
8 | color: #333;
9 | margin: 0;
10 | padding: 8px;
11 | box-sizing: border-box;
12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
13 | }
14 |
15 | a {
16 | color: rgb(0,100,200);
17 | text-decoration: none;
18 | }
19 |
20 | a:hover {
21 | text-decoration: underline;
22 | }
23 |
24 | a:visited {
25 | color: rgb(0,80,160);
26 | }
27 |
28 | label {
29 | display: block;
30 | }
31 |
32 | input, button, select, textarea {
33 | font-family: inherit;
34 | font-size: inherit;
35 | -webkit-padding: 0.4em 0;
36 | padding: 0.4em;
37 | margin: 0 0 0.5em 0;
38 | box-sizing: border-box;
39 | border: 1px solid #ccc;
40 | border-radius: 2px;
41 | }
42 |
43 | input:disabled {
44 | color: #ccc;
45 | }
46 |
47 | button {
48 | color: #333;
49 | background-color: #f4f4f4;
50 | outline: none;
51 | }
52 |
53 | button:disabled {
54 | color: #999;
55 | }
56 |
57 | button:not(:disabled):active {
58 | background-color: #ddd;
59 | }
60 |
61 | button:focus {
62 | border-color: #666;
63 | }
64 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Svelte app
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/rollup.config.js:
--------------------------------------------------------------------------------
1 | import svelte from 'rollup-plugin-svelte';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import resolve from '@rollup/plugin-node-resolve';
4 | import livereload from 'rollup-plugin-livereload';
5 | import { terser } from 'rollup-plugin-terser';
6 | import css from 'rollup-plugin-css-only';
7 | import rust from '@wasm-tool/rollup-plugin-rust';
8 |
9 | const production = !process.env.ROLLUP_WATCH;
10 |
11 | function serve() {
12 | let server;
13 |
14 | function toExit() {
15 | if (server) server.kill(0);
16 | }
17 |
18 | return {
19 | writeBundle() {
20 | if (server) return;
21 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
22 | stdio: ['ignore', 'inherit', 'inherit'],
23 | shell: true
24 | });
25 |
26 | process.on('SIGTERM', toExit);
27 | process.on('exit', toExit);
28 | }
29 | };
30 | }
31 |
32 | export default {
33 | input: 'src/main.js',
34 | output: {
35 | sourcemap: true,
36 | format: 'iife',
37 | name: 'app',
38 | file: 'public/build/bundle.js'
39 | },
40 | plugins: [
41 | rust({
42 | verbose: true,
43 | serverPath: "build/"
44 | }),
45 | svelte({
46 | compilerOptions: {
47 | // enable run-time checks when not in production
48 | dev: !production
49 | }
50 | }),
51 | // we'll extract any component CSS out into
52 | // a separate file - better for performance
53 | css({ output: 'bundle.css' }),
54 |
55 | // If you have external dependencies installed from
56 | // npm, you'll most likely need these plugins. In
57 | // some cases you'll need additional configuration -
58 | // consult the documentation for details:
59 | // https://github.com/rollup/plugins/tree/master/packages/commonjs
60 | resolve({
61 | browser: true,
62 | dedupe: ['svelte']
63 | }),
64 | commonjs(),
65 |
66 | // In dev mode, call `npm run start` once
67 | // the bundle has been generated
68 | !production && serve(),
69 |
70 | // Watch the `public` directory and refresh the
71 | // browser on changes when not in production
72 | !production && livereload('public'),
73 |
74 | // If we're building for production (npm run build
75 | // instead of npm run dev), minify
76 | production && terser()
77 | ],
78 | watch: {
79 | clearScreen: false
80 | }
81 | };
82 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/scripts/setupTypeScript.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** This script modifies the project to support TS code in .svelte files like:
4 |
5 |
8 |
9 | As well as validating the code for CI.
10 | */
11 |
12 | /** To work on this script:
13 | rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
14 | */
15 |
16 | const fs = require("fs")
17 | const path = require("path")
18 | const { argv } = require("process")
19 |
20 | const projectRoot = argv[2] || path.join(__dirname, "..")
21 |
22 | // Add deps to pkg.json
23 | const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
24 | packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
25 | "svelte-check": "^2.0.0",
26 | "svelte-preprocess": "^4.0.0",
27 | "@rollup/plugin-typescript": "^8.0.0",
28 | "typescript": "^4.0.0",
29 | "tslib": "^2.0.0",
30 | "@tsconfig/svelte": "^2.0.0"
31 | })
32 |
33 | // Add script for checking
34 | packageJSON.scripts = Object.assign(packageJSON.scripts, {
35 | "check": "svelte-check --tsconfig ./tsconfig.json"
36 | })
37 |
38 | // Write the package JSON
39 | fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
40 |
41 | // mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
42 | const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
43 | const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
44 | fs.renameSync(beforeMainJSPath, afterMainTSPath)
45 |
46 | // Switch the app.svelte file to use TS
47 | const appSveltePath = path.join(projectRoot, "src", "App.svelte")
48 | let appFile = fs.readFileSync(appSveltePath, "utf8")
49 | appFile = appFile.replace("
49 |
50 |
51 |
52 |
53 | Connecting Rust to Svelte Through WASM !
54 |
55 |
56 |
57 |
58 |
59 |
68 |
69 |
70 |
71 |
72 |
73 | File Contents Are :
74 | {content}
75 |
76 |
77 |
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/svelte/src/main.js:
--------------------------------------------------------------------------------
1 | import App from './App.svelte';
2 | import wasm from '../../rust/Cargo.toml';
3 | import "carbon-components-svelte/css/g80.css";
4 |
5 | const init = async () => {
6 | const bindings = await wasm();
7 |
8 | const app = new App({
9 | target: document.body,
10 | props: {
11 | bindings,
12 | },
13 | });
14 | };
15 |
16 | init();
--------------------------------------------------------------------------------
/connect-svelte-and-rust-using-wasm/text.txt:
--------------------------------------------------------------------------------
1 | A,B,C,D;1.5,1.5,5.1,5.1;7.5,5.7,5.5,7.7
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/.devcontainer/app-frontend/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app-frontend",
3 | "dockerComposeFile":"./docker-compose.yaml",
4 | "service": "frontend",
5 | "workspaceFolder": "/frontend",
6 | "mounts": ["type=bind,source=../../frontend,target=/frontend"],
7 | "shutdownAction": "stopCompose"
8 | }
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/.devcontainer/app-frontend/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 |
3 | services:
4 | frontend:
5 | networks:
6 | - app_demo
7 | build:
8 | dockerfile: "./frontend.Dockerfile"
9 | context: "../../"
10 | ports:
11 | - 3000
12 | backend:
13 | networks:
14 | - app_demo
15 | build:
16 | dockerfile: "./backend.Dockerfile"
17 | context: "../../"
18 | ports:
19 | - 5000
20 | db:
21 | image: mongo:7.0.2
22 | volumes:
23 | - type: volume
24 | source: dbdata
25 | target: /var/lib/mongodb
26 | networks:
27 | - app_demo
28 | ports:
29 | - 27017
30 |
31 | networks:
32 | app_demo:
33 |
34 |
35 | volumes:
36 | dbdata:
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/.devcontainer/node/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/.devcontainer/node/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-test",
3 | "build": {
4 | "dockerfile": "Dockerfile"
5 | }
6 | }
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/.devcontainer/ruby/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:alpine3.18
2 |
3 | RUN apk update && apk add --virtual build-dependencies build-base
4 | RUN gem install rails
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/.devcontainer/ruby/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ruby-test",
3 | "build": {
4 | "dockerfile": "Dockerfile"
5 | }
6 | }
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/backend.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.9.18-alpine3.18
2 |
3 | RUN pip install flask
4 |
5 | COPY ./backend /backend
6 |
7 | WORKDIR /backend
8 |
9 | # we must specify the host as 0.0.0.0 otherwise cannot access outside container
10 | CMD ["flask","--app","/backend/server.py","run","--host","0.0.0.0"]
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/backend/server.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 |
3 | app = Flask(__name__)
4 |
5 | @app.route("/")
6 | def hello_world():
7 | return "Hello, World!
"
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/docker-compose-manual.yaml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 |
3 | services:
4 | frontend:
5 | networks:
6 | - app_demo
7 | build:
8 | dockerfile: "./frontend.Dockerfile"
9 | context: "."
10 | volumes:
11 | - type: bind
12 | source: ./frontend
13 | target: /frontend
14 | ports:
15 | - 3000:3000
16 | backend:
17 | networks:
18 | - app_demo
19 | build:
20 | dockerfile: "./backend.Dockerfile"
21 | context: "."
22 | ports:
23 | - 5000
24 | db:
25 | image: mongo:7.0.2
26 | volumes:
27 | - type: volume
28 | source: dbdata
29 | target: /var/lib/mongodb
30 | networks:
31 | - app_demo
32 | ports:
33 | - 27017
34 |
35 | networks:
36 | app_demo:
37 |
38 |
39 | volumes:
40 | dbdata:
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/frontend.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20
2 |
3 | RUN npm install -g http-server -y
4 |
5 | COPY ./frontend /frontend
6 |
7 | WORKDIR /frontend
8 |
9 | CMD ["http-server",".","-p","3000"]
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo App
7 |
8 |
9 | Hello World!
10 |
11 |
--------------------------------------------------------------------------------
/dev-containers-for-easy-dev-setup/ruby-test/test.rb:
--------------------------------------------------------------------------------
1 | puts "Hello Dev-container World!"
--------------------------------------------------------------------------------
/excalibur-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | test/images/actual.png
7 | test/images/diff-*.png
8 | /test-results/
9 | /playwright-report/
10 | /blob-report/
11 | /playwright/.cache/
12 | public/assets/*
--------------------------------------------------------------------------------
/excalibur-tutorial/Readme.md:
--------------------------------------------------------------------------------
1 | # Excalibur JS tutorial
2 |
3 | Code for game development with excalibur JS tutorial.
--------------------------------------------------------------------------------
/excalibur-tutorial/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YJDoc2/LogRocket-Blog-Code/e305aa008c0de324590c34c58fe391e313312aaf/excalibur-tutorial/favicon.ico
--------------------------------------------------------------------------------
/excalibur-tutorial/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Excalibur + Vite
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/excalibur-tutorial/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "excalibur-tutorial",
3 | "version": "0.0.0",
4 | "description": "",
5 | "main": "src/main.ts",
6 | "scripts": {
7 | "dev": "vite",
8 | "start": "vite",
9 | "build": "tsc && vite build",
10 | "serve": "vite preview",
11 | "test": "npm run build && npx playwright test",
12 | "test:integration-update": "npx playwright test --update-snapshots"
13 | },
14 | "repository": {},
15 | "keywords": [
16 | "excalibur",
17 | "excaliburjs",
18 | "vite",
19 | "game-engine"
20 | ],
21 | "author": "",
22 | "license": "",
23 | "bugs": {},
24 | "homepage": "",
25 | "dependencies": {
26 | "@excaliburjs/plugin-tiled": "0.30.2",
27 | "excalibur": "0.30.3"
28 | },
29 | "devDependencies": {
30 | "@playwright/test": "^1.49.1",
31 | "@types/node": "^22.10.2",
32 | "typescript": "5.7.3",
33 | "vite": "6.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/excalibur-tutorial/public/images/sword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YJDoc2/LogRocket-Blog-Code/e305aa008c0de324590c34c58fe391e313312aaf/excalibur-tutorial/public/images/sword.png
--------------------------------------------------------------------------------
/excalibur-tutorial/src/cameraStrategy.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BoundingBox,
3 | Camera,
4 | CameraStrategy,
5 | Engine,
6 | vec,
7 | Vector,
8 | } from "excalibur";
9 | import { Player } from "./player";
10 |
11 | export class BoundingBoxAroundActor implements CameraStrategy {
12 | target: Player;
13 | bounds: BoundingBox;
14 | constructor(target: Player, box: BoundingBox) {
15 | this.target = target;
16 | this.bounds = box;
17 | }
18 | action(
19 | target: Player,
20 | camera: Camera,
21 | engine: Engine,
22 | elapsed: number
23 | ): Vector {
24 | let pos = target.center;
25 | const focus = camera.getFocus();
26 | let focusX = focus.x;
27 | let focusY = focus.y;
28 | let box = this.bounds;
29 | if (pos.x < box.left + engine.halfDrawWidth) {
30 | focusX = box.left + engine.halfDrawWidth;
31 | } else if (pos.x > box.right - engine.halfDrawWidth) {
32 | focusX = box.right - engine.halfDrawWidth;
33 | } else {
34 | focusX = pos.x;
35 | }
36 |
37 | if (pos.y < box.top + engine.halfDrawHeight) {
38 | focusY = box.top + engine.halfDrawHeight;
39 | } else if (pos.y > box.bottom - engine.halfDrawHeight) {
40 | focusY = box.bottom - engine.halfDrawHeight;
41 | } else {
42 | focusY = pos.y;
43 | }
44 |
45 | return vec(focusX, focusY);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/constant.ts:
--------------------------------------------------------------------------------
1 | export const PLAYER_WALK_DISTANCE = 5;
2 | export const PLAYER_ATTACK_RANGE = 80;
3 | export const GOBLIN_WALK_DISTANCE = 3;
4 | export const GOBLIN_DETECTION_DISTANCE = 250;
5 | export const GOBLIN_ATTACK_DISTANCE = 75;
6 | export const GOBLIN_ATTACK_COOLDOWN = 1.5 * 1000;
7 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/files.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.png";
--------------------------------------------------------------------------------
/excalibur-tutorial/src/goblin.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Actor,
3 | Engine,
4 | SpriteSheet,
5 | vec,
6 | Vector,
7 | Animation,
8 | AnimationStrategy,
9 | range,
10 | EventEmitter,
11 | } from "excalibur";
12 | import { Resources } from "./resources";
13 | import {
14 | GOBLIN_ATTACK_COOLDOWN,
15 | GOBLIN_ATTACK_DISTANCE,
16 | GOBLIN_DETECTION_DISTANCE,
17 | GOBLIN_WALK_DISTANCE,
18 | PLAYER_ATTACK_RANGE,
19 | } from "./constant";
20 | import { Player } from "./player";
21 |
22 | export class Goblin extends Actor {
23 | idleAnimationRight: Animation;
24 | idleAnimationLeft: Animation;
25 | walkingAnimationLeft: Animation;
26 | walkingAnimationRight: Animation;
27 | attackAnimationRight: Animation;
28 | attackAnimationLeft: Animation;
29 | facingRight: boolean;
30 | events: EventEmitter;
31 | health = 3;
32 | attacking = false;
33 | lastAttack = 0;
34 |
35 | player: Player | null = null;
36 | constructor(startPos: Vector, events: EventEmitter) {
37 | super({
38 | name: "Goblin",
39 | pos: startPos,
40 | width: 100,
41 | height: 100,
42 | z: 7,
43 | scale: vec(1, 1),
44 | });
45 | this.events = events;
46 | this.facingRight = true;
47 | let spriteSheet = SpriteSheet.fromImageSource({
48 | image: Resources.Goblin,
49 | grid: {
50 | rows: 8,
51 | columns: 6,
52 | spriteWidth: 192,
53 | spriteHeight: 192,
54 | },
55 | });
56 | this.idleAnimationRight = Animation.fromSpriteSheet(
57 | spriteSheet,
58 | range(0, 6),
59 | 100,
60 | AnimationStrategy.Loop
61 | );
62 |
63 | this.idleAnimationLeft = this.idleAnimationRight.clone();
64 | this.idleAnimationLeft.flipHorizontal = true;
65 |
66 | this.walkingAnimationRight = Animation.fromSpriteSheet(
67 | spriteSheet,
68 | range(7, 12),
69 | 100,
70 | AnimationStrategy.Loop
71 | );
72 | this.walkingAnimationLeft = this.walkingAnimationRight.clone();
73 | this.walkingAnimationLeft.flipHorizontal = true;
74 |
75 | this.attackAnimationRight = Animation.fromSpriteSheet(
76 | spriteSheet,
77 | range(13, 18),
78 | 100,
79 | AnimationStrategy.Freeze
80 | );
81 | this.attackAnimationRight.events.on("end", (a) => {
82 | this.attacking = false;
83 | });
84 | this.attackAnimationLeft = this.attackAnimationRight.clone();
85 | this.attackAnimationLeft.flipHorizontal = true;
86 | this.attackAnimationLeft.events.on("end", (a) => {
87 | this.attacking = false;
88 | });
89 | }
90 | onInitialize(engine: Engine): void {
91 | this.graphics.use(this.idleAnimationRight);
92 | for (const a of engine.currentScene.actors) {
93 | if (a instanceof Player) {
94 | this.player = a;
95 | }
96 | }
97 | this.events.on("attack", ({ pos, right }) => {
98 | let diff = this.pos.sub(pos).magnitude;
99 | if (diff > PLAYER_ATTACK_RANGE) {
100 | return;
101 | }
102 |
103 | if ((pos.x < this.pos.x && right) || (pos.x > this.pos.x && !right)) {
104 | this.health -= 1;
105 | if (this.health <= 0) {
106 | this.kill();
107 | }
108 | }
109 | });
110 | }
111 |
112 | update(engine: Engine, elapsed: number) {
113 | super.update(engine, elapsed);
114 |
115 | let playerPos = this.player?.pos || vec(Infinity, Infinity);
116 | let diff = this.pos.sub(playerPos);
117 | let dist = diff.magnitude;
118 | let now = Date.now();
119 |
120 | if (dist < GOBLIN_DETECTION_DISTANCE && dist > GOBLIN_ATTACK_DISTANCE) {
121 | let step = diff.normalize().negate().scale(GOBLIN_WALK_DISTANCE);
122 | this.pos = this.pos.add(step);
123 | if (step.x < 0) {
124 | this.facingRight = false;
125 | this.graphics.use(this.walkingAnimationLeft);
126 | } else {
127 | this.facingRight = true;
128 | this.graphics.use(this.walkingAnimationRight);
129 | }
130 | this.attacking = false;
131 | } else if (
132 | dist < GOBLIN_ATTACK_DISTANCE &&
133 | now - this.lastAttack > GOBLIN_ATTACK_COOLDOWN
134 | ) {
135 | if (!this.attacking) {
136 | this.attacking = true;
137 | this.attackAnimationLeft.reset();
138 | this.attackAnimationRight.reset();
139 | if (this.facingRight) {
140 | this.graphics.use(this.attackAnimationRight);
141 | } else {
142 | this.graphics.use(this.attackAnimationLeft);
143 | }
144 | this.events.emit("enemy-attack", {});
145 | this.lastAttack = now;
146 | }
147 | } else if (!this.attacking) {
148 | if (this.facingRight) {
149 | this.graphics.use(this.idleAnimationRight);
150 | } else {
151 | this.graphics.use(this.idleAnimationLeft);
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/level.ts:
--------------------------------------------------------------------------------
1 | import { TiledResource } from "@excaliburjs/plugin-tiled";
2 | import { Scene, SceneActivationContext } from "excalibur";
3 |
4 | export class Level extends Scene {
5 | name: string;
6 | map: TiledResource;
7 | constructor(name: string, map: TiledResource) {
8 | super();
9 | this.name = name;
10 | this.map = map;
11 | }
12 |
13 | onActivate(context: SceneActivationContext): void {
14 | this.map.addToScene(this);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/levelIcon.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Actor,
3 | Color,
4 | Engine,
5 | Rectangle,
6 | Vector,
7 | Text,
8 | Font,
9 | FontUnit,
10 | GraphicsGroup,
11 | vec,
12 | } from "excalibur";
13 |
14 | export class LevelIcon extends Actor {
15 | label: string;
16 | constructor(cb: Function, pos: Vector, label: string) {
17 | super({
18 | name: "LevelIcon",
19 | pos: pos,
20 | width: 100,
21 | height: 100,
22 | });
23 | this.label = label;
24 | this.on("pointerdown", () => {
25 | cb();
26 | });
27 | }
28 | onInitialize(engine: Engine): void {
29 | const square = new Rectangle({
30 | width: 75,
31 | height: 75,
32 | color: Color.Magenta,
33 | });
34 | const title = new Text({
35 | text: this.label,
36 | font: new Font({
37 | family: "impact",
38 | size: 16,
39 | unit: FontUnit.Px,
40 | }),
41 | });
42 | let group = new GraphicsGroup({
43 | members: [
44 | {
45 | graphic: square,
46 | offset: vec(0, 0),
47 | },
48 | {
49 | graphic: title,
50 | offset: vec(7, 25),
51 | },
52 | ],
53 | });
54 | this.graphics.add(group);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/levelSelector.ts:
--------------------------------------------------------------------------------
1 | import { Engine, Scene, vec } from "excalibur";
2 | import { LevelIcon } from "./levelIcon";
3 |
4 | export class LevelSelector extends Scene {
5 | override onInitialize(engine: Engine): void {
6 | // Scene.onInitialize is where we recommend you perform the composition for your game
7 | let l1 = new LevelIcon(
8 | () => {
9 | engine.goToScene('level1');
10 | },
11 | vec(150, 150),
12 | "Level 1"
13 | );
14 | let l2 = new LevelIcon(
15 | () => {
16 | console.log("clicked level 2");
17 | },
18 | vec(250, 150),
19 | "Level 2"
20 | );
21 | this.add(l1);
22 | this.add(l2);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/main.ts:
--------------------------------------------------------------------------------
1 | import { DisplayMode, Engine } from "excalibur";
2 | import { Level1Map, loader } from "./resources";
3 |
4 | import { LevelSelector } from "./levelSelector";
5 | import { Level } from "./level";
6 |
7 | // Goal is to keep main.ts small and just enough to configure the engine
8 | const game = new Engine({
9 | width: 800, // Logical width and height in game pixels
10 | height: 600,
11 | displayMode: DisplayMode.FitScreenAndFill, // Display mode tells excalibur how to fill the window
12 | pixelArt: true, // pixelArt will turn on the correct settings to render pixel art without jaggies or shimmering artifacts
13 | scenes: {
14 | levelSelector: LevelSelector,
15 | level1: new Level("Level 1", Level1Map),
16 | },
17 | // physics: {
18 | // solver: SolverStrategy.Realistic,
19 | // substep: 5 // Sub step the physics simulation for more robust simulations
20 | // },
21 | // fixedUpdateTimestep: 16 // Turn on fixed update timestep when consistent physic simulation is important
22 | });
23 |
24 | game.start("levelSelector", {
25 | loader,
26 | });
27 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/player.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Actor,
3 | Engine,
4 | Keys,
5 | SpriteSheet,
6 | vec,
7 | Vector,
8 | Animation,
9 | AnimationStrategy,
10 | range,
11 | CollisionType,
12 | BoundingBox,
13 | EventEmitter,
14 | } from "excalibur";
15 | import { Resources } from "./resources";
16 | import { PLAYER_WALK_DISTANCE } from "./constant";
17 | import { BoundingBoxAroundActor } from "./cameraStrategy";
18 |
19 | export class Player extends Actor {
20 | idleAnimationRight: Animation;
21 | idleAnimationLeft: Animation;
22 | walkingAnimationLeft: Animation;
23 | walkingAnimationRight: Animation;
24 | attackAnimationLeft: Animation;
25 | attackAnimationRight: Animation;
26 | facingRight: boolean;
27 | events: EventEmitter;
28 | attacking: boolean = false;
29 | health = 10;
30 | constructor(startPos: Vector, events: EventEmitter) {
31 | super({
32 | name: "Player",
33 | pos: startPos,
34 | width: 100,
35 | height: 100,
36 | z: 10,
37 | collisionType: CollisionType.Active,
38 | scale: vec(1, 1),
39 | });
40 | this.events = events;
41 | this.facingRight = true;
42 | let spriteSheet = SpriteSheet.fromImageSource({
43 | image: Resources.Knight,
44 | grid: {
45 | rows: 8,
46 | columns: 6,
47 | spriteWidth: 192,
48 | spriteHeight: 192,
49 | },
50 | });
51 | this.idleAnimationRight = Animation.fromSpriteSheet(
52 | spriteSheet,
53 | range(0, 5),
54 | 100,
55 | AnimationStrategy.Loop
56 | );
57 |
58 | this.idleAnimationLeft = this.idleAnimationRight.clone();
59 | this.idleAnimationLeft.flipHorizontal = true;
60 |
61 | this.walkingAnimationRight = Animation.fromSpriteSheet(
62 | spriteSheet,
63 | range(6, 11),
64 | 100,
65 | AnimationStrategy.Loop
66 | );
67 | this.walkingAnimationLeft = this.walkingAnimationRight.clone();
68 | this.walkingAnimationLeft.flipHorizontal = true;
69 |
70 | this.attackAnimationRight = Animation.fromSpriteSheet(
71 | spriteSheet,
72 | range(12, 17),
73 | 100,
74 | AnimationStrategy.Freeze
75 | );
76 | this.attackAnimationRight.events.on("end", (a) => {
77 | this.events.emit("attack", { pos: this.pos, right: this.facingRight });
78 | this.attacking = false;
79 | });
80 | this.attackAnimationLeft = this.attackAnimationRight.clone();
81 | this.attackAnimationLeft.flipHorizontal = true;
82 | this.attackAnimationLeft.events.on("end", (a) => {
83 | this.events.emit("attack", { pos: this.pos, right: this.facingRight });
84 | this.attacking = false;
85 | });
86 | }
87 | onInitialize(engine: Engine): void {
88 | let boundingBox = new BoundingBox(
89 | 0,
90 | 0,
91 | engine.currentScene.tileMaps[0].width,
92 | engine.currentScene.tileMaps[0].height
93 | );
94 | engine.currentScene.camera.addStrategy(
95 | new BoundingBoxAroundActor(this, boundingBox)
96 | );
97 | this.graphics.use(this.idleAnimationRight);
98 | this.events.on("enemy-attack", () => {
99 | this.health -= 1;
100 | if (this.health <= 0) {
101 | this.kill();
102 | }
103 | });
104 | }
105 |
106 | update(engine: Engine, elapsed: number) {
107 | super.update(engine, elapsed);
108 |
109 | let updateVector = null;
110 | let idle = false;
111 |
112 | let attacking = this.attacking;
113 |
114 | if (engine.input.keyboard.isHeld(Keys.ArrowRight) && !attacking) {
115 | this.facingRight = true;
116 | updateVector = vec(PLAYER_WALK_DISTANCE, 0);
117 | } else if (engine.input.keyboard.isHeld(Keys.ArrowLeft) && !attacking) {
118 | this.facingRight = false;
119 | updateVector = vec(-PLAYER_WALK_DISTANCE, 0);
120 | } else if (engine.input.keyboard.isHeld(Keys.ArrowUp) && !attacking) {
121 | updateVector = vec(0, -PLAYER_WALK_DISTANCE);
122 | } else if (engine.input.keyboard.isHeld(Keys.ArrowDown) && !attacking) {
123 | updateVector = vec(0, PLAYER_WALK_DISTANCE);
124 | } else {
125 | updateVector = vec(0, 0);
126 | idle = true;
127 | }
128 | this.pos = this.pos.add(updateVector);
129 | if (!attacking) {
130 | if (engine.input.keyboard.wasPressed(Keys.Space)) {
131 | this.attacking = true;
132 | this.attackAnimationLeft.reset();
133 | this.attackAnimationRight.reset();
134 | if (this.facingRight) {
135 | this.graphics.use(this.attackAnimationRight);
136 | } else {
137 | this.graphics.use(this.attackAnimationLeft);
138 | }
139 | } else if (idle) {
140 | if (this.facingRight) {
141 | this.graphics.use(this.idleAnimationRight);
142 | } else {
143 | this.graphics.use(this.idleAnimationLeft);
144 | }
145 | } else {
146 | if (this.facingRight) {
147 | this.graphics.use(this.walkingAnimationRight);
148 | } else {
149 | this.graphics.use(this.walkingAnimationLeft);
150 | }
151 | }
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/resources.ts:
--------------------------------------------------------------------------------
1 | import { FactoryProps, TiledResource } from "@excaliburjs/plugin-tiled";
2 | import { ImageSource, Loader, vec, EventEmitter } from "excalibur";
3 | import { Player } from "./player";
4 | import { Goblin } from "./goblin";
5 |
6 | const eventEmitter = new EventEmitter();
7 |
8 | // It is convenient to put your resources in one place
9 | export const Resources = {
10 | Sword: new ImageSource("./images/sword.png"), // Vite public/ directory serves the root images
11 | Knight: new ImageSource(
12 | "./assets/Factions/Knights/Troops/Warrior/Blue/Warrior_Blue.png"
13 | ),
14 | Goblin: new ImageSource(
15 | "./assets/Factions/Goblins/Troops/Torch/Blue/Torch_Blue.png"
16 | ),
17 | } as const; // the 'as const' is a neat typescript trick to get strong typing on your resources.
18 | // So when you type Resources.Sword -> ImageSource
19 |
20 | // We build a loader and add all of our resources to the boot loader
21 | // You can build your own loader by extending DefaultLoader
22 | export const loader = new Loader();
23 | for (const res of Object.values(Resources)) {
24 | loader.addResource(res);
25 | }
26 |
27 | export const Level1Map = new TiledResource("./assets/level1.tmx", {
28 | useMapBackgroundColor: true,
29 | entityClassNameFactories: {
30 | Player: (props: FactoryProps) => {
31 | return new Player(vec(props.worldPos.x, props.worldPos.y), eventEmitter);
32 | },
33 | Goblin: (props: FactoryProps) => {
34 | return new Goblin(vec(props.worldPos.x, props.worldPos.y), eventEmitter);
35 | },
36 | },
37 | });
38 | loader.addResource(Level1Map);
39 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/style.css:
--------------------------------------------------------------------------------
1 | @media (prefers-color-scheme: dark) {
2 | body {
3 | background-color: black;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/tiledLevel.ts:
--------------------------------------------------------------------------------
1 | import { Scene } from "excalibur";
2 |
3 | export class TiledLevel extends Scene {
4 | constructor() {
5 | super();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/excalibur-tutorial/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/excalibur-tutorial/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "resolveJsonModule": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": false,
16 | "noImplicitReturns": true
17 | },
18 | "include": ["./src"]
19 | }
20 |
--------------------------------------------------------------------------------
/excalibur-tutorial/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 |
3 | // if you use tiled maps
4 | // there is a collision between react w/ typescript .tsx
5 | // and tiled tileset files .tsx
6 | // this forces vite to not interpret tsx as react
7 | const tiledPlugin = () => {
8 | return {
9 | name: 'tiled-tileset-plugin',
10 | resolveId: {
11 | order: 'pre',
12 | handler(sourceId, importer, options) {
13 | if (!sourceId.endsWith(".tsx")) return;
14 | return { id: 'tileset:' + sourceId, external: 'relative' }
15 | }
16 | }
17 | };
18 | }
19 |
20 | export default defineConfig({
21 | base: './', // optionally give a base path, useful for itch.io to serve relative instead of the default absolut
22 | plugins: [tiledPlugin()], // hint vite that tiled tilesets should be treated as external
23 | // currently excalibur plugins are commonjs
24 | // this forces vite to keep things from bundling ESM together with commonjs
25 | optimizeDeps: {
26 | exclude: ["excalibur"],
27 | },
28 | build: {
29 | assetsInlineLimit: 0, // excalibur cannot handle inlined xml in prod mode
30 | sourcemap: true,
31 | // Vite uses rollup currently for prod builds so a separate config is needed
32 | // to keep vite from bundling ESM together with commonjs
33 | rollupOptions: {
34 | output: {
35 | format: 'umd'
36 | }
37 | }
38 | }
39 | });
40 |
--------------------------------------------------------------------------------
/generics-in-rust/.gitignore:
--------------------------------------------------------------------------------
1 | **/target/
2 |
--------------------------------------------------------------------------------
/generics-in-rust/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "generics-in-rust"
7 | version = "0.1.0"
8 |
--------------------------------------------------------------------------------
/generics-in-rust/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "generics-in-rust"
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 |
--------------------------------------------------------------------------------
/generics-in-rust/Readme.md:
--------------------------------------------------------------------------------
1 | # Understanding Rust generics and how to use them
2 |
3 | ---
4 |
5 | Blog link : [https://blog.logrocket.com/understanding-rust-generics-how-use/](https://blog.logrocket.com/understanding-rust-generics-how-use/)
6 |
--------------------------------------------------------------------------------
/generics-in-rust/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::marker::PhantomData;
2 |
3 | struct Low;
4 | struct Medium;
5 | struct High;
6 |
7 | struct Heater {
8 | state: PhantomData,
9 | }
10 |
11 | impl Heater {
12 | fn turn_to_medium(self) -> Heater {
13 | Heater { state: PhantomData }
14 | }
15 | }
16 |
17 | impl Heater {}
18 |
19 | fn only_for_medium_heater(h: &mut Heater) {}
20 |
21 | struct Wrapper {
22 | data: DataType,
23 | }
24 |
25 | struct Ref<'a> {
26 | reference: &'a u8,
27 | }
28 |
29 | fn sort(a: Sortable, b: Sortable) -> bool {
30 | a < b
31 | }
32 | fn sort2(a: T, b: T) -> bool
33 | where
34 | T: Ord + Eq,
35 | {
36 | a < b
37 | }
38 |
39 | fn return_reference<'a, 'b>(in1: &'a [usize], in2: &'b [usize]) -> &'a usize {
40 | &in1[0]
41 | }
42 |
43 | fn return_reference2<'a, 'b: 'a>(in1: &'a [usize], in2: &'b [usize]) -> &'a usize {
44 | &in2[0]
45 | }
46 |
47 | fn main() {
48 | println!("Hello, world!");
49 | let c: Vec = [1, 2, 3].into_iter().collect();
50 | let c: Vec<_> = [1, 2, 3].into_iter().collect();
51 | let d1 = Wrapper { data: 5 };
52 | let d2 = Wrapper {
53 | data: "data".to_owned(),
54 | };
55 |
56 | let t1: Heater = Heater { state: PhantomData };
57 | }
58 |
--------------------------------------------------------------------------------
/javascript-parser-generators/Readme.md:
--------------------------------------------------------------------------------
1 | Code for post Javascript parser generators.
2 |
3 | Link will be added after post publishing.
4 |
--------------------------------------------------------------------------------
/javascript-parser-generators/chevrotain.mjs:
--------------------------------------------------------------------------------
1 | import { createToken, Lexer, EmbeddedActionsParser } from "chevrotain";
2 | const nameKey = createToken({ name: "nameKey", pattern: /name/ });
3 | const classKey = createToken({ name: "classKey", pattern: /class/ });
4 | const irandKey = createToken({ name: "irandKey", pattern: /item_randomness/ });
5 | const colon = createToken({ name: "colon", pattern: /:/ });
6 | const whiteSpace = createToken({
7 | name: "WhiteSpace",
8 | pattern: /\s+/,
9 | group: Lexer.SKIPPED,
10 | });
11 | const nameString = createToken({ name: "nameString", pattern: /[a-zA-Z]+/ });
12 | const classString = createToken({
13 | name: "classString",
14 | pattern: /human|dwarf|wizard/i,
15 | });
16 | const irandString = createToken({
17 | name: "randString",
18 | pattern: /very low|low|medium|high|very high/i,
19 | });
20 |
21 | const allTokens = [
22 | nameKey,
23 | classKey,
24 | classString,
25 | irandKey,
26 | irandString,
27 | whiteSpace,
28 | colon,
29 | nameString,
30 | ];
31 |
32 | const lexer = new Lexer(allTokens);
33 |
34 | class ConfigParser extends EmbeddedActionsParser {
35 | constructor() {
36 | super(allTokens);
37 |
38 | const $ = this;
39 |
40 | $.RULE("config", () => {
41 | return $.SUBRULE($.configKeys);
42 | });
43 |
44 | $.RULE("configKeys", () => {
45 | let obj = {};
46 | $.MANY(() => {
47 | $.OR([
48 | {
49 | ALT: () => {
50 | obj.name = $.SUBRULE($.characterName);
51 | },
52 | },
53 | {
54 | ALT: () => {
55 | obj.class = $.SUBRULE($.characterClass);
56 | },
57 | },
58 | {
59 | ALT: () => {
60 | obj.item_randomness = $.SUBRULE($.itemRandomness);
61 | },
62 | },
63 | ]);
64 | });
65 | return obj;
66 | });
67 |
68 | $.RULE("characterName", () => {
69 | $.CONSUME(nameKey);
70 | $.CONSUME(colon);
71 | let name = $.CONSUME(nameString).image;
72 | return name;
73 | });
74 | $.RULE("characterClass", () => {
75 | $.CONSUME(classKey);
76 | $.CONSUME(colon);
77 | let cls = $.CONSUME(classString).image.toLowerCase();
78 | return cls;
79 | });
80 | $.RULE("itemRandomness", () => {
81 | $.CONSUME(irandKey);
82 | $.CONSUME(colon);
83 | let temp = $.CONSUME(irandString).image.toLowerCase();
84 |
85 | let bounds = {
86 | "very low": [0.05, 0.2],
87 | low: [0.2, 0.4],
88 | medium: [0.4, 0.6],
89 | high: [0.6, 0.8],
90 | "very high": [0.8, 1],
91 | }[temp];
92 | let rand = Math.random();
93 | return $.ACTION(() => bounds[0] + (bounds[1] - bounds[0]) * rand);
94 | });
95 |
96 | // very important to call this after all the rules have been defined.
97 | // otherwise the parser may not work correctly as it will lack information
98 | // derived during the self analysis phase.
99 | this.performSelfAnalysis();
100 | }
101 | }
102 |
103 | const parser = new ConfigParser();
104 |
105 | // wrapping it all together
106 | const lexResult = lexer.tokenize(
107 | "name: Rincewind\nclass: Wizard\nitem_randomness: very high\n",
108 | );
109 | // setting a new input will RESET the parser instance's state.
110 | parser.input = lexResult.tokens;
111 | // any top level rule may be used as an entry point
112 | const value = parser.config();
113 | console.log(value);
114 |
--------------------------------------------------------------------------------
/javascript-parser-generators/grammar.js:
--------------------------------------------------------------------------------
1 | // @generated by Peggy 4.0.2.
2 | //
3 | // https://peggyjs.org/
4 |
5 | "use strict";
6 |
7 |
8 |
9 | // const { characterClass} = require('../character.js')
10 |
11 | function peg$subclass(child, parent) {
12 | function C() { this.constructor = child; }
13 | C.prototype = parent.prototype;
14 | child.prototype = new C();
15 | }
16 |
17 | function peg$SyntaxError(message, expected, found, location) {
18 | var self = Error.call(this, message);
19 | // istanbul ignore next Check is a necessary evil to support older environments
20 | if (Object.setPrototypeOf) {
21 | Object.setPrototypeOf(self, peg$SyntaxError.prototype);
22 | }
23 | self.expected = expected;
24 | self.found = found;
25 | self.location = location;
26 | self.name = "SyntaxError";
27 | return self;
28 | }
29 |
30 | peg$subclass(peg$SyntaxError, Error);
31 |
32 | function peg$padEnd(str, targetLength, padString) {
33 | padString = padString || " ";
34 | if (str.length > targetLength) { return str; }
35 | targetLength -= str.length;
36 | padString += padString.repeat(targetLength);
37 | return str + padString.slice(0, targetLength);
38 | }
39 |
40 | peg$SyntaxError.prototype.format = function(sources) {
41 | var str = "Error: " + this.message;
42 | if (this.location) {
43 | var src = null;
44 | var k;
45 | for (k = 0; k < sources.length; k++) {
46 | if (sources[k].source === this.location.source) {
47 | src = sources[k].text.split(/\r\n|\n|\r/g);
48 | break;
49 | }
50 | }
51 | var s = this.location.start;
52 | var offset_s = (this.location.source && (typeof this.location.source.offset === "function"))
53 | ? this.location.source.offset(s)
54 | : s;
55 | var loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column;
56 | if (src) {
57 | var e = this.location.end;
58 | var filler = peg$padEnd("", offset_s.line.toString().length, ' ');
59 | var line = src[s.line - 1];
60 | var last = s.line === e.line ? e.column : line.length + 1;
61 | var hatLen = (last - s.column) || 1;
62 | str += "\n --> " + loc + "\n"
63 | + filler + " |\n"
64 | + offset_s.line + " | " + line + "\n"
65 | + filler + " | " + peg$padEnd("", s.column - 1, ' ')
66 | + peg$padEnd("", hatLen, "^");
67 | } else {
68 | str += "\n at " + loc;
69 | }
70 | }
71 | return str;
72 | };
73 |
74 | peg$SyntaxError.buildMessage = function(expected, found) {
75 | var DESCRIBE_EXPECTATION_FNS = {
76 | literal: function(expectation) {
77 | return "\"" + literalEscape(expectation.text) + "\"";
78 | },
79 |
80 | class: function(expectation) {
81 | var escapedParts = expectation.parts.map(function(part) {
82 | return Array.isArray(part)
83 | ? classEscape(part[0]) + "-" + classEscape(part[1])
84 | : classEscape(part);
85 | });
86 |
87 | return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]";
88 | },
89 |
90 | any: function() {
91 | return "any character";
92 | },
93 |
94 | end: function() {
95 | return "end of input";
96 | },
97 |
98 | other: function(expectation) {
99 | return expectation.description;
100 | }
101 | };
102 |
103 | function hex(ch) {
104 | return ch.charCodeAt(0).toString(16).toUpperCase();
105 | }
106 |
107 | function literalEscape(s) {
108 | return s
109 | .replace(/\\/g, "\\\\")
110 | .replace(/"/g, "\\\"")
111 | .replace(/\0/g, "\\0")
112 | .replace(/\t/g, "\\t")
113 | .replace(/\n/g, "\\n")
114 | .replace(/\r/g, "\\r")
115 | .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
116 | .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
117 | }
118 |
119 | function classEscape(s) {
120 | return s
121 | .replace(/\\/g, "\\\\")
122 | .replace(/\]/g, "\\]")
123 | .replace(/\^/g, "\\^")
124 | .replace(/-/g, "\\-")
125 | .replace(/\0/g, "\\0")
126 | .replace(/\t/g, "\\t")
127 | .replace(/\n/g, "\\n")
128 | .replace(/\r/g, "\\r")
129 | .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
130 | .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
131 | }
132 |
133 | function describeExpectation(expectation) {
134 | return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
135 | }
136 |
137 | function describeExpected(expected) {
138 | var descriptions = expected.map(describeExpectation);
139 | var i, j;
140 |
141 | descriptions.sort();
142 |
143 | if (descriptions.length > 0) {
144 | for (i = 1, j = 1; i < descriptions.length; i++) {
145 | if (descriptions[i - 1] !== descriptions[i]) {
146 | descriptions[j] = descriptions[i];
147 | j++;
148 | }
149 | }
150 | descriptions.length = j;
151 | }
152 |
153 | switch (descriptions.length) {
154 | case 1:
155 | return descriptions[0];
156 |
157 | case 2:
158 | return descriptions[0] + " or " + descriptions[1];
159 |
160 | default:
161 | return descriptions.slice(0, -1).join(", ")
162 | + ", or "
163 | + descriptions[descriptions.length - 1];
164 | }
165 | }
166 |
167 | function describeFound(found) {
168 | return found ? "\"" + literalEscape(found) + "\"" : "end of input";
169 | }
170 |
171 | return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
172 | };
173 |
174 | function peg$parse(input, options) {
175 | options = options !== undefined ? options : {};
176 |
177 | var peg$FAILED = {};
178 | var peg$source = options.grammarSource;
179 |
180 | var peg$startRuleFunctions = { Config: peg$parseConfig };
181 | var peg$startRuleFunction = peg$parseConfig;
182 |
183 | var peg$c0 = "name";
184 | var peg$c1 = ":";
185 | var peg$c2 = "class";
186 | var peg$c3 = "item_randomness";
187 | var peg$c4 = "wizard";
188 | var peg$c5 = "dwarf";
189 | var peg$c6 = "human";
190 | var peg$c7 = "very low";
191 | var peg$c8 = "low";
192 | var peg$c9 = "medium";
193 | var peg$c10 = "high";
194 | var peg$c11 = "very high";
195 |
196 | var peg$r0 = /^[a-zA-Z]/;
197 | var peg$r1 = /^[ \t\n\r]/;
198 |
199 | var peg$e0 = peg$otherExpectation("Name");
200 | var peg$e1 = peg$literalExpectation("name", true);
201 | var peg$e2 = peg$literalExpectation(":", false);
202 | var peg$e3 = peg$classExpectation([["a", "z"], ["A", "Z"]], false, false);
203 | var peg$e4 = peg$literalExpectation("class", true);
204 | var peg$e5 = peg$literalExpectation("item_randomness", true);
205 | var peg$e6 = peg$literalExpectation("Wizard", true);
206 | var peg$e7 = peg$literalExpectation("Dwarf", true);
207 | var peg$e8 = peg$literalExpectation("Human", true);
208 | var peg$e9 = peg$literalExpectation("very low", true);
209 | var peg$e10 = peg$literalExpectation("low", true);
210 | var peg$e11 = peg$literalExpectation("medium", true);
211 | var peg$e12 = peg$literalExpectation("high", true);
212 | var peg$e13 = peg$literalExpectation("very high", true);
213 | var peg$e14 = peg$otherExpectation("whitespace");
214 | var peg$e15 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false);
215 |
216 | var peg$f0 = function() { return config; };
217 | var peg$f1 = function(val) {
218 | config.name = val;
219 | };
220 | var peg$f2 = function(cls) {
221 | // config.class = characterClass[cls];
222 | config.class = cls
223 | };
224 | var peg$f3 = function(rand) {
225 | let temp = rand.toLowerCase();
226 | let bounds = {
227 | "very low" : [ 0.05 , 0.2 ],
228 | "low" : [ 0.2 , 0.4],
229 | "medium" : [ 0.4 , 0.6],
230 | "high" : [ 0.6 , 0.8],
231 | "very high" : [ 0.8 , 1 ]
232 | }[temp];
233 | let r = Math.random();
234 | config.itemRand = bounds[0]+(bounds[1]-bounds[0])*r
235 | };
236 | var peg$currPos = options.peg$currPos | 0;
237 | var peg$savedPos = peg$currPos;
238 | var peg$posDetailsCache = [{ line: 1, column: 1 }];
239 | var peg$maxFailPos = peg$currPos;
240 | var peg$maxFailExpected = options.peg$maxFailExpected || [];
241 | var peg$silentFails = options.peg$silentFails | 0;
242 |
243 | var peg$result;
244 |
245 | if (options.startRule) {
246 | if (!(options.startRule in peg$startRuleFunctions)) {
247 | throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
248 | }
249 |
250 | peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
251 | }
252 |
253 | function text() {
254 | return input.substring(peg$savedPos, peg$currPos);
255 | }
256 |
257 | function offset() {
258 | return peg$savedPos;
259 | }
260 |
261 | function range() {
262 | return {
263 | source: peg$source,
264 | start: peg$savedPos,
265 | end: peg$currPos
266 | };
267 | }
268 |
269 | function location() {
270 | return peg$computeLocation(peg$savedPos, peg$currPos);
271 | }
272 |
273 | function expected(description, location) {
274 | location = location !== undefined
275 | ? location
276 | : peg$computeLocation(peg$savedPos, peg$currPos);
277 |
278 | throw peg$buildStructuredError(
279 | [peg$otherExpectation(description)],
280 | input.substring(peg$savedPos, peg$currPos),
281 | location
282 | );
283 | }
284 |
285 | function error(message, location) {
286 | location = location !== undefined
287 | ? location
288 | : peg$computeLocation(peg$savedPos, peg$currPos);
289 |
290 | throw peg$buildSimpleError(message, location);
291 | }
292 |
293 | function peg$literalExpectation(text, ignoreCase) {
294 | return { type: "literal", text: text, ignoreCase: ignoreCase };
295 | }
296 |
297 | function peg$classExpectation(parts, inverted, ignoreCase) {
298 | return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
299 | }
300 |
301 | function peg$anyExpectation() {
302 | return { type: "any" };
303 | }
304 |
305 | function peg$endExpectation() {
306 | return { type: "end" };
307 | }
308 |
309 | function peg$otherExpectation(description) {
310 | return { type: "other", description: description };
311 | }
312 |
313 | function peg$computePosDetails(pos) {
314 | var details = peg$posDetailsCache[pos];
315 | var p;
316 |
317 | if (details) {
318 | return details;
319 | } else {
320 | if (pos >= peg$posDetailsCache.length) {
321 | p = peg$posDetailsCache.length - 1;
322 | } else {
323 | p = pos;
324 | while (!peg$posDetailsCache[--p]) {}
325 | }
326 |
327 | details = peg$posDetailsCache[p];
328 | details = {
329 | line: details.line,
330 | column: details.column
331 | };
332 |
333 | while (p < pos) {
334 | if (input.charCodeAt(p) === 10) {
335 | details.line++;
336 | details.column = 1;
337 | } else {
338 | details.column++;
339 | }
340 |
341 | p++;
342 | }
343 |
344 | peg$posDetailsCache[pos] = details;
345 |
346 | return details;
347 | }
348 | }
349 |
350 | function peg$computeLocation(startPos, endPos, offset) {
351 | var startPosDetails = peg$computePosDetails(startPos);
352 | var endPosDetails = peg$computePosDetails(endPos);
353 |
354 | var res = {
355 | source: peg$source,
356 | start: {
357 | offset: startPos,
358 | line: startPosDetails.line,
359 | column: startPosDetails.column
360 | },
361 | end: {
362 | offset: endPos,
363 | line: endPosDetails.line,
364 | column: endPosDetails.column
365 | }
366 | };
367 | if (offset && peg$source && (typeof peg$source.offset === "function")) {
368 | res.start = peg$source.offset(res.start);
369 | res.end = peg$source.offset(res.end);
370 | }
371 | return res;
372 | }
373 |
374 | function peg$fail(expected) {
375 | if (peg$currPos < peg$maxFailPos) { return; }
376 |
377 | if (peg$currPos > peg$maxFailPos) {
378 | peg$maxFailPos = peg$currPos;
379 | peg$maxFailExpected = [];
380 | }
381 |
382 | peg$maxFailExpected.push(expected);
383 | }
384 |
385 | function peg$buildSimpleError(message, location) {
386 | return new peg$SyntaxError(message, null, null, location);
387 | }
388 |
389 | function peg$buildStructuredError(expected, found, location) {
390 | return new peg$SyntaxError(
391 | peg$SyntaxError.buildMessage(expected, found),
392 | expected,
393 | found,
394 | location
395 | );
396 | }
397 |
398 | function peg$parseConfig() {
399 | var s0, s1, s2, s3;
400 |
401 | s0 = peg$currPos;
402 | s1 = [];
403 | s2 = peg$parseCharacterName();
404 | if (s2 === peg$FAILED) {
405 | s2 = peg$parseCharacterClass();
406 | if (s2 === peg$FAILED) {
407 | s2 = peg$parseItemRandom();
408 | }
409 | }
410 | while (s2 !== peg$FAILED) {
411 | s1.push(s2);
412 | s2 = peg$currPos;
413 | s3 = peg$parse_();
414 | s3 = peg$parseCharacterName();
415 | if (s3 === peg$FAILED) {
416 | s3 = peg$parseCharacterClass();
417 | if (s3 === peg$FAILED) {
418 | s3 = peg$parseItemRandom();
419 | }
420 | }
421 | if (s3 === peg$FAILED) {
422 | peg$currPos = s2;
423 | s2 = peg$FAILED;
424 | } else {
425 | s2 = s3;
426 | }
427 | }
428 | peg$savedPos = s0;
429 | s1 = peg$f0();
430 | s0 = s1;
431 |
432 | return s0;
433 | }
434 |
435 | function peg$parseCharacterName() {
436 | var s0, s1, s2, s3, s4, s5, s6, s7;
437 |
438 | peg$silentFails++;
439 | s0 = peg$currPos;
440 | s1 = input.substr(peg$currPos, 4);
441 | if (s1.toLowerCase() === peg$c0) {
442 | peg$currPos += 4;
443 | } else {
444 | s1 = peg$FAILED;
445 | if (peg$silentFails === 0) { peg$fail(peg$e1); }
446 | }
447 | if (s1 !== peg$FAILED) {
448 | s2 = peg$parse_();
449 | if (input.charCodeAt(peg$currPos) === 58) {
450 | s3 = peg$c1;
451 | peg$currPos++;
452 | } else {
453 | s3 = peg$FAILED;
454 | if (peg$silentFails === 0) { peg$fail(peg$e2); }
455 | }
456 | if (s3 !== peg$FAILED) {
457 | s4 = peg$parse_();
458 | s5 = peg$currPos;
459 | s6 = [];
460 | s7 = input.charAt(peg$currPos);
461 | if (peg$r0.test(s7)) {
462 | peg$currPos++;
463 | } else {
464 | s7 = peg$FAILED;
465 | if (peg$silentFails === 0) { peg$fail(peg$e3); }
466 | }
467 | if (s7 !== peg$FAILED) {
468 | while (s7 !== peg$FAILED) {
469 | s6.push(s7);
470 | s7 = input.charAt(peg$currPos);
471 | if (peg$r0.test(s7)) {
472 | peg$currPos++;
473 | } else {
474 | s7 = peg$FAILED;
475 | if (peg$silentFails === 0) { peg$fail(peg$e3); }
476 | }
477 | }
478 | } else {
479 | s6 = peg$FAILED;
480 | }
481 | if (s6 !== peg$FAILED) {
482 | s5 = input.substring(s5, peg$currPos);
483 | } else {
484 | s5 = s6;
485 | }
486 | if (s5 !== peg$FAILED) {
487 | peg$savedPos = s0;
488 | s0 = peg$f1(s5);
489 | } else {
490 | peg$currPos = s0;
491 | s0 = peg$FAILED;
492 | }
493 | } else {
494 | peg$currPos = s0;
495 | s0 = peg$FAILED;
496 | }
497 | } else {
498 | peg$currPos = s0;
499 | s0 = peg$FAILED;
500 | }
501 | peg$silentFails--;
502 | if (s0 === peg$FAILED) {
503 | s1 = peg$FAILED;
504 | if (peg$silentFails === 0) { peg$fail(peg$e0); }
505 | }
506 |
507 | return s0;
508 | }
509 |
510 | function peg$parseCharacterClass() {
511 | var s0, s1, s2, s3, s4, s5;
512 |
513 | s0 = peg$currPos;
514 | s1 = input.substr(peg$currPos, 5);
515 | if (s1.toLowerCase() === peg$c2) {
516 | peg$currPos += 5;
517 | } else {
518 | s1 = peg$FAILED;
519 | if (peg$silentFails === 0) { peg$fail(peg$e4); }
520 | }
521 | if (s1 !== peg$FAILED) {
522 | s2 = peg$parse_();
523 | if (input.charCodeAt(peg$currPos) === 58) {
524 | s3 = peg$c1;
525 | peg$currPos++;
526 | } else {
527 | s3 = peg$FAILED;
528 | if (peg$silentFails === 0) { peg$fail(peg$e2); }
529 | }
530 | if (s3 !== peg$FAILED) {
531 | s4 = peg$parse_();
532 | s5 = peg$parseClass();
533 | if (s5 !== peg$FAILED) {
534 | peg$savedPos = s0;
535 | s0 = peg$f2(s5);
536 | } else {
537 | peg$currPos = s0;
538 | s0 = peg$FAILED;
539 | }
540 | } else {
541 | peg$currPos = s0;
542 | s0 = peg$FAILED;
543 | }
544 | } else {
545 | peg$currPos = s0;
546 | s0 = peg$FAILED;
547 | }
548 |
549 | return s0;
550 | }
551 |
552 | function peg$parseItemRandom() {
553 | var s0, s1, s2, s3, s4, s5;
554 |
555 | s0 = peg$currPos;
556 | s1 = input.substr(peg$currPos, 15);
557 | if (s1.toLowerCase() === peg$c3) {
558 | peg$currPos += 15;
559 | } else {
560 | s1 = peg$FAILED;
561 | if (peg$silentFails === 0) { peg$fail(peg$e5); }
562 | }
563 | if (s1 !== peg$FAILED) {
564 | s2 = peg$parse_();
565 | if (input.charCodeAt(peg$currPos) === 58) {
566 | s3 = peg$c1;
567 | peg$currPos++;
568 | } else {
569 | s3 = peg$FAILED;
570 | if (peg$silentFails === 0) { peg$fail(peg$e2); }
571 | }
572 | if (s3 !== peg$FAILED) {
573 | s4 = peg$parse_();
574 | s5 = peg$parseRndVal();
575 | if (s5 !== peg$FAILED) {
576 | peg$savedPos = s0;
577 | s0 = peg$f3(s5);
578 | } else {
579 | peg$currPos = s0;
580 | s0 = peg$FAILED;
581 | }
582 | } else {
583 | peg$currPos = s0;
584 | s0 = peg$FAILED;
585 | }
586 | } else {
587 | peg$currPos = s0;
588 | s0 = peg$FAILED;
589 | }
590 |
591 | return s0;
592 | }
593 |
594 | function peg$parseClass() {
595 | var s0;
596 |
597 | s0 = input.substr(peg$currPos, 6);
598 | if (s0.toLowerCase() === peg$c4) {
599 | peg$currPos += 6;
600 | } else {
601 | s0 = peg$FAILED;
602 | if (peg$silentFails === 0) { peg$fail(peg$e6); }
603 | }
604 | if (s0 === peg$FAILED) {
605 | s0 = input.substr(peg$currPos, 5);
606 | if (s0.toLowerCase() === peg$c5) {
607 | peg$currPos += 5;
608 | } else {
609 | s0 = peg$FAILED;
610 | if (peg$silentFails === 0) { peg$fail(peg$e7); }
611 | }
612 | if (s0 === peg$FAILED) {
613 | s0 = input.substr(peg$currPos, 5);
614 | if (s0.toLowerCase() === peg$c6) {
615 | peg$currPos += 5;
616 | } else {
617 | s0 = peg$FAILED;
618 | if (peg$silentFails === 0) { peg$fail(peg$e8); }
619 | }
620 | }
621 | }
622 |
623 | return s0;
624 | }
625 |
626 | function peg$parseRndVal() {
627 | var s0;
628 |
629 | s0 = input.substr(peg$currPos, 8);
630 | if (s0.toLowerCase() === peg$c7) {
631 | peg$currPos += 8;
632 | } else {
633 | s0 = peg$FAILED;
634 | if (peg$silentFails === 0) { peg$fail(peg$e9); }
635 | }
636 | if (s0 === peg$FAILED) {
637 | s0 = input.substr(peg$currPos, 3);
638 | if (s0.toLowerCase() === peg$c8) {
639 | peg$currPos += 3;
640 | } else {
641 | s0 = peg$FAILED;
642 | if (peg$silentFails === 0) { peg$fail(peg$e10); }
643 | }
644 | if (s0 === peg$FAILED) {
645 | s0 = input.substr(peg$currPos, 6);
646 | if (s0.toLowerCase() === peg$c9) {
647 | peg$currPos += 6;
648 | } else {
649 | s0 = peg$FAILED;
650 | if (peg$silentFails === 0) { peg$fail(peg$e11); }
651 | }
652 | if (s0 === peg$FAILED) {
653 | s0 = input.substr(peg$currPos, 4);
654 | if (s0.toLowerCase() === peg$c10) {
655 | peg$currPos += 4;
656 | } else {
657 | s0 = peg$FAILED;
658 | if (peg$silentFails === 0) { peg$fail(peg$e12); }
659 | }
660 | if (s0 === peg$FAILED) {
661 | s0 = input.substr(peg$currPos, 9);
662 | if (s0.toLowerCase() === peg$c11) {
663 | peg$currPos += 9;
664 | } else {
665 | s0 = peg$FAILED;
666 | if (peg$silentFails === 0) { peg$fail(peg$e13); }
667 | }
668 | }
669 | }
670 | }
671 | }
672 |
673 | return s0;
674 | }
675 |
676 | function peg$parse_() {
677 | var s0, s1;
678 |
679 | peg$silentFails++;
680 | s0 = [];
681 | s1 = input.charAt(peg$currPos);
682 | if (peg$r1.test(s1)) {
683 | peg$currPos++;
684 | } else {
685 | s1 = peg$FAILED;
686 | if (peg$silentFails === 0) { peg$fail(peg$e15); }
687 | }
688 | while (s1 !== peg$FAILED) {
689 | s0.push(s1);
690 | s1 = input.charAt(peg$currPos);
691 | if (peg$r1.test(s1)) {
692 | peg$currPos++;
693 | } else {
694 | s1 = peg$FAILED;
695 | if (peg$silentFails === 0) { peg$fail(peg$e15); }
696 | }
697 | }
698 | peg$silentFails--;
699 | s1 = peg$FAILED;
700 | if (peg$silentFails === 0) { peg$fail(peg$e14); }
701 |
702 | return s0;
703 | }
704 |
705 |
706 | const config = {};
707 |
708 | peg$result = peg$startRuleFunction();
709 |
710 | if (options.peg$library) {
711 | return /** @type {any} */ ({
712 | peg$result,
713 | peg$currPos,
714 | peg$FAILED,
715 | peg$maxFailExpected,
716 | peg$maxFailPos
717 | });
718 | }
719 | if (peg$result !== peg$FAILED && peg$currPos === input.length) {
720 | return peg$result;
721 | } else {
722 | if (peg$result !== peg$FAILED && peg$currPos < input.length) {
723 | peg$fail(peg$endExpectation());
724 | }
725 |
726 | throw peg$buildStructuredError(
727 | peg$maxFailExpected,
728 | peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
729 | peg$maxFailPos < input.length
730 | ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
731 | : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
732 | );
733 | }
734 | }
735 |
736 | module.exports = {
737 | StartRules: ["Config"],
738 | SyntaxError: peg$SyntaxError,
739 | parse: peg$parse
740 | };
741 |
--------------------------------------------------------------------------------
/javascript-parser-generators/grammar.pegjs:
--------------------------------------------------------------------------------
1 | {{
2 | // const { characterClass} = require('../character.js')
3 | }}
4 |
5 | {
6 | const config = {};
7 | }
8 |
9 | Config = (CharacterName / CharacterClass / ItemRandom)|.., _ | { return config; }
10 |
11 | CharacterName "Name" = "name"i _? ":" _? val:$[a-zA-Z]+{
12 | config.name = val;
13 | }
14 |
15 |
16 | CharacterClass = "class"i _? ":" _? cls:Class {
17 | // config.class = characterClass[cls];
18 | config.class = cls
19 | }
20 |
21 | ItemRandom = "item_randomness"i _? ":" _? rand:RndVal{
22 | let temp = rand.toLowerCase();
23 | let bounds = {
24 | "very low" : [ 0.05 , 0.2 ],
25 | "low" : [ 0.2 , 0.4],
26 | "medium" : [ 0.4 , 0.6],
27 | "high" : [ 0.6 , 0.8],
28 | "very high" : [ 0.8 , 1 ]
29 | }[temp];
30 | let r = Math.random();
31 | config.itemRand = bounds[0]+(bounds[1]-bounds[0])*r
32 | }
33 |
34 | Class = "Wizard"i / "Dwarf"i / "Human"i
35 | RndVal = "very low"i / "low"i / "medium"i / "high"i / "very high"i
36 | _ "whitespace" = [ \t\n\r]*
37 |
--------------------------------------------------------------------------------
/javascript-parser-generators/ohm-example.js:
--------------------------------------------------------------------------------
1 | const ohm = require("ohm-js");
2 |
3 | const configGrammer = ohm.grammar(String.raw`
4 | ConfigGrammer{
5 | Config
6 | = ListOf< ConfigKey, "">
7 | ConfigKey
8 | = characterName | CharacterClass | ItemRand
9 |
10 | characterName
11 | = "name" spaces ":" spaces letter+
12 | CharacterClass
13 | = "class" ":" class
14 | ItemRand
15 | = "item_randomness" ":" randomness
16 | class
17 | = caseInsensitive<"Wizard"> | caseInsensitive<"Dwarf"> | caseInsensitive<"Human">
18 | randomness
19 | = caseInsensitive<"very low"> | caseInsensitive<"low"> | caseInsensitive<"medium">
20 | | caseInsensitive<"high"> | caseInsensitive<"very high">
21 | }
22 | `);
23 |
24 | const userInput = String.raw`
25 | name: Rincewind
26 | class: wizard
27 | item_randomness: Very High
28 | `;
29 | const m = configGrammer.match(userInput.trim());
30 | console.log(m.succeeded());
31 | const semantics = configGrammer.createSemantics();
32 | semantics.addOperation("parse", {
33 | Config(config) {
34 | const obj = {};
35 | for (const c of config.asIteration().children) {
36 | let v = c.parse();
37 | obj[v[0]] = v[1];
38 | }
39 | return obj;
40 | },
41 | characterName(_1, _2, _3, _4, name) {
42 | return ["name", name.sourceString];
43 | },
44 | CharacterClass(_1, _2, cls) {
45 | return ["class", cls.sourceString.toLowerCase()];
46 | },
47 | ItemRand(_1, _2, r) {
48 | let temp = r.sourceString.toLowerCase();
49 | let bounds = {
50 | "very low": [0.05, 0.2],
51 | low: [0.2, 0.4],
52 | medium: [0.4, 0.6],
53 | high: [0.6, 0.8],
54 | "very high": [0.8, 1],
55 | }[temp];
56 | let rand = Math.random();
57 | return ["itemRand", bounds[0] + (bounds[1] - bounds[0]) * rand];
58 | },
59 | });
60 | console.log(semantics(m).parse());
61 |
--------------------------------------------------------------------------------
/javascript-parser-generators/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "javascript-parser-generators",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "javascript-parser-generators",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "chevrotain": "^11.0.3",
13 | "ohm-js": "^17.1.0"
14 | }
15 | },
16 | "node_modules/@chevrotain/cst-dts-gen": {
17 | "version": "11.0.3",
18 | "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
19 | "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==",
20 | "dependencies": {
21 | "@chevrotain/gast": "11.0.3",
22 | "@chevrotain/types": "11.0.3",
23 | "lodash-es": "4.17.21"
24 | }
25 | },
26 | "node_modules/@chevrotain/gast": {
27 | "version": "11.0.3",
28 | "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz",
29 | "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==",
30 | "dependencies": {
31 | "@chevrotain/types": "11.0.3",
32 | "lodash-es": "4.17.21"
33 | }
34 | },
35 | "node_modules/@chevrotain/regexp-to-ast": {
36 | "version": "11.0.3",
37 | "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz",
38 | "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA=="
39 | },
40 | "node_modules/@chevrotain/types": {
41 | "version": "11.0.3",
42 | "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz",
43 | "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="
44 | },
45 | "node_modules/@chevrotain/utils": {
46 | "version": "11.0.3",
47 | "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
48 | "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="
49 | },
50 | "node_modules/chevrotain": {
51 | "version": "11.0.3",
52 | "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
53 | "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
54 | "dependencies": {
55 | "@chevrotain/cst-dts-gen": "11.0.3",
56 | "@chevrotain/gast": "11.0.3",
57 | "@chevrotain/regexp-to-ast": "11.0.3",
58 | "@chevrotain/types": "11.0.3",
59 | "@chevrotain/utils": "11.0.3",
60 | "lodash-es": "4.17.21"
61 | }
62 | },
63 | "node_modules/lodash-es": {
64 | "version": "4.17.21",
65 | "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
66 | "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
67 | },
68 | "node_modules/ohm-js": {
69 | "version": "17.1.0",
70 | "resolved": "https://registry.npmjs.org/ohm-js/-/ohm-js-17.1.0.tgz",
71 | "integrity": "sha512-xc3B5dgAjTBQGHaH7B58M2Pmv6WvzrJ/3/7LeUzXNg0/sY3jQPdSd/S2SstppaleO77rifR1tyhdfFGNIwxf2Q==",
72 | "engines": {
73 | "node": ">=0.12.1"
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/javascript-parser-generators/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "javascript-parser-generators",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "ohm-example.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "chevrotain": "^11.0.3",
14 | "ohm-js": "^17.1.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/native-lazy-types/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "lazy_static"
7 | version = "1.5.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
10 |
11 | [[package]]
12 | name = "native_lazy"
13 | version = "0.1.0"
14 | dependencies = [
15 | "lazy_static",
16 | "once_cell",
17 | ]
18 |
19 | [[package]]
20 | name = "once_cell"
21 | version = "1.20.0"
22 | source = "registry+https://github.com/rust-lang/crates.io-index"
23 | checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe"
24 |
--------------------------------------------------------------------------------
/native-lazy-types/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "native_lazy"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | lazy_static = "1.5.0"
8 | once_cell = "1.20.0"
9 |
--------------------------------------------------------------------------------
/native-lazy-types/README.md:
--------------------------------------------------------------------------------
1 | # How to use the lazy initialization pattern with Rust 1.80
2 |
3 | This is code used in examples in the LogRocket article How to use the lazy initialization pattern with Rust 1.80.
4 |
--------------------------------------------------------------------------------
/native-lazy-types/src/lazy_static_example.rs:
--------------------------------------------------------------------------------
1 | use lazy_static::lazy_static;
2 |
3 | lazy_static! {
4 | static ref LOG_LEVEL: String = get_log_level();
5 | }
6 |
7 | fn get_log_level() -> String {
8 | match std::env::var("LOG_LEVEL") {
9 | Ok(s) => s,
10 | Err(_) => "WARN".to_string(),
11 | }
12 | }
13 |
14 | pub fn _main() {
15 | println!("{}", *LOG_LEVEL);
16 | }
17 |
--------------------------------------------------------------------------------
/native-lazy-types/src/main.rs:
--------------------------------------------------------------------------------
1 | mod lazy_static_example;
2 | mod native_lazy_example;
3 | mod native_once_example;
4 | mod once_cell_example;
5 |
6 | fn main() {
7 | lazy_static_example::_main();
8 | once_cell_example::_main();
9 | native_lazy_example::_main();
10 | native_once_example::_main();
11 | }
12 |
--------------------------------------------------------------------------------
/native-lazy-types/src/native_lazy_example.rs:
--------------------------------------------------------------------------------
1 | use std::sync::LazyLock;
2 |
3 | static LOG_LEVEL: LazyLock = LazyLock::new(get_log_level);
4 |
5 | fn get_log_level() -> String {
6 | match std::env::var("LOG_LEVEL") {
7 | Ok(s) => s,
8 | Err(_) => "WARN".to_string(),
9 | }
10 | }
11 |
12 | pub fn _main() {
13 | println!("{}", *LOG_LEVEL);
14 | }
15 |
--------------------------------------------------------------------------------
/native-lazy-types/src/native_once_example.rs:
--------------------------------------------------------------------------------
1 | use std::sync::OnceLock;
2 |
3 | static LOG_LEVEL: OnceLock = OnceLock::new();
4 |
5 | fn get_log_level() -> String {
6 | match std::env::var("LOG_LEVEL") {
7 | Ok(s) => s,
8 | Err(_) => "WARN".to_string(),
9 | }
10 | }
11 |
12 | pub fn _main() {
13 | let log_level = LOG_LEVEL.get_or_init(get_log_level);
14 | println!("{}", log_level);
15 | }
16 |
--------------------------------------------------------------------------------
/native-lazy-types/src/once_cell_example.rs:
--------------------------------------------------------------------------------
1 | use once_cell::sync::OnceCell;
2 |
3 | static LOG_LEVEL: OnceCell = OnceCell::new();
4 |
5 | fn get_log_level() -> String {
6 | match std::env::var("LOG_LEVEL") {
7 | Ok(s) => s,
8 | Err(_) => "WARN".to_string(),
9 | }
10 | }
11 |
12 | pub fn _main() {
13 | let log_level = LOG_LEVEL.get_or_init(get_log_level);
14 | println!("{}", log_level);
15 | }
16 |
--------------------------------------------------------------------------------
/parser-with-pest/.gitignore:
--------------------------------------------------------------------------------
1 | /target
--------------------------------------------------------------------------------
/parser-with-pest/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "block-buffer"
7 | version = "0.10.3"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
10 | dependencies = [
11 | "generic-array",
12 | ]
13 |
14 | [[package]]
15 | name = "cfg-if"
16 | version = "1.0.0"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
19 |
20 | [[package]]
21 | name = "cpufeatures"
22 | version = "0.2.5"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
25 | dependencies = [
26 | "libc",
27 | ]
28 |
29 | [[package]]
30 | name = "crypto-common"
31 | version = "0.1.6"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
34 | dependencies = [
35 | "generic-array",
36 | "typenum",
37 | ]
38 |
39 | [[package]]
40 | name = "digest"
41 | version = "0.10.6"
42 | source = "registry+https://github.com/rust-lang/crates.io-index"
43 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
44 | dependencies = [
45 | "block-buffer",
46 | "crypto-common",
47 | ]
48 |
49 | [[package]]
50 | name = "generic-array"
51 | version = "0.14.6"
52 | source = "registry+https://github.com/rust-lang/crates.io-index"
53 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
54 | dependencies = [
55 | "typenum",
56 | "version_check",
57 | ]
58 |
59 | [[package]]
60 | name = "libc"
61 | version = "0.2.138"
62 | source = "registry+https://github.com/rust-lang/crates.io-index"
63 | checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
64 |
65 | [[package]]
66 | name = "once_cell"
67 | version = "1.16.0"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
70 |
71 | [[package]]
72 | name = "parser-with-pest"
73 | version = "0.1.0"
74 | dependencies = [
75 | "pest",
76 | "pest_derive",
77 | ]
78 |
79 | [[package]]
80 | name = "pest"
81 | version = "2.5.1"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0"
84 | dependencies = [
85 | "thiserror",
86 | "ucd-trie",
87 | ]
88 |
89 | [[package]]
90 | name = "pest_derive"
91 | version = "2.5.1"
92 | source = "registry+https://github.com/rust-lang/crates.io-index"
93 | checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344"
94 | dependencies = [
95 | "pest",
96 | "pest_generator",
97 | ]
98 |
99 | [[package]]
100 | name = "pest_generator"
101 | version = "2.5.1"
102 | source = "registry+https://github.com/rust-lang/crates.io-index"
103 | checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c"
104 | dependencies = [
105 | "pest",
106 | "pest_meta",
107 | "proc-macro2",
108 | "quote",
109 | "syn",
110 | ]
111 |
112 | [[package]]
113 | name = "pest_meta"
114 | version = "2.5.1"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20"
117 | dependencies = [
118 | "once_cell",
119 | "pest",
120 | "sha1",
121 | ]
122 |
123 | [[package]]
124 | name = "proc-macro2"
125 | version = "1.0.48"
126 | source = "registry+https://github.com/rust-lang/crates.io-index"
127 | checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f"
128 | dependencies = [
129 | "unicode-ident",
130 | ]
131 |
132 | [[package]]
133 | name = "quote"
134 | version = "1.0.22"
135 | source = "registry+https://github.com/rust-lang/crates.io-index"
136 | checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8"
137 | dependencies = [
138 | "proc-macro2",
139 | ]
140 |
141 | [[package]]
142 | name = "sha1"
143 | version = "0.10.5"
144 | source = "registry+https://github.com/rust-lang/crates.io-index"
145 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
146 | dependencies = [
147 | "cfg-if",
148 | "cpufeatures",
149 | "digest",
150 | ]
151 |
152 | [[package]]
153 | name = "syn"
154 | version = "1.0.106"
155 | source = "registry+https://github.com/rust-lang/crates.io-index"
156 | checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b"
157 | dependencies = [
158 | "proc-macro2",
159 | "quote",
160 | "unicode-ident",
161 | ]
162 |
163 | [[package]]
164 | name = "thiserror"
165 | version = "1.0.38"
166 | source = "registry+https://github.com/rust-lang/crates.io-index"
167 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
168 | dependencies = [
169 | "thiserror-impl",
170 | ]
171 |
172 | [[package]]
173 | name = "thiserror-impl"
174 | version = "1.0.38"
175 | source = "registry+https://github.com/rust-lang/crates.io-index"
176 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
177 | dependencies = [
178 | "proc-macro2",
179 | "quote",
180 | "syn",
181 | ]
182 |
183 | [[package]]
184 | name = "typenum"
185 | version = "1.16.0"
186 | source = "registry+https://github.com/rust-lang/crates.io-index"
187 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
188 |
189 | [[package]]
190 | name = "ucd-trie"
191 | version = "0.1.5"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 | checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
194 |
195 | [[package]]
196 | name = "unicode-ident"
197 | version = "1.0.6"
198 | source = "registry+https://github.com/rust-lang/crates.io-index"
199 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
200 |
201 | [[package]]
202 | name = "version_check"
203 | version = "0.9.4"
204 | source = "registry+https://github.com/rust-lang/crates.io-index"
205 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
206 |
--------------------------------------------------------------------------------
/parser-with-pest/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "parser-with-pest"
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 | pest = "2.5.1"
10 | pest_derive = "2.5.1"
11 |
--------------------------------------------------------------------------------
/parser-with-pest/Readme.md:
--------------------------------------------------------------------------------
1 | # Parser with Pest
2 |
3 | Code for writing parser with Pest.
4 | This contains the example of writing a basic html parser with Pest in Rust. The implementation is not particularly efficient, but gets the work done for the example.
--------------------------------------------------------------------------------
/parser-with-pest/src/document.rs:
--------------------------------------------------------------------------------
1 | use std::collections::VecDeque;
2 |
3 | use crate::Rule;
4 | use pest::iterators::Pair;
5 |
6 | // Stores individual text or tag node
7 | #[derive(Debug)]
8 | pub enum Node {
9 | TagNode(Tag),
10 | TextNode(String),
11 | }
12 |
13 | // For storing attribute info
14 | #[derive(Debug)]
15 | pub struct Attr {
16 | name: String,
17 | value: String,
18 | }
19 |
20 | // Represents a html tag
21 | #[derive(Debug)]
22 | pub struct Tag {
23 | typ: TagType,
24 | name: String,
25 | attrs: Vec,
26 | }
27 |
28 | // represents what kind of tag it is
29 | #[derive(Debug, PartialEq, Eq)]
30 | pub enum TagType {
31 | Start,
32 | End,
33 | SelfClosing,
34 | }
35 |
36 | // represents a document tree
37 | #[derive(Debug)]
38 | pub struct NodeTree {
39 | root: Node,
40 | children: Vec,
41 | }
42 |
43 | impl NodeTree {
44 | fn new(node: Node) -> Self {
45 | Self {
46 | root: node,
47 | children: Vec::new(),
48 | }
49 | }
50 | fn _print_indent(indent: u8) {
51 | for _ in 0..indent {
52 | print!(" ");
53 | }
54 | }
55 | fn _print(tree: &Vec, indent: u8) {
56 | for node in tree {
57 | NodeTree::_print_indent(indent);
58 | NodeTree::_print_node(&node.root);
59 | NodeTree::_print(&node.children, indent + 2)
60 | }
61 | }
62 | fn _print_node(node: &Node) {
63 | match node {
64 | Node::TagNode(tag) => {
65 | print!("- {}", tag.name);
66 | }
67 | Node::TextNode(s) => {
68 | print!("- text({})", s);
69 | }
70 | }
71 | print!("\n");
72 | }
73 | pub fn print_tree(&self) {
74 | NodeTree::_print_node(&self.root);
75 | NodeTree::_print(&self.children, 2);
76 | }
77 | }
78 |
79 | // this will parse a tag to convert from Pest representation to our representation
80 | pub fn parse_tag(pair: Pair) -> Tag {
81 | // as tag will always have on inner , because the way we have defined the rule
82 | let tag = pair.into_inner().next().unwrap();
83 |
84 | // map the rule to tag type
85 | let tag_type = match tag.as_rule() {
86 | Rule::start_tag => TagType::Start,
87 | Rule::end_tag => TagType::End,
88 | Rule::self_closing_tag => TagType::SelfClosing,
89 | _ => unreachable!(),
90 | };
91 |
92 | let mut tag_data = tag.into_inner();
93 |
94 | // extract name
95 | let name = tag_data.next().unwrap().as_str().to_owned();
96 |
97 |
98 | let mut attributes = Vec::new();
99 |
100 | // parse all attributes on a tag,
101 | for attr in tag_data {
102 | if !matches!(attr.as_rule(), Rule::attr) {
103 | unreachable!("as per syntax, the tag name is followed by zero or more attr only");
104 | }
105 | let mut temp = attr.into_inner();
106 | // as per the parsing rule, attr has first the name, then the value
107 | let attr_name = temp.next().unwrap().as_str();
108 | let attr_val = temp.next().unwrap().as_str();
109 | attributes.push(Attr {
110 | name: attr_name.to_owned(),
111 | value: attr_val.to_owned(),
112 | });
113 | }
114 |
115 | Tag {
116 | typ: tag_type,
117 | name: name,
118 | attrs: attributes,
119 | }
120 | }
121 |
122 | // parses the list of tags into a document tree
123 | pub fn parse(nodes: Vec) -> NodeTree {
124 | let mut stack = VecDeque::new();
125 | let mut crr: Option = None;
126 |
127 | // iterate over the nodes
128 | for node in nodes {
129 | match node {
130 | // if it is text, then simply push it as a child of current active node
131 | Node::TextNode(_) => {
132 | crr.as_mut().unwrap().children.push(NodeTree::new(node));
133 | }
134 | // else check what kind of tag it is
135 | Node::TagNode(ref tag) => match tag.typ {
136 | // if tag is starting, then push current active tag into stack,
137 | // and set this tag as current active
138 | TagType::Start => {
139 | if crr.is_some() {
140 | stack.push_back(crr.unwrap());
141 | }
142 | crr = Some(NodeTree::new(node));
143 | }
144 | // if tag is self closing, add it as child of current active node
145 | TagType::SelfClosing => {
146 | crr.as_mut().unwrap().children.push(NodeTree::new(node));
147 | }
148 | // if it is end tag, we have to take care of some cases
149 | TagType::End => {
150 | // get name of current active tag
151 | let crr_tag_name = match crr.as_ref().unwrap().root {
152 | Node::TagNode(ref tag) => tag.name.clone(),
153 | _ => unreachable!(), // as text is always pushed as child, current has to be a tag
154 | };
155 | // if closing tag corresponds to current active tag,
156 | // pop tag from stack, add current active as its child,
157 | // and set it as current active
158 | if tag.name == crr_tag_name {
159 | if let Some(mut temp) = stack.pop_back() {
160 | temp.children.push(crr.unwrap());
161 | crr = Some(temp);
162 | }
163 | } else {
164 | // this is a rouge closing tag.
165 | // Either we have skipped some closing tag, and thus this is
166 | // closing some ancestor tag, eg closing div in ''
167 | // or this closing tag had no corresponding opening tag. eg
168 | let mut exists = false;
169 | // we first find if corresponding opening tag exists
170 | for node in &stack {
171 | match &node.root {
172 | Node::TagNode(t) => {
173 | if t.name == tag.name {
174 | exists = true;
175 | }
176 | }
177 | _ => unreachable!(),
178 | }
179 | }
180 | // if we don't have corresponding opening tag, we can ignore this closing tag
181 | if !exists {
182 | continue;
183 | }
184 |
185 | // We have some corresponding opening tag
186 | // keep popping trees from stack, set current as children of popped
187 | // and set the popped as current
188 | while let Some(mut tree) = stack.pop_back() {
189 | let match_found = match tree.root {
190 | Node::TagNode(ref ttag) => ttag.name == tag.name,
191 | _ => false,
192 | };
193 | tree.children.push(crr.unwrap());
194 | crr = Some(tree);
195 |
196 | // as the matched tag is closed, we have to do another pop
197 | // to set correct current tag
198 | if match_found {
199 | let mut tree = stack.pop_back().unwrap();
200 | tree.children.push(crr.unwrap());
201 | crr = Some(tree);
202 | break;
203 | }
204 | }
205 | }
206 | }
207 | }
208 | }
209 | }
210 | return crr.unwrap();
211 | }
212 |
--------------------------------------------------------------------------------
/parser-with-pest/src/html.pest:
--------------------------------------------------------------------------------
1 | document = { SOI ~ ( tag ~ text?)* ~ EOI }
2 |
3 | tag = ${ start_tag | self_closing_tag | end_tag }
4 |
5 | start_tag = { "<" ~ tag_name ~ (WHITESPACE+ ~ attr)* ~ ">" }
6 | end_tag = { "" ~ tag_name ~ (WHITESPACE+ ~ attr)* ~ ">" }
7 | self_closing_tag = { "<" ~ tag_name ~ (WHITESPACE+ ~ attr)* ~ "/>" }
8 |
9 | tag_name = { ASCII_ALPHA+ }
10 |
11 | text = { (ASCII_ALPHANUMERIC | other_symbols | non_tag_start | WHITESPACE )+ }
12 |
13 | non_tag_start = ${ "<" ~ WHITESPACE+}
14 | other_symbols = { "@" | ";" | "," | ">" }
15 |
16 | attr = { attr_name ~ "=" ~ "\"" ~ text ~ "\""}
17 |
18 | attr_name = { ASCII_ALPHA+ }
19 |
20 | WHITESPACE = _{ " " | NEWLINE }
--------------------------------------------------------------------------------
/parser-with-pest/src/main.rs:
--------------------------------------------------------------------------------
1 | use pest::Parser;
2 | use pest_derive::Parser;
3 |
4 | #[allow(dead_code)]
5 | mod document;
6 |
7 | const HTML: &str = r#"
8 |
9 |
10 |
11 |
12 |
13 | Hello
14 |

15 |
16 |
21 |
22 | jkl
23 |
24 |
25 |
26 |
27 |
28 | "#;
29 |
30 | #[derive(Parser)]
31 | #[grammar = "html.pest"]
32 | pub struct HtmlParser;
33 |
34 | fn main() {
35 | let parse = HtmlParser::parse(Rule::document, HTML).unwrap();
36 | let mut nodes = Vec::new();
37 | for pair in parse.into_iter() {
38 | match pair.as_rule() {
39 | Rule::document => {
40 | for tag in pair.into_inner() {
41 | if matches!(tag.as_rule(), Rule::tag) {
42 | nodes.push(document::Node::TagNode(document::parse_tag(tag)));
43 | } else if matches!(tag.as_rule(), Rule::text) {
44 | nodes.push(document::Node::TextNode(tag.as_span().as_str().to_string()));
45 | }
46 | }
47 | }
48 | _ => unreachable!(), // as we are parsing the document rule, if it is successful, no other rule will be returned
49 | }
50 | }
51 | let doc = document::parse(nodes);
52 | doc.print_tree();
53 | }
54 |
--------------------------------------------------------------------------------
/tdd-in-deno/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
--------------------------------------------------------------------------------
/tdd-in-deno/bdd_test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | afterEach,
3 | beforeEach,
4 | describe,
5 | it,
6 | } from "https://deno.land/std@0.155.0/testing/bdd.ts";
7 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
8 |
9 | import {
10 | deleteFromInventory,
11 | deleteUser,
12 | fetchUsers,
13 | getDefaultInventory,
14 | insertIntoInventory,
15 | insertUser,
16 | InventoryObject,
17 | User,
18 | } from "./stubs.ts";
19 |
20 | describe("User DB operations testing", () => {
21 | it("fetches correct user based on username", () => {
22 | const user = fetchUsers("testUser");
23 | assertEquals(user.length, 3);
24 | });
25 |
26 | it("inserts user correctly", () => {
27 | const newUser: User = {
28 | name: "newTestUser",
29 | id: "newtestuser",
30 | password: "newtestuser",
31 | };
32 | insertUser(newUser);
33 |
34 | const user = fetchUsers("new");
35 | assertEquals(user.length, 1);
36 | assertEquals(user[0].name, "newTestUser");
37 | });
38 |
39 | it("deletes user correctly", () => {
40 | deleteUser("newTestUser", "newtestuser");
41 | const user = fetchUsers("test");
42 | assertEquals(user.length, 3);
43 | });
44 | });
45 |
46 | describe("Inventory operations", () => {
47 | let inv: Array;
48 |
49 | beforeEach(() => {
50 | inv = getDefaultInventory();
51 | });
52 |
53 | afterEach(() => {
54 | inv = [];
55 | });
56 |
57 | it("inserts object into inventory", () => {
58 | insertIntoInventory(inv, {
59 | ojbType: "book",
60 | name: "just Another book",
61 | id: 4,
62 | owner: "testUser3",
63 | });
64 |
65 | assertEquals(inv.length, 4);
66 | assertEquals(inv[3], {
67 | ojbType: "book",
68 | name: "just Another book",
69 | id: 4,
70 | owner: "testUser3",
71 | });
72 | });
73 |
74 | it("deletes object from inventory", () => {
75 | inv = deleteFromInventory(inv, 3);
76 | assertEquals(inv.length, 2);
77 |
78 | const defaultInv = getDefaultInventory();
79 |
80 | assertEquals(inv[0], defaultInv[0]);
81 | assertEquals(inv[1], defaultInv[1]);
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/tdd-in-deno/external_lib_test.ts:
--------------------------------------------------------------------------------
1 | import chai from "https://cdn.skypack.dev/chai@4.3.4?dts";
2 | import { describe, it } from "https://deno.land/x/deno_mocha/mod.ts";
3 |
4 | const assert = chai.assert;
5 | const expect = chai.expect;
6 |
7 | import { deleteUser, fetchUsers, insertUser, User } from "./stubs.ts";
8 |
9 | describe("User DB operations testing using chai/mocha", () => {
10 | it("fetches correct user based on username", () => {
11 | const user = fetchUsers("testUser");
12 | assert(user.length === 3);
13 | });
14 |
15 | it("inserts user correctly", () => {
16 | const newUser: User = {
17 | name: "newTestUser",
18 | id: "newtestuser",
19 | password: "newtestuser",
20 | };
21 | insertUser(newUser);
22 |
23 | const user = fetchUsers("new");
24 | expect(user).to.have.lengthOf(1);
25 | expect(user[0].name).to.be.a("string");
26 | expect(user[0].name).to.equal("newTestUser");
27 | });
28 |
29 | it("deletes user correctly", () => {
30 | deleteUser("newTestUser", "newtestuser");
31 | const user = fetchUsers("test");
32 | expect(user).to.have.lengthOf(3);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tdd-in-deno/simple_test.ts:
--------------------------------------------------------------------------------
1 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
2 |
3 | import { fetchUsers } from "./stubs.ts";
4 |
5 | Deno.test("return empty array on empty string", () => {
6 | const users = fetchUsers("");
7 | assertEquals(users.length, 0);
8 | });
9 |
10 | Deno.test("return all users matching given name", () => {
11 | const users = fetchUsers("test");
12 | assertEquals(users.length, 3);
13 | assertEquals(users[0].name, "testUser1");
14 | assertEquals(users[1].name, "testUser2");
15 | assertEquals(users[2].name, "testUser3");
16 | });
17 |
18 | Deno.test("return empty array on not matching name", () => {
19 | const users = fetchUsers("abc");
20 | assertEquals(users.length, 0);
21 | });
22 |
--------------------------------------------------------------------------------
/tdd-in-deno/stepped_test.ts:
--------------------------------------------------------------------------------
1 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
2 |
3 | import { deleteUser, fetchUsers, insertUser, User } from "./stubs.ts";
4 |
5 | Deno.test("update User Name", async (t) => {
6 | const name = "testUser1";
7 | const newName = "testUserUpdated";
8 | let user: User;
9 |
10 | await t.step("fetch user", () => {
11 | const temp = fetchUsers(name);
12 | assertEquals(temp.length, 1);
13 | assertEquals(temp[0].name, name);
14 | user = temp[0];
15 | });
16 |
17 | await t.step("update and store", () => {
18 | const newUser: User = {
19 | ...user,
20 | name: newName,
21 | };
22 | insertUser(newUser);
23 |
24 | let temp = fetchUsers(newName);
25 |
26 | assertEquals(temp.length, 1);
27 | assertEquals(temp[0].name, newName);
28 | assertEquals(temp[0].id, user.id);
29 | assertEquals(temp[0].password, user.password);
30 |
31 | temp = fetchUsers(name);
32 | assertEquals(temp.length, 1);
33 | assertEquals(temp[0].name, name);
34 | });
35 |
36 | await t.step("delete old user data", () => {
37 | deleteUser(user.name, user.id);
38 |
39 | let temp = fetchUsers(name);
40 | assertEquals(temp.length, 0);
41 |
42 | temp = fetchUsers(newName);
43 | assertEquals(temp.length, 1);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/tdd-in-deno/stubs.ts:
--------------------------------------------------------------------------------
1 | export interface User {
2 | name: string;
3 | id: string;
4 | password: string;
5 | }
6 |
7 | export interface InventoryObject {
8 | ojbType: string;
9 | name: string;
10 | id: number;
11 | owner: string;
12 | }
13 |
14 | let users: Array = [
15 | {
16 | name: "testUser1",
17 | id: "tu1",
18 | password: "tu1",
19 | },
20 | {
21 | name: "testUser2",
22 | id: "tu2",
23 | password: "tu2",
24 | },
25 | {
26 | name: "testUser3",
27 | id: "tu3",
28 | password: "tu3",
29 | },
30 | ];
31 |
32 | const defaultInventory: Array = [
33 | {
34 | ojbType: "book",
35 | name: `It's a nice book`,
36 | id: 1,
37 | owner: "testUser1",
38 | },
39 | {
40 | ojbType: "sword",
41 | name: `sting`,
42 | id: 2,
43 | owner: "testUser2",
44 | },
45 | {
46 | ojbType: "ring",
47 | name: `precious`,
48 | id: 3,
49 | owner: "testUser3",
50 | },
51 | ];
52 |
53 | export const fetchUsers = (name: string): Array => {
54 | if (name.length === 0) {
55 | return [];
56 | } else {
57 | return users.filter((u) => u.name.includes(name));
58 | }
59 | };
60 |
61 | export const insertUser = (u: User) => {
62 | users.push(u);
63 | };
64 |
65 | export const deleteUser = (name: string, id: string) => {
66 | users = users.filter((u) => u.name !== name || u.id !== id);
67 | };
68 |
69 | export const getDefaultInventory = () => {
70 | return JSON.parse(JSON.stringify(defaultInventory));
71 | };
72 |
73 | export const insertIntoInventory = (
74 | inv: Array,
75 | obj: InventoryObject,
76 | ) => {
77 | inv.push(obj);
78 | };
79 |
80 | export const deleteFromInventory = (
81 | inv: Array,
82 | id: number,
83 | ) => {
84 | return inv.filter((obj) => obj.id !== id);
85 | };
86 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/bubbletea/go.mod:
--------------------------------------------------------------------------------
1 | module bubbletea-example
2 |
3 | go 1.22.0
4 |
5 | require (
6 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
7 | github.com/charmbracelet/bubbletea v0.26.2 // indirect
8 | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
9 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
10 | github.com/mattn/go-isatty v0.0.18 // indirect
11 | github.com/mattn/go-localereader v0.0.1 // indirect
12 | github.com/mattn/go-runewidth v0.0.15 // indirect
13 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
14 | github.com/muesli/cancelreader v0.2.2 // indirect
15 | github.com/muesli/reflow v0.3.0 // indirect
16 | github.com/muesli/termenv v0.15.2 // indirect
17 | github.com/rivo/uniseg v0.4.6 // indirect
18 | golang.org/x/sync v0.7.0 // indirect
19 | golang.org/x/sys v0.20.0 // indirect
20 | golang.org/x/term v0.20.0 // indirect
21 | golang.org/x/text v0.3.8 // indirect
22 | )
23 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/bubbletea/go.sum:
--------------------------------------------------------------------------------
1 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
2 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
3 | github.com/charmbracelet/bubbletea v0.26.2 h1:Eeb+n75Om9gQ+I6YpbCXQRKHt5Pn4vMwusQpwLiEgJQ=
4 | github.com/charmbracelet/bubbletea v0.26.2/go.mod h1:6I0nZ3YHUrQj7YHIHlM8RySX4ZIthTliMY+W8X8b+Gs=
5 | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
6 | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
7 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
8 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
9 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
10 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
11 | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
12 | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
13 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
14 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
15 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
16 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
17 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
18 | github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
19 | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
20 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
21 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
22 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
23 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
24 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
25 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
26 | github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
27 | github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
28 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
29 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
30 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
31 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
32 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
33 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
34 | golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
35 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
36 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
37 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
38 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/bubbletea/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | tea "github.com/charmbracelet/bubbletea"
6 | "log"
7 | "os"
8 | )
9 |
10 | type model struct {
11 | cwd string
12 | entries []string
13 | selected int
14 | }
15 |
16 | func initialModel() model {
17 | entries, err := os.ReadDir(".")
18 | if err != nil {
19 | log.Fatal(err)
20 | }
21 | var dirs = make([]string, len(entries))
22 | for i, e := range entries {
23 | dirs[i] = e.Name()
24 | }
25 | return model{
26 | cwd: ".",
27 | entries: dirs,
28 | selected: 0,
29 | }
30 | }
31 |
32 | func (m model) Init() tea.Cmd {
33 | return nil
34 | }
35 |
36 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
37 | switch msg := msg.(type) {
38 |
39 | case tea.KeyMsg:
40 |
41 | switch msg.String() {
42 |
43 | case "ctrl+c", "q":
44 | return m, tea.Quit
45 |
46 | case "up":
47 | if m.selected > 0 {
48 | m.selected--
49 | }
50 |
51 | case "down":
52 | if m.selected < len(m.entries)-1 {
53 | m.selected++
54 | }
55 |
56 | case "enter", " ":
57 | entry := m.entries[m.selected]
58 | m.selected = 0
59 | m.cwd = m.cwd + "/" + entry
60 | entries, err := os.ReadDir(m.cwd)
61 | if err != nil {
62 | log.Fatal(err)
63 | }
64 | var dirs = make([]string, len(entries))
65 | for i, e := range entries {
66 | dirs[i] = e.Name()
67 | }
68 | m.entries = dirs
69 | }
70 | }
71 |
72 | return m, nil
73 | }
74 |
75 | func (m model) View() string {
76 | s := "Directory List\n\n"
77 |
78 | for i, dir := range m.entries {
79 |
80 | cursor := " "
81 | if m.selected == i {
82 | cursor = ">"
83 | }
84 |
85 | s += fmt.Sprintf("%s %s\n", cursor, dir)
86 | }
87 |
88 | s += "\nPress q to quit.\n"
89 |
90 | return s
91 | }
92 |
93 | func main() {
94 | p := tea.NewProgram(initialModel())
95 | if _, err := p.Run(); err != nil {
96 | fmt.Printf("Alas, there's been an error: %v", err)
97 | os.Exit(1)
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/clap/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "anstream"
7 | version = "0.6.13"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
10 | dependencies = [
11 | "anstyle",
12 | "anstyle-parse",
13 | "anstyle-query",
14 | "anstyle-wincon",
15 | "colorchoice",
16 | "utf8parse",
17 | ]
18 |
19 | [[package]]
20 | name = "anstyle"
21 | version = "1.0.6"
22 | source = "registry+https://github.com/rust-lang/crates.io-index"
23 | checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
24 |
25 | [[package]]
26 | name = "anstyle-parse"
27 | version = "0.2.3"
28 | source = "registry+https://github.com/rust-lang/crates.io-index"
29 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
30 | dependencies = [
31 | "utf8parse",
32 | ]
33 |
34 | [[package]]
35 | name = "anstyle-query"
36 | version = "1.0.2"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
39 | dependencies = [
40 | "windows-sys",
41 | ]
42 |
43 | [[package]]
44 | name = "anstyle-wincon"
45 | version = "3.0.2"
46 | source = "registry+https://github.com/rust-lang/crates.io-index"
47 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
48 | dependencies = [
49 | "anstyle",
50 | "windows-sys",
51 | ]
52 |
53 | [[package]]
54 | name = "clap"
55 | version = "4.5.4"
56 | source = "registry+https://github.com/rust-lang/crates.io-index"
57 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
58 | dependencies = [
59 | "clap_builder",
60 | "clap_derive",
61 | ]
62 |
63 | [[package]]
64 | name = "clap-example"
65 | version = "0.1.0"
66 | dependencies = [
67 | "clap",
68 | ]
69 |
70 | [[package]]
71 | name = "clap_builder"
72 | version = "4.5.2"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
75 | dependencies = [
76 | "anstream",
77 | "anstyle",
78 | "clap_lex",
79 | "strsim",
80 | ]
81 |
82 | [[package]]
83 | name = "clap_derive"
84 | version = "4.5.4"
85 | source = "registry+https://github.com/rust-lang/crates.io-index"
86 | checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
87 | dependencies = [
88 | "heck",
89 | "proc-macro2",
90 | "quote",
91 | "syn",
92 | ]
93 |
94 | [[package]]
95 | name = "clap_lex"
96 | version = "0.7.0"
97 | source = "registry+https://github.com/rust-lang/crates.io-index"
98 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
99 |
100 | [[package]]
101 | name = "colorchoice"
102 | version = "1.0.0"
103 | source = "registry+https://github.com/rust-lang/crates.io-index"
104 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
105 |
106 | [[package]]
107 | name = "heck"
108 | version = "0.5.0"
109 | source = "registry+https://github.com/rust-lang/crates.io-index"
110 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
111 |
112 | [[package]]
113 | name = "proc-macro2"
114 | version = "1.0.81"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
117 | dependencies = [
118 | "unicode-ident",
119 | ]
120 |
121 | [[package]]
122 | name = "quote"
123 | version = "1.0.36"
124 | source = "registry+https://github.com/rust-lang/crates.io-index"
125 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
126 | dependencies = [
127 | "proc-macro2",
128 | ]
129 |
130 | [[package]]
131 | name = "strsim"
132 | version = "0.11.1"
133 | source = "registry+https://github.com/rust-lang/crates.io-index"
134 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
135 |
136 | [[package]]
137 | name = "syn"
138 | version = "2.0.60"
139 | source = "registry+https://github.com/rust-lang/crates.io-index"
140 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
141 | dependencies = [
142 | "proc-macro2",
143 | "quote",
144 | "unicode-ident",
145 | ]
146 |
147 | [[package]]
148 | name = "unicode-ident"
149 | version = "1.0.12"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
152 |
153 | [[package]]
154 | name = "utf8parse"
155 | version = "0.2.1"
156 | source = "registry+https://github.com/rust-lang/crates.io-index"
157 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
158 |
159 | [[package]]
160 | name = "windows-sys"
161 | version = "0.52.0"
162 | source = "registry+https://github.com/rust-lang/crates.io-index"
163 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
164 | dependencies = [
165 | "windows-targets",
166 | ]
167 |
168 | [[package]]
169 | name = "windows-targets"
170 | version = "0.52.5"
171 | source = "registry+https://github.com/rust-lang/crates.io-index"
172 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
173 | dependencies = [
174 | "windows_aarch64_gnullvm",
175 | "windows_aarch64_msvc",
176 | "windows_i686_gnu",
177 | "windows_i686_gnullvm",
178 | "windows_i686_msvc",
179 | "windows_x86_64_gnu",
180 | "windows_x86_64_gnullvm",
181 | "windows_x86_64_msvc",
182 | ]
183 |
184 | [[package]]
185 | name = "windows_aarch64_gnullvm"
186 | version = "0.52.5"
187 | source = "registry+https://github.com/rust-lang/crates.io-index"
188 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
189 |
190 | [[package]]
191 | name = "windows_aarch64_msvc"
192 | version = "0.52.5"
193 | source = "registry+https://github.com/rust-lang/crates.io-index"
194 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
195 |
196 | [[package]]
197 | name = "windows_i686_gnu"
198 | version = "0.52.5"
199 | source = "registry+https://github.com/rust-lang/crates.io-index"
200 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
201 |
202 | [[package]]
203 | name = "windows_i686_gnullvm"
204 | version = "0.52.5"
205 | source = "registry+https://github.com/rust-lang/crates.io-index"
206 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
207 |
208 | [[package]]
209 | name = "windows_i686_msvc"
210 | version = "0.52.5"
211 | source = "registry+https://github.com/rust-lang/crates.io-index"
212 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
213 |
214 | [[package]]
215 | name = "windows_x86_64_gnu"
216 | version = "0.52.5"
217 | source = "registry+https://github.com/rust-lang/crates.io-index"
218 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
219 |
220 | [[package]]
221 | name = "windows_x86_64_gnullvm"
222 | version = "0.52.5"
223 | source = "registry+https://github.com/rust-lang/crates.io-index"
224 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
225 |
226 | [[package]]
227 | name = "windows_x86_64_msvc"
228 | version = "0.52.5"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
231 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/clap/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "clap-example"
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 | clap = { version = "4.5.4", features = ["derive"] }
10 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/clap/src/main.rs:
--------------------------------------------------------------------------------
1 | use clap::{Parser, Subcommand};
2 | use std::path::PathBuf;
3 |
4 | fn parse_version(v: &str) -> Result {
5 | println!("version : {v}");
6 | return Ok(Version {
7 | major: 0,
8 | minor: 0,
9 | patch: 0,
10 | });
11 | }
12 | fn parse_url(url: &str) -> Result {
13 | println!("url: {url}");
14 | return Ok(url.to_owned());
15 | }
16 |
17 | #[allow(dead_code)]
18 | #[derive(Debug, Clone, Copy)]
19 | struct Version {
20 | major: u16,
21 | minor: u16,
22 | patch: u16,
23 | }
24 |
25 | #[derive(Debug, Subcommand)]
26 | enum Commands {
27 | /// Find a packge from name
28 | Find {
29 | /// name of the package to find
30 | name: String,
31 | /// specific version to find
32 | #[clap(long,short,value_parser=parse_version)]
33 | version: Option,
34 | /// name of the package to find
35 | #[clap(long,short,value_parser=parse_url)]
36 | registry: Option,
37 | },
38 | Download {
39 | /// name of the package to find
40 | name: String,
41 | /// output path for the downloaded package
42 | output: PathBuf,
43 | /// display progress or not
44 | #[clap(long, short, default_value_t = false)]
45 | silent: bool,
46 | /// specific version to find
47 | #[clap(long,short,value_parser=parse_version)]
48 | version: Option,
49 | /// name of the package to find
50 | #[clap(long,short,value_parser=parse_url)]
51 | registry: Option,
52 | },
53 | }
54 |
55 | #[derive(Parser, Debug)]
56 | #[command(version,long_about = None)]
57 | #[command(about = "An example of clap-rs for the blogpost on tuis")]
58 | struct Args {
59 | #[command(subcommand)]
60 | cmd: Commands,
61 | }
62 |
63 | fn main() {
64 | let t = Args::parse();
65 | dbg!(t);
66 | }
67 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/enquirer/index.js:
--------------------------------------------------------------------------------
1 | const { prompt } = require("enquirer");
2 |
3 | const results = prompt([
4 | {
5 | type: "input",
6 | name: "name",
7 | message: "What is name of your character?",
8 | },
9 | {
10 | type: "select",
11 | name: "class",
12 | message: "What is your character class?",
13 | choices: [
14 | {
15 | name: "Dwarf",
16 | value: "dwarf",
17 | },
18 | { name: "Wizard", value: "wizard" },
19 | { name: "Dragon", value: "dragon" },
20 | ],
21 | },
22 | {
23 | type: "Toggle",
24 | name: "custom",
25 | message: "Do advance customization?",
26 | enabled: "Yes",
27 | disabled: "No",
28 | },
29 | {
30 | type: "select",
31 | name: "difficulty",
32 | message: "Select difficulty level",
33 | choices: [
34 | { message: "Easy", value: "1" },
35 | { message: "Medium", value: "2" },
36 | { message: "Hard", value: "3" },
37 | ],
38 | initial: "2",
39 | skip: function () {
40 | return !this.state.answers.custom;
41 | },
42 | },
43 | {
44 | type: "select",
45 | name: "random",
46 | message: "Select item randomness level",
47 | choices: [
48 | { message: "Minimum", value: "1" },
49 | { message: "Low", value: "2" },
50 | { message: "Medium", value: "3" },
51 | { message: "High", value: "4" },
52 | { message: "Maximum", value: "5" },
53 | ],
54 | initial: "3",
55 | skip: function () {
56 | return !this.state.answers.custom;
57 | },
58 | },
59 | ]);
60 |
61 | async function main() {
62 | const response = await results;
63 | console.log(response);
64 | }
65 |
66 | main();
67 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/enquirer/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "enquirer",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "enquirer",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "enquirer": "^2.4.1"
13 | }
14 | },
15 | "node_modules/ansi-colors": {
16 | "version": "4.1.3",
17 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
18 | "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
19 | "engines": {
20 | "node": ">=6"
21 | }
22 | },
23 | "node_modules/ansi-regex": {
24 | "version": "5.0.1",
25 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
26 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
27 | "engines": {
28 | "node": ">=8"
29 | }
30 | },
31 | "node_modules/enquirer": {
32 | "version": "2.4.1",
33 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
34 | "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
35 | "dependencies": {
36 | "ansi-colors": "^4.1.1",
37 | "strip-ansi": "^6.0.1"
38 | },
39 | "engines": {
40 | "node": ">=8.6"
41 | }
42 | },
43 | "node_modules/strip-ansi": {
44 | "version": "6.0.1",
45 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
46 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
47 | "dependencies": {
48 | "ansi-regex": "^5.0.1"
49 | },
50 | "engines": {
51 | "node": ">=8"
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/enquirer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "enquirer-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "enquirer": "^2.4.1"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/gum/main.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | echo "Name of the package to serach for :"
4 | NAME=$(gum input)
5 | echo "Version of package to find (leave empty for any version)"
6 | VERSION=$(gum input --value="*")
7 | echo "Select registry :"
8 | REGISTRY=$(gum choose https://reg{1..3}.com)
9 |
10 | echo "name $NAME, version $VERSION, registry $REGISTRY"
11 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/huh/go.mod:
--------------------------------------------------------------------------------
1 | module huh_example
2 |
3 | go 1.22.0
4 |
5 | require (
6 | github.com/atotto/clipboard v0.1.4 // indirect
7 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
8 | github.com/catppuccin/go v0.2.0 // indirect
9 | github.com/charmbracelet/bubbles v0.17.2-0.20240108170749-ec883029c8e6 // indirect
10 | github.com/charmbracelet/bubbletea v0.25.0 // indirect
11 | github.com/charmbracelet/huh v0.3.0 // indirect
12 | github.com/charmbracelet/lipgloss v0.9.1 // indirect
13 | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
14 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
15 | github.com/mattn/go-isatty v0.0.20 // indirect
16 | github.com/mattn/go-localereader v0.0.1 // indirect
17 | github.com/mattn/go-runewidth v0.0.15 // indirect
18 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
19 | github.com/muesli/cancelreader v0.2.2 // indirect
20 | github.com/muesli/reflow v0.3.0 // indirect
21 | github.com/muesli/termenv v0.15.2 // indirect
22 | github.com/rivo/uniseg v0.4.4 // indirect
23 | golang.org/x/sync v0.4.0 // indirect
24 | golang.org/x/sys v0.13.0 // indirect
25 | golang.org/x/term v0.13.0 // indirect
26 | golang.org/x/text v0.13.0 // indirect
27 | )
28 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/huh/go.sum:
--------------------------------------------------------------------------------
1 | github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
2 | github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
3 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
4 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
5 | github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
6 | github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
7 | github.com/charmbracelet/bubbles v0.17.2-0.20240108170749-ec883029c8e6 h1:6nVCV8pqGaeyxetur3gpX3AAaiyKgzjIoCPV3NXKZBE=
8 | github.com/charmbracelet/bubbles v0.17.2-0.20240108170749-ec883029c8e6/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
9 | github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
10 | github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
11 | github.com/charmbracelet/huh v0.3.0 h1:CxPplWkgW2yUTDDG0Z4S5HH8SJOosWHd4LxCvi0XsKE=
12 | github.com/charmbracelet/huh v0.3.0/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA=
13 | github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
14 | github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
15 | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
16 | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
17 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
18 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
19 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
20 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
21 | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
22 | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
23 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
24 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
25 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
26 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
27 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
28 | github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
29 | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
30 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
31 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
32 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
33 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
34 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
35 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
36 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
37 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
38 | golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
39 | golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
40 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
41 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
42 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
43 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44 | golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
45 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
46 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
47 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
48 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/huh/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/charmbracelet/huh"
7 | "log"
8 | )
9 |
10 | func validateVersion(v string) error {
11 | if v == "test" {
12 | return errors.New("Test Error")
13 | }
14 | return nil
15 | }
16 |
17 | func main() {
18 | var name string
19 | version := "*"
20 | var registry string
21 | form := huh.NewForm(
22 | huh.NewGroup(
23 | huh.NewInput().
24 | Title("Package name to search for").
25 | CharLimit(100).
26 | Value(&name),
27 | huh.NewInput().
28 | Title("Version").
29 | CharLimit(100).
30 | Validate(validateVersion).
31 | Value(&version),
32 | huh.NewSelect[string]().
33 | Title("Select Registry to search in").
34 | Options(
35 | huh.NewOption("Registry 1", "https://reg1.com"),
36 | huh.NewOption("Registry 2", "https://reg2.com"),
37 | huh.NewOption("Registry 3", "https://reg3.com"),
38 | ).
39 | Value(®istry),
40 | ),
41 | )
42 | err := form.Run()
43 | if err != nil {
44 | log.Fatal(err)
45 | }
46 |
47 | fmt.Println(name, version, registry)
48 | }
49 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.yml]
11 | indent_style = space
12 | indent_size = 2
13 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ink-example",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "bin": "dist/cli.js",
6 | "type": "module",
7 | "engines": {
8 | "node": ">=16"
9 | },
10 | "scripts": {
11 | "build": "babel --out-dir=dist source",
12 | "dev": "babel --out-dir=dist --watch source",
13 | "test": "prettier --check . && xo && ava"
14 | },
15 | "files": [
16 | "dist"
17 | ],
18 | "dependencies": {
19 | "ink": "^4.1.0",
20 | "meow": "^11.0.0",
21 | "react": "^18.2.0"
22 | },
23 | "devDependencies": {
24 | "@babel/cli": "^7.21.0",
25 | "@babel/preset-react": "^7.18.6",
26 | "@vdemedes/prettier-config": "^2.0.1",
27 | "ava": "^5.2.0",
28 | "chalk": "^5.2.0",
29 | "eslint-config-xo-react": "^0.27.0",
30 | "eslint-plugin-react": "^7.32.2",
31 | "eslint-plugin-react-hooks": "^4.6.0",
32 | "import-jsx": "^5.0.0",
33 | "ink-testing-library": "^3.0.0",
34 | "prettier": "^2.8.7",
35 | "xo": "^0.53.1"
36 | },
37 | "ava": {
38 | "environmentVariables": {
39 | "NODE_NO_WARNINGS": "1"
40 | },
41 | "nodeArguments": [
42 | "--loader=import-jsx"
43 | ]
44 | },
45 | "xo": {
46 | "extends": "xo-react",
47 | "prettier": true,
48 | "rules": {
49 | "react/prop-types": "off"
50 | }
51 | },
52 | "prettier": "@vdemedes/prettier-config",
53 | "babel": {
54 | "presets": [
55 | "@babel/preset-react"
56 | ]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/readme.md:
--------------------------------------------------------------------------------
1 | # ink-example
2 |
3 | > This readme is automatically generated by [create-ink-app](https://github.com/vadimdemedes/create-ink-app)
4 |
5 | ## Install
6 |
7 | ```bash
8 | $ npm install --global ink-example
9 | ```
10 |
11 | ## CLI
12 |
13 | ```
14 | $ ink-example --help
15 |
16 | Usage
17 | $ ink-example
18 |
19 | Options
20 | --name Your name
21 |
22 | Examples
23 | $ ink-example --name=Jane
24 | Hello, Jane
25 | ```
26 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/source/app.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Text, Box, Newline, useInput, useApp} from 'ink';
3 |
4 | export default function App() {
5 | const [text, setText] = useState(' ');
6 | const {exit} = useApp();
7 | useInput((input, key) => {
8 | if (key.ctrl && input === 'd') {
9 | // save the file
10 | exit();
11 | } else {
12 | let newText;
13 | if (key.return) {
14 | newText = text + '\n ';
15 | } else {
16 | newText = text + input;
17 | }
18 | setText(newText);
19 | }
20 | });
21 | return (
22 | <>
23 |
24 |
25 | Input your text
26 |
27 |
28 |
29 | {text.split('\n').map((t, i, arr) => (
30 |
31 | {'>'}
32 | {t}
33 | {i == arr.length - 1 ? '_' : ''}
34 |
35 | ))}
36 | >
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/source/cli.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {render} from 'ink';
3 | import App from './app.js';
4 |
5 | render();
6 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ink-example/test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import chalk from 'chalk';
3 | import test from 'ava';
4 | import {render} from 'ink-testing-library';
5 | import App from './source/app.js';
6 |
7 | test('greet unknown user', t => {
8 | const {lastFrame} = render();
9 |
10 | t.is(lastFrame(), `Hello, ${chalk.green('Stranger')}`);
11 | });
12 |
13 | test('greet user with a name', t => {
14 | const {lastFrame} = render();
15 |
16 | t.is(lastFrame(), `Hello, ${chalk.green('Jane')}`);
17 | });
18 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ratatui-example/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "ahash"
7 | version = "0.8.11"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
10 | dependencies = [
11 | "cfg-if",
12 | "once_cell",
13 | "version_check",
14 | "zerocopy",
15 | ]
16 |
17 | [[package]]
18 | name = "allocator-api2"
19 | version = "0.2.18"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
22 |
23 | [[package]]
24 | name = "autocfg"
25 | version = "1.3.0"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
28 |
29 | [[package]]
30 | name = "bitflags"
31 | version = "2.5.0"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
34 |
35 | [[package]]
36 | name = "cassowary"
37 | version = "0.3.0"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
40 |
41 | [[package]]
42 | name = "castaway"
43 | version = "0.2.2"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
46 | dependencies = [
47 | "rustversion",
48 | ]
49 |
50 | [[package]]
51 | name = "cfg-if"
52 | version = "1.0.0"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
55 |
56 | [[package]]
57 | name = "compact_str"
58 | version = "0.7.1"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
61 | dependencies = [
62 | "castaway",
63 | "cfg-if",
64 | "itoa",
65 | "ryu",
66 | "static_assertions",
67 | ]
68 |
69 | [[package]]
70 | name = "crossterm"
71 | version = "0.27.0"
72 | source = "registry+https://github.com/rust-lang/crates.io-index"
73 | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
74 | dependencies = [
75 | "bitflags",
76 | "crossterm_winapi",
77 | "libc",
78 | "mio",
79 | "parking_lot",
80 | "signal-hook",
81 | "signal-hook-mio",
82 | "winapi",
83 | ]
84 |
85 | [[package]]
86 | name = "crossterm_winapi"
87 | version = "0.9.1"
88 | source = "registry+https://github.com/rust-lang/crates.io-index"
89 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
90 | dependencies = [
91 | "winapi",
92 | ]
93 |
94 | [[package]]
95 | name = "either"
96 | version = "1.12.0"
97 | source = "registry+https://github.com/rust-lang/crates.io-index"
98 | checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
99 |
100 | [[package]]
101 | name = "hashbrown"
102 | version = "0.14.5"
103 | source = "registry+https://github.com/rust-lang/crates.io-index"
104 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
105 | dependencies = [
106 | "ahash",
107 | "allocator-api2",
108 | ]
109 |
110 | [[package]]
111 | name = "heck"
112 | version = "0.4.1"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
115 |
116 | [[package]]
117 | name = "itertools"
118 | version = "0.12.1"
119 | source = "registry+https://github.com/rust-lang/crates.io-index"
120 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
121 | dependencies = [
122 | "either",
123 | ]
124 |
125 | [[package]]
126 | name = "itoa"
127 | version = "1.0.11"
128 | source = "registry+https://github.com/rust-lang/crates.io-index"
129 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
130 |
131 | [[package]]
132 | name = "libc"
133 | version = "0.2.155"
134 | source = "registry+https://github.com/rust-lang/crates.io-index"
135 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
136 |
137 | [[package]]
138 | name = "lock_api"
139 | version = "0.4.12"
140 | source = "registry+https://github.com/rust-lang/crates.io-index"
141 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
142 | dependencies = [
143 | "autocfg",
144 | "scopeguard",
145 | ]
146 |
147 | [[package]]
148 | name = "log"
149 | version = "0.4.21"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
152 |
153 | [[package]]
154 | name = "lru"
155 | version = "0.12.3"
156 | source = "registry+https://github.com/rust-lang/crates.io-index"
157 | checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
158 | dependencies = [
159 | "hashbrown",
160 | ]
161 |
162 | [[package]]
163 | name = "mio"
164 | version = "0.8.11"
165 | source = "registry+https://github.com/rust-lang/crates.io-index"
166 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
167 | dependencies = [
168 | "libc",
169 | "log",
170 | "wasi",
171 | "windows-sys",
172 | ]
173 |
174 | [[package]]
175 | name = "once_cell"
176 | version = "1.19.0"
177 | source = "registry+https://github.com/rust-lang/crates.io-index"
178 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
179 |
180 | [[package]]
181 | name = "parking_lot"
182 | version = "0.12.2"
183 | source = "registry+https://github.com/rust-lang/crates.io-index"
184 | checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
185 | dependencies = [
186 | "lock_api",
187 | "parking_lot_core",
188 | ]
189 |
190 | [[package]]
191 | name = "parking_lot_core"
192 | version = "0.9.10"
193 | source = "registry+https://github.com/rust-lang/crates.io-index"
194 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
195 | dependencies = [
196 | "cfg-if",
197 | "libc",
198 | "redox_syscall",
199 | "smallvec",
200 | "windows-targets 0.52.5",
201 | ]
202 |
203 | [[package]]
204 | name = "paste"
205 | version = "1.0.15"
206 | source = "registry+https://github.com/rust-lang/crates.io-index"
207 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
208 |
209 | [[package]]
210 | name = "proc-macro2"
211 | version = "1.0.83"
212 | source = "registry+https://github.com/rust-lang/crates.io-index"
213 | checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
214 | dependencies = [
215 | "unicode-ident",
216 | ]
217 |
218 | [[package]]
219 | name = "quote"
220 | version = "1.0.36"
221 | source = "registry+https://github.com/rust-lang/crates.io-index"
222 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
223 | dependencies = [
224 | "proc-macro2",
225 | ]
226 |
227 | [[package]]
228 | name = "ratatui"
229 | version = "0.26.3"
230 | source = "registry+https://github.com/rust-lang/crates.io-index"
231 | checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef"
232 | dependencies = [
233 | "bitflags",
234 | "cassowary",
235 | "compact_str",
236 | "crossterm",
237 | "itertools",
238 | "lru",
239 | "paste",
240 | "stability",
241 | "strum",
242 | "unicode-segmentation",
243 | "unicode-truncate",
244 | "unicode-width",
245 | ]
246 |
247 | [[package]]
248 | name = "ratatui-example"
249 | version = "0.1.0"
250 | dependencies = [
251 | "crossterm",
252 | "ratatui",
253 | ]
254 |
255 | [[package]]
256 | name = "redox_syscall"
257 | version = "0.5.1"
258 | source = "registry+https://github.com/rust-lang/crates.io-index"
259 | checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
260 | dependencies = [
261 | "bitflags",
262 | ]
263 |
264 | [[package]]
265 | name = "rustversion"
266 | version = "1.0.17"
267 | source = "registry+https://github.com/rust-lang/crates.io-index"
268 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
269 |
270 | [[package]]
271 | name = "ryu"
272 | version = "1.0.18"
273 | source = "registry+https://github.com/rust-lang/crates.io-index"
274 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
275 |
276 | [[package]]
277 | name = "scopeguard"
278 | version = "1.2.0"
279 | source = "registry+https://github.com/rust-lang/crates.io-index"
280 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
281 |
282 | [[package]]
283 | name = "signal-hook"
284 | version = "0.3.17"
285 | source = "registry+https://github.com/rust-lang/crates.io-index"
286 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
287 | dependencies = [
288 | "libc",
289 | "signal-hook-registry",
290 | ]
291 |
292 | [[package]]
293 | name = "signal-hook-mio"
294 | version = "0.2.3"
295 | source = "registry+https://github.com/rust-lang/crates.io-index"
296 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
297 | dependencies = [
298 | "libc",
299 | "mio",
300 | "signal-hook",
301 | ]
302 |
303 | [[package]]
304 | name = "signal-hook-registry"
305 | version = "1.4.2"
306 | source = "registry+https://github.com/rust-lang/crates.io-index"
307 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
308 | dependencies = [
309 | "libc",
310 | ]
311 |
312 | [[package]]
313 | name = "smallvec"
314 | version = "1.13.2"
315 | source = "registry+https://github.com/rust-lang/crates.io-index"
316 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
317 |
318 | [[package]]
319 | name = "stability"
320 | version = "0.2.0"
321 | source = "registry+https://github.com/rust-lang/crates.io-index"
322 | checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
323 | dependencies = [
324 | "quote",
325 | "syn",
326 | ]
327 |
328 | [[package]]
329 | name = "static_assertions"
330 | version = "1.1.0"
331 | source = "registry+https://github.com/rust-lang/crates.io-index"
332 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
333 |
334 | [[package]]
335 | name = "strum"
336 | version = "0.26.2"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
339 | dependencies = [
340 | "strum_macros",
341 | ]
342 |
343 | [[package]]
344 | name = "strum_macros"
345 | version = "0.26.2"
346 | source = "registry+https://github.com/rust-lang/crates.io-index"
347 | checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
348 | dependencies = [
349 | "heck",
350 | "proc-macro2",
351 | "quote",
352 | "rustversion",
353 | "syn",
354 | ]
355 |
356 | [[package]]
357 | name = "syn"
358 | version = "2.0.65"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
361 | dependencies = [
362 | "proc-macro2",
363 | "quote",
364 | "unicode-ident",
365 | ]
366 |
367 | [[package]]
368 | name = "unicode-ident"
369 | version = "1.0.12"
370 | source = "registry+https://github.com/rust-lang/crates.io-index"
371 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
372 |
373 | [[package]]
374 | name = "unicode-segmentation"
375 | version = "1.11.0"
376 | source = "registry+https://github.com/rust-lang/crates.io-index"
377 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
378 |
379 | [[package]]
380 | name = "unicode-truncate"
381 | version = "1.0.0"
382 | source = "registry+https://github.com/rust-lang/crates.io-index"
383 | checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
384 | dependencies = [
385 | "itertools",
386 | "unicode-width",
387 | ]
388 |
389 | [[package]]
390 | name = "unicode-width"
391 | version = "0.1.12"
392 | source = "registry+https://github.com/rust-lang/crates.io-index"
393 | checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
394 |
395 | [[package]]
396 | name = "version_check"
397 | version = "0.9.4"
398 | source = "registry+https://github.com/rust-lang/crates.io-index"
399 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
400 |
401 | [[package]]
402 | name = "wasi"
403 | version = "0.11.0+wasi-snapshot-preview1"
404 | source = "registry+https://github.com/rust-lang/crates.io-index"
405 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
406 |
407 | [[package]]
408 | name = "winapi"
409 | version = "0.3.9"
410 | source = "registry+https://github.com/rust-lang/crates.io-index"
411 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
412 | dependencies = [
413 | "winapi-i686-pc-windows-gnu",
414 | "winapi-x86_64-pc-windows-gnu",
415 | ]
416 |
417 | [[package]]
418 | name = "winapi-i686-pc-windows-gnu"
419 | version = "0.4.0"
420 | source = "registry+https://github.com/rust-lang/crates.io-index"
421 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
422 |
423 | [[package]]
424 | name = "winapi-x86_64-pc-windows-gnu"
425 | version = "0.4.0"
426 | source = "registry+https://github.com/rust-lang/crates.io-index"
427 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
428 |
429 | [[package]]
430 | name = "windows-sys"
431 | version = "0.48.0"
432 | source = "registry+https://github.com/rust-lang/crates.io-index"
433 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
434 | dependencies = [
435 | "windows-targets 0.48.5",
436 | ]
437 |
438 | [[package]]
439 | name = "windows-targets"
440 | version = "0.48.5"
441 | source = "registry+https://github.com/rust-lang/crates.io-index"
442 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
443 | dependencies = [
444 | "windows_aarch64_gnullvm 0.48.5",
445 | "windows_aarch64_msvc 0.48.5",
446 | "windows_i686_gnu 0.48.5",
447 | "windows_i686_msvc 0.48.5",
448 | "windows_x86_64_gnu 0.48.5",
449 | "windows_x86_64_gnullvm 0.48.5",
450 | "windows_x86_64_msvc 0.48.5",
451 | ]
452 |
453 | [[package]]
454 | name = "windows-targets"
455 | version = "0.52.5"
456 | source = "registry+https://github.com/rust-lang/crates.io-index"
457 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
458 | dependencies = [
459 | "windows_aarch64_gnullvm 0.52.5",
460 | "windows_aarch64_msvc 0.52.5",
461 | "windows_i686_gnu 0.52.5",
462 | "windows_i686_gnullvm",
463 | "windows_i686_msvc 0.52.5",
464 | "windows_x86_64_gnu 0.52.5",
465 | "windows_x86_64_gnullvm 0.52.5",
466 | "windows_x86_64_msvc 0.52.5",
467 | ]
468 |
469 | [[package]]
470 | name = "windows_aarch64_gnullvm"
471 | version = "0.48.5"
472 | source = "registry+https://github.com/rust-lang/crates.io-index"
473 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
474 |
475 | [[package]]
476 | name = "windows_aarch64_gnullvm"
477 | version = "0.52.5"
478 | source = "registry+https://github.com/rust-lang/crates.io-index"
479 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
480 |
481 | [[package]]
482 | name = "windows_aarch64_msvc"
483 | version = "0.48.5"
484 | source = "registry+https://github.com/rust-lang/crates.io-index"
485 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
486 |
487 | [[package]]
488 | name = "windows_aarch64_msvc"
489 | version = "0.52.5"
490 | source = "registry+https://github.com/rust-lang/crates.io-index"
491 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
492 |
493 | [[package]]
494 | name = "windows_i686_gnu"
495 | version = "0.48.5"
496 | source = "registry+https://github.com/rust-lang/crates.io-index"
497 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
498 |
499 | [[package]]
500 | name = "windows_i686_gnu"
501 | version = "0.52.5"
502 | source = "registry+https://github.com/rust-lang/crates.io-index"
503 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
504 |
505 | [[package]]
506 | name = "windows_i686_gnullvm"
507 | version = "0.52.5"
508 | source = "registry+https://github.com/rust-lang/crates.io-index"
509 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
510 |
511 | [[package]]
512 | name = "windows_i686_msvc"
513 | version = "0.48.5"
514 | source = "registry+https://github.com/rust-lang/crates.io-index"
515 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
516 |
517 | [[package]]
518 | name = "windows_i686_msvc"
519 | version = "0.52.5"
520 | source = "registry+https://github.com/rust-lang/crates.io-index"
521 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
522 |
523 | [[package]]
524 | name = "windows_x86_64_gnu"
525 | version = "0.48.5"
526 | source = "registry+https://github.com/rust-lang/crates.io-index"
527 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
528 |
529 | [[package]]
530 | name = "windows_x86_64_gnu"
531 | version = "0.52.5"
532 | source = "registry+https://github.com/rust-lang/crates.io-index"
533 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
534 |
535 | [[package]]
536 | name = "windows_x86_64_gnullvm"
537 | version = "0.48.5"
538 | source = "registry+https://github.com/rust-lang/crates.io-index"
539 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
540 |
541 | [[package]]
542 | name = "windows_x86_64_gnullvm"
543 | version = "0.52.5"
544 | source = "registry+https://github.com/rust-lang/crates.io-index"
545 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
546 |
547 | [[package]]
548 | name = "windows_x86_64_msvc"
549 | version = "0.48.5"
550 | source = "registry+https://github.com/rust-lang/crates.io-index"
551 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
552 |
553 | [[package]]
554 | name = "windows_x86_64_msvc"
555 | version = "0.52.5"
556 | source = "registry+https://github.com/rust-lang/crates.io-index"
557 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
558 |
559 | [[package]]
560 | name = "zerocopy"
561 | version = "0.7.34"
562 | source = "registry+https://github.com/rust-lang/crates.io-index"
563 | checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
564 | dependencies = [
565 | "zerocopy-derive",
566 | ]
567 |
568 | [[package]]
569 | name = "zerocopy-derive"
570 | version = "0.7.34"
571 | source = "registry+https://github.com/rust-lang/crates.io-index"
572 | checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
573 | dependencies = [
574 | "proc-macro2",
575 | "quote",
576 | "syn",
577 | ]
578 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ratatui-example/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "ratatui-example"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | crossterm = "0.27.0"
8 | ratatui = "0.26.3"
9 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/ratatui-example/src/main.rs:
--------------------------------------------------------------------------------
1 | use crossterm::{
2 | event::{self, KeyCode, KeyEventKind},
3 | terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
4 | ExecutableCommand,
5 | };
6 | use ratatui::{prelude::*, widgets::*};
7 | use std::{
8 | io::{stdout, Result},
9 | path::PathBuf,
10 | };
11 |
12 | fn main() -> Result<()> {
13 | stdout().execute(EnterAlternateScreen)?;
14 | enable_raw_mode()?;
15 | let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
16 | terminal.clear()?;
17 |
18 | let mut cwd = PathBuf::from(".");
19 | let mut selected = 0;
20 | let mut state = ListState::default();
21 | let mut entries: Vec = std::fs::read_dir(cwd.clone())
22 | .unwrap()
23 | .map(|entry| entry.unwrap().file_name())
24 | .map(|s| s.into_string().unwrap())
25 | .collect::>();
26 |
27 | loop {
28 | if event::poll(std::time::Duration::from_millis(16))? {
29 | if let event::Event::Key(key) = event::read()? {
30 | if key.kind == KeyEventKind::Press {
31 | match key.code {
32 | KeyCode::Char('q') => {
33 | break;
34 | }
35 | KeyCode::Up => selected = (entries.len() + selected - 1) % entries.len(),
36 | KeyCode::Down => selected = (selected + 1) % entries.len(),
37 | KeyCode::Enter => {
38 | cwd = cwd.join(entries[selected].clone());
39 | entries = std::fs::read_dir(cwd.clone())
40 | .unwrap()
41 | .map(|entry| entry.unwrap().file_name())
42 | .map(|s| s.into_string().unwrap())
43 | .collect::>();
44 | selected = 0;
45 | }
46 | _ => {}
47 | }
48 | }
49 | }
50 | }
51 | let list = List::new(entries.clone())
52 | .block(Block::bordered().title("Directory Entries"))
53 | .style(Style::default().fg(Color::White))
54 | .highlight_style(
55 | Style::default()
56 | .add_modifier(Modifier::BOLD)
57 | .bg(Color::White)
58 | .fg(Color::Black),
59 | )
60 | .highlight_symbol(">");
61 |
62 | terminal.draw(|frame| {
63 | let area = frame.size();
64 | state.select(Some(selected));
65 | frame.render_stateful_widget(list, area, &mut state);
66 | })?;
67 | }
68 |
69 | stdout().execute(LeaveAlternateScreen)?;
70 | disable_raw_mode()?;
71 | Ok(())
72 | }
73 |
--------------------------------------------------------------------------------
/tui-libraries-for-interactive-apps/textual/app.py:
--------------------------------------------------------------------------------
1 | import os
2 | from textual.app import App
3 | from textual.widgets import Header, Footer, Button, Static
4 |
5 |
6 | def create_id(s:str):
7 | return s.replace(".","_").replace("@","_")
8 |
9 | class DirDisplay(Static):
10 | directory = "."
11 | dir_list = [Button(x,id=x) for x in os.listdir(".")]
12 |
13 | def on_button_pressed(self, event):
14 | self.directory = os.path.join(self.directory,str(event.button.label))
15 | self.dir_list = [];
16 | for dir in os.listdir(self.directory):
17 | self.dir_list.append(Button(dir,id=create_id(dir)))
18 | self.remove_children()
19 | self.mount_all(self.dir_list)
20 |
21 | def compose(self):
22 | return self.dir_list
23 |
24 | class Explorer(App):
25 |
26 | def compose(self):
27 | yield Header()
28 | yield DirDisplay()
29 | yield Footer()
30 |
31 | if __name__ == "__main__":
32 | # print(os.listdir("."))
33 | app = Explorer()
34 | app.run()
35 |
--------------------------------------------------------------------------------