├── ffi ├── README.md ├── secp256k1-sys │ ├── wrapper.h │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── include │ │ └── test.c ├── example_04 │ ├── ffi │ │ ├── cbindgen.toml │ │ ├── example_04_header.h │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── Cargo.toml │ ├── .gitignore │ ├── README.md │ ├── python │ │ └── main.py │ └── src │ │ └── lib.rs ├── example_10 │ ├── csrc │ │ ├── compile.sh │ │ └── sumsquare.c │ ├── libsumsquare.so │ ├── Cargo.toml │ ├── .gitignore │ └── src │ │ └── main.rs ├── c-call-rust │ ├── src │ │ ├── c_call_rust.h │ │ └── lib.rs │ ├── makefile │ ├── c-src │ │ └── main.c │ ├── Cargo.toml │ └── .gitignore ├── example_09 │ ├── libcfoo.so │ ├── csrc │ │ ├── libcfoo.so │ │ └── cfoo.c │ ├── Cargo.toml │ ├── .gitignore │ └── src │ │ └── main.rs ├── example_02 │ ├── src │ │ ├── example_02.h │ │ └── lib.rs │ ├── Makefile │ ├── Cargo.toml │ ├── .gitignore │ └── csrc │ │ └── main.c ├── rust-call-c │ ├── src │ │ ├── ffi_array │ │ │ ├── libc_utils.so │ │ │ └── c_utils.c │ │ ├── ffi_opaque │ │ │ ├── libffi_test.so │ │ │ ├── Makefile │ │ │ ├── ffi_test.h │ │ │ └── ffi_test.c │ │ ├── array.rs │ │ ├── time.rs │ │ ├── opaque.rs │ │ ├── main.rs │ │ └── layout.rs │ ├── Cargo.toml │ └── .gitignore ├── example_01 │ ├── src │ │ ├── example_01.h │ │ └── lib.rs │ ├── Makefile │ ├── Cargo.toml │ ├── .gitignore │ └── csrc │ │ └── hello.c ├── example_03 │ ├── cbindgen.toml │ ├── src │ │ ├── example_03.h │ │ └── lib.rs │ ├── Cargo.toml │ ├── Makefile │ ├── example_03_header.h │ ├── .gitignore │ └── csrc │ │ └── main.c ├── nix │ ├── Cargo.toml │ ├── src │ │ ├── main.c │ │ └── main.rs │ └── .gitignore └── string │ ├── rustre │ ├── Cargo.toml │ ├── .gitignore │ └── src │ │ └── main.rs │ ├── print.js │ ├── woops.c │ ├── print.c │ └── upper.c ├── basics ├── 01_hello │ ├── .gitignore │ ├── Cargo.toml │ ├── Cargo.lock │ ├── README.md │ └── src │ │ └── main.rs ├── 02_ownership │ ├── .gitignore │ ├── Cargo.toml │ ├── Cargo.lock │ ├── README.md │ └── src │ │ └── main.rs ├── 03_generics │ ├── .gitignore │ ├── Cargo.toml │ ├── Cargo.lock │ ├── README.md │ └── src │ │ └── main.rs ├── 04_modules │ ├── .gitignore │ ├── Cargo.toml │ ├── Cargo.lock │ ├── src │ │ ├── main.rs │ │ ├── text.rs │ │ ├── maps.rs │ │ ├── slices.rs │ │ └── vectors.rs │ └── README.md ├── 05_error_handling │ ├── .gitignore │ ├── Cargo.toml │ ├── Cargo.lock │ ├── tests │ │ └── integration_test.rs │ ├── src │ │ ├── main.rs │ │ └── lib.rs │ └── README.md └── README.md ├── hands-on ├── merkle-tree │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── Cargo.lock │ └── src │ │ └── lib.rs ├── httpie-lite │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── rust-codes-flow │ ├── .gitignore │ ├── rust-toolchain.toml │ ├── rustfmt.toml │ ├── cli │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── Cargo.toml │ ├── core │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ ├── models.rs │ │ │ ├── normalizer.rs │ │ │ ├── hashing.rs │ │ │ └── extractor.rs │ ├── README.md │ └── Cargo.lock └── README.md ├── .gitignore └── README.md /ffi/README.md: -------------------------------------------------------------------------------- 1 | # FFI -------------------------------------------------------------------------------- /basics/01_hello/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /basics/02_ownership/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /basics/03_generics/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /basics/04_modules/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /hands-on/merkle-tree/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /basics/05_error_handling/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /hands-on/httpie-lite/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | CLAUDE.md -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /ffi/secp256k1-sys/wrapper.h: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /ffi/secp256k1-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /ffi/example_04/ffi/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | no_includes = true -------------------------------------------------------------------------------- /ffi/example_10/csrc/compile.sh: -------------------------------------------------------------------------------- 1 | gcc -fPIC -shared -o ../libsumsquare.so sumsquare.c -------------------------------------------------------------------------------- /ffi/c-call-rust/src/c_call_rust.h: -------------------------------------------------------------------------------- 1 | void call_from_rust(); 2 | int sum(const int* my_array, int length); -------------------------------------------------------------------------------- /ffi/example_09/libcfoo.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesterli/rust-practice/HEAD/ffi/example_09/libcfoo.so -------------------------------------------------------------------------------- /ffi/example_09/csrc/libcfoo.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesterli/rust-practice/HEAD/ffi/example_09/csrc/libcfoo.so -------------------------------------------------------------------------------- /ffi/example_10/libsumsquare.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesterli/rust-practice/HEAD/ffi/example_10/libsumsquare.so -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = ["rustfmt", "clippy"] 4 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2024" 2 | use_try_shorthand = true 3 | use_field_init_shorthand = true 4 | -------------------------------------------------------------------------------- /basics/01_hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ffi/example_02/src/example_02.h: -------------------------------------------------------------------------------- 1 | int handle_result(const char *str); 2 | int handle_option(float x, float y); 3 | int no_panic(); -------------------------------------------------------------------------------- /basics/03_generics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generics" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /basics/04_modules/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "modules" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /basics/02_ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ownership" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /basics/05_error_handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "error-handling" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /ffi/rust-call-c/src/ffi_array/libc_utils.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesterli/rust-practice/HEAD/ffi/rust-call-c/src/ffi_array/libc_utils.so -------------------------------------------------------------------------------- /ffi/rust-call-c/src/ffi_opaque/libffi_test.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesterli/rust-practice/HEAD/ffi/rust-call-c/src/ffi_opaque/libffi_test.so -------------------------------------------------------------------------------- /ffi/example_01/src/example_01.h: -------------------------------------------------------------------------------- 1 | void print_str(const char *str); 2 | char *change_str(char str[]); 3 | char *generate_str(); 4 | void free_str(char *); -------------------------------------------------------------------------------- /hands-on/merkle-tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "merkle-tree" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | ring = "0.17.14" -------------------------------------------------------------------------------- /ffi/example_03/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | no_includes = true 3 | 4 | [export] 5 | prefix = "capi_" 6 | 7 | [enum] 8 | rename_variants = "SnakeCase" -------------------------------------------------------------------------------- /basics/01_hello/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "hello" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /basics/04_modules/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "modules" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /basics/02_ownership/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ownership" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /basics/03_generics/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "generics" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /basics/05_error_handling/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "error-handling" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /ffi/rust-call-c/src/ffi_array/c_utils.c: -------------------------------------------------------------------------------- 1 | int sum(const int* my_array, int length) { 2 | int total = 0; 3 | 4 | for(int i = 0; i < length; i++) { 5 | total += my_array[i]; 6 | } 7 | 8 | return total; 9 | } -------------------------------------------------------------------------------- /ffi/rust-call-c/src/ffi_opaque/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Werror 3 | OBJ=libffi_test.o 4 | 5 | libffi_test.so: $(OBJ) 6 | gcc -shared -o $@ $(OBJ) 7 | 8 | $(OBJ): ffi_test.c ffi_test.h 9 | $(CC) -c -o $(OBJ) -fpic $< $(CFLAGS) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Practice 2 | 3 | It includes basics learning, hands-on projects, and cross-language interoperability examples. 4 | 5 | * [Basics](./basics/README.md) 6 | * [Hands-on](./hands-on/README.md) 7 | * [FFI](./ffi/README.md) 8 | -------------------------------------------------------------------------------- /ffi/example_10/csrc/sumsquare.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef void (*SumSquareCB)(int result, void *user_data); 4 | 5 | void sum_square_cb(int a, int b, SumSquareCB cb, void *user_data) { 6 | int result = a*a + b*b; 7 | cb(result, user_data); 8 | } -------------------------------------------------------------------------------- /ffi/example_09/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_09" 3 | version = "0.1.0" 4 | authors = ["ubuntu"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /ffi/example_03/src/example_03.h: -------------------------------------------------------------------------------- 1 | typedef struct Student 2 | { 3 | int num; 4 | int total; 5 | char name[20]; 6 | float scores[3]; 7 | } Student; 8 | 9 | Student *student_new(); 10 | Student *student_alice(); 11 | void student_free(Student *p_stu); 12 | -------------------------------------------------------------------------------- /ffi/example_10/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_10" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /ffi/nix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nix" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | nix = "0.17.0" -------------------------------------------------------------------------------- /ffi/string/rustre/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustre" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /ffi/example_04/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_04" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | 11 | -------------------------------------------------------------------------------- /ffi/string/print.js: -------------------------------------------------------------------------------- 1 | // in `print.js` 2 | 3 | const { argv, stdout } = process; 4 | 5 | // we have to skip *two* arguments: the path to node, 6 | // and the path to our script 7 | for (const arg of argv.slice(2)) { 8 | stdout.write(arg.toUpperCase()); 9 | stdout.write("\n"); 10 | } -------------------------------------------------------------------------------- /ffi/rust-call-c/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-call-c" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc = "0.2" 11 | libloading = "0.6.2" -------------------------------------------------------------------------------- /ffi/c-call-rust/makefile: -------------------------------------------------------------------------------- 1 | GCC_BIN ?= $(shell which gcc) 2 | CARGO_BIN ?= $(shell which cargo) 3 | 4 | run: clean build 5 | ./c-src/main 6 | 7 | clean: 8 | $(CARGO_BIN) clean 9 | rm -f ./c-src/main 10 | 11 | build: 12 | $(CARGO_BIN) build 13 | $(GCC_BIN) -o ./c-src/main ./c-src/main.c -Isrc -L ./target/debug -lc_call_rust -------------------------------------------------------------------------------- /ffi/example_03/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_03" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | 11 | 12 | [lib] 13 | crate-type = ["cdylib"] -------------------------------------------------------------------------------- /ffi/example_02/Makefile: -------------------------------------------------------------------------------- 1 | GCC_BIN ?= $(shell which gcc) 2 | CARGO_BIN ?= $(shell which cargo) 3 | 4 | run: clean build 5 | ./csrc/main 6 | 7 | clean: 8 | $(CARGO_BIN) clean 9 | rm -f ./csrc/main 10 | 11 | build: 12 | $(CARGO_BIN) build --release 13 | $(GCC_BIN) -o ./csrc/main ./csrc/main.c -Isrc -L. -l:target/release/libexample_02.so -------------------------------------------------------------------------------- /ffi/example_03/Makefile: -------------------------------------------------------------------------------- 1 | GCC_BIN ?= $(shell which gcc) 2 | CARGO_BIN ?= $(shell which cargo) 3 | 4 | run: clean build 5 | ./csrc/main 6 | 7 | clean: 8 | $(CARGO_BIN) clean 9 | rm -f ./csrc/main 10 | 11 | build: 12 | $(CARGO_BIN) build --release 13 | $(GCC_BIN) -o ./csrc/main ./csrc/main.c -Isrc -L. -l:target/release/libexample_03.so -------------------------------------------------------------------------------- /ffi/example_01/Makefile: -------------------------------------------------------------------------------- 1 | GCC_BIN ?= $(shell which gcc) 2 | CARGO_BIN ?= $(shell which cargo) 3 | 4 | run: clean build 5 | ./csrc/hello 6 | 7 | clean: 8 | $(CARGO_BIN) clean 9 | rm -f ./csrc/hello 10 | 11 | build: 12 | $(CARGO_BIN) build --release 13 | $(GCC_BIN) -o ./csrc/hello ./csrc/hello.c -Isrc -L. -l:target/release/libexample_01.so -------------------------------------------------------------------------------- /ffi/example_04/ffi/example_04_header.h: -------------------------------------------------------------------------------- 1 | typedef struct c_tuple { 2 | unsigned int integer; 3 | bool boolean; 4 | } c_tuple; 5 | 6 | unsigned int fibonacci(unsigned int index); 7 | 8 | unsigned int count_char(const char *s); 9 | 10 | struct c_tuple handle_tuple(struct c_tuple tup); 11 | 12 | int sum_of_even(const int *ptr, size_t len); 13 | -------------------------------------------------------------------------------- /ffi/nix/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | pid_t child = fork(); 7 | if (child) 8 | { 9 | sleep(5); 10 | kill(child, SIGKILL); 11 | } 12 | else 13 | { 14 | for (;;) 15 | // 循环直到被 kill 掉 16 | ; 17 | } 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /ffi/c-call-rust/c-src/main.c: -------------------------------------------------------------------------------- 1 | #include "c_call_rust.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | call_from_rust(); 8 | 9 | int my_array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 10 | int total = sum(my_array, 10); 11 | printf("The total is %d\n", total); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /ffi/example_01/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_01" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | 11 | [lib] 12 | name = "example_01" 13 | crate-type = ["staticlib", "cdylib"] -------------------------------------------------------------------------------- /ffi/c-call-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c-call-rust" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | 11 | [lib] 12 | name = "c_call_rust" 13 | crate-type = ["staticlib", "cdylib"] 14 | -------------------------------------------------------------------------------- /ffi/example_04/ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_04" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc = "0.2" 11 | 12 | [lib] 13 | name = "example_04" 14 | crate-type = ["cdylib"] -------------------------------------------------------------------------------- /basics/04_modules/src/main.rs: -------------------------------------------------------------------------------- 1 | // Exercises for Module 4 — Modules & Collections 2 | // Run: cargo run 3 | mod vectors; 4 | mod maps; 5 | mod text; 6 | mod slices; 7 | 8 | fn main() { 9 | println!("=== Modules & Collections ===\n"); 10 | 11 | vectors::run(); 12 | maps::run(); 13 | text::run(); 14 | slices::run(); 15 | 16 | println!("\n=== End ==="); 17 | } -------------------------------------------------------------------------------- /ffi/example_02/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_02" 3 | version = "0.1.0" 4 | authors = ["lesterli "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | rand = "0.7.3" 11 | 12 | [lib] 13 | name = "example_02" 14 | crate-type = ["staticlib", "cdylib"] -------------------------------------------------------------------------------- /ffi/nix/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /ffi/c-call-rust/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /ffi/example_03/example_03_header.h: -------------------------------------------------------------------------------- 1 | typedef enum { 2 | boy, 3 | girl, 4 | } capi_gender; 5 | 6 | typedef struct { 7 | int num; 8 | int total; 9 | char name[20]; 10 | float scores[3]; 11 | capi_gender gender; 12 | } capi_student; 13 | 14 | capi_student *student_new(void); 15 | 16 | capi_student *student_alice(void); 17 | 18 | void student_free(capi_student *p_stu); 19 | -------------------------------------------------------------------------------- /ffi/rust-call-c/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /ffi/rust-call-c/src/ffi_opaque/ffi_test.h: -------------------------------------------------------------------------------- 1 | #ifndef FFI_TEST_H 2 | #define FFI_TEST_H 3 | 4 | #include 5 | 6 | struct object; 7 | 8 | struct object* init(void); 9 | void free_object(struct object*); 10 | int get_api_version(void); 11 | int get_info(const struct object*); 12 | void set_info(struct object*, int); 13 | size_t sizeof_obj(void); 14 | 15 | #endif /* FFI_TEST_H */ -------------------------------------------------------------------------------- /ffi/secp256k1-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libsecp256k1-sys" 3 | version = "0.1.0" 4 | authors = ["ubuntu"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | name = "secp256k1" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | 15 | [build-dependencies] 16 | bindgen = "0.55.1" 17 | -------------------------------------------------------------------------------- /ffi/string/rustre/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /hands-on/httpie-lite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "httpie-lite" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | anyhow = "1.0.100" 8 | clap = { version = "4.5.53", features = ["derive"] } 9 | colored = "3.0.0" 10 | mime = "0.3.17" 11 | reqwest = { version = "0.12.24", features = ["json"] } 12 | syntect = "5.3.0" 13 | tokio = { version = "1.48.0", features = ["full"] } -------------------------------------------------------------------------------- /ffi/example_01/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | vgcore.* 13 | -------------------------------------------------------------------------------- /ffi/example_02/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | vgcore.* 13 | -------------------------------------------------------------------------------- /ffi/example_03/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | vgcore.* 13 | -------------------------------------------------------------------------------- /ffi/example_09/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | vgcore.* 13 | -------------------------------------------------------------------------------- /ffi/example_10/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | vgcore.* 13 | -------------------------------------------------------------------------------- /ffi/string/woops.c: -------------------------------------------------------------------------------- 1 | // in `woops.c` 2 | 3 | #include 4 | #include 5 | 6 | void uppercase(char *s) { 7 | // this is peak C right there 8 | do { 9 | *s = toupper(*s); 10 | } while (*s++); 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | char *arg = argv[1]; 15 | 16 | char *upp = arg; 17 | uppercase(upp); 18 | 19 | printf("upp = %s\n", upp); 20 | } -------------------------------------------------------------------------------- /ffi/example_04/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target 4 | **/target 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | vgcore.* 14 | -------------------------------------------------------------------------------- /ffi/rust-call-c/src/array.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_int; 2 | 3 | // 对 C 库中的 sum 函数进行 Rust 绑定: 4 | extern "C" { 5 | fn sum(my_array: *const c_int, length: c_int) -> c_int; 6 | } 7 | 8 | fn main() { 9 | let numbers: [c_int; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 10 | 11 | unsafe { 12 | let total = sum(numbers.as_ptr(), numbers.len() as c_int); 13 | println!("The total is {}", total); 14 | 15 | assert_eq!(total, numbers.iter().sum()); 16 | } 17 | } -------------------------------------------------------------------------------- /ffi/example_04/README.md: -------------------------------------------------------------------------------- 1 | ## example_04 2 | 3 | 在 Python 中调用 Rust 代码的示例。 4 | 5 | * Python FFI 库有 [ctypes](https://docs.python.org/3/library/ctypes.html) 和 [cffi](https://cffi.readthedocs.io/en/latest/) 库 6 | 7 | ### 示例说明 8 | 9 | * `src` 目录: Rust 源码 10 | * `ffi` 目录: Rust 源码导出的 FFI 代码 11 | * `python` 目录: Python 源码 12 | * 本示例采用的是 `cffi` 库 13 | 14 | ### 运行步骤 15 | 16 | 1. 在 `ffi` 目录下,执行 `cargo build` 生成 Rust 源码的动态库(Linux平台,`libexample_04.so`)。 17 | 2. 在 `python` 目录下,运行 `python main.py`。 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ffi/string/rustre/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut upp = String::with_capacity(512); 3 | for arg in std::env::args().skip(1) { 4 | upp.clear(); 5 | uppercase(&arg, &mut upp); 6 | println!("upp = {}", upp); 7 | println!("arg = {}", arg); 8 | } 9 | } 10 | 11 | // was `mut dst: String` 12 | fn uppercase(src: &str, dst: &mut String) { 13 | for c in src.chars() { 14 | for c in c.to_uppercase() { 15 | dst.push(c); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /hands-on/README.md: -------------------------------------------------------------------------------- 1 | # Hands-on projects 2 | 3 | * [httpie-lite](./httpie-lite/README.md), minimal async HTTP client CLI to demo request/response handling, pretty printing, and basic POST/GET usage. 4 | * [merkle-tree](./merkle-tree/README.md), simple Merkle Tree implementation with root computation, proof generation and verification. 5 | * [rust-codes-flow](./rust-codes-flow/README.md), a fast CLI tool that converts any public Rust GitHub repository into a clean JSONL dataset optimized for training AI coding agents. 6 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | edition = "2024" 5 | license = "MIT" 6 | description = "CLI tool for converting Rust repositories to JSONL datasets for AI training" 7 | 8 | [dependencies] 9 | core = { path = "../core" } 10 | clap = { version = "4.4", features = ["derive"] } 11 | rayon = "1.8" 12 | walkdir = "2.4" 13 | indicatif = "0.18" 14 | anyhow = "1.0" 15 | serde_json = "1.0" 16 | num_cpus = "1.16" 17 | 18 | [[bin]] 19 | name = "rustcodesflow" 20 | path = "src/main.rs" 21 | -------------------------------------------------------------------------------- /basics/04_modules/src/text.rs: -------------------------------------------------------------------------------- 1 | // String examples 2 | pub fn run() { 3 | println!("-- text (String) module --"); 4 | string_example(); 5 | } 6 | 7 | fn string_example() { 8 | let mut s = String::from("str"); 9 | s.push_str("add"); 10 | println!("s {}", s); 11 | 12 | // iterate characters and bytes 13 | println!("chars:"); 14 | for c in s.chars() { 15 | println!(" char {}", c); 16 | } 17 | 18 | println!("bytes:"); 19 | for b in s.bytes() { 20 | println!(" byte {}", b); 21 | } 22 | } -------------------------------------------------------------------------------- /ffi/rust-call-c/src/time.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, size_t}; 2 | 3 | #[repr(C)] 4 | pub struct tm { 5 | pub tm_sec: c_int, 6 | pub tm_min: c_int, 7 | pub tm_hour: c_int, 8 | pub tm_mday: c_int, 9 | pub tm_mon: c_int, 10 | pub tm_year: c_int, 11 | pub tm_wday: c_int, 12 | pub tm_yday: c_int, 13 | pub tm_isdst: c_int, 14 | } 15 | 16 | 17 | extern { 18 | // 标准库 strftime函数的 Rust FFI 绑定 19 | #[link_name = "strftime"] 20 | pub fn strftime_in_rust(stra: *mut u8, maxsize: size_t, format: *const u8, timeptr: *mut tm) -> size_t; 21 | } -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "core", 4 | "cli" 5 | ] 6 | 7 | resolver = "3" 8 | 9 | [workspace.dependencies] 10 | syn = { version = "2.0", features = ["full", "visit-mut", "extra-traits"] } 11 | quote = "1.0" 12 | proc-macro2 = "1.0" 13 | prettyplease = "0.2" 14 | blake3 = "1.5" 15 | serde = { version = "1.0", features = ["derive"] } 16 | serde_json = "1.0" 17 | rayon = "1.8" 18 | walkdir = "2.4" 19 | indicatif = { version = "0.18", features = ["rayon"] } 20 | anyhow = "1.0" 21 | clap = { version = "4.4", features = ["derive"] } 22 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core" 3 | version = "0.1.0" 4 | edition = "2024" 5 | license = "MIT" 6 | description = "Core library for Rust code parsing, normalization, and dataset generation" 7 | 8 | [dependencies] 9 | syn = { version = "2.0", features = ["full", "visit-mut", "extra-traits"] } 10 | quote = "1.0" 11 | proc-macro2 = "1.0" 12 | prettyplease = "0.2" 13 | blake3 = "1.5" 14 | serde = { version = "1.0", features = ["derive"] } 15 | serde_json = "1.0" 16 | anyhow = "1.0" 17 | thiserror = "2.0" 18 | hex = "0.4" 19 | 20 | [lib] 21 | path = "src/lib.rs" 22 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! RustCodeFlow Core Library 2 | //! 3 | //! Core library for parsing, normalizing, and extracting Rust code items 4 | //! into structured datasets for AI training. 5 | 6 | pub mod extractor; 7 | pub mod hashing; 8 | pub mod models; 9 | pub mod normalizer; 10 | 11 | pub use models::{ 12 | Content, CoreError, CoreResult, ExtractConfig, ExtractedItem, ItemKind, ItemMeta, 13 | ProjectContext, RagContext, 14 | }; 15 | 16 | pub use extractor::extract_items_from_file; 17 | pub use hashing::{HashStats, hash_and_update_items}; 18 | pub use normalizer::normalize_items; 19 | -------------------------------------------------------------------------------- /ffi/secp256k1-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | fn main() { 5 | println!("cargo:rustc-link-lib=secp256k1"); 6 | println!("cargo:rerun-if-changed=wrapper.h"); 7 | 8 | let bindings = bindgen::Builder::default() 9 | .header("wrapper.h") 10 | .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 11 | .generate() 12 | .expect("Unable to generate bindings"); 13 | 14 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 15 | bindings 16 | .write_to_file(out_path.join("bindings.rs")) 17 | .expect("Couldn't write bindings!"); 18 | } -------------------------------------------------------------------------------- /ffi/example_02/csrc/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "example_02.h" 4 | 5 | int main(void) { 6 | char version[] = "v1"; 7 | printf("C call Rust Result: %d\n", handle_result(version)); 8 | *(version+1) = '2'; 9 | printf("C call Rust Result: %d\n", handle_result(version)); 10 | char *n_version = NULL; 11 | printf("C call Rust Result error: %d\n", handle_result(n_version)); 12 | 13 | float x = 2.0, y = 3.0; 14 | printf("C call Rust Option value: %d\n", handle_option(x, y)); 15 | x = 2.0, y = 0.0; 16 | printf("C call Rust Option None: %d\n", handle_option(x, y)); 17 | 18 | printf("C call Rust panic: %d\n", no_panic()); 19 | } -------------------------------------------------------------------------------- /ffi/rust-call-c/src/opaque.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_int; 2 | 3 | 4 | #[repr(C)] 5 | pub struct OpaqueObject { 6 | _private: [u8; 0], 7 | } 8 | 9 | extern "C" { 10 | pub fn free_object(obj: *mut OpaqueObject); 11 | pub fn init() -> *mut OpaqueObject; 12 | pub fn get_api_version() -> c_int; 13 | pub fn get_info(obj: *const OpaqueObject) -> c_int; 14 | pub fn set_info(obj: *mut OpaqueObject, info: c_int); 15 | } 16 | 17 | fn main() { 18 | unsafe { 19 | let obj = init(); 20 | println!("Original value: {}", get_info(obj)); 21 | 22 | set_info(obj, 521); 23 | println!("New value: {}", get_info(obj)); 24 | } 25 | } -------------------------------------------------------------------------------- /ffi/example_09/csrc/cfoo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef struct Student 6 | { 7 | int num; 8 | int total; 9 | char name[20]; 10 | float scores[3]; 11 | } Student; 12 | 13 | void print_data(Student *stu) 14 | { 15 | printf("C side print: %d %s %d %.2f %.2f %.2f\n", 16 | stu->num, 17 | stu->name, 18 | stu->total, 19 | stu->scores[0], 20 | stu->scores[1], 21 | stu->scores[2]); 22 | } 23 | 24 | void fill_data(Student *stu) 25 | { 26 | stu->num = 2; 27 | stu->total = 100; 28 | strcpy(stu->name, "Bob"); 29 | stu->scores[0] = 60.6; 30 | stu->scores[1] = 70.7; 31 | stu->scores[2] = 80.8; 32 | } -------------------------------------------------------------------------------- /basics/05_error_handling/tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::path::PathBuf; 3 | 4 | use error_handling::{parse_number, read_username_from_file, safe_divide}; 5 | 6 | #[test] 7 | fn integration_parse_and_divide() { 8 | assert_eq!(parse_number("8").unwrap(), 8); 9 | assert!(safe_divide(4, 2).is_ok()); 10 | assert!(safe_divide(4, 0).is_err()); 11 | } 12 | 13 | #[test] 14 | fn integration_read_username() { 15 | let mut path: PathBuf = std::env::temp_dir(); 16 | path.push("rust_integration_user.txt"); 17 | fs::write(&path, "integration_user\n").expect("write tmp"); 18 | let got = read_username_from_file(path.to_str().unwrap()).expect("read file"); 19 | assert_eq!(got, "integration_user"); 20 | let _ = fs::remove_file(path); 21 | } -------------------------------------------------------------------------------- /ffi/c-call-rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::os::raw::c_char; 3 | use std::os::raw::c_int; 4 | use std::slice; 5 | 6 | #[no_mangle] 7 | pub extern "C" fn call_from_rust() { 8 | println!("This is a Rust function for C!"); 9 | } 10 | 11 | // #[no_mangle] 12 | // pub extern rust_printer(input: *const c_char) -> *mut c_char { 13 | // let mut hello = String::from("Hello World!"); 14 | // let c_str_to_print = CString::new(hello).unwrap(); 15 | // // 使用 as_ptr 将 CString 转化成 char 指针传给 C 函数 16 | // c_str_to_print.as_ptr() 17 | // } 18 | 19 | 20 | #[no_mangle] 21 | pub extern fn sum(array: *const c_int, length: c_int) -> c_int { 22 | assert!(!array.is_null(), "Null pointer in sum()"); 23 | unsafe { 24 | let array: &[c_int] = slice::from_raw_parts(array, length as usize); 25 | array.into_iter().sum() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hands-on/merkle-tree/README.md: -------------------------------------------------------------------------------- 1 | ## Merkle Tree 2 | 3 | The implementation of Merkle Tree in Rust. 4 | 5 | This tree is stored in a vector. 6 | 7 | For example, there are four items, merkle tree is kept like: 8 | [hash0,hash1,hash2,hash3,hash01,hash23,root] 9 | 10 | While building a tree, if there is an odd number of nodes at the given level, the last node will be duplicated. 11 | 12 | ### Run unit tests 13 | 14 | ```bash 15 | cargo test 16 | ``` 17 | 18 | ### Usage example 19 | 20 | ```Rust 21 | use ring::digest::{Algorithm, SHA512}; 22 | use merkle_tree::MerkleTree; 23 | 24 | static ALGO: &'static Algorithm = &SHA512; 25 | 26 | fn main() { 27 | let values = vec!["one", "two", "three", "four"]; 28 | let tree = MerkleTree::new(&values, ALGO); 29 | let proof = tree.build_proof(&"one"); 30 | let vec = proof.unwrap(); 31 | tree.validate(&vec); 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /ffi/secp256k1-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | 5 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use super::*; 10 | 11 | #[test] 12 | fn test_context_sign() { 13 | assert_eq!(SECP256K1_CONTEXT_SIGN, 513); 14 | } 15 | 16 | #[test] 17 | fn test_create_pubkey() { 18 | // secp256k1返回公钥 19 | let mut pubkey: secp256k1_pubkey = secp256k1_pubkey { 20 | data: [0; 64], 21 | }; 22 | let prikey: u8 = 1; 23 | 24 | unsafe { 25 | let context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); 26 | assert!(!context.is_null()); 27 | let ret = secp256k1_ec_pubkey_create(& *context, &mut pubkey, &prikey); 28 | assert_eq!(ret, 1); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /hands-on/httpie-lite/README.md: -------------------------------------------------------------------------------- 1 | ## httpie-lite 2 | 3 | A [HTTPie](https://github.com/httpie/cli)-like CLI demo written in Rust. Implements async HTTP requests (`GET`/`POST`) with pretty printing and optional syntax highlighting. 4 | 5 | - `clap` for CLI parsing (subcommands: `get`, `post`). 6 | - `reqwest` + `tokio` for async HTTP requests. 7 | - Prints status, headers and body; uses `mime` to detect content type. 8 | - Syntax highlighting for JSON/HTML via `syntect` and `colored` output. 9 | - Error handling with `anyhow`. 10 | 11 | ### Quick start 12 | 13 | - Build 14 | 15 | ```bash 16 | cd hands-on/httpie-lite 17 | cargo build 18 | ``` 19 | 20 | - GET 21 | 22 | ```bash 23 | cargo run --bin httpie-lite -- get https://httpbin.org/get 24 | ``` 25 | 26 | - POST (simple key=value form -> JSON body) 27 | 28 | ```bash 29 | cargo run --bin httpie-lite -- post https://httpbin.org/post name=alice age=30 30 | ``` -------------------------------------------------------------------------------- /basics/04_modules/src/maps.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | // HashMap examples and a word counter 4 | pub fn run() { 5 | println!("-- maps (HashMap) module --"); 6 | hashmap_example(); 7 | count_words(); 8 | } 9 | 10 | fn hashmap_example() { 11 | let mut hmap = HashMap::new(); 12 | hmap.insert("one", 1); 13 | hmap.insert("one", 2); // demonstrate overwrite 14 | for (key, value) in &hmap { 15 | println!("init hashmap {}: {}", key, value); 16 | } 17 | { 18 | let val = hmap.entry("two").or_insert(3); 19 | println!("insert {}", val); 20 | } 21 | println!("after insert hashmap {:?}", hmap); 22 | } 23 | 24 | fn count_words() { 25 | let text = "hello world wonderful world"; 26 | let mut map = HashMap::new(); 27 | 28 | for word in text.split_whitespace() { 29 | let count = map.entry(word).or_insert(0); 30 | *count += 1; 31 | } 32 | println!("word counts: {:?}", map); 33 | } -------------------------------------------------------------------------------- /ffi/example_03/csrc/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "example_03.h" 4 | 5 | int main(void) { 6 | Student *c_ptr = student_alice(); 7 | printf("Student Num: %d\t Total: %d\t Name: %s\t\n", c_ptr->num, c_ptr->total, c_ptr->name); 8 | student_free(c_ptr); 9 | 10 | 11 | Student *stu = student_new(); 12 | printf("Before fill data: Student Num: %d\t Total: %d\t Name: %s\t Scores: %.1f\t%.1f\t%.1f\n", 13 | stu->num, stu->total, stu->name, 14 | stu->scores[0], stu->scores[1], stu->scores[2]); 15 | stu->num = 2; 16 | stu->total = 212; 17 | strcpy(stu->name, "Bob"); 18 | stu->scores[0] = 60.6; 19 | stu->scores[1] = 70.7; 20 | stu->scores[2] = 80.8; 21 | printf("After fill data: Student Num: %d\t Total: %d\t Name: %s\t Scores: %.1f\t%.1f\t%.1f\n", 22 | stu->num, stu->total, stu->name, 23 | stu->scores[0], stu->scores[1], stu->scores[2]); 24 | student_free(stu); 25 | 26 | return 0; 27 | } 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /basics/04_modules/src/slices.rs: -------------------------------------------------------------------------------- 1 | // Slice examples: &[T] and &str 2 | pub fn run() { 3 | println!("-- slices module --"); 4 | slices_example(); 5 | string_slice_demo(); 6 | } 7 | 8 | fn slices_example() { 9 | let mut foo = [0u8; 5]; 10 | foo[1] = 1; 11 | foo[2] = 2; 12 | 13 | let bar = &foo[..3]; 14 | println!("array: {:?} slice (..3): {:?}", foo, bar); 15 | } 16 | 17 | fn string_slice_demo() { 18 | let s = String::from("hello world"); 19 | let first = first_word(&s); 20 | println!("first_word of '{}' is '{}'", s, first); 21 | 22 | // show safe slicing that won't panic 23 | let full = safe_split_first(&s); 24 | println!("safe_split_first -> '{}'", full); 25 | } 26 | 27 | fn first_word(s: &str) -> &str { 28 | for (i, &b) in s.as_bytes().iter().enumerate() { 29 | if b == b' ' { 30 | return &s[..i]; 31 | } 32 | } 33 | &s[..] 34 | } 35 | 36 | fn safe_split_first(s: &str) -> &str { 37 | s.split_whitespace().next().unwrap_or(s) 38 | } -------------------------------------------------------------------------------- /ffi/nix/src/main.rs: -------------------------------------------------------------------------------- 1 | // use nix::unistd::*; 2 | 3 | // fn main() { 4 | // match fork() { 5 | // Ok(ForkResult::Parent { child }) => { 6 | // // 在父进程中 7 | // println!("Hello, I am parent thread: {}", getpid()); 8 | // } 9 | // Ok(ForkResult::Child) => { 10 | // // 在子进程中 11 | // println!("Hello, I am child thread: {}", getpid()); 12 | // println!("My parent thread: {}", getppid()); 13 | // } 14 | // Err(errno) => { 15 | // // fork 创建子进程失败 16 | // println!("Fork creation failed!"); 17 | // } 18 | // } 19 | // } 20 | 21 | use nix::sys::signal::*; 22 | use nix::unistd::*; 23 | 24 | fn main() { 25 | match fork().expect("fork failed") { 26 | ForkResult::Parent{ child } => { 27 | sleep(5); 28 | kill(child, SIGKILL).expect("kill failed"); 29 | } 30 | ForkResult::Child => { 31 | // 直到被 kill 掉 32 | loop {} 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /basics/README.md: -------------------------------------------------------------------------------- 1 | # Basics 2 | 3 | This folder is for beginner Rust developers. It helps you build a solid foundation through small exercises. Each module focuses on a single core concept so learning stays focused and practical. 4 | 5 | ## 🎯 Goals 6 | 7 | * [Basic syntax](./01_hello/README.md) 8 | * [Ownership and Borrowing](./02_ownership/README.md) 9 | * [Generics and Traits](./03_generics/README.md) 10 | * [Modules and Collections](./04_modules/README.md) 11 | * [Error handling and Testing](./05_error_handling/README.md) 12 | 13 | References: [Experimental fork of TRPL](https://rust-book.cs.brown.edu/experiment-intro.html) 14 | 15 | ### Modules 16 | 17 | Each module focuses on one essential Rust concept. 18 | 19 | * `01_hello/` — Basic syntax 20 | * `02_ownership/` — Ownership and Borrowing 21 | * `03_generics/` — Generics and Traits 22 | * `04_modules/` — Modules and Vec / String / HashMap 23 | * `05_erro_handling/` — Option / Result error handling and testing 24 | 25 | ⏱ **Estimated time**: 10 hours 26 | 27 | ## 🚀 Quick Start 28 | 29 | ```bash 30 | cd basics/01_hello 31 | cargo run 32 | ``` -------------------------------------------------------------------------------- /ffi/rust-call-c/src/ffi_opaque/ffi_test.c: -------------------------------------------------------------------------------- 1 | #include "ffi_test.h" 2 | 3 | struct object { 4 | int info; 5 | }; 6 | 7 | /** 8 | * Returns the size of the object struct. 9 | */ 10 | size_t sizeof_obj(void) { 11 | return sizeof(struct object); 12 | } 13 | 14 | /** 15 | * Allocates memory for a new object on the heap and returns a pointer to it. 16 | */ 17 | struct object* init(void) { 18 | struct object* obj = (struct object*)malloc(sizeof_obj()); 19 | obj->info = 0; 20 | 21 | return obj; 22 | } 23 | 24 | /** 25 | * Frees the memory allocated for the object. 26 | */ 27 | void free_object(struct object* obj) { 28 | free(obj); 29 | } 30 | 31 | /** 32 | * Returns the version of the C API used by this library. 33 | */ 34 | int get_api_version(void) { 35 | return 0; 36 | } 37 | 38 | /** 39 | * Returns the value of the info field of the given object struct. 40 | */ 41 | int get_info(const struct object* obj) { 42 | return obj->info; 43 | } 44 | 45 | /** 46 | * Sets the info field of the object struct. 47 | */ 48 | void set_info(struct object* obj, int arg) { 49 | obj->info = arg; 50 | } -------------------------------------------------------------------------------- /basics/05_error_handling/src/main.rs: -------------------------------------------------------------------------------- 1 | // Exercises for Module 5: Error Handling and Testing 2 | // Run: cargo run 3 | 4 | use std::error::Error; 5 | 6 | fn main() -> Result<(), Box> { 7 | println!("=== Error Handling & Testing ===\n"); 8 | 9 | // Demonstrate parse_number 10 | match error_handling::parse_number("123") { 11 | Ok(n) => println!("parse_number -> {}", n), 12 | Err(e) => println!("parse_number error: {}", e), 13 | } 14 | 15 | // Demonstrate safe_divide 16 | match error_handling::safe_divide(10, 0) { 17 | Ok(r) => println!("10 / 0 = {}", r), 18 | Err(e) => println!("safe_divide error: {}", e), 19 | } 20 | 21 | // Demonstrate read_username_from_file using a temporary file 22 | let tmp = std::env::temp_dir().join("rust_demo_user.txt"); 23 | std::fs::write(&tmp, "demo_user\n")?; 24 | let user = error_handling::read_username_from_file(tmp.to_str().unwrap())?; 25 | println!("read_username_from_file -> '{}'", user); 26 | let _ = std::fs::remove_file(tmp); 27 | 28 | println!("\nRun tests: cargo test"); 29 | 30 | Ok(()) 31 | } -------------------------------------------------------------------------------- /basics/04_modules/src/vectors.rs: -------------------------------------------------------------------------------- 1 | // Vec examples and helpers 2 | pub fn run() { 3 | println!("-- vectors module --"); 4 | vector_example(); 5 | overlap_example(); 6 | new_vector_example(); 7 | } 8 | 9 | fn vector_example() { 10 | let v: Vec = Vec::new(); 11 | println!("vec size {}", v.len()); 12 | 13 | let mut v = Vec::new(); 14 | v.push(8); 15 | println!("after push vec size {}", v.len()); 16 | 17 | let v = vec![1, 2]; 18 | println!("vec get element {}", &v[0]); 19 | for i in &v { 20 | println!("vec each item {}", i); 21 | } 22 | } 23 | 24 | fn overlap_example() { 25 | let a = [1, 2, 3]; 26 | let b = [1, 2, 3, 4]; 27 | 28 | let c: Vec = a.iter().zip(&b).map(|(a, b)| a & b).collect(); 29 | println!("overlap {:?}", c); 30 | } 31 | 32 | fn bunch_of_numbers() -> Vec { 33 | (0..10).collect() 34 | } 35 | 36 | fn new_vector_example() { 37 | let nums = bunch_of_numbers(); 38 | 39 | match nums.last() { 40 | Some(&0) => println!("Last number is zero"), 41 | Some(n) => println!("Last number is {}", n), 42 | None => println!("There are no numbers"), 43 | } 44 | } -------------------------------------------------------------------------------- /ffi/example_09/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_char, c_float, c_int}; 2 | 3 | #[repr(C)] 4 | #[derive(Debug)] 5 | pub struct CStudent { 6 | pub num: c_int, 7 | pub total: c_int, 8 | pub name: [c_char; 20], 9 | pub scores: [c_float; 3], 10 | } 11 | 12 | // Default constructor 13 | impl Default for CStudent { 14 | fn default() -> Self { 15 | CStudent { 16 | num: 0 as c_int, 17 | total: 0 as c_int, 18 | name: [0 as c_char; 20], 19 | scores: [0.0 as c_float; 3], 20 | } 21 | } 22 | } 23 | 24 | #[link(name = "cfoo")] 25 | extern "C" { 26 | fn print_data(p_stu: *mut CStudent); 27 | fn fill_data(p_stu: *mut CStudent); 28 | } 29 | 30 | 31 | fn main() { 32 | // Initialization of allocated memory 33 | let new_stu: CStudent = Default::default(); 34 | println!("rust side print new_stu: {:?}", new_stu); 35 | let box_new_stu = Box::new(new_stu); 36 | let p_stu = Box::into_raw(box_new_stu); 37 | 38 | unsafe { 39 | fill_data(p_stu); 40 | print_data(p_stu); 41 | //Box::from_raw(p_stu); 42 | println!("rust side print Bob: {:?}", Box::from_raw(p_stu)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ffi/example_10/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_int, c_void}; 2 | 3 | pub type SumSquareCB = unsafe extern fn(c_int, *mut c_void); 4 | 5 | #[link(name = "sumsquare")] 6 | extern { 7 | pub fn sum_square_cb(a: c_int, b: c_int, cb: SumSquareCB, user_data: *mut c_void); 8 | } 9 | 10 | unsafe extern fn hook(result: c_int, user_data: *mut c_void) 11 | where 12 | F: FnMut(c_int), 13 | { 14 | (*(user_data as *mut F))(result) 15 | } 16 | 17 | pub fn get_callback(_closure: &F) -> SumSquareCB 18 | where 19 | F: FnMut(c_int), 20 | { 21 | hook:: 22 | } 23 | 24 | #[derive(Debug, Default, Clone, PartialEq)] 25 | struct SumRecord { 26 | total: c_int, 27 | calls: usize, 28 | } 29 | 30 | fn main() { 31 | let mut record = SumRecord::default(); 32 | 33 | unsafe { 34 | let mut closure = |result: c_int| { 35 | record.total += result; 36 | record.calls += 1; 37 | }; 38 | let callback = get_callback(&closure); 39 | 40 | sum_square_cb(1, 2, callback, &mut closure as *mut _ as *mut c_void); 41 | 42 | sum_square_cb(3, 4, callback, &mut closure as *mut _ as *mut c_void); 43 | } 44 | 45 | println!("The sum is {:?}", record); 46 | } 47 | -------------------------------------------------------------------------------- /ffi/example_01/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_char; 2 | use std::ffi::{CStr, CString}; 3 | 4 | #[no_mangle] 5 | pub extern "C" fn print_str(s: *const c_char) { 6 | let slice = unsafe { 7 | assert!(!s.is_null()); 8 | CStr::from_ptr(s) 9 | }; 10 | let r_str = slice.to_str().unwrap(); 11 | println!("Rust side print: {:?}", r_str); 12 | } 13 | 14 | #[no_mangle] 15 | pub extern "C" fn change_str(s: *mut c_char) -> *mut c_char { 16 | let mut string = unsafe { 17 | assert!(!s.is_null()); 18 | CStr::from_ptr(s).to_string_lossy().into_owned() 19 | }; 20 | string.push_str(" World!"); 21 | println!("Rust side change: {:?}", string); 22 | let c_str_changed = CString::new(string).unwrap(); 23 | c_str_changed.into_raw() 24 | } 25 | 26 | #[no_mangle] 27 | pub extern "C" fn generate_str() -> *mut c_char { 28 | let ping = String::from("ping"); 29 | println!("Rust side generate: {:?}", ping); 30 | let c_str_ping = CString::new(ping).unwrap(); 31 | c_str_ping.into_raw() 32 | } 33 | 34 | #[no_mangle] 35 | pub extern "C" fn free_str(s: *mut c_char) { 36 | unsafe { 37 | if s.is_null() { 38 | return; 39 | } 40 | CString::from_raw(s) 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /ffi/rust-call-c/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::os::raw::c_char; 3 | 4 | // 外部块 5 | extern "C" { 6 | // 标准库 abs函数 7 | #[link_name = "abs"] 8 | fn abs_in_rust(input: i32) -> i32; 9 | 10 | #[link_name = "printf"] 11 | fn printf_in_rust(input: *const c_char) -> i32; 12 | } 13 | 14 | fn abs_example() { 15 | unsafe { 16 | println!("abs(-1) is {}", abs_in_rust(-1)); 17 | } 18 | } 19 | 20 | use std::str; 21 | 22 | mod time; 23 | 24 | fn time_example() { 25 | let mut v: Vec = vec![0; 80]; 26 | let mut t = time::tm { 27 | tm_sec: 15, 28 | tm_min: 09, 29 | tm_hour: 18, 30 | tm_mday: 14, 31 | tm_mon: 04, 32 | tm_year: 120, 33 | tm_wday: 4, 34 | tm_yday: 135, 35 | tm_isdst: 0, 36 | }; 37 | let format = b"%Y-%m-%d %H:%M:%S\0".as_ptr(); 38 | unsafe { 39 | time::strftime_in_rust(v.as_mut_ptr(), 80, format, &mut t); 40 | 41 | let s = match str::from_utf8(v.as_slice()) { 42 | Ok(r) => r, 43 | Err(e) => panic!("Invalid UTF-8 sequence: {}", e), 44 | }; 45 | 46 | println!("result: {}", s); 47 | } 48 | } 49 | 50 | fn main() { 51 | abs_example(); 52 | time_example(); 53 | } -------------------------------------------------------------------------------- /ffi/string/print.c: -------------------------------------------------------------------------------- 1 | // in `print.c` 2 | 3 | #include // printf 4 | #include // uint8_t 5 | 6 | void print_spaced(char *s) { 7 | // start at the beginning 8 | int i = 0; 9 | 10 | while (1) { 11 | // we're going to be shifting bytes around, 12 | // so treat them like unsigned 8-bit values 13 | uint8_t c = s[i]; 14 | if (c == 0) { 15 | // reached null terminator, stop printing 16 | break; 17 | } 18 | 19 | // length of the sequence, ie., number of bytes 20 | // that encode a single Unicode scalar value 21 | int len = 1; 22 | if (c >> 5 == 0b110) { 23 | len = 2; 24 | } else if (c >> 4 == 0b1110) { 25 | len = 3; 26 | } else if (c >> 3 == 0b11110) { 27 | len = 4; 28 | } 29 | 30 | // print the entire UTF-8-encoded Unicode scalar value 31 | for (; len > 0; len--) { 32 | printf("%c", s[i]); 33 | i++; 34 | } 35 | // print space separator 36 | printf(" "); 37 | } 38 | } 39 | 40 | int main(int argc, char **argv) { 41 | for (int i = 1; i < argc; i++) { 42 | print_spaced(argv[i]); 43 | printf("\n"); 44 | } 45 | 46 | return 0; 47 | } -------------------------------------------------------------------------------- /ffi/example_04/python/main.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | try: 4 | from cffi import FFI 5 | except ImportError: 6 | print "pip install cffi" 7 | 8 | ffi = FFI() 9 | 10 | ffi.cdef(""" 11 | typedef struct c_tuple { 12 | unsigned int integer; 13 | bool boolean; 14 | } c_tuple; 15 | unsigned int fibonacci(unsigned int index); 16 | unsigned int count_char(const char *s); 17 | struct c_tuple handle_tuple(struct c_tuple tup); 18 | int sum_of_even(const int *ptr, size_t len); 19 | """) 20 | 21 | lib = ffi.dlopen("../ffi/target/debug/libexample_04.so") 22 | 23 | print "fibonacci(2) from Rust: ", lib.fibonacci(2) 24 | print "fibonacci(4) from Rust: ", lib.fibonacci(4) 25 | print "fibonacci(6) from Rust: ", lib.fibonacci(6) 26 | 27 | print 'count_char("hello") from Rust: ', lib.count_char("hello") 28 | print 'count_char("你好") from Rust: ', lib.count_char(u"你好".encode('utf-8')) 29 | 30 | 31 | py_cdata = ffi.new('c_tuple *') 32 | py_cdata.integer = 100 33 | py_cdata.boolean = True 34 | print('cdata = {0}, {1}'.format(py_cdata.integer, py_cdata.boolean)) 35 | new_py_cdata = lib.handle_tuple(py_cdata[0]) 36 | print('change cdata = {0}, {1}'.format(new_py_cdata.integer, new_py_cdata.boolean)) 37 | 38 | 39 | array = ffi.new("int[]", [1, 4, 9, 16, 25]) 40 | print 'sum_of_even from Rust: ', lib.sum_of_even(array, len(array)) 41 | 42 | -------------------------------------------------------------------------------- /ffi/rust-call-c/src/layout.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | // 默认布局 4 | struct DefaultStruct { 5 | first: i8, 6 | second: i16, 7 | third: i8 8 | } 9 | 10 | // 默认布局,对齐方式降低到 1 11 | #[repr(packed(1))] 12 | struct PackedStruct { 13 | first: i8, 14 | second: i16, 15 | third: i8 16 | } 17 | 18 | // C 布局 19 | #[repr(C)] 20 | struct CStruct { 21 | first: i8, 22 | second: i16, 23 | third: i8 24 | } 25 | 26 | // C 布局, 对齐方式升高到 4 27 | #[repr(C, align(4))] 28 | struct AlignedStruct { 29 | first: i8, 30 | second: i16, 31 | third: i8 32 | } 33 | 34 | // C 布局的元组结构体 35 | #[repr(C)] 36 | struct TupleStruct(i8, i16, i8); 37 | 38 | // C 布局,重新调整字段的顺序可以缩小类型大小 39 | #[repr(C)] 40 | struct FieldStructOptimized { 41 | first: i8, 42 | third: i8, 43 | second: i16 44 | } 45 | 46 | // 联合类型的大小等于其字段类型的最大值 47 | #[repr(C)] 48 | union ExampleUnion { 49 | smaller: i8, 50 | larger: i16 51 | } 52 | 53 | 54 | fn main() { 55 | assert_eq!(mem::align_of::(), 2); 56 | assert_eq!(mem::size_of::(), 6); 57 | assert_eq!(mem::align_of::(), 2); 58 | 59 | assert_eq!(mem::align_of::(), 1); 60 | assert_eq!(mem::align_of::(), 4); 61 | 62 | assert_eq!(mem::size_of::(), 4); 63 | 64 | assert_eq!(mem::size_of::(), 6); 65 | 66 | assert_eq!(mem::size_of::(), 2); 67 | } -------------------------------------------------------------------------------- /ffi/example_04/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn fibonacci(index: u32) -> u32 { 2 | if index <= 2 { 3 | 1 4 | } else { 5 | fibonacci(index - 1) + fibonacci(index - 2) 6 | } 7 | } 8 | 9 | pub fn count_char(s: &str) -> u32 { 10 | s.chars().count() as u32 11 | } 12 | 13 | pub fn handle_tuple(pair: (u32, bool)) -> (u32, bool) { 14 | let (integer, boolean) = pair; 15 | 16 | (integer + 1, !boolean) 17 | } 18 | 19 | pub fn sum_of_even_fixed(array: [i32; 10]) -> i32 { 20 | array.iter() 21 | .filter(|&&num| num % 2 == 0) 22 | .fold(0, |sum, &num| sum + num) 23 | } 24 | 25 | pub fn sum_of_even(slice: &[i32]) -> i32 { 26 | slice.iter() 27 | .filter(|&&num| num % 2 == 0) 28 | .fold(0, |sum, &num| sum + num) 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | use super::*; 34 | 35 | #[test] 36 | fn test_fibonacci() { 37 | assert_eq!(fibonacci(2), 1); 38 | assert_eq!(fibonacci(4), 3); 39 | assert_eq!(fibonacci(6), 8); 40 | } 41 | 42 | #[test] 43 | fn test_count_char() { 44 | let hello = "hello"; 45 | assert_eq!(count_char(hello), 5); 46 | let hello_utf8 = "你好"; 47 | assert_eq!(count_char(hello_utf8), 2); 48 | } 49 | 50 | #[test] 51 | fn test_handle_tuple() { 52 | let pair = (100, true); 53 | assert_eq!(handle_tuple(pair), (101, false)); 54 | } 55 | 56 | #[test] 57 | fn test_sum_of_even() { 58 | let array = [1,2,3,4,5,6,7,8,9,10]; 59 | assert_eq!(sum_of_even(&array), 30); 60 | assert_eq!(sum_of_even(&array[..5]), 6); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ffi/example_04/ffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_char, c_uint, c_int}; 2 | use std::ffi::CStr; 3 | use std::convert::From; 4 | use std::slice; 5 | use libc::size_t; 6 | 7 | #[no_mangle] 8 | pub extern "C" fn fibonacci(index: c_uint) -> c_uint { 9 | if index <= 2 { 10 | 1 11 | } else { 12 | fibonacci(index - 1) + fibonacci(index - 2) 13 | } 14 | } 15 | 16 | #[no_mangle] 17 | pub extern "C" fn count_char(s: *const c_char) -> c_uint { 18 | let c_str = unsafe { 19 | assert!(!s.is_null()); 20 | CStr::from_ptr(s) 21 | }; 22 | let r_str = c_str.to_str().unwrap(); 23 | r_str.chars().count() as u32 24 | } 25 | 26 | // A struct that can be passed between C and Rust 27 | #[repr(C)] 28 | pub struct c_tuple { 29 | integer: c_uint, 30 | boolean: bool, 31 | } 32 | 33 | impl From for (u32, bool) { 34 | fn from(tup: c_tuple) -> (u32, bool) { 35 | (tup.integer, tup.boolean) 36 | } 37 | } 38 | 39 | impl From<(u32, bool)> for c_tuple { 40 | fn from(tup: (u32, bool)) -> c_tuple { 41 | c_tuple { 42 | integer: tup.0, 43 | boolean: tup.1, 44 | } 45 | } 46 | } 47 | 48 | #[no_mangle] 49 | pub extern "C" fn handle_tuple(tup: c_tuple) -> c_tuple { 50 | let (integer, boolean) = tup.into(); 51 | 52 | (integer + 1, !boolean).into() 53 | } 54 | 55 | #[no_mangle] 56 | pub extern "C" fn sum_of_even(ptr: *const c_int, len: size_t) -> c_int { 57 | let slice = unsafe { 58 | assert!(!ptr.is_null()); 59 | slice::from_raw_parts(ptr, len as usize) 60 | }; 61 | 62 | let sum = slice.iter() 63 | .filter(|&&num| num % 2 == 0) 64 | .fold(0, |sum, &num| sum + num); 65 | sum as c_int 66 | } -------------------------------------------------------------------------------- /ffi/example_02/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_char, c_float, c_void}; 2 | use std::ffi::CStr; 3 | use std::panic::catch_unwind; 4 | 5 | fn may_panic() { 6 | if rand::random() { 7 | panic!("panic happens"); 8 | } 9 | } 10 | 11 | #[no_mangle] 12 | pub unsafe extern "C" fn no_panic() -> i32 { 13 | let result = catch_unwind(may_panic); 14 | match result { 15 | Ok(_) => 0, 16 | Err(_) => -1, 17 | } 18 | } 19 | 20 | #[derive(Debug)] 21 | enum Version { Version1, Version2 } 22 | 23 | fn parse_version(header: &str) -> Result { 24 | match header { 25 | "" => Err("invalid header length"), 26 | "v1" => Ok(Version::Version1), 27 | "v2" => Ok(Version::Version2), 28 | _ => Err("invalid version"), 29 | } 30 | } 31 | 32 | #[no_mangle] 33 | pub unsafe extern "C" fn handle_result(s: *const c_char) -> i32 { 34 | if (s as *mut c_void).is_null() { 35 | return -1; 36 | } 37 | 38 | let vb = CStr::from_ptr(s).to_str().unwrap(); 39 | let version = parse_version(vb); 40 | match version { 41 | Ok(_) => 0, 42 | Err(_) => -1, 43 | } 44 | } 45 | 46 | fn divide(numerator: f32, denominator: f32) -> Option { 47 | if denominator == 0.0 { 48 | None 49 | } else { 50 | Some(numerator / denominator) 51 | } 52 | } 53 | 54 | #[no_mangle] 55 | pub unsafe extern "C" fn handle_option(x: c_float, y: c_float) -> i32 { 56 | // The return value of the function is an option 57 | let result = divide(x, y); 58 | 59 | // Pattern match to retrieve the value 60 | match result { 61 | // The division was valid 62 | Some(_) => 0, 63 | // The division was invalid 64 | None => -1, 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /ffi/example_03/src/lib.rs: -------------------------------------------------------------------------------- 1 | // We have a lot of c-types in here, stop warning about their names! 2 | #![allow(non_camel_case_types)] 3 | 4 | use std::os::raw::{c_char, c_float, c_int}; 5 | 6 | #[repr(C)] 7 | #[derive(Debug)] 8 | pub enum gender { 9 | BOY, 10 | GIRL, 11 | } 12 | 13 | #[repr(C)] 14 | #[derive(Debug)] 15 | pub struct student { 16 | pub num: c_int, 17 | pub total: c_int, 18 | pub name: [c_char; 20], 19 | pub scores: [c_float; 3], 20 | pub gender: gender, 21 | } 22 | 23 | // Default constructor 24 | impl Default for student { 25 | fn default() -> Self { 26 | student { 27 | num: 0 as c_int, 28 | total: 0 as c_int, 29 | name: [0 as c_char; 20], 30 | scores: [0.0 as c_float; 3], 31 | gender: gender::BOY, 32 | } 33 | } 34 | } 35 | 36 | #[no_mangle] 37 | pub extern "C" fn student_new() -> *mut student { 38 | let new_stu: student = Default::default(); 39 | Box::into_raw(Box::new(new_stu)) 40 | } 41 | 42 | #[no_mangle] 43 | pub extern "C" fn student_alice() -> *mut student { 44 | let mut init_char_array: [c_char; 20] = [0; 20]; 45 | for (dest, src) in init_char_array.iter_mut().zip(b"Alice\0".iter()) { 46 | *dest = *src as _; 47 | } 48 | let scores = [92.5, 87.5, 90.0]; 49 | let alice = student { 50 | num: 1 as c_int, 51 | total: 280, 52 | name: init_char_array, 53 | scores, 54 | gender: gender::GIRL, 55 | }; 56 | Box::into_raw(Box::new(alice)) 57 | } 58 | 59 | #[no_mangle] 60 | pub extern "C" fn student_free(p_stu: *mut student) { 61 | if !p_stu.is_null() { 62 | unsafe { 63 | println!("rust side print: {:?}", Box::from_raw(p_stu)); 64 | Box::from_raw(p_stu) 65 | }; 66 | } 67 | } -------------------------------------------------------------------------------- /ffi/example_01/csrc/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "example_01.h" 6 | 7 | int main(void) { 8 | // basic string - char array 9 | char hello1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; 10 | printf("C hello 1: %s\n", hello1); 11 | char hello2[6] = "Hello"; 12 | printf("C hello 2: %s\n", hello2); 13 | if (strcmp(hello1, hello2) ==0) 14 | { 15 | printf("hello 1 and 2 are equal\n"); 16 | } else 17 | { 18 | printf("hello 1 and 2 are different\n"); 19 | } 20 | 21 | // basic string - char pointer 22 | // char *str; 23 | // str = "hello"; // Stored in read only part of data segment 24 | // *(str+1) = 'i'; // Segmentation fault error: trying to modify read only memory 25 | 26 | char hello_s[] = "hello"; // Stored in stack segment 27 | *hello_s = 'H'; // No problem: String is now Hello 28 | printf("new string in stack is %s\n", hello_s); 29 | 30 | int size = 6; 31 | char *hello_h = (char *)malloc(sizeof(char)*size); // Stored in heap segment 32 | *(hello_h+0) = 'h'; 33 | *(hello_h+1) = 'e'; 34 | *(hello_h+2) = 'l'; 35 | *(hello_h+3) = 'l'; 36 | *(hello_h+4) = 'o'; 37 | *(hello_h+5) = '\0'; 38 | *(hello_h+0) = 'H'; // No problem: String is now Hello 39 | printf("new string in heap is: %s\n", hello_h); 40 | free(hello_h); 41 | 42 | char *c_str = "hello"; 43 | print_str(c_str); // Problem: still reachable 44 | c_str = change_str(c_str); // change the previous content 45 | printf("C side result: %s\n", c_str); 46 | free_str(c_str); 47 | 48 | // C generate strings 49 | char *c_hello = (char *)malloc(sizeof(char)*size); 50 | *(c_hello+0) = 'H'; 51 | *(c_hello+1) = 'e'; 52 | *(c_hello+2) = 'l'; 53 | *(c_hello+3) = 'l'; 54 | *(c_hello+4) = 'o'; 55 | *(c_hello+5) = '\0'; 56 | printf("C side generate: %s\n", c_hello); 57 | print_str(c_hello); 58 | char *c_hello_world = change_str(c_hello); 59 | printf("C side result: %s\n", c_hello_world); 60 | free(c_hello); 61 | free_str(c_hello_world); 62 | 63 | char *c_ping = generate_str(); 64 | printf("C side print: %s\n", c_ping); 65 | free_str(c_ping); 66 | } -------------------------------------------------------------------------------- /basics/05_error_handling/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Module 5 exercises: Error handling utilities + unit tests. 2 | 3 | use std::fs; 4 | use std::io; 5 | 6 | /// Parse an integer from a string (demonstrates Result from std). 7 | pub fn parse_number(s: &str) -> Result { 8 | s.trim().parse::() 9 | } 10 | 11 | /// Divide two integers, returning an error on division by zero. 12 | pub fn safe_divide(a: i32, b: i32) -> Result { 13 | if b == 0 { 14 | Err("division by zero".to_string()) 15 | } else { 16 | Ok(a / b) 17 | } 18 | } 19 | 20 | /// Read the entire file contents and return as String. Demonstrates `?` propagation. 21 | pub fn read_username_from_file(path: &str) -> Result { 22 | // Using read_to_string which returns Result 23 | let contents = fs::read_to_string(path)?; 24 | Ok(contents.trim().to_string()) 25 | } 26 | 27 | /// Example function that intentionally panics on non-positive input. 28 | /// Used to demonstrate `#[should_panic]` in tests. 29 | pub fn must_be_positive(n: i32) -> i32 { 30 | if n <= 0 { 31 | panic!("value must be positive, got {}", n); 32 | } 33 | n 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | use std::fs; 40 | use std::path::PathBuf; 41 | 42 | #[test] 43 | fn test_parse_number_ok() { 44 | assert_eq!(parse_number("42").unwrap(), 42); 45 | assert_eq!(parse_number(" -7 \n").unwrap(), -7); 46 | } 47 | 48 | #[test] 49 | fn test_parse_number_err() { 50 | assert!(parse_number("abc").is_err()); 51 | } 52 | 53 | #[test] 54 | fn test_safe_divide() { 55 | assert_eq!(safe_divide(10, 2).unwrap(), 5); 56 | assert!(safe_divide(1, 0).is_err()); 57 | } 58 | 59 | #[test] 60 | fn test_read_username_from_file() { 61 | // create a temporary file in the OS temp dir 62 | let mut path: PathBuf = std::env::temp_dir(); 63 | path.push("rust_test_username.txt"); 64 | let path_str = path.to_str().unwrap(); 65 | 66 | // write and read 67 | fs::write(&path, "alice\n").expect("write tmp file"); 68 | let username = read_username_from_file(path_str).expect("read username"); 69 | assert_eq!(username, "alice"); 70 | 71 | // cleanup (ignore errors) 72 | let _ = fs::remove_file(path); 73 | } 74 | 75 | #[test] 76 | #[should_panic(expected = "value must be positive")] 77 | fn test_must_be_positive_panics() { 78 | // should panic 79 | let _ = must_be_positive(0); 80 | } 81 | } -------------------------------------------------------------------------------- /ffi/secp256k1-sys/include/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "./secp256k1.h" 4 | 5 | // 返回16进制字符代表的整数值 6 | int hex2int(unsigned char x){ 7 | if(x >= '0' && x <= '9'){ 8 | return (x - '0'); 9 | } 10 | if(x >= 'A' && x <= 'F'){ 11 | return (x - 'A' + 10); 12 | } 13 | if(x >= 'a' && x <= 'f'){ 14 | return (x - 'a' + 10); 15 | } 16 | return -1; 17 | } 18 | /** 测试主函数 */ 19 | int main(int argc, char** argv) { 20 | unsigned char prikeyhex[] = "9a9a6539856be209b8ea2adbd155c0919646d108515b60b7b13d6a79f1ae5174"; 21 | int len = sizeof(prikeyhex) / 2; // 私钥长度 - 32字节 22 | unsigned char prikey[len]; // 私钥存储 23 | int ii; // 索引值 24 | int ret; // 返回值 25 | 26 | unsigned char CPubKey[65]; // 公钥存储 27 | size_t clen; // 返回公钥长度 28 | 29 | secp256k1_context *secp256k1_context_sign; 30 | secp256k1_pubkey pubkey; // secp256k1返回公钥 31 | // 将私钥字符串转换为字节存储 32 | for(ii = 0; ii < sizeof(prikeyhex); ii+=2){ 33 | prikey[ii/2] = hex2int(prikeyhex[ii]) * 16 + hex2int(prikeyhex[ii + 1]); 34 | } 35 | // 打印私钥 36 | printf("Private key: "); 37 | for(ii = 0; ii < len; ii++) 38 | { 39 | printf("%02x",prikey[ii]); 40 | } 41 | printf("\n"); 42 | // 生成公钥 43 | secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); 44 | ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, prikey); 45 | 46 | // 打印公钥 47 | if(ret){ 48 | printf("Public key : "); 49 | printf("[X("); 50 | for(ii = 63; ii >= 32; ii--){ 51 | printf("%02x", pubkey.data[ii]); 52 | } 53 | printf("):Y("); 54 | for(ii = 31; ii >= 0; ii--){ 55 | printf("%02x", pubkey.data[ii]); 56 | } 57 | printf(")]\n"); 58 | // 获取压缩公钥 59 | clen = 65; 60 | secp256k1_ec_pubkey_serialize(secp256k1_context_sign, CPubKey, &clen, &pubkey, SECP256K1_EC_COMPRESSED); 61 | printf("Compressed key : "); 62 | for(ii = 0; ii < clen; ii++){ 63 | printf("%02x", CPubKey[ii]); 64 | } 65 | printf("\n"); 66 | // 获取非压缩公钥 67 | clen = 65; 68 | secp256k1_ec_pubkey_serialize(secp256k1_context_sign, CPubKey, &clen, &pubkey, SECP256K1_EC_UNCOMPRESSED); 69 | printf("Uncompressed key: "); 70 | for(ii = 0; ii < clen; ii++){ 71 | printf("%02x", CPubKey[ii]); 72 | } 73 | printf("\n"); 74 | } 75 | if (secp256k1_context_sign) { 76 | secp256k1_context_destroy(secp256k1_context_sign); 77 | } 78 | return 0; 79 | } -------------------------------------------------------------------------------- /basics/02_ownership/README.md: -------------------------------------------------------------------------------- 1 | ## 🛡️ Module 2: Ownership and Borrowing 2 | 3 | This module explores **ownership** and **borrowing** that Rust's most unique and powerful features and explains how these rules enable Rust to manage memory safely. 4 | 5 | By the end of this module, you should be able to: 6 | 7 | 1. Understand Rust's **ownership** and how it works. 8 | 2. Use **references** (borrowing) to share data without transferring ownership. 9 | 10 | ### 1. Ownership 11 | 12 | Ownership is the core discipline of heap/pointer management. Three Ownership Rules: 13 | 14 | * Each value in Rust has an owner (the variable it's assigned to). 15 | * There can only be one owner at a time. 16 | * When the owner goes out of scope, the value is dropped (memory is freed). 17 | 18 | The key differences between languages are who manages the memory in the Heap. Rust introduces a third approach between garbage collection (GC) and manual management: Rust compiler (via the borrow checker) inserts the freeing logic (the call to drop) automatically at compile time when a variable goes out of scope. 19 | * When a variable with data on the Heap (like a `String`) is assigned to another variable or passed to a function, the **ownership is transferred** (moved) and the original variable is invalidated. 20 | 21 | ```rust 22 | let s1 = String::from("hello"); 23 | let s2 = s1; // s1's ownership is moved to s2. s1 is now invalid. 24 | // println!("{}", s1); // Compile-time ERROR! (Use after Move) 25 | ``` 26 | 27 | * When a type with data stored on the stack (like `i32` or `bool`) is assigned to a new variable, the value is simply copied, the original variable remains valid. 28 | 29 | ```rust 30 | let x = 5; // i32 implements Copy 31 | let y = x; // The value 5 is copied. 32 | println!("x is {}, y is {}", x, y); // Both are valid 33 | ``` 34 | 35 | ### 2. References and Borrowing 36 | 37 | References provide the ability to read and write data without consuming ownership of it. References are created with borrows (immutable references `&` and mutable references `&mut`) and used with dereferences (`*`), often implicitly. 38 | 39 | Rust’s borrow checker enforces a system of permissions that ensures references are used safely: 40 | 41 | * All variables can read, own, and (optionally) write their data. 42 | * Creating a reference will transfer permissions from the borrowed place to the reference. 43 | * Permissions are returned once the reference’s lifetime has ended. 44 | * Data must outlive all references that point to it. 45 | 46 | ```rust 47 | let mut s = String::from("data"); 48 | let r1 = &s; // OK: First immutable reference (Reader) 49 | let r2 = &s; // OK: Second immutable reference (Reader) 50 | // let r3 = &mut s; // ERROR: Cannot get a mutable reference while immutable ones exist! 51 | ``` -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/core/src/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::fmt; 3 | 4 | /// Represents the type of code item extracted 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 6 | #[serde(rename_all = "lowercase")] 7 | pub enum ItemKind { 8 | Function, 9 | Method, 10 | Impl, 11 | Trait, 12 | Module, 13 | } 14 | 15 | impl fmt::Display for ItemKind { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | match self { 18 | ItemKind::Function => write!(f, "function"), 19 | ItemKind::Method => write!(f, "method"), 20 | ItemKind::Impl => write!(f, "impl"), 21 | ItemKind::Trait => write!(f, "trait"), 22 | ItemKind::Module => write!(f, "module"), 23 | } 24 | } 25 | } 26 | 27 | /// Contains project context information 28 | #[derive(Debug, Clone, Serialize, Deserialize)] 29 | pub struct ProjectContext { 30 | pub repo_url: String, 31 | pub commit_hash: String, 32 | pub file_path: String, 33 | } 34 | 35 | /// Contains metadata about the extracted item 36 | #[derive(Debug, Clone, Serialize, Deserialize)] 37 | pub struct ItemMeta { 38 | pub kind: ItemKind, 39 | pub name: String, 40 | pub fully_qualified_name: String, 41 | pub start_line: u32, 42 | pub end_line: u32, 43 | } 44 | 45 | /// Contains the normalized content and metadata 46 | #[derive(Debug, Clone, Serialize, Deserialize)] 47 | pub struct Content { 48 | pub signature: String, 49 | pub body_normalized: String, 50 | pub semantic_hash: String, 51 | pub docstring: Option, 52 | pub imports: Vec, 53 | } 54 | 55 | /// Contains contextual information for RAG 56 | #[derive(Debug, Clone, Serialize, Deserialize)] 57 | pub struct RagContext { 58 | pub context_before: Option, 59 | pub context_after: Option, 60 | } 61 | 62 | /// Represents a complete extracted code item 63 | #[derive(Debug, Clone, Serialize, Deserialize)] 64 | pub struct ExtractedItem { 65 | pub project_context: ProjectContext, 66 | pub item_meta: ItemMeta, 67 | pub content: Content, 68 | pub rag_context: RagContext, 69 | } 70 | 71 | /// Error types for the library 72 | #[derive(Debug, thiserror::Error)] 73 | pub enum CoreError { 74 | #[error("Syn parsing error: {0}")] 75 | SynParse(#[from] syn::Error), 76 | #[error("IO error: {0}")] 77 | Io(#[from] std::io::Error), 78 | #[error("JSON error: {0}")] 79 | Json(#[from] serde_json::Error), 80 | #[error("Hashing error: {0}")] 81 | Hash(String), 82 | #[error("Invalid input: {0}")] 83 | InvalidInput(String), 84 | #[error("Strip prefix error: {0}")] 85 | StripPrefix(#[from] std::path::StripPrefixError), 86 | } 87 | 88 | /// Result type for core operations 89 | pub type CoreResult = Result; 90 | 91 | /// Configuration for extraction process 92 | #[derive(Debug, Clone)] 93 | pub struct ExtractConfig { 94 | pub keep_docs: bool, 95 | pub include_context: bool, 96 | pub context_lines: usize, 97 | } 98 | 99 | impl Default for ExtractConfig { 100 | fn default() -> Self { 101 | Self { 102 | keep_docs: false, 103 | include_context: false, 104 | context_lines: 50, 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /basics/03_generics/README.md: -------------------------------------------------------------------------------- 1 | ## ⚙️ Module 3: Generics and Traits 2 | 3 | This module focuses on **code design** and **reusability**. **Generics** and **Traits** are the primary tools Rust uses to write flexible code that avoids duplication while maintaining performance and static safety. 4 | 5 | By the end of this module, you should be able to: 6 | 7 | 1. Use **Generics** (``) to define functions, structs, and enums that work with any data type. 8 | 2. Define **Traits** to declare shared behavior (methods) that different types can implement. 9 | * Combine Generics and Traits using **Trait Bounds** to specify constraints on generic types. 10 | * Use common **Derivable Traits** (e.g., `Debug`, `Clone`). 11 | 12 | ### 1. Generics: abstraction over types 13 | 14 | **Generics** are abstract placeholders for concrete types or other properties. They allow you to define code once, and have it work correctly for multiple different types, eliminating code duplication. 15 | 16 | * **Syntax:** Generic type parameters are declared using angle brackets (``, ``, etc.) after the item name. 17 | * **Generic Functions:** Used when the logic is the same regardless of the input type. 18 | ```rust 19 | fn largest(list: &[T]) -> &T { /* ... */ } 20 | ``` 21 | * **Generic Structs and Enums:** Used for container types that can hold different kinds of data. **Standard library types** like `Option`, `Result`, and `Vec` are all defined using generics. 22 | ```rust 23 | struct Point { x: T, y: T } 24 | ``` 25 | * **Performance:** Rust handles generics through **monomorphization**. At compile time, the compiler effectively creates specific, optimized copies of the generic code for every concrete type used (e.g., one function for `largest` and another for `largest`). This guarantees the performance of static dispatch with zero runtime cost. 26 | 27 | ### 2. Traits: defining shared behavior 28 | 29 | A **Trait** defines a contract for behavior, a set of methods that a type must implement. They are similar to **interfaces** in other languages and are the primary mechanism for polymorphism in Rust. 30 | 31 | * **Definition:** You define a trait using the `trait` keyword. 32 | ```rust 33 | pub trait Summary { 34 | fn summarize(&self) -> String; 35 | // Traits can also contain default implementations 36 | fn read_more_link(&self) -> String { 37 | String::from("(Read more...)") 38 | } 39 | } 40 | ``` 41 | * **Implementation:** You implement a trait for a specific type using the `impl` block. A type can implement multiple traits. 42 | ```rust 43 | impl Summary for Post { 44 | fn summarize(&self) -> String { 45 | format!("{} by {}", self.title, self.author) 46 | } 47 | } 48 | ``` 49 | * **Derivable Traits:** Many common traits (`Debug`, `Clone`, `Copy`, `PartialEq`) can be automatically implemented by the compiler using the `#[derive]` macro. 50 | 51 | * **Trait Bounds** allow you to restrict a generic type parameter `T` to only types that implement a specific behavior (trait). 52 | | Syntax | Example | Description | 53 | | :--- | :--- | :--- | 54 | | **`impl` Trait** | `fn notify(item: &impl Summary)` | Syntactic sugar for a single trait bound. | 55 | | **Colon Notation** | `fn notify(item: &T)` | The classic way to constrain a generic type `T`. | 56 | | **Multiple Bounds** | `fn notify(item: &T)` | Requires the type `T` to implement *both* traits. | 57 | | **`where` Clause** | `fn notify(item: &T) where T: Summary + Display` | Used to clean up function signatures when dealing with many bounds. | -------------------------------------------------------------------------------- /basics/04_modules/README.md: -------------------------------------------------------------------------------- 1 | ## Module 4: Project Structure and Common Collections 2 | 3 | This module focuses on writing code that scales. You'll learn how to organize large projects using the **Module System** and how to use Rust's standard **common collections** for managing dynamic sets of data. 4 | 5 | By the end of this module, you should be able to: 6 | 7 | 1. Differentiate between **Packages**, **Crates**, and **Modules**. 8 | * Control the **privacy** of code using the `pub` keyword. 9 | * Manage code organization and paths using `mod`, `use`, and the **module tree**. 10 | 2. Use the three standard library collections: **Vectors**, **Strings**, and **Hash Maps**. 11 | 12 | ### 1. Module System 13 | 14 | Rust's **module system** is a hierarchy that structures and manages code within a project. It helps control scope, which items are public, and where to find the definitions of code. 15 | 16 | **Packages and Crates** 17 | 18 | | Term | Definition | 19 | | :--- | :--- | 20 | | **Package** | A feature of **Cargo** that holds one or more *crates*. It contains the `Cargo.toml` file. | 21 | | **Crate** | The unit of compilation in Rust. A package must contain at least one crate. | 22 | | **Binary Crate** | An executable program (e.g., `src/main.rs`). | 23 | | **Library Crate** | Code intended to be used by other projects (e.g., `src/lib.rs`). | 24 | 25 | **Modules and Paths** 26 | 27 | * **Modules (`mod`)**: Modules are containers that hold definitions (functions, structs, other modules). They form a **tree structure** within a crate, starting from the crate root (`src/main.rs` or `src/lib.rs`). 28 | * **Privacy Rules**: All items (functions, methods, etc.) are **private by default**. To make an item accessible from its parent module or outside, you must prefix it with `pub`. 29 | * **Paths**: To refer to an item within the module tree, you use a **path**. 30 | * **Absolute Path**: Starts from the crate root (`crate::`). 31 | * **Relative Path**: Starts from the current module (`super::` to go up one level, or the item's name for a sibling module). 32 | * **Bringing Paths into Scope (`use`)**: The `use` keyword creates a shortcut to an item, allowing you to refer to it directly without typing out the full path every time. This is standard practice for external dependencies and heavily used local modules. 33 | 34 | ### 2. Common Collections 35 | 36 | Collections are data structures that can hold multiple values, where the amount of data is not known at compile time. Therefore, the data they manage is always stored on the **Heap**. 37 | 38 | **Vectors** (`Vec`) 39 | 40 | * **Purpose**: Stores a variable-sized list of values of the **same type** (`T`). 41 | * **Characteristics**: Elements are stored contiguously in memory, making access fast. Can grow or shrink at runtime. 42 | * **Ownership**: When the `Vec` itself goes out of scope, it is dropped, and all the elements it owns are also dropped (memory is freed). 43 | 44 | **Strings** (`String` vs. `&str`) 45 | 46 | Rust has two primary string types: 47 | 48 | * **`String`**: The **owner** of the data. It is a growable, mutable, **UTF-8 encoded** sequence of characters stored on the **Heap**. This is what you use when you need to modify or own text data. 49 | * **`&str` (String Slice)**: An **immutable reference** to a portion of a `String` or a string literal (which is stored in the binary). This is what you use for fast, read-only string views. 50 | * **Complexity**: Unlike many languages, Rust's `String` is complex due to its commitment to UTF-8 safety, requiring care when indexing or manipulating characters. 51 | 52 | **Hash Maps** (`HashMap`) 53 | 54 | * **Purpose**: Stores mappings from keys (`K`) to values (`V`). 55 | * **Characteristics**: Keys must be of the same type, and values must be of the same type. Allows fast retrieval of a value based on its key. 56 | * **Ownership**: The Hash Map takes **ownership** of the data for both the keys and the values when they are inserted. 57 | 58 | | Collection Type | Access Method | Ownership Rule | 59 | | :--- | :--- | :--- | 60 | | `Vec` | Indexing (`[]`) or `.get()` | Stores owned values. | 61 | | `String` | Methods (e.g., `.push_str()`) | Is the owner of the data. | 62 | | `HashMap` | `.get()` or `.insert()` | Takes ownership of keys and values. | -------------------------------------------------------------------------------- /basics/01_hello/README.md: -------------------------------------------------------------------------------- 1 | ## Module 1: Basic syntax 2 | 3 | By the end of this module, you should be able to: 4 | 5 | 1. Declare variables and understand Rust's **immutability-by-default** philosophy. 6 | 2. Use Rust's basic data types. 7 | 3. Define and call functions, understanding how Rust uses **expressions** and **statements**. 8 | 4. Implement basic **Control Flow** constructs like `if/else`. 9 | 10 | ### 1. Variables and Mutability 11 | 12 | * **Immutability:** Variables are **immutable** by default. Once a value is bound to a name using `let`, it cannot be changed. 13 | ```rust 14 | let x = 5; // Immutable 15 | // x = 6; // ERROR! 16 | ``` 17 | * **Mutability:** Use the **`mut`** keyword to explicitly opt-in to making a variable changeable. 18 | ```rust 19 | let mut y = 5; // Mutable 20 | y = 6; // OK 21 | ``` 22 | * **Constants:** Defined with the **`const`** keyword. They must be set at compile time, and their type **must** be annotated. 23 | ```rust 24 | const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; 25 | ``` 26 | * **Shadowing:** Declaring a *new* variable with the same name as a previous one. This is distinct from mutation and allows you to change the type while keeping the same name. 27 | ```rust 28 | let spaces = " "; // String type 29 | let spaces = spaces.len(); // u8 type (shadowing) 30 | ``` 31 | 32 | ### 2. Data Types 33 | 34 | Rust is a **statically typed** language, meaning it must know the type of all variables at compile time, though it often uses **type inference** to deduce the type for you. 35 | 36 | * **Scalar Types (Single Value):** 37 | 38 | * **Integers:** Signed (`i8`, `i16`, `i32`, `i64`, `i128`, `isize`) and Unsigned (`u8`, `u16`, `u32`, `u64`, `u128`, `usize`). `i32` is the default. 39 | * **Floating-Point:** `f32` (single-precision) and `f64` (double-precision, the default). 40 | * **Boolean:** `bool` (`true` or `false`). 41 | * **Character:** `char` (four bytes, represents a Unicode Scalar Value). 42 | 43 | * **Compound Types (Groups of Values):** 44 | 45 | * **Tuples:** A fixed-length, ordered list of values of potentially **different types**. 46 | ```rust 47 | let tup: (i32, f64, u8) = (500, 6.4, 1); 48 | let five_hundred = tup.0; // Access by index 49 | ``` 50 | * **Arrays:** A fixed-length collection of values where **every element must have the same type**. Stored on the stack. 51 | ```rust 52 | let a = [1, 2, 3, 4, 5]; 53 | let first = a[0]; // Access by index 54 | ``` 55 | 56 | ### 3. Functions 57 | 58 | * **Definition:** Functions are declared using the **`fn`** keyword. The main entry point is `fn main()`. 59 | * **Parameters:** Type annotations **must** be declared for all function parameters. 60 | ```rust 61 | fn add_numbers(x: i32, y: i32) { ... } 62 | ``` 63 | * **Statements vs. Expressions:** 64 | * **Statements** perform an action but do not return a value (e.g., `let x = 6;`). 65 | * **Expressions** evaluate to a resulting value. In Rust, almost everything is an expression, including function calls, macros, and blocks defined with curly brackets. 66 | * **Return Values:** The value of the **final expression** in the function body is returned implicitly. You **do not** use a semicolon on the final expression. 67 | ```rust 68 | fn five() -> i32 { // -> i32 denotes the return type 69 | 5 // This is an expression, no semicolon 70 | } 71 | ``` 72 | 73 | ### 4. Control Flow 74 | 75 | * **Conditional Execution (`if/else`):** The condition in an `if` expression **must** be a `bool` (Boolean). Rust does not automatically convert non-boolean types to a boolean. 76 | * `if` is an **expression**, meaning it can return a value (e.g., in a `let` statement), but all arms must return the same type. 77 | * **Looping:** 78 | * **`loop`:** Repeats code indefinitely until explicitly told to stop using `break`. 79 | * `loop` can return a value using `break value;`. 80 | * **`while`:** Repeats as long as a condition remains `true`. 81 | * **`for`:** Used for iterating over collections (or ranges), which is the safest and most idiomatic way to loop in Rust. 82 | ```rust 83 | // Iterate through a range, 1 up to (but not including) 4 84 | for number in 1..4 { 85 | println!("{number}"); 86 | } 87 | ``` -------------------------------------------------------------------------------- /ffi/string/upper.c: -------------------------------------------------------------------------------- 1 | // in `upper.c` 2 | 3 | #include // printf 4 | #include // uint8_t, uint32_t 5 | #include // exit 6 | 7 | void encode_utf8(uint32_t *src, char *dst) { 8 | int i = 0; 9 | int j = 0; 10 | 11 | while (1) { 12 | uint32_t scalar = src[i]; 13 | 14 | if (scalar == 0) { 15 | dst[j] = 0; // null terminator 16 | break; 17 | } 18 | 19 | if (scalar > 0b11111111111) { 20 | fprintf(stderr, "Can only encode codepoints <= 0x%x", 0b11111111111); 21 | exit(1); 22 | } 23 | 24 | if (scalar > 0b1111111) { // 7 bits 25 | // 2-byte sequence 26 | 27 | uint8_t b1 = 0b11000000 | ((uint8_t) ((scalar & 0b11111000000) >> 6)); 28 | // 2-byte marker first 5 of 11 bits 29 | 30 | uint8_t b2 = 0b10000000 | ((uint8_t) (scalar & 0b111111)); 31 | // continuation last 6 of 11 bits 32 | 33 | dst[j + 0] = b1; 34 | dst[j + 1] = b2; 35 | j += 2; 36 | } else { 37 | // 1-byte sequence 38 | dst[j] = (char) scalar; 39 | j++; 40 | } 41 | 42 | i++; 43 | } 44 | } 45 | 46 | void decode_utf8(char *src, uint32_t *dst) { 47 | int i = 0; 48 | int j = 0; 49 | 50 | while (1) { 51 | uint8_t c = src[i]; 52 | if (c == 0) { 53 | dst[j] = 0; 54 | break; // null terminator 55 | } 56 | 57 | uint32_t scalar; 58 | int len; 59 | 60 | if (c >> 3 == 0b11110) { 61 | fprintf(stderr, "decode_utf8: 4-byte sequences are not supported!\n"); 62 | exit(1); 63 | } if (c >> 4 == 0b1110) { 64 | fprintf(stderr, "decode_utf8: 3-byte sequences are not supported!\n"); 65 | exit(1); 66 | } else if (c >> 5 == 0b110) { 67 | // 2-byte sequence 68 | uint32_t b1 = (uint32_t) src[i]; 69 | uint32_t b2 = (uint32_t) src[i + 1]; 70 | uint32_t mask1 = 0b0000011111000000; 71 | uint32_t mask2 = 0b0000000000111111; 72 | 73 | scalar = ((b1 << 6) & mask1) | ((b2 << 0) & mask2); 74 | len = 2; 75 | } else { 76 | // 1-byte sequence 77 | scalar = (uint32_t) c; 78 | len = 1; 79 | } 80 | dst[j++] = scalar; 81 | i += len; 82 | } 83 | } 84 | 85 | #include // toupper 86 | 87 | int main(int argc, char **argv) { 88 | uint32_t scalars[1024]; // hopefully that's enough 89 | decode_utf8(argv[1], scalars); 90 | 91 | for (int i = 0;; i++) { 92 | if (scalars[i] == 0) { 93 | break; 94 | } 95 | printf("U+%04X ", scalars[i]); 96 | } 97 | printf("\n"); 98 | 99 | // this is the highest codepoint we can decode/encode successfully 100 | const size_t table_size = 0b11111111111; 101 | uint32_t lower_to_upper[table_size]; 102 | // initialize the table to just return the codepoint unchanged 103 | for (uint32_t cp = 0; cp < table_size; cp++) { 104 | lower_to_upper[cp] = cp; 105 | } 106 | // set a-z => A-Z 107 | for (int c = 97; c <= 122; c++) { // ha. 108 | lower_to_upper[(uint32_t) c] = (uint32_t) toupper(c); 109 | } 110 | 111 | // note: nested functions is a GNU extension! 112 | void set(char *lower, char *upper) { 113 | uint32_t lower_s[1024]; 114 | uint32_t upper_s[1024]; 115 | decode_utf8(lower, lower_s); 116 | decode_utf8(upper, upper_s); 117 | for (int i = 0;; i++) { 118 | if (lower_s[i] == 0) { 119 | break; 120 | } 121 | lower_to_upper[lower_s[i]] = upper_s[i]; 122 | } 123 | } 124 | // set a few more 125 | set( 126 | "éêèàâëüöïÿôîçæœ", 127 | "ÉÊÈÀÂËÜÖÏŸÔÎÇÆŒ" 128 | ); 129 | 130 | // now convert our scalars to upper-case 131 | for (int i = 0;; i++) { 132 | if (scalars[i] == 0) { 133 | break; 134 | } 135 | scalars[i] = lower_to_upper[scalars[i]]; 136 | } 137 | 138 | uint8_t result[1024]; // yolo 139 | encode_utf8(scalars, result); 140 | 141 | printf("%s\n", result); 142 | 143 | return 0; 144 | } -------------------------------------------------------------------------------- /basics/05_error_handling/README.md: -------------------------------------------------------------------------------- 1 | ## 🛑 Module 5: Error Handling and Testing 2 | 3 | This module covers the final steps of writing production-ready Rust code: managing failure gracefully using Rust's distinct error handling model, and verifying correct behavior using the built-in testing features. 4 | 5 | By the end of this module, you should be able to: 6 | 7 | 1. Differentiate between **recoverable** and **unrecoverable** errors in Rust. 8 | * Use the `Result` enum and the `?` operator for professional error propagation. 9 | 2. Write automated **Unit Tests** and **Integration Tests** using the `#[test]` attribute. 10 | * Control and configure the test runner using **`cargo test`** flags. 11 | 12 | ### 1. Error Handling 13 | 14 | Rust distinguishes between two types of errors, forcing the programmer to handle them explicitly, which leads to more robust software. 15 | 16 | **Unrecoverable Errors** (`panic!`) 17 | 18 | * **Purpose**: Used when a program reaches a state that should **never** happen, indicating an irrecoverable bug (e.g., accessing an index beyond an array's bounds). 19 | * **Action**: The `panic!` macro starts the process of **unwinding** (cleaning up the stack and freeing memory) or **aborting** (ending the program immediately). 20 | * **Best Practice**: Use `panic!` only for logic errors, prototypes, or tests where failure is expected. For core application logic, use `Result`. 21 | 22 | **Recoverable Errors** (`Result`) 23 | 24 | * **Purpose**: Used when an error is expected and can be handled (e.g., file not found, network failure). 25 | * **The `Result` Enum**: This is a generic enum that represents either success or failure: 26 | ```rust 27 | enum Result { 28 | Ok(T), // Success: Contains the successful return value of type T 29 | Err(E), // Failure: Contains the error value of type E 30 | } 31 | ``` 32 | * **Handling with `match`**: The most explicit way to handle a `Result` is by using a `match` expression to take different actions based on `Ok` or `Err`. 33 | 34 | **Error Propagation** 35 | 36 | The `?` operator simplifies error handling by acting as shorthand for a `match` expression: 37 | 38 | * If the `Result` is `Ok`, the `?` operator extracts the value inside and continues execution. 39 | * If the `Result` is `Err`, it immediately returns the error value from the current function. 40 | 41 | The `?` operator is the preferred way to propagate errors up the call stack. 42 | 43 | ### 2. Writing Automated Tests 44 | 45 | Rust has built-in support for testing integrated directly into the language and managed by Cargo. 46 | 47 | **Unit Tests** 48 | 49 | * **Location**: Placed in the same file as the code they are testing, typically within a `mod tests { ... }` block annotated with `#[cfg(test)]`. 50 | * **Syntax**: Every test function is simply a regular function annotated with `#[test]`. 51 | * **Verification**: You use **assertion macros** to check conditions: 52 | * `assert!(condition)`: Panics if the condition is false. 53 | * `assert_eq!(left, right)`: Panics if the two values are not equal. 54 | * `assert_ne!(left, right)`: Panics if the two values are equal. 55 | * **Testing `panic!`**: You can check that code *correctly* panics using the `#[should_panic]` annotation. 56 | 57 | **Integration Tests** 58 | 59 | * **Purpose**: Test the public interface of your library crate as a whole, ensuring different parts work together correctly. 60 | * **Location**: Placed in a dedicated **`tests` directory** next to the `src` directory (e.g., `project_root/tests/`). 61 | * **Execution**: Each file in the `tests` directory is compiled as its own external crate and run against your library. 62 | 63 | **Running Tests with Cargo** 64 | 65 | The **`cargo test`** command automatically discovers and runs all tests in your project (both unit and integration tests). 66 | 67 | | Command | Action | 68 | | :--- | :--- | 69 | | `cargo test` | Runs all tests in the project. | 70 | | `cargo test test_name` | Runs only tests whose name contains `test_name`. | 71 | | `cargo test -- --test-threads=1` | Runs tests sequentially (useful when tests require exclusive access to shared resources). | 72 | | `cargo test -- --show-output` | Shows the output of successful tests (which is usually suppressed). | 73 | 74 | ### How to run 75 | 76 | Run exercises: 77 | 78 | ```bash 79 | cd basics/05_error_handling 80 | cargo run 81 | ``` 82 | 83 | Run tests: 84 | 85 | ```bash 86 | cd basics/05_error_handling 87 | cargo test 88 | ``` -------------------------------------------------------------------------------- /basics/02_ownership/src/main.rs: -------------------------------------------------------------------------------- 1 | // Exercises for Module 2 — Ownership and Borrowing 2 | // Run: cargo run 3 | 4 | fn main() { 5 | println!("=== Ownership & Borrowing exercises ===\n"); 6 | 7 | ownership_move_and_clone(); 8 | copy_types_demo(); 9 | borrowing_rules_demo(); 10 | mutable_borrow_example(); 11 | fix_ownership_error_example(); 12 | slice_examples(); 13 | 14 | println!("\n=== End ==="); 15 | } 16 | 17 | /// Demonstrate move semantics and how to keep data with clone(). 18 | fn ownership_move_and_clone() { 19 | println!("-- ownership_move_and_clone --"); 20 | 21 | let s1 = String::from("hello"); 22 | // Move: s2 takes ownership of the heap data 23 | let s2 = s1; 24 | println!("s2 = {}", s2); 25 | // s1 is now invalid (use of s1 here would be a compile error). 26 | // 27 | // If you need to keep using the original, clone the data: 28 | let s3 = String::from("world"); 29 | let s4 = s3.clone(); // deep copy of heap data 30 | println!("s3 = {}, s4 = {}", s3, s4); // both usable 31 | } 32 | 33 | /// Types that implement Copy are copied, not moved. 34 | fn copy_types_demo() { 35 | println!("-- copy_types_demo --"); 36 | 37 | let x = 42; // i32 implements Copy 38 | let y = x; // copy, x still valid 39 | println!("x = {}, y = {}", x, y); 40 | 41 | let b = true; 42 | let c = b; 43 | println!("b = {}, c = {}", b, c); 44 | } 45 | 46 | /// Immutable borrowing: multiple readers allowed. 47 | fn borrowing_rules_demo() { 48 | println!("-- borrowing_rules_demo --"); 49 | 50 | let s = String::from("read-only"); 51 | let r1 = &s; 52 | let r2 = &s; 53 | // multiple immutable borrows are fine 54 | println!("r1 = {}, r2 = {}", r1, r2); 55 | 56 | // declare s2 outside the inner block so we can mutate it later 57 | let mut s2 = String::from("changeable"); 58 | { 59 | let r3 = &s2; // immutable borrow 60 | println!("r3 = {}", r3); 61 | // r3's last use is above, so the mutable borrow below is allowed 62 | } // r3 goes out of scope here 63 | 64 | // now take a mutable borrow and mutate s2 65 | { 66 | let mr = &mut s2; 67 | mr.push_str("!"); 68 | println!("mr after mutation = {}", mr); 69 | } 70 | 71 | println!("s2 final = {}", s2); 72 | } 73 | 74 | /// Mutable borrowing: only one mutable reference at a time. 75 | fn mutable_borrow_example() { 76 | println!("-- mutable_borrow_example --"); 77 | 78 | let mut s = String::from("hello"); 79 | { 80 | let mr = &mut s; // one mutable borrow 81 | mr.push_str(" world"); 82 | println!("mr = {}", mr); 83 | } // mr goes out of scope here, returning borrow to owner 84 | 85 | // Now we can borrow mutably again or immutably 86 | let r = &s; 87 | println!("after mutation, r = {}", r); 88 | } 89 | 90 | /// Example showing how to fix common ownership errors by borrowing. 91 | fn fix_ownership_error_example() { 92 | println!("-- fix_ownership_error_example --"); 93 | 94 | let mut s = String::from("fix me"); 95 | append_exclamation(&mut s); // pass a mutable reference 96 | println!("s after append_exclamation = {}", s); 97 | 98 | // BAD pattern (for reference): returning a reference to a local value is disallowed. 99 | // The following would not compile if uncommented: 100 | // 101 | // fn bad_return() -> &String { 102 | // let s = String::from("temp"); 103 | // &s // ERROR: cannot return reference to local variable `s` 104 | // } 105 | // 106 | // The fix: return the owned String instead, or accept an input reference and return a slice. 107 | } 108 | 109 | /// Appends an exclamation mark via a mutable borrow. 110 | fn append_exclamation(s: &mut String) { 111 | s.push('!'); 112 | } 113 | 114 | /// Slice examples: &str and &[T] 115 | fn slice_examples() { 116 | println!("-- slice_examples --"); 117 | 118 | // string slice (&str) 119 | let s = String::from("hello world"); 120 | let word = first_word(&s); 121 | println!("first_word of '{}' is '{}'", s, word); 122 | 123 | // &str can be taken from a string literal too 124 | let lit: &str = "static slice"; 125 | println!("literal slice = {}", lit); 126 | 127 | // array slice (&[T]) 128 | let arr = [10, 20, 30, 40]; 129 | let mid = &arr[1..3]; // &[i32] 130 | println!("arr = {:?}, mid slice = {:?}", arr, mid); 131 | 132 | // slice a String as bytes and convert to &str where valid UTF-8 133 | let s2 = String::from("rustacean"); 134 | let part = &s2[0..4]; // "rust" 135 | println!("part of '{}': {}", s2, part); 136 | } 137 | 138 | /// Return the first word as a &str slice — idiomatic safe slice example. 139 | fn first_word(s: &str) -> &str { 140 | // iterate bytes to find the first space 141 | for (i, &b) in s.as_bytes().iter().enumerate() { 142 | if b == b' ' { 143 | return &s[..i]; 144 | } 145 | } 146 | &s[..] 147 | } -------------------------------------------------------------------------------- /hands-on/merkle-tree/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "cc" 7 | version = "1.2.47" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" 10 | dependencies = [ 11 | "find-msvc-tools", 12 | "shlex", 13 | ] 14 | 15 | [[package]] 16 | name = "cfg-if" 17 | version = "1.0.4" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 20 | 21 | [[package]] 22 | name = "find-msvc-tools" 23 | version = "0.1.5" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 26 | 27 | [[package]] 28 | name = "getrandom" 29 | version = "0.2.16" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 32 | dependencies = [ 33 | "cfg-if", 34 | "libc", 35 | "wasi", 36 | ] 37 | 38 | [[package]] 39 | name = "libc" 40 | version = "0.2.177" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" 43 | 44 | [[package]] 45 | name = "merkle_tree" 46 | version = "0.1.0" 47 | dependencies = [ 48 | "ring", 49 | ] 50 | 51 | [[package]] 52 | name = "ring" 53 | version = "0.17.14" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 56 | dependencies = [ 57 | "cc", 58 | "cfg-if", 59 | "getrandom", 60 | "libc", 61 | "untrusted", 62 | "windows-sys", 63 | ] 64 | 65 | [[package]] 66 | name = "shlex" 67 | version = "1.3.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 70 | 71 | [[package]] 72 | name = "untrusted" 73 | version = "0.9.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 76 | 77 | [[package]] 78 | name = "wasi" 79 | version = "0.11.1+wasi-snapshot-preview1" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 82 | 83 | [[package]] 84 | name = "windows-sys" 85 | version = "0.52.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 88 | dependencies = [ 89 | "windows-targets", 90 | ] 91 | 92 | [[package]] 93 | name = "windows-targets" 94 | version = "0.52.6" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 97 | dependencies = [ 98 | "windows_aarch64_gnullvm", 99 | "windows_aarch64_msvc", 100 | "windows_i686_gnu", 101 | "windows_i686_gnullvm", 102 | "windows_i686_msvc", 103 | "windows_x86_64_gnu", 104 | "windows_x86_64_gnullvm", 105 | "windows_x86_64_msvc", 106 | ] 107 | 108 | [[package]] 109 | name = "windows_aarch64_gnullvm" 110 | version = "0.52.6" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 113 | 114 | [[package]] 115 | name = "windows_aarch64_msvc" 116 | version = "0.52.6" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 119 | 120 | [[package]] 121 | name = "windows_i686_gnu" 122 | version = "0.52.6" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 125 | 126 | [[package]] 127 | name = "windows_i686_gnullvm" 128 | version = "0.52.6" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 131 | 132 | [[package]] 133 | name = "windows_i686_msvc" 134 | version = "0.52.6" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 137 | 138 | [[package]] 139 | name = "windows_x86_64_gnu" 140 | version = "0.52.6" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 143 | 144 | [[package]] 145 | name = "windows_x86_64_gnullvm" 146 | version = "0.52.6" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 149 | 150 | [[package]] 151 | name = "windows_x86_64_msvc" 152 | version = "0.52.6" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 155 | -------------------------------------------------------------------------------- /hands-on/httpie-lite/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Result}; 2 | use clap::{Args, Parser, Subcommand}; 3 | use colored::*; 4 | use mime::Mime; 5 | use reqwest::{header, Client, Response, Url}; 6 | use std::{collections::HashMap, str::FromStr}; 7 | use syntect::{ 8 | easy::HighlightLines, 9 | highlighting::{ThemeSet}, 10 | parsing::SyntaxSet, 11 | util::{as_24_bit_terminal_escaped, LinesWithEndings}, 12 | }; 13 | 14 | #[derive(Parser, Debug)] 15 | struct Opts { 16 | #[command(subcommand)] 17 | cmd: Commands, 18 | } 19 | 20 | #[derive(Subcommand, Debug)] 21 | enum Commands { 22 | Get(Get), 23 | Post(Post), 24 | } 25 | 26 | #[derive(Args, Debug)] 27 | struct Get { 28 | #[arg(value_parser = parse_url)] 29 | url: String, 30 | } 31 | 32 | #[derive(Args, Debug)] 33 | struct Post { 34 | #[arg(value_parser = parse_url)] 35 | url: String, 36 | #[arg(value_parser = parse_kv_pair)] 37 | body: Vec, 38 | } 39 | 40 | #[derive(Clone, Debug, PartialEq)] 41 | struct KvPair { 42 | k: String, 43 | v: String, 44 | } 45 | 46 | impl FromStr for KvPair { 47 | type Err = anyhow::Error; 48 | 49 | fn from_str(s: &str) -> Result { 50 | let mut split = s.split('='); 51 | let err = || anyhow!(format!("Failed to parse {}", s)); 52 | 53 | Ok(Self { 54 | k: (split.next().ok_or_else(err)?).to_string(), 55 | v: (split.next().ok_or_else(err)?).to_string(), 56 | }) 57 | } 58 | } 59 | 60 | fn parse_kv_pair(s: &str) -> Result { 61 | s.parse() 62 | } 63 | 64 | fn parse_url(s: &str) -> Result { 65 | let _url: Url = s.parse()?; 66 | 67 | Ok(s.into()) 68 | } 69 | 70 | async fn get(client: Client, args: &Get) -> Result<()> { 71 | let resp = client.get(&args.url).send().await?; 72 | println!("{:?}", resp.text().await?); 73 | 74 | Ok(()) 75 | } 76 | 77 | async fn post(client: Client, args: &Post) -> Result<()> { 78 | let mut body = HashMap::new(); 79 | for pair in args.body.iter() { 80 | body.insert(&pair.k, &pair.v); 81 | } 82 | 83 | let resp = client.post(&args.url).json(&body).send().await?; 84 | 85 | Ok(print_resp(resp).await?) 86 | } 87 | 88 | fn print_status(resp: &Response) { 89 | let status = format!("{:?} {}", resp.version(), resp.status()).blue(); 90 | println!("{}\n", status); 91 | } 92 | 93 | fn print_headers(resp: &Response) { 94 | for (name, value) in resp.headers() { 95 | println!("{}: {:?}", name.to_string().green(), value); 96 | } 97 | print!("\n"); 98 | } 99 | 100 | fn print_body(m: Option, body: &String) { 101 | match m { 102 | Some(v) if v == mime::APPLICATION_JSON => print_syntect(body, "json"), 103 | Some(v) if v == mime::TEXT_HTML => print_syntect(body, "html"), 104 | 105 | _ => println!("{}", body), 106 | } 107 | } 108 | 109 | fn print_syntect(s: &str, ext: &str) { 110 | // Load these once at the start of your program 111 | let ps = SyntaxSet::load_defaults_newlines(); 112 | let ts = ThemeSet::load_defaults(); 113 | 114 | // find syntax by extension; if not found, fallback to plain print 115 | if let Some(syntax) = ps.find_syntax_by_extension(ext) { 116 | // choose theme if available, else fallback 117 | let theme = ts.themes.get("base16-ocean.dark"); 118 | if let Some(theme) = theme { 119 | let mut h = HighlightLines::new(syntax, theme); 120 | for line in LinesWithEndings::from(s) { 121 | match h.highlight_line(line, &ps) { 122 | Ok(ranges) => { 123 | let escaped = as_24_bit_terminal_escaped(&ranges[..], true); 124 | print!("{}", escaped); 125 | } 126 | Err(_) => { 127 | // highlight failure for this line -> print raw line 128 | print!("{}", line); 129 | } 130 | } 131 | } 132 | return; 133 | } 134 | } 135 | 136 | // fallback: plain print when syntax or theme not found 137 | print!("{}", s); 138 | } 139 | 140 | fn get_content_type(resp: &Response) -> Option { 141 | resp.headers() 142 | .get(header::CONTENT_TYPE) 143 | .and_then(|v| v.to_str().ok()) // avoid panics on invalid header bytes 144 | .and_then(|s| s.parse().ok()) // avoid panics on invalid mime parsing 145 | } 146 | 147 | async fn print_resp(resp: Response) -> Result<()> { 148 | print_status(&resp); 149 | print_headers(&resp); 150 | let mime = get_content_type(&resp); 151 | let body = resp.text().await?; 152 | print_body(mime, &body); 153 | 154 | Ok(()) 155 | } 156 | 157 | #[tokio::main] 158 | async fn main() -> Result<()> { 159 | let opts: Opts = Opts::parse(); 160 | println!("{:?}", opts); 161 | let client = Client::new(); 162 | let result = match opts.cmd { 163 | Commands::Get(ref args) => get(client, args).await?, 164 | Commands::Post(ref args) => post(client, args).await?, 165 | }; 166 | 167 | Ok(result) 168 | } -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/README.md: -------------------------------------------------------------------------------- 1 | # RustCodesFlow: Rust Codes Dataset Generator 2 | 3 | RustCodesFlow is a fast CLI tool that converts any public Rust GitHub repository into a clean JSONL dataset optimized for training AI coding agents. 4 | 5 | RustCodesFlow transforms Rust source code into normalized datasets that teach AI the logic and structure of code rather than arbitrary formatting differences. By anonymizing identifiers and normalizing formatting, it maximizes learning efficiency while preserving semantic meaning. 6 | 7 | ## Quick Start 8 | 9 | ```bash 10 | # Install 11 | git clone https://github.com/lesterli/rust-practice.git 12 | cd rust-practice/hands-on/rust-codes-flow 13 | cargo install --path cli 14 | 15 | # Convert a repository to JSONL dataset 16 | rustcodesflow https://github.com/rust-lang/rust --output rust_compiler_dataset.jsonl 17 | ``` 18 | 19 | ## Key Features 20 | 21 | - ⚡ **Lightning Fast**: Parallel processing with rayon for optimal performance 22 | - 🎯 **AI-Optimized**: Identifier anonymization (var_0, var_1, T0, T1) for better learning 23 | - 🏗️ **Full AST Parsing**: Complete extraction using syn with full fidelity 24 | - 📊 **Rich Metadata**: Semantic hashes, context, imports, and signatures 25 | - 📝 **Documentation Options**: Optional docstring preservation 26 | 27 | ## Architecture 28 | 29 | ### Structure 30 | 31 | ``` 32 | rust-codes-flow/ # Workspace root 33 | ├── Cargo.toml # Workspace definition 34 | ├── core/ # Core logic library 35 | │ ├── src/ 36 | │ │ ├── lib.rs # Library entry point 37 | │ │ ├── extractor.rs # Syn parsing & AST traversal 38 | │ │ ├── normalizer.rs # AST manipulation & identifier anonymization 39 | │ │ ├── hashing.rs # BLAKE3 semantic hashing 40 | │ │ └── models.rs # Data structures & JSON schemas 41 | │ └── Cargo.toml 42 | ├── cli/ # Binary entry point 43 | │ ├── src/ 44 | │ │ └── main.rs # CLI logic, git integration, parallel processing 45 | │ └── Cargo.toml 46 | └── README.md 47 | ``` 48 | 49 | ### Data Pipeline 50 | 51 | 1. **Input**: GitHub URL or local path 52 | 2. **Discovery**: Find all `.rs` files using walkdir 53 | 3. **Parse**: Full AST parsing with syn 54 | 4. **Extract**: Functions, methods, traits, impl blocks, modules 55 | 5. **Normalize**: Anonymize identifiers, remove comments, standardize formatting 56 | 6. **Hash**: BLAKE3 semantic hashing for deduplication 57 | 7. **Enrich**: Extract context and imports 58 | 8. **Output**: Stream to JSONL with progress tracking 59 | 60 | ## Usage Examples 61 | 62 | ### Basic Conversion 63 | 64 | ```bash 65 | # Process a GitHub repository 66 | rustcodesflow https://github.com/serde-rs/serde --output serde_dataset.jsonl 67 | 68 | # Process local repository 69 | rustcodesflow ./my-rust-project --output local_dataset.jsonl 70 | ``` 71 | 72 | ### Command Line Options 73 | 74 | - `--output, -o`: Output JSONL file path (required) 75 | - `--keep-docs`: Preserve docstring comments in output 76 | - `--full-context`: Include 50 lines of context before/after each item 77 | - `--threads`: Number of parallel threads (default: logical CPUs) 78 | 79 | ## JSONL Output Schema 80 | 81 | Each line in the output file represents one extracted code item: 82 | 83 | ```json 84 | { 85 | "project_context": { 86 | "repo_url": "https://github.com/rust-lang/rust", 87 | "commit_hash": "a1b2c3d4e5f6789...", 88 | "file_path": "src/libstd/io/mod.rs" 89 | }, 90 | "item_meta": { 91 | "kind": "function", 92 | "name": "read_to_end", 93 | "fully_qualified_name": "std::io::Read::read_to_end", 94 | "start_line": 124, 95 | "end_line": 156 96 | }, 97 | "content": { 98 | "signature": "fn read_to_end(&mut self, buf: &mut Vec) -> Result", 99 | "body_normalized": "fn read_to_end(var0: &mut self, var1: &mut Vec) -> Result { ... }", 100 | "semantic_hash": "8f4343460d268d83a3c8f3c9e4d5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", 101 | "docstring": "Reads entire input into this buffer.", 102 | "imports": [ 103 | "use std::io::{self, BufRead, Result}", 104 | "use crate::io::BufReader" 105 | ] 106 | }, 107 | "rag_context": { 108 | "context_before": "impl BufReader {", 109 | "context_after": " Ok(buf.len())" 110 | } 111 | } 112 | ``` 113 | 114 | ### Field Descriptions 115 | 116 | | Field | Type | Description | 117 | |-------|------|-------------| 118 | | `kind` | string | Item type: `function`, `method`, `impl`, `trait`, `module` | 119 | | `fully_qualified_name` | string | Best-effort qualified name | 120 | | `body_normalized` | string | Source code with normalized identifiers | 121 | | `semantic_hash` | string | BLAKE3 hash of normalized content for deduplication | 122 | | `context_before/after` | string | Surrounding source lines (optional) | 123 | | `imports` | array | Direct imports used by this item | 124 | 125 | ## Use Cases 126 | 127 | - **LLM Training**: Train models on normalized Rust code patterns 128 | - **RAG Systems**: Build retrieval-augmented generation for code intelligence 129 | - **Code Analysis**: Analyze architectural patterns across repositories 130 | - **Autocomplete**: Generate training data for AI-powered code completion 131 | 132 | ## License 133 | 134 | MIT License - see LICENSE file for details. 135 | 136 | --- 137 | 138 | Built with ❤️ for the Rust and AI communities. 139 | -------------------------------------------------------------------------------- /basics/03_generics/src/main.rs: -------------------------------------------------------------------------------- 1 | // Exercises for Module 3 — Generics and Traits 2 | // Run: cargo run 3 | 4 | use std::fmt::Display; 5 | 6 | fn main() { 7 | println!("=== Generics, Traits, Lifetimes ===\n"); 8 | 9 | generics_examples(); 10 | traits_examples(); 11 | lifetimes_examples(); 12 | 13 | println!("\n=== End ==="); 14 | } 15 | 16 | /* ------------------------- 17 | Generics examples 18 | ------------------------- */ 19 | 20 | fn generics_examples() { 21 | println!("-- generics_examples --"); 22 | 23 | // generic function returning a reference to the largest element 24 | let nums = vec![3, 7, 2, 9, 4]; 25 | let max = largest(&nums); 26 | println!("largest in {:?} is {}", nums, max); 27 | 28 | let words = vec!["apple", "pear", "banana"]; 29 | let max_word = largest(&words); 30 | println!("largest in {:?} is {}", words, max_word); 31 | 32 | // generic struct 33 | let p1 = Point { x: 5, y: 10.4 }; 34 | let p2 = Point { x: "left", y: 'L' }; 35 | println!("p1: ({}, {}), p2: ({}, {})", p1.x, p1.y, p2.x, p2.y); 36 | 37 | let p3 = p1.mixup(p2); 38 | println!("mixup -> ({}, {})", p3.x, p3.y); 39 | } 40 | 41 | /// return reference to largest element; T needs PartialOrd 42 | fn largest(list: &[T]) -> &T { 43 | assert!(!list.is_empty(), "list must be non-empty"); 44 | let mut largest = &list[0]; 45 | for item in &list[1..] { 46 | if item > largest { 47 | largest = item; 48 | } 49 | } 50 | largest 51 | } 52 | 53 | struct Point { 54 | x: T, 55 | y: U, 56 | } 57 | 58 | impl Point { 59 | // produce a Point mixing types from two points 60 | fn mixup(self, other: Point) -> Point { 61 | Point { 62 | x: self.x, 63 | y: other.y, 64 | } 65 | } 66 | } 67 | 68 | /* ------------------------- 69 | Traits examples 70 | ------------------------- */ 71 | 72 | fn traits_examples() { 73 | println!("\n-- traits_examples --"); 74 | 75 | let article = Article { 76 | headline: String::from("Generics in Rust"), 77 | author: String::from("Jane"), 78 | content: String::from("Short intro..."), 79 | }; 80 | let tweet = Tweet { 81 | username: String::from("rustacean"), 82 | content: String::from("I ❤️ Rust"), 83 | }; 84 | 85 | // simple use of an impl Trait argument 86 | notify(&article); 87 | notify(&tweet); 88 | 89 | // generic function with trait bounds and where clause 90 | let announced = announce_and_return_summary(&tweet, "breaking"); 91 | println!("announce_and_return_summary -> {}", announced); 92 | } 93 | 94 | pub trait Summary { 95 | fn summarize(&self) -> String; 96 | 97 | // default method 98 | fn read_more_link(&self) -> String { 99 | String::from("(read more)") 100 | } 101 | } 102 | 103 | struct Article { 104 | headline: String, 105 | author: String, 106 | content: String, 107 | } 108 | 109 | impl Summary for Article { 110 | fn summarize(&self) -> String { 111 | format!("{} by {} with {}", self.headline, self.author, self.content) 112 | } 113 | } 114 | 115 | struct Tweet { 116 | username: String, 117 | content: String, 118 | } 119 | 120 | impl Summary for Tweet { 121 | fn summarize(&self) -> String { 122 | format!("@{}: {}", self.username, self.content) 123 | } 124 | } 125 | 126 | // syntactic sugar: impl Trait 127 | fn notify(item: &impl Summary) { 128 | println!("notify: {}", item.summarize()); 129 | } 130 | 131 | // generic version with trait bounds and where 132 | fn announce_and_return_summary(item: &T, ann: U) -> String 133 | where 134 | T: Summary + Display, 135 | U: AsRef + Display, 136 | { 137 | println!("announcement: {}", ann); 138 | format!("{} -- {}", item, item.read_more_link()) 139 | } 140 | 141 | impl Display for Tweet { 142 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 143 | write!(f, "Tweet by {}: {}", self.username, self.content) 144 | } 145 | } 146 | 147 | /* ------------------------- 148 | Lifetimes examples 149 | ------------------------- */ 150 | 151 | fn lifetimes_examples() { 152 | println!("\n-- lifetimes_examples --"); 153 | 154 | let string1 = String::from("long string is long"); 155 | let string2 = "short"; 156 | 157 | let result = longest(string1.as_str(), string2); 158 | println!("longest('{}','{}') -> '{}'", string1, string2, result); 159 | 160 | // struct holding a reference requires an explicit lifetime 161 | let novel = String::from("Call me Ishmael."); 162 | // if no '.' is found, fall back to the entire `novel` 163 | let first_sentence = novel.split('.').next().unwrap_or(&novel[..]); 164 | let i = ImportantExcerpt { 165 | part: first_sentence, 166 | }; 167 | println!("ImportantExcerpt.part = '{}'", i.part); 168 | 169 | // static lifetime example 170 | let s: &'static str = "I have a static lifetime."; 171 | println!("{}", s); 172 | 173 | // function combining lifetimes and generics 174 | let ann = String::from("note"); 175 | let res = longest_with_announcement(first_sentence, string2, &ann); 176 | println!("longest_with_announcement -> '{}'", res); 177 | } 178 | 179 | // returns the longer of two string slices; the returned lifetime is tied to the inputs 180 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 181 | if x.len() >= y.len() { 182 | x 183 | } else { 184 | y 185 | } 186 | } 187 | 188 | struct ImportantExcerpt<'a> { 189 | part: &'a str, 190 | } 191 | 192 | fn longest_with_announcement<'a, T>(x: &'a str, y: &'a str, ann: &T) -> &'a str 193 | where 194 | T: Display, 195 | { 196 | println!("Announcement: {}", ann); 197 | longest(x, y) 198 | } -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/core/src/normalizer.rs: -------------------------------------------------------------------------------- 1 | use crate::models::*; 2 | use std::collections::HashMap; 3 | use syn::{self, visit_mut::VisitMut}; 4 | 5 | /// Scope-aware identifier normalizer 6 | /// Maps identifiers to neutral names like var_0, var_1, T0, T1, etc. 7 | pub struct Normalizer<'ast> { 8 | // Stack of scope mappings: original_name -> normalized_name 9 | scope_stack: Vec>, 10 | // Counter for generating normalized names 11 | var_counter: usize, 12 | type_counter: usize, 13 | // Configuration 14 | config: &'ast ExtractConfig, 15 | } 16 | 17 | impl<'ast> Normalizer<'ast> { 18 | pub fn new(config: &'ast ExtractConfig) -> Self { 19 | Self { 20 | scope_stack: Vec::new(), 21 | var_counter: 0, 22 | type_counter: 0, 23 | config, 24 | } 25 | } 26 | 27 | pub fn normalize_file(&mut self, _file: &mut syn::File) -> String { 28 | // Reset counters for each file 29 | self.var_counter = 0; 30 | self.type_counter = 0; 31 | self.scope_stack.clear(); 32 | 33 | // For now, return a simplified implementation that doesn't require AST modification 34 | // In a full implementation, we would visit and modify the AST 35 | "".to_string() 36 | } 37 | 38 | pub fn extract_docstring(&self, _item: &syn::Item) -> Option { 39 | if !self.config.keep_docs { 40 | return None; 41 | } 42 | 43 | // Simple docstring extraction - in a real implementation you'd parse attributes 44 | None 45 | } 46 | 47 | pub fn extract_imports(&self, _file: &syn::File) -> Vec { 48 | // Simple import extraction - in a real implementation you'd traverse use statements 49 | Vec::new() 50 | } 51 | 52 | fn enter_scope(&mut self) { 53 | self.scope_stack.push(HashMap::new()); 54 | } 55 | 56 | fn exit_scope(&mut self) { 57 | self.scope_stack.pop(); 58 | } 59 | 60 | fn normalize_identifier(&mut self, ident: &syn::Ident) -> syn::Ident { 61 | let original = ident.to_string(); 62 | 63 | // Check if this identifier is already in any scope 64 | for scope in self.scope_stack.iter().rev() { 65 | if let Some(normalized) = scope.get(&original) { 66 | return syn::Ident::new(normalized, ident.span()); 67 | } 68 | } 69 | 70 | // Generate new normalized name 71 | let normalized = if original.chars().next().is_some_and(|c| c.is_uppercase()) { 72 | // It's a type name 73 | let normalized = format!("T{}", self.type_counter); 74 | self.type_counter += 1; 75 | normalized 76 | } else { 77 | // It's a variable/function name 78 | let normalized = format!("var_{}", self.var_counter); 79 | self.var_counter += 1; 80 | normalized 81 | }; 82 | 83 | // Store in current scope 84 | if let Some(current_scope) = self.scope_stack.last_mut() { 85 | current_scope.insert(original, normalized.clone()); 86 | } 87 | 88 | syn::Ident::new(&normalized, ident.span()) 89 | } 90 | 91 | fn is_local_variable(&self, ident: &syn::Ident) -> bool { 92 | let name = ident.to_string(); 93 | 94 | // Skip standard library and known patterns 95 | if name.starts_with("std::") || name.starts_with("core::") || name.starts_with("crate::") { 96 | return false; 97 | } 98 | 99 | // Check if this identifier is declared in current or parent scopes 100 | for scope in self.scope_stack.iter().rev() { 101 | if scope.contains_key(&name) { 102 | return true; 103 | } 104 | } 105 | 106 | false 107 | } 108 | } 109 | 110 | impl<'ast> VisitMut for Normalizer<'ast> { 111 | fn visit_generics_mut(&mut self, generics: &mut syn::Generics) { 112 | // Normalize generic parameters 113 | for param in &mut generics.params { 114 | match param { 115 | syn::GenericParam::Type(type_param) => { 116 | // Normalize the type parameter name 117 | let original = type_param.ident.to_string(); 118 | let normalized = format!("T{}", self.type_counter); 119 | self.type_counter += 1; 120 | 121 | if let Some(current_scope) = self.scope_stack.last_mut() { 122 | current_scope.insert(original, normalized.clone()); 123 | } 124 | 125 | type_param.ident = syn::Ident::new(&normalized, type_param.ident.span()); 126 | } 127 | syn::GenericParam::Lifetime(_) => {} 128 | syn::GenericParam::Const(_) => {} 129 | } 130 | } 131 | } 132 | 133 | fn visit_pat_ident_mut(&mut self, pat: &mut syn::PatIdent) { 134 | // This is a variable declaration 135 | let normalized = self.normalize_identifier(&pat.ident); 136 | pat.ident = normalized; 137 | } 138 | 139 | fn visit_expr_path_mut(&mut self, expr: &mut syn::ExprPath) { 140 | // Check if this is a local variable usage 141 | if let Some(path_seg) = expr.path.segments.first() { 142 | if self.is_local_variable(&path_seg.ident) { 143 | let normalized = self.normalize_identifier(&path_seg.ident); 144 | expr.path.segments[0].ident = normalized; 145 | } 146 | } 147 | 148 | syn::visit_mut::visit_expr_path_mut(self, expr); 149 | } 150 | 151 | fn visit_item_fn_mut(&mut self, item: &mut syn::ItemFn) { 152 | self.enter_scope(); 153 | 154 | // Normalize function name 155 | let normalized_name = self.normalize_identifier(&item.sig.ident); 156 | item.sig.ident = normalized_name; 157 | 158 | // Normalize function parameters 159 | for input in &mut item.sig.inputs { 160 | match input { 161 | syn::FnArg::Receiver(_) => {} 162 | syn::FnArg::Typed(pat_type) => { 163 | self.visit_pat_mut(&mut pat_type.pat); 164 | } 165 | } 166 | } 167 | 168 | // Visit function body 169 | self.visit_block_mut(&mut item.block); 170 | 171 | self.exit_scope(); 172 | } 173 | 174 | fn visit_block_mut(&mut self, block: &mut syn::Block) { 175 | self.enter_scope(); 176 | 177 | for stmt in &mut block.stmts { 178 | self.visit_stmt_mut(stmt); 179 | } 180 | 181 | self.exit_scope(); 182 | } 183 | 184 | fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { 185 | // Normalize module name 186 | let normalized = self.normalize_identifier(&item.ident); 187 | item.ident = normalized; 188 | 189 | if let Some((_, items)) = &mut item.content { 190 | self.enter_scope(); 191 | for item in items { 192 | self.visit_item_mut(item); 193 | } 194 | self.exit_scope(); 195 | } 196 | } 197 | 198 | fn visit_item_trait_mut(&mut self, item: &mut syn::ItemTrait) { 199 | // Normalize trait name 200 | let normalized = self.normalize_identifier(&item.ident); 201 | item.ident = normalized; 202 | 203 | self.enter_scope(); 204 | 205 | // Normalize generics 206 | self.visit_generics_mut(&mut item.generics); 207 | 208 | // Visit trait items 209 | for item in &mut item.items { 210 | self.visit_trait_item_mut(item); 211 | } 212 | 213 | self.exit_scope(); 214 | } 215 | 216 | fn visit_item_impl_mut(&mut self, item: &mut syn::ItemImpl) { 217 | self.enter_scope(); 218 | 219 | // Visit impl block 220 | self.visit_generics_mut(&mut item.generics); 221 | 222 | // Handle different impl types - simplified approach 223 | // Just visit the self type regardless of trait impl or not 224 | self.visit_type_mut(&mut item.self_ty); 225 | 226 | for item in &mut item.items { 227 | self.visit_impl_item_mut(item); 228 | } 229 | 230 | self.exit_scope(); 231 | } 232 | } 233 | 234 | /// Normalize a complete ExtractedItem 235 | pub fn normalize_item(item: &mut ExtractedItem, config: &ExtractConfig) -> CoreResult<()> { 236 | let _normalizer = Normalizer::new(config); 237 | 238 | // For now, just keep the original content 239 | // In a full implementation, we would: 240 | // 1. Parse the body back into AST 241 | // 2. Normalize the AST 242 | // 3. Convert back to string 243 | 244 | // Update the item with basic info 245 | item.content.docstring = None; 246 | item.content.imports = Vec::new(); 247 | 248 | Ok(()) 249 | } 250 | 251 | /// Batch normalize multiple items 252 | pub fn normalize_items(items: &mut [ExtractedItem], config: &ExtractConfig) -> CoreResult<()> { 253 | for item in items { 254 | normalize_item(item, config)?; 255 | } 256 | Ok(()) 257 | } 258 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/core/src/hashing.rs: -------------------------------------------------------------------------------- 1 | use crate::models::*; 2 | use blake3::Hasher; 3 | 4 | /// Semantic hashing utilities for deduplication 5 | /// 6 | /// This module provides functions to generate strong semantic hashes 7 | /// from normalized Rust code items, ensuring that functionally equivalent 8 | /// code produces the same hash regardless of formatting differences. 9 | /// 10 | /// Generate a semantic hash from an ExtractedItem's normalized content 11 | /// 12 | /// This function creates a BLAKE3 hash of the normalized code body, 13 | /// signature, and other semantic content to create a unique identifier 14 | /// for deduplication purposes. 15 | pub fn semantic_hash_item(item: &ExtractedItem) -> CoreResult { 16 | // Create a hasher for this item 17 | let mut hasher = Hasher::new(); 18 | 19 | // Hash the normalized body (most important for semantic similarity) 20 | hasher.update(item.content.body_normalized.as_bytes()); 21 | 22 | // Hash the signature 23 | hasher.update(item.content.signature.as_bytes()); 24 | 25 | // Hash the item kind 26 | hasher.update(item.item_meta.kind.to_string().as_bytes()); 27 | 28 | // Generate the final hash 29 | let hash = hasher.finalize(); 30 | 31 | // Return as hex string with blake3: prefix 32 | Ok(format!("blake3:{}", hash.to_hex())) 33 | } 34 | 35 | /// Generate a semantic hash from just the normalized code string 36 | /// 37 | /// This is useful for comparing code similarity without full metadata. 38 | pub fn semantic_hash_code(code: &str, item_kind: &str) -> CoreResult { 39 | let mut hasher = Hasher::new(); 40 | 41 | hasher.update(code.as_bytes()); 42 | hasher.update(item_kind.as_bytes()); 43 | 44 | let hash = hasher.finalize(); 45 | Ok(format!("blake3:{}", hash.to_hex())) 46 | } 47 | 48 | /// Batch hash multiple items efficiently 49 | /// 50 | /// This function takes a slice of items and returns a vector of their semantic hashes. 51 | pub fn semantic_hash_items(items: &[ExtractedItem]) -> CoreResult> { 52 | let mut hashes = Vec::with_capacity(items.len()); 53 | 54 | for item in items { 55 | hashes.push(semantic_hash_item(item)?); 56 | } 57 | 58 | Ok(hashes) 59 | } 60 | 61 | /// Update an ExtractedItem with its semantic hash 62 | /// 63 | /// This is a convenience function that both generates and assigns the hash 64 | /// to the item's content. 65 | pub fn hash_and_update_item(item: &mut ExtractedItem) -> CoreResult<()> { 66 | let hash = semantic_hash_item(item)?; 67 | item.content.semantic_hash = hash; 68 | Ok(()) 69 | } 70 | 71 | /// Batch hash and update multiple items 72 | /// 73 | /// This function efficiently processes multiple items, generating their 74 | /// semantic hashes and updating their content in place. 75 | pub fn hash_and_update_items(items: &mut [ExtractedItem]) -> CoreResult<()> { 76 | for item in items { 77 | hash_and_update_item(item)?; 78 | } 79 | Ok(()) 80 | } 81 | 82 | /// Verify semantic similarity between two items 83 | /// 84 | /// Returns true if the two items have identical semantic content 85 | /// (normalized body, signature, and type). 86 | pub fn are_semantically_similar(item1: &ExtractedItem, item2: &ExtractedItem) -> CoreResult { 87 | let hash1 = semantic_hash_item(item1)?; 88 | let hash2 = semantic_hash_item(item2)?; 89 | 90 | Ok(hash1 == hash2) 91 | } 92 | 93 | /// Extract just the hash bytes for low-level comparisons 94 | /// 95 | /// Returns the raw BLAKE3 hash bytes, useful for custom similarity 96 | /// algorithms or cryptographic operations. 97 | pub fn hash_bytes(item: &ExtractedItem) -> CoreResult<[u8; 32]> { 98 | let mut hasher = Hasher::new(); 99 | 100 | hasher.update(item.content.body_normalized.as_bytes()); 101 | hasher.update(item.content.signature.as_bytes()); 102 | hasher.update(item.item_meta.kind.to_string().as_bytes()); 103 | 104 | let hash = hasher.finalize(); 105 | Ok(*hash.as_bytes()) 106 | } 107 | 108 | /// Calculate Hamming distance between two semantic hashes 109 | /// 110 | /// Returns the number of differing bits between two hash values. 111 | /// Useful for approximate similarity matching. 112 | pub fn hash_hamming_distance(hash1: &str, hash2: &str) -> Option { 113 | if !hash1.starts_with("blake3:") || !hash2.starts_with("blake3:") { 114 | return None; 115 | } 116 | 117 | let bytes1 = &hex::decode(&hash1[7..]).ok()?[0..32]; 118 | let bytes2 = &hex::decode(&hash2[7..]).ok()?[0..32]; 119 | 120 | let mut distance = 0; 121 | for i in 0..32 { 122 | let xored = bytes1[i] ^ bytes2[i]; 123 | // Count set bits in the xor result 124 | distance += xored.count_ones() as usize; 125 | } 126 | 127 | Some(distance) 128 | } 129 | 130 | /// Find similar items within a collection 131 | /// 132 | /// Given a collection of items and a similarity threshold (0-255), 133 | /// returns pairs of indices that are semantically similar. 134 | /// Lower threshold means more similarity required. 135 | pub fn find_similar_items( 136 | items: &[ExtractedItem], 137 | max_distance: u8, 138 | ) -> CoreResult> { 139 | let mut similar_pairs = Vec::new(); 140 | let hashes: Vec = semantic_hash_items(items)?; 141 | 142 | for i in 0..items.len() { 143 | for j in (i + 1)..items.len() { 144 | if let Some(distance) = hash_hamming_distance(&hashes[i], &hashes[j]) { 145 | if distance <= max_distance as usize { 146 | similar_pairs.push((i, j, distance)); 147 | } 148 | } 149 | } 150 | } 151 | 152 | Ok(similar_pairs) 153 | } 154 | 155 | /// Build a deduplication map from semantic hashes 156 | /// 157 | /// Returns a HashMap where keys are semantic hashes and values are 158 | /// indices of items with that hash. Useful for removing duplicates. 159 | pub fn build_dedup_map( 160 | items: &[ExtractedItem], 161 | ) -> CoreResult>> { 162 | let mut dedup_map = std::collections::HashMap::new(); 163 | let hashes = semantic_hash_items(items)?; 164 | 165 | for (idx, hash) in hashes.into_iter().enumerate() { 166 | dedup_map.entry(hash).or_insert_with(Vec::new).push(idx); 167 | } 168 | 169 | Ok(dedup_map) 170 | } 171 | 172 | /// Statistics about hash distribution 173 | /// 174 | /// Provides insights into the quality of semantic hashing by analyzing 175 | /// hash collisions and distribution. 176 | pub struct HashStats { 177 | pub total_items: usize, 178 | pub unique_hashes: usize, 179 | pub collision_count: usize, 180 | pub max_collision_group: usize, 181 | pub collision_rate: f64, 182 | } 183 | 184 | impl HashStats { 185 | /// Analyze hash statistics for a collection of items 186 | pub fn analyze(items: &[ExtractedItem]) -> CoreResult { 187 | let dedup_map = build_dedup_map(items)?; 188 | let unique_hashes = dedup_map.len(); 189 | let mut collision_count = 0; 190 | let mut max_collision_group = 0; 191 | 192 | for group in dedup_map.values() { 193 | if group.len() > 1 { 194 | collision_count += group.len() - 1; 195 | } 196 | max_collision_group = max_collision_group.max(group.len()); 197 | } 198 | 199 | let collision_rate = if items.is_empty() { 200 | 0.0 201 | } else { 202 | collision_count as f64 / items.len() as f64 203 | }; 204 | 205 | Ok(HashStats { 206 | total_items: items.len(), 207 | unique_hashes, 208 | collision_count, 209 | max_collision_group, 210 | collision_rate, 211 | }) 212 | } 213 | 214 | /// Pretty-print the statistics 215 | pub fn print(&self) { 216 | println!("Hash Statistics:"); 217 | println!(" Total items: {}", self.total_items); 218 | println!(" Unique hashes: {}", self.unique_hashes); 219 | println!(" Collisions: {}", self.collision_count); 220 | println!(" Max collision group: {}", self.max_collision_group); 221 | println!(" Collision rate: {:.2}%", self.collision_rate * 100.0); 222 | } 223 | } 224 | 225 | #[cfg(test)] 226 | mod tests { 227 | use super::*; 228 | 229 | #[test] 230 | fn test_semantic_hashing_consistency() { 231 | use crate::models::*; 232 | 233 | let item1 = ExtractedItem { 234 | project_context: ProjectContext { 235 | repo_url: "test".to_string(), 236 | commit_hash: "test".to_string(), 237 | file_path: "test.rs".to_string(), 238 | }, 239 | item_meta: ItemMeta { 240 | kind: ItemKind::Function, 241 | name: "test_fn".to_string(), 242 | fully_qualified_name: "test::test_fn".to_string(), 243 | start_line: 1, 244 | end_line: 5, 245 | }, 246 | content: Content { 247 | signature: "fn test()".to_string(), 248 | body_normalized: "fn test() {}".to_string(), 249 | semantic_hash: String::new(), 250 | docstring: None, 251 | imports: Vec::new(), 252 | }, 253 | rag_context: RagContext { 254 | context_before: None, 255 | context_after: None, 256 | }, 257 | }; 258 | 259 | let item2 = item1.clone(); 260 | 261 | let hash1 = semantic_hash_item(&item1).unwrap(); 262 | let hash2 = semantic_hash_item(&item2).unwrap(); 263 | 264 | assert_eq!(hash1, hash2, "Identical items should have identical hashes"); 265 | } 266 | 267 | #[test] 268 | fn test_hamming_distance() { 269 | let hash1 = "blake3:0000000000000000000000000000000000000000000000000000000000000000"; 270 | let hash2 = "blake3:0000000000000000000000000000000000000000000000000000000000000001"; 271 | 272 | let distance = hash_hamming_distance(hash1, hash2).unwrap(); 273 | assert_eq!( 274 | distance, 1, 275 | "Single bit difference should give distance of 1" 276 | ); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/cli/src/main.rs: -------------------------------------------------------------------------------- 1 | //! RustCodeFlow CLI 2 | //! 3 | //! CLI tool for converting Rust repositories to JSONL datasets for AI training. 4 | 5 | use clap::Parser; 6 | use indicatif::{ProgressBar, ProgressStyle}; 7 | use std::fs; 8 | use std::io::Write; 9 | use std::path::PathBuf; 10 | use walkdir::WalkDir; 11 | 12 | use core::*; 13 | 14 | #[derive(Parser, Debug)] 15 | #[command(name = "rustcodeflow")] 16 | #[command(about = "Convert Rust repositories to JSONL datasets for AI training")] 17 | struct Cli { 18 | /// Source: GitHub URL or local path to Rust repository 19 | source: String, 20 | 21 | /// Output JSONL file path 22 | #[arg(short, long)] 23 | output: PathBuf, 24 | 25 | /// Preserve documentation comments in output 26 | #[arg(long, default_value = "false")] 27 | keep_docs: bool, 28 | 29 | /// Include 50 lines of context before/after each item 30 | #[arg(long, default_value = "false")] 31 | full_context: bool, 32 | 33 | /// Number of parallel threads (default: logical CPUs) 34 | #[arg(long, default_value_t = num_cpus::get())] 35 | threads: usize, 36 | 37 | /// Clean up temporary repository after processing 38 | #[arg(long, default_value = "true")] 39 | cleanup: bool, 40 | } 41 | 42 | fn main() -> anyhow::Result<()> { 43 | let cli = Cli::parse(); 44 | 45 | println!("🚀 RustCodeFlow: Converting Rust repositories to AI datasets"); 46 | println!("📁 Source: {}", cli.source); 47 | println!("📄 Output: {}", cli.output.display()); 48 | println!( 49 | "🔧 Options: docs={}, context={}, threads={}", 50 | cli.keep_docs, cli.full_context, cli.threads 51 | ); 52 | 53 | // Set up thread pool 54 | rayon::ThreadPoolBuilder::new() 55 | .num_threads(cli.threads) 56 | .build_global()?; 57 | 58 | // Create configuration 59 | let config = ExtractConfig { 60 | keep_docs: cli.keep_docs, 61 | include_context: cli.full_context, 62 | context_lines: 50, 63 | }; 64 | 65 | // Clone repository or verify local path 66 | let repo_path = clone_or_verify_repo(&cli.source)?; 67 | 68 | // Get repository metadata 69 | let (repo_url, commit_hash) = get_repo_metadata(&repo_path)?; 70 | 71 | // Find all Rust files 72 | let rust_files = find_rust_files(&repo_path)?; 73 | 74 | if rust_files.is_empty() { 75 | anyhow::bail!("No .rs files found in repository"); 76 | } 77 | 78 | println!("📚 Found {} Rust files to process", rust_files.len()); 79 | 80 | // Set up progress bar 81 | let progress_style = ProgressStyle::default_bar() 82 | .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} files ({eta})")? 83 | .progress_chars("█░"); 84 | 85 | let progress_bar = ProgressBar::new(rust_files.len() as u64); 86 | progress_bar.set_style(progress_style.clone()); 87 | 88 | // Process files in parallel 89 | let mut all_items = Vec::new(); 90 | 91 | for (index, file_path) in rust_files.iter().enumerate() { 92 | progress_bar.set_position(index as u64); 93 | match extract_items_from_file( 94 | file_path, 95 | repo_url.to_string(), 96 | commit_hash.to_string(), 97 | &config, 98 | ) { 99 | Ok(items) => all_items.extend(items), 100 | Err(e) => { 101 | eprintln!("Warning: Failed to process {}: {}", file_path.display(), e); 102 | } 103 | } 104 | } 105 | 106 | progress_bar.finish_with_message("✅ Processing complete!"); 107 | 108 | println!("📊 Extracted {} code items", all_items.len()); 109 | 110 | // Normalize items (identifier anonymization, docstring extraction) 111 | println!("🔧 Normalizing identifiers and extracting metadata..."); 112 | normalize_items(&mut all_items, &config)?; 113 | 114 | // Generate semantic hashes 115 | println!("🔐 Generating semantic hashes for deduplication..."); 116 | hash_and_update_items(&mut all_items)?; 117 | 118 | // Write to JSONL 119 | println!("💾 Writing to JSONL file..."); 120 | write_jsonl(&all_items, &cli.output)?; 121 | 122 | // Print summary statistics 123 | print_summary(&all_items)?; 124 | 125 | println!("✨ Dataset generation complete!"); 126 | println!("📁 Output: {}", cli.output.display()); 127 | 128 | // Clean up temporary repository if requested 129 | if cli.cleanup && cli.source.starts_with("http") { 130 | let repo_name = extract_repo_name(&cli.source)?; 131 | let temp_dir = std::env::temp_dir().join(format!("rustcodeflow_{}", repo_name)); 132 | 133 | if temp_dir.exists() { 134 | println!("🧹 Cleaning up temporary repository..."); 135 | fs::remove_dir_all(&temp_dir) 136 | .map_err(|e| anyhow::anyhow!("Failed to clean up temporary repository: {}", e))?; 137 | println!("✅ Temporary repository cleaned up"); 138 | } 139 | } 140 | 141 | Ok(()) 142 | } 143 | 144 | /// Clone GitHub repository or verify local path exists 145 | fn clone_or_verify_repo(source: &str) -> anyhow::Result { 146 | let repo_name = extract_repo_name(source)?; 147 | let temp_dir = std::env::temp_dir().join(format!("rustcodeflow_{}", repo_name)); 148 | 149 | if source.starts_with("http") { 150 | // GitHub URL - clone or update 151 | if temp_dir.exists() { 152 | println!("📂 Updating existing repository at {}", temp_dir.display()); 153 | let output = std::process::Command::new("git") 154 | .args(["pull", "origin", "main"]) 155 | .current_dir(&temp_dir) 156 | .output()?; 157 | 158 | if !output.status.success() { 159 | println!("⚠️ Git pull failed, trying master branch..."); 160 | let output = std::process::Command::new("git") 161 | .args(["pull", "origin", "master"]) 162 | .current_dir(&temp_dir) 163 | .output()?; 164 | 165 | if !output.status.success() { 166 | anyhow::bail!( 167 | "Failed to update repository: {}", 168 | String::from_utf8_lossy(&output.stderr) 169 | ); 170 | } 171 | } 172 | } else { 173 | println!("📥 Cloning repository..."); 174 | let output = std::process::Command::new("git") 175 | .args(["clone", "--depth", "1", source, temp_dir.to_str().unwrap()]) 176 | .output()?; 177 | 178 | if !output.status.success() { 179 | anyhow::bail!( 180 | "Failed to clone repository: {}", 181 | String::from_utf8_lossy(&output.stderr) 182 | ); 183 | } 184 | } 185 | 186 | Ok(temp_dir) 187 | } else { 188 | // Local path 189 | let path = PathBuf::from(source); 190 | if !path.exists() { 191 | anyhow::bail!("Local path does not exist: {}", source); 192 | } 193 | Ok(path) 194 | } 195 | } 196 | 197 | /// Extract repository name from URL or path 198 | fn extract_repo_name(source: &str) -> anyhow::Result { 199 | if let Some(path) = PathBuf::from(source).file_name() { 200 | Ok(path.to_string_lossy().to_string()) 201 | } else { 202 | // Extract from GitHub URL 203 | if let Some(last_slash) = source.rfind('/') { 204 | let repo_part = &source[last_slash + 1..]; 205 | if let Some(dot_git) = repo_part.find(".git") { 206 | Ok(repo_part[..dot_git].to_string()) 207 | } else { 208 | Ok(repo_part.to_string()) 209 | } 210 | } else { 211 | Ok("unknown_repo".to_string()) 212 | } 213 | } 214 | } 215 | 216 | /// Get repository metadata (URL and commit hash) 217 | fn get_repo_metadata(repo_path: &PathBuf) -> anyhow::Result<(String, String)> { 218 | // Get commit hash 219 | let output = std::process::Command::new("git") 220 | .args(["rev-parse", "HEAD"]) 221 | .current_dir(repo_path) 222 | .output()?; 223 | 224 | if !output.status.success() { 225 | anyhow::bail!( 226 | "Failed to get commit hash: {}", 227 | String::from_utf8_lossy(&output.stderr) 228 | ); 229 | } 230 | 231 | let commit_hash = String::from_utf8_lossy(&output.stdout).trim().to_string(); 232 | 233 | // Try to get remote URL 234 | let output = std::process::Command::new("git") 235 | .args(["remote", "get-url", "origin"]) 236 | .current_dir(repo_path) 237 | .output()?; 238 | 239 | let repo_url = if output.status.success() { 240 | String::from_utf8_lossy(&output.stdout).trim().to_string() 241 | } else { 242 | "local://".to_string() + &repo_path.display().to_string() 243 | }; 244 | 245 | Ok((repo_url, commit_hash)) 246 | } 247 | 248 | /// Find all .rs files in repository 249 | fn find_rust_files(repo_path: &PathBuf) -> anyhow::Result> { 250 | let mut rust_files = Vec::new(); 251 | 252 | for entry in WalkDir::new(repo_path) { 253 | let entry = entry?; 254 | let path = entry.path(); 255 | 256 | if path.extension().and_then(|s| s.to_str()) == Some("rs") { 257 | rust_files.push(path.to_path_buf()); 258 | } 259 | } 260 | 261 | rust_files.sort(); 262 | Ok(rust_files) 263 | } 264 | 265 | /// Write items to JSONL file 266 | fn write_jsonl(items: &[ExtractedItem], output_file: &PathBuf) -> CoreResult<()> { 267 | let file = fs::File::create(output_file)?; 268 | let mut writer = std::io::BufWriter::new(file); 269 | 270 | for item in items { 271 | let json_line = serde_json::to_string(item)?; 272 | writeln!(writer, "{}", json_line)?; 273 | } 274 | 275 | Ok(()) 276 | } 277 | 278 | /// Print summary statistics 279 | fn print_summary(items: &[ExtractedItem]) -> CoreResult<()> { 280 | use std::collections::HashMap; 281 | 282 | let mut kind_counts = HashMap::new(); 283 | let mut total_lines = 0; 284 | 285 | for item in items { 286 | *kind_counts.entry(&item.item_meta.kind).or_insert(0) += 1; 287 | total_lines += (item.item_meta.end_line - item.item_meta.start_line + 1) as usize; 288 | } 289 | 290 | println!("\n📈 Summary Statistics:"); 291 | println!(" Total items: {}", items.len()); 292 | println!(" Total lines of code: {}", total_lines); 293 | println!( 294 | " Average lines per item: {:.1}", 295 | total_lines as f64 / items.len() as f64 296 | ); 297 | 298 | println!("\n📋 Item breakdown:"); 299 | for (kind, count) in kind_counts { 300 | println!(" {}: {}", kind, count); 301 | } 302 | 303 | // Hash statistics 304 | let hash_stats = HashStats::analyze(items)?; 305 | hash_stats.print(); 306 | 307 | Ok(()) 308 | } 309 | -------------------------------------------------------------------------------- /basics/01_hello/src/main.rs: -------------------------------------------------------------------------------- 1 | /// ========== begin variables ========== 2 | const INT1: i32 = 1; 3 | const BIT2: u32 = 1 << 1; 4 | const STRING: &'static str = "String"; 5 | 6 | fn variables() { 7 | // 在函数中访问常量 8 | println!("{:?}", INT1); 9 | println!("{:?}", BIT2); 10 | println!("{:?}", STRING); 11 | 12 | // 报错!不能修改一个 `const` 常量 13 | // INT1 = 5; 14 | let a = 1; 15 | // 默认不可变绑定,不能重新赋值 16 | // a = 2; 17 | // 可使用mut关键字,创建可变绑定 18 | println!("a {:p}", &a); 19 | let mut b = 2; 20 | println!("b: {}", b); 21 | b = 3; 22 | println!("b was changed: {}", b); 23 | 24 | // 变量遮蔽:连续定义同名变量 25 | let s = "Hello Rust"; 26 | println!("s is {}", s); 27 | let s = "Hello World"; 28 | // 变量生命周期,词法作用域 29 | { 30 | let s = "Hello Rust"; 31 | println!("s is {}", s); 32 | } 33 | println!("s is {}", s); 34 | 35 | pub fn tmp() -> i32 { 36 | return 1; 37 | } 38 | // 借用操作符&,获取表达式内存地址 39 | let x = &tmp(); 40 | // 值表达式不能出现在位置上下文中,E0070 41 | // tmp() = *x; 42 | println!("x is memory address: {:p}", x); 43 | // 声明动态数组,vec! 44 | let mut c = vec![1,2,3]; 45 | // 使用借用操作符&,得到引用类型 46 | let d = &mut c; 47 | d.push(4); 48 | println!("{:?}", d); 49 | // 字面常量是值表达式,在位置上下文中求值创建临时值 50 | let e = &42; 51 | // 使用解引用操作符*,取得引用中的值 52 | println!("reference e's value is {}", *e) 53 | } 54 | /// ========== end variables ========== 55 | 56 | 57 | /// ========== begin primitives ========== 58 | fn basic_example() { 59 | // 布尔类型 60 | let a_boolean: bool = true; 61 | println!("a_boolean = {}", a_boolean); 62 | 63 | // 数值类型 64 | let a_float: f32 = 1.0; // 变量常规声明 65 | let an_integer = 6i16; // 变量后缀声明 66 | println!("a_float = {}", a_float); 67 | println!("an_integer = {}", an_integer); 68 | 69 | // 可根据上下文自动推断类型 70 | let mut inferred_type = 8; // 根据下一行的赋值推断为i64类型 71 | println!("inferred_type = {}", inferred_type); 72 | inferred_type = 64i64; 73 | println!("inferred_type = {}", inferred_type); 74 | 75 | // 无法类型推断时,按默认方式取类型 76 | let default_float = 2.0; // 浮点数值为f64 77 | let default_integer = 5; // 整型数值为i32 78 | println!("default_float = {}", default_float); 79 | println!("default_integer = {}", default_integer); 80 | 81 | // 字符类型 82 | let a_char: char = 'a'; 83 | println!("a_char = {}", a_char); 84 | } 85 | 86 | fn array_example () { 87 | // 创建数组方式一:[x, y, z] 88 | let arr: [i32; 3] = [1, 2, 3]; 89 | let mut mut_arr = [4, 5, 6]; 90 | mut_arr[0] = 0; 91 | 92 | assert_eq!(3, arr[2]); 93 | assert_eq!(0, mut_arr[0]); 94 | // 这个循环输出: 1 2 3 95 | for x in &arr { 96 | print!("{} ", x); 97 | } 98 | println!(); 99 | 100 | // 创建数组方式二:[x; N] 101 | let mut array: [i32; 3] = [0; 3]; 102 | array[1] = 1; 103 | array[2] = 2; 104 | 105 | assert_eq!([1, 2], &array[1..]); 106 | // 元素个数小于等于32的数组,实现了`trait IntoIterator` 107 | // 这个循环输出: 0 1 2 108 | for x in &array { 109 | print!("{} ", x); 110 | } 111 | println!(); 112 | 113 | let array: [i32; 33] = [0; 33]; 114 | // error[E0277]: `&[i32; 33]` is not an iterator 115 | // for x in &array { 116 | // print!("{} ", x); 117 | // } 118 | // 通过调用slice方法将数组强制类型转换为slice 119 | for x in array.iter() { 120 | print!("{} ", x); 121 | } 122 | println!(); 123 | } 124 | 125 | fn tuple_example() { 126 | let tup: (u8, i32, f64) = (1, 100, 1.1314); 127 | let (x, y, z) = tup; 128 | let f_number = tup.2; 129 | let one_tup = (1.1,); 130 | println!("elements in tuple {},{},{}", x, y, z); 131 | println!("third element in tuple {}", f_number); 132 | println!("one element in tuple {}", one_tup.0); 133 | } 134 | 135 | fn struct_example() { 136 | struct Person { 137 | age: u8, 138 | is_child: bool, 139 | } 140 | struct OnePerson(u8, bool); 141 | struct UnitStruct; 142 | let alice = Person {age: 10, is_child: true}; 143 | let bob = OnePerson(32, false); 144 | let x = UnitStruct; 145 | println!("alice age {} is child {}", alice.age, alice.is_child); 146 | println!("bob age {} is child {}", bob.0, bob.1); 147 | println!("unit struct {:p}", &x); 148 | 149 | impl Person { 150 | fn create_person(age: u8, is_child: bool) -> Person { 151 | Person{age, is_child} 152 | } 153 | fn check_child(&self) -> bool { 154 | if self.is_child && self.age < 18 { 155 | return true; 156 | } else { 157 | return false; 158 | } 159 | } 160 | } 161 | let peter = Person::create_person(33, true); 162 | println!("peter age {} is child {}", peter.age, peter.is_child); 163 | println!("peter is child {}", peter.check_child()); 164 | } 165 | 166 | fn enum_example() { 167 | enum Number { 168 | Integer(i64), 169 | Float { 170 | inner: f64 171 | }, 172 | } 173 | let a = Number::Integer(10); 174 | let b = Number::Float { 175 | inner: 3.14 176 | }; 177 | match a { 178 | Number::Integer(n) => println!("a is integer: {}", n), 179 | Number::Float {inner} => println!("a is float: {}", inner), 180 | } 181 | if let Number::Float { inner } = b { 182 | println!("b is float: {}", inner); 183 | } 184 | } 185 | 186 | fn primitives() { 187 | basic_example(); 188 | array_example(); 189 | tuple_example(); 190 | struct_example(); 191 | enum_example(); 192 | } 193 | /// ========== end primitives ========== 194 | 195 | 196 | /// ========== begin functions ========== 197 | /// 函数作为参数 198 | pub fn math(op: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 { 199 | op(a,b) 200 | } 201 | /// 求和函数 202 | fn sum(a: i32, b: i32) -> i32 { 203 | // 表达式,无分号返回求值结果 204 | a + b 205 | // return 仅在需要提前返回时使用 206 | // return a + b; 207 | } 208 | fn product(a: i32, b: i32) -> i32 { 209 | a * b 210 | } 211 | 212 | /// 函数作为返回值 213 | fn is_true() -> bool { true } 214 | pub fn true_maker() -> fn() -> bool { is_true } 215 | 216 | /// CTFE编译时函数执行 217 | const fn init_len() -> usize { return 5; } 218 | 219 | /// 匿名函数闭包作为参数 220 | fn closure_math i32>(op: F) -> i32 { 221 | // 通过添加一对圆括号,调用传入的闭包 222 | op() 223 | } 224 | 225 | /// 匿名函数闭包作为返回值 226 | fn two_times_impl() -> impl Fn(i32) -> i32 { 227 | let i = 2; 228 | // 使用 move 转移变量 i 的所有权,避免悬挂指针,安全返回闭包 229 | move |j| j * i 230 | } 231 | 232 | /// geektime: function 233 | fn apply(value: i32, f: fn(i32) -> i32) -> i32 { 234 | f(value) 235 | } 236 | 237 | fn square(value: i32) -> i32 { 238 | value * value 239 | } 240 | 241 | fn cube(value: i32) -> i32 { 242 | value * value * value 243 | } 244 | 245 | fn pi() -> f64 { 246 | 3.1415925 247 | } 248 | 249 | fn not_pi() { 250 | // 如果最后一个表达式后添加了; 分号,隐含其返回值为 unit 251 | 3.1425926; 252 | } 253 | 254 | fn answer() -> () { 255 | // 声明语句 256 | let a = 40; 257 | let b = 2; 258 | // 表达式语句:以分号结尾的表达式 259 | // println! 宏语句:名字以叹号结尾,并像函数一样被调用 260 | println!("40 + 2 = {}", sum(a, b)); 261 | } 262 | 263 | fn functions() { 264 | println!("is_pi: {:?}, is_unit1: {:?}", pi(), not_pi()); 265 | 266 | println!("apply square: {}", apply(2, square)); 267 | println!("apply cube: {}", apply(2, cube)); 268 | 269 | // 默认函数名是函数类型,参数显式指定了函数的类型,被转换成函数指针类型 270 | let a = 2; 271 | let b = 3; 272 | println!("2+3={}", math(sum, a, b)); 273 | println!("2*3={}", math(product, a, b)); 274 | 275 | // 返回函数指针 276 | println!("return {:p}", true_maker()); 277 | // 函数指针加上括号,就会调用该函数 278 | println!("return {}", true_maker()()); 279 | 280 | // 数组的长度是编译时常量,必须在编译时确定其值 281 | let arr = [0; init_len()]; 282 | println!("array length is {}", arr.len()); 283 | 284 | let out = 42; 285 | // add 函数内使用外部定义的变量 out,编译器会报错 286 | // fn add(i: i32, j: i32) -> i32 { i + j + out } 287 | // 匿名函数,闭包可捕获外部变量 out 288 | let closure_annotated = |i: i32, j: i32| -> i32 { i + j + out }; 289 | // 闭包自动推断输入和返回类型,个人觉得可读性不好 290 | let closure_inferred = |i, j| i + j + out; 291 | let i = 1; 292 | let j = 2; 293 | println!("closure annotated: 1+2+42={}", closure_annotated(i, j)); 294 | println!("closure inferred: 1+2+42={}", closure_inferred(i, j)); 295 | 296 | // 传入闭包:|| a + b 297 | println!("closure: 2+3={}", closure_math(|| a + b)); 298 | // 传入闭包:|| a * b 299 | println!("closure: 2*3={}", closure_math(|| a * b)); 300 | 301 | let result = two_times_impl(); 302 | println!("closure: 2's two times is {}", result(2)); 303 | 304 | answer(); 305 | } 306 | /// ========== end functions ========== 307 | 308 | 309 | /// ========== begin control_flow ========== 310 | /** 311 | * 条件表达式 312 | */ 313 | fn if_expr(x: i32) -> i32 { 314 | let n = if x < 10 && x > -10 { 315 | 10 * x 316 | } else { 317 | // 如果传入奇数,返回类型为i32,编译器是否会报错? 318 | x / 2 319 | }; 320 | return n; 321 | } 322 | 323 | /** 324 | * 循环表达式 while 325 | */ 326 | fn while_expr() { 327 | let mut n = 1; 328 | while n < 16 { 329 | if n % 15 == 0 { 330 | println!("3 and 5‘s multiple {}", n); 331 | } else if n % 5 == 0 { 332 | println!("5‘s multiple {}", n); 333 | } 334 | n += 1; 335 | } 336 | } 337 | 338 | /** 339 | * 循环表达式 loop 340 | */ 341 | fn loop_expr() { 342 | let mut n = 1; 343 | loop { 344 | if n % 15 == 0 { 345 | println!("3 and 5‘s multiple {}", n); 346 | } else if n % 3 == 0 { 347 | println!("3‘s multiple {}", n); 348 | } else if n > 16 { 349 | break; 350 | } 351 | n += 1; 352 | } 353 | } 354 | 355 | /** 356 | * 循环表达式 for...in 357 | */ 358 | fn for_expr() { 359 | for n in 1..16 { 360 | if n % 15 == 0 { 361 | println!("3 and 5‘s multiple {}", n); 362 | } else if n % 5 == 0 { 363 | println!("5‘s multiple {}", n); 364 | } 365 | } 366 | } 367 | 368 | /** 369 | * match表达式 370 | */ 371 | fn match_expr(n: i32) { 372 | match n { 373 | 0 => println!("match number"), 374 | 1..=3 => println!("match range"), 375 | | 5 | 7 | 13 => println!("match branch"), 376 | n @ 42 => println!("binding {}", n), 377 | _ => println!("default"), 378 | } 379 | } 380 | 381 | /** 382 | * while let表达式 383 | */ 384 | fn while_let_pop() { 385 | let mut v = vec![1,2,3]; 386 | // 动态数组的pop方法会返回Option类型,数组被取空会返回None 387 | // 使用match表达式,需要匹配两种情况:Some(x)和None 388 | while let Some(x) = v.pop() { 389 | println!("{}", x); 390 | } 391 | } 392 | 393 | fn control_flow() { 394 | let x = 13; 395 | // Rust编译器根据上下文,会将结果截取 396 | println!("result={}", if_expr(x)); 397 | 398 | while_expr(); 399 | loop_expr(); 400 | for_expr(); 401 | 402 | let mut n = 2; 403 | match_expr(n); 404 | n = 5; 405 | match_expr(n); 406 | n = 42; 407 | match_expr(n); 408 | n = 100; 409 | match_expr(n); 410 | 411 | while_let_pop(); 412 | } 413 | /// ========== end control_flow ========== 414 | 415 | fn main() { 416 | println!("Hello, world!"); 417 | variables(); 418 | primitives(); 419 | functions(); 420 | control_flow(); 421 | } 422 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/core/src/extractor.rs: -------------------------------------------------------------------------------- 1 | use crate::models::*; 2 | use quote::ToTokens; 3 | use std::collections::HashMap; 4 | use std::path::Path; 5 | use syn::{self, visit_mut::VisitMut}; 6 | 7 | /// Visitor that extracts Rust code items from AST 8 | pub struct ExtractVisitor<'ast> { 9 | pub items: Vec, 10 | current_file: Option, 11 | repo_url: Option, 12 | commit_hash: Option, 13 | current_module: Vec, 14 | line_mapping: HashMap, // byte pos -> line number 15 | source_lines: Vec, 16 | config: &'ast ExtractConfig, 17 | } 18 | 19 | impl<'ast> ExtractVisitor<'ast> { 20 | pub fn new(config: &'ast ExtractConfig) -> Self { 21 | Self { 22 | items: Vec::new(), 23 | current_file: None, 24 | repo_url: None, 25 | commit_hash: None, 26 | current_module: Vec::new(), 27 | line_mapping: HashMap::new(), 28 | source_lines: Vec::new(), 29 | config, 30 | } 31 | } 32 | 33 | pub fn set_source(&mut self, source: &str) { 34 | self.line_mapping.clear(); 35 | self.source_lines.clear(); 36 | 37 | // Build line mapping 38 | let mut byte_pos = 0; 39 | let lines: Vec<&str> = source.lines().collect(); 40 | 41 | for (line_num, line) in lines.iter().enumerate() { 42 | self.line_mapping.insert(byte_pos, line_num + 1); 43 | self.source_lines.push(line.to_string()); 44 | byte_pos += line.len() + 1; // +1 for newline 45 | } 46 | } 47 | 48 | pub fn set_file_info(&mut self, file_path: String, repo_url: String, commit_hash: String) { 49 | self.current_file = Some(file_path); 50 | self.repo_url = Some(repo_url); 51 | self.commit_hash = Some(commit_hash); 52 | } 53 | 54 | fn extract_context(&self, start_line: usize, end_line: usize) -> RagContext { 55 | if !self.config.include_context { 56 | return RagContext { 57 | context_before: None, 58 | context_after: None, 59 | }; 60 | } 61 | 62 | let before_start = start_line.saturating_sub(self.config.context_lines); 63 | let after_end = end_line + self.config.context_lines; 64 | 65 | let context_before = if before_start < start_line { 66 | let lines: Vec = self 67 | .source_lines 68 | .get(before_start.saturating_sub(1)..start_line.saturating_sub(1)) 69 | .unwrap_or(&[]) 70 | .to_vec(); 71 | if lines.is_empty() { 72 | None 73 | } else { 74 | Some(lines.join("\n")) 75 | } 76 | } else { 77 | None 78 | }; 79 | 80 | let context_after = if after_end > end_line { 81 | let max_len = self.source_lines.len(); 82 | // simple bounds check to prevent panic 83 | let start = end_line.min(max_len); 84 | let end = after_end.min(max_len); 85 | 86 | if start < end { 87 | let lines: Vec = self.source_lines[start..end].to_vec(); 88 | Some(lines.join("\n")) 89 | } else { 90 | None 91 | } 92 | } else { 93 | None 94 | }; 95 | 96 | RagContext { 97 | context_before, 98 | context_after, 99 | } 100 | } 101 | } 102 | 103 | impl<'ast> VisitMut for ExtractVisitor<'ast> { 104 | fn visit_item_fn_mut(&mut self, item: &mut syn::ItemFn) { 105 | let start_line = 1; // Placeholder: Real implementation requires span-to-line mapping 106 | let end_line = 1; 107 | 108 | let item_meta = ItemMeta { 109 | kind: ItemKind::Function, 110 | name: item.sig.ident.to_string(), 111 | fully_qualified_name: format!("{}::{}", self.current_module.join("::"), item.sig.ident), 112 | start_line: start_line as u32, 113 | end_line: end_line as u32, 114 | }; 115 | 116 | let content = Content { 117 | signature: item.sig.clone().into_token_stream().to_string(), 118 | body_normalized: item.clone().into_token_stream().to_string(), 119 | semantic_hash: String::new(), 120 | docstring: None, 121 | imports: Vec::new(), 122 | }; 123 | 124 | let rag_context = self.extract_context(start_line, end_line); 125 | 126 | let project_context = ProjectContext { 127 | repo_url: self.repo_url.clone().unwrap_or_default(), 128 | commit_hash: self.commit_hash.clone().unwrap_or_default(), 129 | file_path: self.current_file.clone().unwrap_or_default(), 130 | }; 131 | 132 | self.items.push(ExtractedItem { 133 | project_context, 134 | item_meta, 135 | content, 136 | rag_context, 137 | }); 138 | 139 | syn::visit_mut::visit_item_fn_mut(self, item); 140 | } 141 | 142 | fn visit_item_impl_mut(&mut self, item: &mut syn::ItemImpl) { 143 | let start_line = 1; 144 | let end_line = 1; 145 | 146 | let item_meta = ItemMeta { 147 | kind: ItemKind::Impl, 148 | name: "impl".to_string(), 149 | fully_qualified_name: format!("{}::impl", self.current_module.join("::")), 150 | start_line: start_line as u32, 151 | end_line: end_line as u32, 152 | }; 153 | 154 | let content = Content { 155 | signature: item.clone().into_token_stream().to_string(), 156 | body_normalized: item.clone().into_token_stream().to_string(), 157 | semantic_hash: String::new(), 158 | docstring: None, 159 | imports: Vec::new(), 160 | }; 161 | 162 | let rag_context = self.extract_context(start_line, end_line); 163 | 164 | let project_context = ProjectContext { 165 | repo_url: self.repo_url.clone().unwrap_or_default(), 166 | commit_hash: self.commit_hash.clone().unwrap_or_default(), 167 | file_path: self.current_file.clone().unwrap_or_default(), 168 | }; 169 | 170 | self.items.push(ExtractedItem { 171 | project_context, 172 | item_meta, 173 | content, 174 | rag_context, 175 | }); 176 | 177 | syn::visit_mut::visit_item_impl_mut(self, item); 178 | } 179 | 180 | fn visit_item_trait_mut(&mut self, item: &mut syn::ItemTrait) { 181 | let start_line = 1; 182 | let end_line = 1; 183 | 184 | let item_meta = ItemMeta { 185 | kind: ItemKind::Trait, 186 | name: item.ident.to_string(), 187 | fully_qualified_name: format!("{}::{}", self.current_module.join("::"), item.ident), 188 | start_line: start_line as u32, 189 | end_line: end_line as u32, 190 | }; 191 | 192 | let content = Content { 193 | signature: item.clone().into_token_stream().to_string(), 194 | body_normalized: item.clone().into_token_stream().to_string(), 195 | semantic_hash: String::new(), 196 | docstring: None, 197 | imports: Vec::new(), 198 | }; 199 | 200 | let rag_context = self.extract_context(start_line, end_line); 201 | 202 | let project_context = ProjectContext { 203 | repo_url: self.repo_url.clone().unwrap_or_default(), 204 | commit_hash: self.commit_hash.clone().unwrap_or_default(), 205 | file_path: self.current_file.clone().unwrap_or_default(), 206 | }; 207 | 208 | self.items.push(ExtractedItem { 209 | project_context, 210 | item_meta, 211 | content, 212 | rag_context, 213 | }); 214 | 215 | syn::visit_mut::visit_item_trait_mut(self, item); 216 | } 217 | 218 | fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { 219 | let start_line = 1; 220 | let end_line = 1; 221 | 222 | self.current_module.push(item.ident.to_string()); 223 | 224 | let item_meta = ItemMeta { 225 | kind: ItemKind::Module, 226 | name: item.ident.to_string(), 227 | fully_qualified_name: self.current_module.join("::"), 228 | start_line: start_line as u32, 229 | end_line: end_line as u32, 230 | }; 231 | 232 | let content = Content { 233 | signature: item.clone().into_token_stream().to_string(), 234 | body_normalized: item.clone().into_token_stream().to_string(), 235 | semantic_hash: String::new(), 236 | docstring: None, 237 | imports: Vec::new(), 238 | }; 239 | 240 | let rag_context = self.extract_context(start_line, end_line); 241 | 242 | let project_context = ProjectContext { 243 | repo_url: self.repo_url.clone().unwrap_or_default(), 244 | commit_hash: self.commit_hash.clone().unwrap_or_default(), 245 | file_path: self.current_file.clone().unwrap_or_default(), 246 | }; 247 | 248 | self.items.push(ExtractedItem { 249 | project_context, 250 | item_meta, 251 | content, 252 | rag_context, 253 | }); 254 | 255 | syn::visit_mut::visit_item_mod_mut(self, item); 256 | self.current_module.pop(); 257 | } 258 | } 259 | 260 | /// Main extraction function taking a raw string source 261 | pub fn extract_items_from_source( 262 | source: &str, 263 | file_path: String, 264 | repo_url: String, 265 | commit_hash: String, 266 | config: &ExtractConfig, 267 | ) -> CoreResult> { 268 | let parsed_file = syn::parse_file(source)?; 269 | 270 | let mut visitor = ExtractVisitor::new(config); 271 | visitor.set_source(source); 272 | visitor.set_file_info(file_path, repo_url, commit_hash); 273 | 274 | let mut parsed_file_mut = parsed_file; 275 | visitor.visit_file_mut(&mut parsed_file_mut); 276 | 277 | Ok(visitor.items) 278 | } 279 | 280 | /// Extract items from a file path using known "rustcodeflow_" pattern to normalize paths 281 | pub fn extract_items_from_file( 282 | file_path: &Path, 283 | repo_url: String, 284 | commit_hash: String, 285 | config: &ExtractConfig, 286 | ) -> CoreResult> { 287 | let source = std::fs::read_to_string(file_path)?; 288 | 289 | // Heuristic: 290 | // Split path into components. Find the component starting with "rustcodeflow_". 291 | // Truncate everything before it. 292 | // Strip "rustcodeflow_" prefix from that specific component to get clean repo name. 293 | 294 | let components: Vec<_> = file_path 295 | .components() 296 | .map(|c| c.as_os_str().to_string_lossy()) 297 | .collect(); 298 | 299 | let start_index = components 300 | .iter() 301 | .position(|c| c.starts_with("rustcodeflow_")); 302 | 303 | let relative_path_str = if let Some(idx) = start_index { 304 | // e.g. ["tmp", "x", "rustcodeflow_rust", "src", "lib.rs"] -> idx=2 305 | // repo_dir = "rustcodeflow_rust" -> "rust" 306 | let repo_dir = &components[idx]; 307 | let clean_repo_name = repo_dir.strip_prefix("rustcodeflow_").unwrap_or(repo_dir); 308 | 309 | let mut parts = vec![clean_repo_name]; 310 | // Append all subsequent components 311 | parts.extend(components.iter().skip(idx + 1).map(|s| s.as_ref())); 312 | parts.join("/") 313 | } else { 314 | // Fallback: If pattern not found (e.g. local run), just normalize slashes 315 | file_path.to_string_lossy().replace('\\', "/") 316 | }; 317 | 318 | extract_items_from_source(&source, relative_path_str, repo_url, commit_hash, config) 319 | } 320 | -------------------------------------------------------------------------------- /hands-on/merkle-tree/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// The implementation of Merkle Tree in Rust. 2 | use std::convert::AsRef; 3 | use std::hash::Hash; 4 | 5 | use ring::digest::{Algorithm, Context, Digest}; 6 | 7 | /// This tree is stored in a vector. 8 | /// 9 | /// For example, there are four items, merkle tree is kept like: 10 | /// [hash0,hash1,hash2,hash3,hash01,hash23,root] 11 | /// 12 | /// # Usage example 13 | /// 14 | /// ``` 15 | /// use ring::digest::{Algorithm, SHA512}; 16 | /// use merkle_tree::MerkleTree; 17 | /// 18 | /// static ALGO: &'static Algorithm = &SHA512; 19 | /// 20 | /// fn main() { 21 | /// let values = vec!["one", "two", "three", "four"]; 22 | /// let tree = MerkleTree::new(&values, ALGO); 23 | /// let proof = tree.build_proof(&"one"); 24 | /// let vec = proof.unwrap(); 25 | /// tree.validate(&vec); 26 | /// } 27 | /// ``` 28 | /// 29 | 30 | pub struct MerkleTree { 31 | array: Vec, 32 | height: usize, 33 | items_count: usize, 34 | algo: &'static Algorithm, 35 | } 36 | 37 | impl MerkleTree { 38 | /// Build Merkle Tree 39 | pub fn new>(values: &Vec, algo: &'static Algorithm) -> MerkleTree { 40 | let (height, array) = build_tree(values, algo); 41 | MerkleTree { 42 | array: array, 43 | height: height, 44 | items_count: values.len(), 45 | algo: algo, 46 | } 47 | } 48 | 49 | /// Generate Merkle Proof 50 | pub fn build_proof>(&self, value: &T) -> Option> { 51 | let hash = get_hash(value.as_ref(), self.algo).as_ref().to_vec(); 52 | let index = self.find_item(&hash); 53 | let mut vec = vec![]; 54 | match index { 55 | Some(i) => { 56 | vec.push(&self.array[(i * self.algo.output_len())..(i * self.algo.output_len() + self.algo.output_len())]); 57 | Some(self.add_level(0, i, self.items_count, vec)) 58 | } 59 | None => None 60 | } 61 | } 62 | 63 | fn find_item(&self, hash: &Vec) -> Option { 64 | let mut result = None; 65 | // linear search item in a loop 66 | for index in 0..self.items_count { 67 | let start = index * self.algo.output_len(); 68 | if hash.as_slice() == &self.array[start..(start + self.algo.output_len())] { 69 | result = Some(index); 70 | break; 71 | } 72 | } 73 | result 74 | } 75 | 76 | /// Recursion 77 | fn add_level<'a>(&'a self, start_index: usize, index: usize, mut level_len: usize, mut result: Vec<&'a [u8]>) -> Vec<&'a [u8]> { 78 | level_len += level_len & 1; 79 | let (sibling, parent) = calculate_relatives(index); 80 | //Add sibling to result 81 | result.push(&self.array[ 82 | (start_index + sibling * self.algo.output_len())..(start_index + sibling * self.algo.output_len() + self.algo.output_len()) 83 | ]); 84 | let next_level_len = level_len / 2; 85 | // Do not include root to proof 86 | if next_level_len == 1 { 87 | return result; 88 | } 89 | self.add_level(start_index + level_len * self.algo.output_len(), parent, next_level_len, result) 90 | } 91 | 92 | pub fn is_empty(&self) -> bool { 93 | self.nodes_count() == 0 94 | } 95 | 96 | pub fn get_root(&self) -> &[u8] { 97 | if self.is_empty() { 98 | return &[]; 99 | } 100 | let root_index = self.array.len() - self.algo.output_len(); 101 | &self.array[root_index..] // Last item 102 | } 103 | 104 | pub fn nodes_count(&self) -> usize { 105 | self.array.len() / self.algo.output_len() 106 | } 107 | 108 | pub fn leafs_count(&self) -> usize { 109 | self.items_count 110 | } 111 | 112 | pub fn data_size(&self) -> usize { 113 | self.array.len() 114 | } 115 | 116 | pub fn height(&self) -> usize { 117 | self.height 118 | } 119 | 120 | /// fold() takes two arguments: an initial hash(01) 121 | /// and a closure with two arguments 122 | pub fn validate(&self, proof: &Vec<&[u8]>) -> bool { 123 | proof[2..].iter() 124 | .fold( 125 | get_pair_hash(proof[0], proof[1], self.algo), 126 | |a, b| get_pair_hash(a.as_ref(), b, self.algo) 127 | ).as_ref() == self.get_root() 128 | } 129 | } 130 | 131 | /// "2i 2i+1" schema 132 | fn calculate_relatives(index: usize) -> (usize, usize) { 133 | let mut sibling = index; 134 | if index & 1 == 0 { 135 | sibling += 1 136 | } else { 137 | sibling -= 1 138 | }; 139 | let parent = (index + 1 + ((index + 1) & 1)) / 2 - 1; 140 | (sibling, parent) 141 | } 142 | 143 | /// While building a tree, if there is an odd number of nodes at the given 144 | /// level, the last node will be duplicated. 145 | fn build_tree>(values: &Vec, algo: &'static Algorithm) -> (usize, Vec) { 146 | let vec_len = calculate_vec_len(values.len(), algo); 147 | let mut tree: Vec = Vec::with_capacity(vec_len); 148 | for (_i, v) in values.iter().enumerate() { //Hash leafs 149 | let digest = get_hash(v.as_ref(), algo); 150 | let hash = digest.as_ref(); 151 | tree.extend_from_slice(hash); 152 | } 153 | let height = build_level(&mut tree, 0, values.len(), algo); 154 | (height, tree) 155 | } 156 | 157 | /// length = (leafs + nodes) * output_len 158 | fn calculate_vec_len(len: usize, algo: &'static Algorithm) -> usize { 159 | //Determine leafs number is even or odd 160 | let mut result = len + (len & 1); 161 | let mut level = result; 162 | while level > 1 { 163 | level += level & 1; 164 | level = level / 2; 165 | result += level; 166 | } 167 | //output_len is the length of a finalized digest 168 | result * algo.output_len() 169 | } 170 | 171 | /// Return tree weight and build nodes 172 | fn build_level(tree: &mut Vec, prev_level_start: usize, mut prev_level_len: usize, algo: &'static Algorithm) -> usize { 173 | if prev_level_len & 1 == 1 { 174 | //Previous level has odd number of children 175 | let prev = &tree[(prev_level_start * algo.output_len() + (prev_level_len - 1) * algo.output_len())..] 176 | .to_owned(); 177 | //Duplicate last item 178 | tree.extend_from_slice(prev); 179 | prev_level_len += 1; 180 | } 181 | let level_len = prev_level_len / 2; 182 | for i in 0..level_len { 183 | let begin = prev_level_start * algo.output_len() + i * 2 * algo.output_len(); 184 | let middle = begin + algo.output_len(); 185 | let end = middle + algo.output_len(); 186 | let hash = get_pair_hash( 187 | &tree[begin..middle], //Left node 188 | &tree[middle..end], //Right node 189 | algo); 190 | tree.extend_from_slice(hash.as_ref()); 191 | }; 192 | if level_len > 1 { 193 | return build_level(tree, prev_level_start + prev_level_len, level_len, algo) + 1; 194 | } 195 | if level_len > 0 { 196 | return 2; 197 | } 198 | return 0; 199 | } 200 | 201 | /// Generate Node hash 202 | pub fn get_pair_hash(x: &[u8], y: &[u8], algo: &'static Algorithm) -> Digest { 203 | let left = x; 204 | let right = y; 205 | let mut ctx = Context::new(algo); 206 | ctx.update(left); 207 | ctx.update(right); 208 | ctx.finish() 209 | } 210 | 211 | /// Hash function 212 | pub fn get_hash(x: &[u8], algo: &'static Algorithm) -> Digest { 213 | let mut ctx = Context::new(algo); 214 | ctx.update(x); 215 | ctx.finish() 216 | } 217 | 218 | #[cfg(test)] 219 | mod tests { 220 | use ring::digest::{Algorithm, Context, Digest, SHA512}; 221 | use super::MerkleTree; 222 | 223 | static ALGO: &'static Algorithm = &SHA512; 224 | 225 | #[test] 226 | fn test_build_tree_with_0_values() { 227 | let values: Vec<&str> = vec![]; 228 | let tree = MerkleTree::new(&values, ALGO); 229 | 230 | assert_eq!(true, tree.is_empty()); 231 | assert_eq!(0, tree.height()); 232 | assert_eq!(0, tree.nodes_count()); 233 | assert_eq!(0, tree.data_size()); 234 | let empty_root: Vec = vec![]; 235 | assert_eq!(empty_root, tree.get_root()); 236 | } 237 | 238 | #[test] 239 | fn test_build_tree_with_odd_number_of_values() { 240 | let values = vec!["one", "two", "three"]; 241 | let tree = MerkleTree::new(&values, ALGO); 242 | 243 | let _d0: Digest = super::get_hash(values[0].as_ref(), ALGO); 244 | let _d1: Digest = super::get_hash(values[1].as_ref(), ALGO); 245 | let _d2: Digest = super::get_hash(values[2].as_ref(), ALGO); 246 | let _d3: Digest = super::get_hash(values[2].as_ref(), ALGO); 247 | 248 | let _d01 = hash_pair(_d0.as_ref(), _d1.as_ref(), ALGO); 249 | let _d23 = hash_pair(_d2.as_ref(), _d3.as_ref(), ALGO); 250 | let _pair = super::get_pair_hash(_d01.as_ref(), _d23.as_ref(), ALGO); 251 | 252 | assert_eq!(false, tree.is_empty()); 253 | assert_eq!(3, tree.height()); 254 | assert_eq!(7, tree.nodes_count()); 255 | assert_eq!(7 * ALGO.output_len(), tree.data_size()); 256 | assert_eq!(_pair.as_ref(), tree.get_root()); 257 | } 258 | 259 | #[test] 260 | fn test_build_tree_with_even_number_of_values() { 261 | let values = vec!["one", "two", "three", "four"]; 262 | let tree = MerkleTree::new(&values, ALGO); 263 | 264 | let _d0: Digest = super::get_hash(values[0].as_ref(), ALGO); 265 | let _d1: Digest = super::get_hash(values[1].as_ref(), ALGO); 266 | let _d2: Digest = super::get_hash(values[2].as_ref(), ALGO); 267 | let _d3: Digest = super::get_hash(values[3].as_ref(), ALGO); 268 | 269 | let _d01 = hash_pair(_d0.as_ref(), _d1.as_ref(), ALGO); 270 | let _d23 = hash_pair(_d2.as_ref(), _d3.as_ref(), ALGO); 271 | let _pair = super::get_pair_hash(_d01.as_ref(), _d23.as_ref(), ALGO); 272 | 273 | assert_eq!(false, tree.is_empty()); 274 | assert_eq!(3, tree.height()); 275 | assert_eq!(7, tree.nodes_count()); 276 | assert_eq!(7 * ALGO.output_len(), tree.data_size()); 277 | assert_eq!(_pair.as_ref(), tree.get_root()); 278 | } 279 | 280 | #[test] 281 | fn test_root_hash_same_if_values_were_same() { 282 | let values = vec!["one", "one", "one", "one"]; 283 | let tree = MerkleTree::new(&values, ALGO); 284 | 285 | let _d0: Digest = super::get_hash(values[0].as_ref(), ALGO); 286 | let _d1: Digest = super::get_hash(values[1].as_ref(), ALGO); 287 | let _d2: Digest = super::get_hash(values[2].as_ref(), ALGO); 288 | let _d3: Digest = super::get_hash(values[3].as_ref(), ALGO); 289 | 290 | let _d01 = hash_pair(_d0.as_ref(), _d1.as_ref(), ALGO); 291 | let _d23 = hash_pair(_d2.as_ref(), _d3.as_ref(), ALGO); 292 | let _pair = super::get_pair_hash(_d23.as_ref(), _d01.as_ref(), ALGO); 293 | 294 | assert_eq!(false, tree.is_empty()); 295 | assert_eq!(3, tree.height()); 296 | assert_eq!(7, tree.nodes_count()); 297 | assert_eq!(7 * ALGO.output_len(), tree.data_size()); 298 | assert_eq!(_pair.as_ref(), tree.get_root()); 299 | } 300 | 301 | #[test] 302 | fn test_root_hash_different_reverse_values() { 303 | let values1 = vec!["one", "two"]; 304 | let tree1 = MerkleTree::new(&values1, ALGO); 305 | 306 | let values2 = vec!["two", "one"]; 307 | let tree2 = MerkleTree::new(&values2, ALGO); 308 | 309 | assert_ne!(tree1.get_root(), tree2.get_root()); 310 | } 311 | 312 | #[test] 313 | fn test_generate_merkle_proof_and_validate() { 314 | let values = vec!["one", "two", "three", "four"]; 315 | let tree = MerkleTree::new(&values, ALGO); 316 | 317 | for v in values { 318 | let proof = tree.build_proof(&v); 319 | assert_eq!(true, proof.is_some()); 320 | let vec = proof.unwrap(); 321 | assert_eq!(3, vec.len()); 322 | tree.validate(&vec); 323 | } 324 | 325 | let absent = vec!["qqq", "www", "eee", "rrr"]; 326 | for v in absent { 327 | let proof = tree.build_proof(&v); 328 | assert_eq!(true, proof.is_none()); 329 | } 330 | } 331 | 332 | #[test] 333 | fn test_provide_bad_merkle_proof() { 334 | let values = vec!["one", "two", "three", "four"]; 335 | let tree = MerkleTree::new(&values, ALGO); 336 | let proof = tree.build_proof(&"one"); 337 | 338 | assert_eq!(true, proof.is_some()); 339 | let _d0: Digest = super::get_hash("five".as_ref(), ALGO); 340 | let proof_vec = proof.unwrap(); 341 | let vec = vec![proof_vec[0], proof_vec[1], _d0.as_ref()]; 342 | assert_eq!(false, tree.validate(&vec)); 343 | } 344 | 345 | // helper function 346 | fn hash_pair(x: &[u8], y: &[u8], algo: &'static Algorithm) -> Digest { 347 | let mut ctx = Context::new(algo); 348 | ctx.update(x); 349 | ctx.update(y); 350 | ctx.finish() 351 | } 352 | 353 | } 354 | 355 | 356 | 357 | -------------------------------------------------------------------------------- /hands-on/rust-codes-flow/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.13" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.7" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" 40 | dependencies = [ 41 | "windows-sys", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.11" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" 49 | dependencies = [ 50 | "anstyle", 51 | "once_cell_polyfill", 52 | "windows-sys", 53 | ] 54 | 55 | [[package]] 56 | name = "anyhow" 57 | version = "1.0.100" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 60 | 61 | [[package]] 62 | name = "arrayref" 63 | version = "0.3.9" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 66 | 67 | [[package]] 68 | name = "arrayvec" 69 | version = "0.7.6" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 72 | 73 | [[package]] 74 | name = "blake3" 75 | version = "1.8.2" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" 78 | dependencies = [ 79 | "arrayref", 80 | "arrayvec", 81 | "cc", 82 | "cfg-if", 83 | "constant_time_eq", 84 | ] 85 | 86 | [[package]] 87 | name = "bumpalo" 88 | version = "3.19.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 91 | 92 | [[package]] 93 | name = "cc" 94 | version = "1.2.48" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" 97 | dependencies = [ 98 | "find-msvc-tools", 99 | "shlex", 100 | ] 101 | 102 | [[package]] 103 | name = "cfg-if" 104 | version = "1.0.4" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 107 | 108 | [[package]] 109 | name = "clap" 110 | version = "4.5.53" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" 113 | dependencies = [ 114 | "clap_builder", 115 | "clap_derive", 116 | ] 117 | 118 | [[package]] 119 | name = "clap_builder" 120 | version = "4.5.53" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" 123 | dependencies = [ 124 | "anstream", 125 | "anstyle", 126 | "clap_lex", 127 | "strsim", 128 | ] 129 | 130 | [[package]] 131 | name = "clap_derive" 132 | version = "4.5.49" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 135 | dependencies = [ 136 | "heck", 137 | "proc-macro2", 138 | "quote", 139 | "syn", 140 | ] 141 | 142 | [[package]] 143 | name = "clap_lex" 144 | version = "0.7.6" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" 147 | 148 | [[package]] 149 | name = "cli" 150 | version = "0.1.0" 151 | dependencies = [ 152 | "anyhow", 153 | "clap", 154 | "core", 155 | "indicatif", 156 | "num_cpus", 157 | "rayon", 158 | "serde_json", 159 | "walkdir", 160 | ] 161 | 162 | [[package]] 163 | name = "colorchoice" 164 | version = "1.0.4" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 167 | 168 | [[package]] 169 | name = "console" 170 | version = "0.16.1" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" 173 | dependencies = [ 174 | "encode_unicode", 175 | "libc", 176 | "once_cell", 177 | "unicode-width", 178 | "windows-sys", 179 | ] 180 | 181 | [[package]] 182 | name = "constant_time_eq" 183 | version = "0.3.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" 186 | 187 | [[package]] 188 | name = "core" 189 | version = "0.1.0" 190 | dependencies = [ 191 | "anyhow", 192 | "blake3", 193 | "hex", 194 | "prettyplease", 195 | "proc-macro2", 196 | "quote", 197 | "serde", 198 | "serde_json", 199 | "syn", 200 | "thiserror", 201 | ] 202 | 203 | [[package]] 204 | name = "crossbeam-deque" 205 | version = "0.8.6" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 208 | dependencies = [ 209 | "crossbeam-epoch", 210 | "crossbeam-utils", 211 | ] 212 | 213 | [[package]] 214 | name = "crossbeam-epoch" 215 | version = "0.9.18" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 218 | dependencies = [ 219 | "crossbeam-utils", 220 | ] 221 | 222 | [[package]] 223 | name = "crossbeam-utils" 224 | version = "0.8.21" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 227 | 228 | [[package]] 229 | name = "either" 230 | version = "1.15.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 233 | 234 | [[package]] 235 | name = "encode_unicode" 236 | version = "1.0.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 239 | 240 | [[package]] 241 | name = "find-msvc-tools" 242 | version = "0.1.5" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 245 | 246 | [[package]] 247 | name = "heck" 248 | version = "0.5.0" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 251 | 252 | [[package]] 253 | name = "hermit-abi" 254 | version = "0.5.2" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 257 | 258 | [[package]] 259 | name = "hex" 260 | version = "0.4.3" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 263 | 264 | [[package]] 265 | name = "indicatif" 266 | version = "0.18.3" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" 269 | dependencies = [ 270 | "console", 271 | "portable-atomic", 272 | "unicode-width", 273 | "unit-prefix", 274 | "web-time", 275 | ] 276 | 277 | [[package]] 278 | name = "is_terminal_polyfill" 279 | version = "1.70.2" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" 282 | 283 | [[package]] 284 | name = "itoa" 285 | version = "1.0.15" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 288 | 289 | [[package]] 290 | name = "js-sys" 291 | version = "0.3.83" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 294 | dependencies = [ 295 | "once_cell", 296 | "wasm-bindgen", 297 | ] 298 | 299 | [[package]] 300 | name = "libc" 301 | version = "0.2.178" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 304 | 305 | [[package]] 306 | name = "memchr" 307 | version = "2.7.6" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 310 | 311 | [[package]] 312 | name = "num_cpus" 313 | version = "1.17.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" 316 | dependencies = [ 317 | "hermit-abi", 318 | "libc", 319 | ] 320 | 321 | [[package]] 322 | name = "once_cell" 323 | version = "1.21.3" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 326 | 327 | [[package]] 328 | name = "once_cell_polyfill" 329 | version = "1.70.2" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 332 | 333 | [[package]] 334 | name = "portable-atomic" 335 | version = "1.11.1" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 338 | 339 | [[package]] 340 | name = "prettyplease" 341 | version = "0.2.37" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 344 | dependencies = [ 345 | "proc-macro2", 346 | "syn", 347 | ] 348 | 349 | [[package]] 350 | name = "proc-macro2" 351 | version = "1.0.103" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 354 | dependencies = [ 355 | "unicode-ident", 356 | ] 357 | 358 | [[package]] 359 | name = "quote" 360 | version = "1.0.42" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 363 | dependencies = [ 364 | "proc-macro2", 365 | ] 366 | 367 | [[package]] 368 | name = "rayon" 369 | version = "1.11.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" 372 | dependencies = [ 373 | "either", 374 | "rayon-core", 375 | ] 376 | 377 | [[package]] 378 | name = "rayon-core" 379 | version = "1.13.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" 382 | dependencies = [ 383 | "crossbeam-deque", 384 | "crossbeam-utils", 385 | ] 386 | 387 | [[package]] 388 | name = "rustversion" 389 | version = "1.0.22" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 392 | 393 | [[package]] 394 | name = "ryu" 395 | version = "1.0.20" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 398 | 399 | [[package]] 400 | name = "same-file" 401 | version = "1.0.6" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 404 | dependencies = [ 405 | "winapi-util", 406 | ] 407 | 408 | [[package]] 409 | name = "serde" 410 | version = "1.0.228" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 413 | dependencies = [ 414 | "serde_core", 415 | "serde_derive", 416 | ] 417 | 418 | [[package]] 419 | name = "serde_core" 420 | version = "1.0.228" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 423 | dependencies = [ 424 | "serde_derive", 425 | ] 426 | 427 | [[package]] 428 | name = "serde_derive" 429 | version = "1.0.228" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 432 | dependencies = [ 433 | "proc-macro2", 434 | "quote", 435 | "syn", 436 | ] 437 | 438 | [[package]] 439 | name = "serde_json" 440 | version = "1.0.145" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 443 | dependencies = [ 444 | "itoa", 445 | "memchr", 446 | "ryu", 447 | "serde", 448 | "serde_core", 449 | ] 450 | 451 | [[package]] 452 | name = "shlex" 453 | version = "1.3.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 456 | 457 | [[package]] 458 | name = "strsim" 459 | version = "0.11.1" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 462 | 463 | [[package]] 464 | name = "syn" 465 | version = "2.0.111" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 468 | dependencies = [ 469 | "proc-macro2", 470 | "quote", 471 | "unicode-ident", 472 | ] 473 | 474 | [[package]] 475 | name = "thiserror" 476 | version = "2.0.17" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 479 | dependencies = [ 480 | "thiserror-impl", 481 | ] 482 | 483 | [[package]] 484 | name = "thiserror-impl" 485 | version = "2.0.17" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 488 | dependencies = [ 489 | "proc-macro2", 490 | "quote", 491 | "syn", 492 | ] 493 | 494 | [[package]] 495 | name = "unicode-ident" 496 | version = "1.0.22" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 499 | 500 | [[package]] 501 | name = "unicode-width" 502 | version = "0.2.2" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" 505 | 506 | [[package]] 507 | name = "unit-prefix" 508 | version = "0.5.2" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" 511 | 512 | [[package]] 513 | name = "utf8parse" 514 | version = "0.2.2" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 517 | 518 | [[package]] 519 | name = "walkdir" 520 | version = "2.5.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 523 | dependencies = [ 524 | "same-file", 525 | "winapi-util", 526 | ] 527 | 528 | [[package]] 529 | name = "wasm-bindgen" 530 | version = "0.2.106" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 533 | dependencies = [ 534 | "cfg-if", 535 | "once_cell", 536 | "rustversion", 537 | "wasm-bindgen-macro", 538 | "wasm-bindgen-shared", 539 | ] 540 | 541 | [[package]] 542 | name = "wasm-bindgen-macro" 543 | version = "0.2.106" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 546 | dependencies = [ 547 | "quote", 548 | "wasm-bindgen-macro-support", 549 | ] 550 | 551 | [[package]] 552 | name = "wasm-bindgen-macro-support" 553 | version = "0.2.106" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 556 | dependencies = [ 557 | "bumpalo", 558 | "proc-macro2", 559 | "quote", 560 | "syn", 561 | "wasm-bindgen-shared", 562 | ] 563 | 564 | [[package]] 565 | name = "wasm-bindgen-shared" 566 | version = "0.2.106" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 569 | dependencies = [ 570 | "unicode-ident", 571 | ] 572 | 573 | [[package]] 574 | name = "web-time" 575 | version = "1.1.0" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 578 | dependencies = [ 579 | "js-sys", 580 | "wasm-bindgen", 581 | ] 582 | 583 | [[package]] 584 | name = "winapi-util" 585 | version = "0.1.11" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 588 | dependencies = [ 589 | "windows-sys", 590 | ] 591 | 592 | [[package]] 593 | name = "windows-link" 594 | version = "0.2.1" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 597 | 598 | [[package]] 599 | name = "windows-sys" 600 | version = "0.61.2" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 603 | dependencies = [ 604 | "windows-link", 605 | ] 606 | --------------------------------------------------------------------------------