diff --git a/.gitignore b/.gitignore index 2ebb751..f11ce61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +### https://github.com/github/gitignore/blob/main/C.gitignore + # Prerequisites *.d @@ -56,3 +58,30 @@ dkms.conf # https://testanything.org/tap-specification *.tap !c/test_report.tap + +### https://github.com/github/gitignore/blob/main/Rust.gitignore + +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# rustc will dump stack traces when hitting an internal compiler error to PWD +rustc-ice-*.txt + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ diff --git a/README.md b/README.md index bedd3b6..bc07695 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,17 @@ The first project of the School 42. -Linted according to the `norminette` v3.3.51 rules (at the time of writing which was a long time ago) and **not anymore**. +Linted according to the `norminette` v3.3.51 rules (at the time of writing which was a long time ago)... **not anymore**. ## Table of Contents - [Overview](#overview) - [Libft in C](#libft-in-c) +- [Libft in Rust](#libft-in-rust) - [References](#references) + - [C](#c) + - [Rust](#rust) + - [C++](#c++) ### Overview @@ -18,29 +22,48 @@ This project was done in C during my learning at the School 42 (School 21). Now I reorganised the original code and placed it in the [c](./c) directory. To build the project, use `make` or `make bonus` which will create a static "libft.a" library. During my time at School 42, I checked the correctness of the library using the following testing programs (**attention**, these programs are written by people, so rely on them cautiously): -- [libft-unit-test](https://github.com/alelievr/libft-unit-test) (contributed more long time ago) -- [libftTester](https://github.com/Tripouille/libftTester) (contributed less and also long time ago) +- [libft-unit-test](https://github.com/alelievr/libft-unit-test) (contributed a while back) +- [libftTester](https://github.com/Tripouille/libftTester) (contributed some time ago) - [libft-war-machine](https://github.com/ska42/libft-war-machine) -It is normal (!) if some of your functions are reported as not protected, you don't need to be scared of opinionated "boom" messages..or maybe you have to depending on the School 42 current subject. +It is normal (!) if some of your functions are flagged as unprotected, you don't need to be scared of opinionated "boom" messages (or maybe you have to according to the School 42 latest subject). -Later on, I decided that I needed (for some unexplicable reason) to learn how to write unit tests in C and this project seems to me like a perfect candidate to start with. I picked [Check](https://libcheck.github.io/check/) framework because it is: +Later on, I got into my head that that I need (for some inexplicable reason) to learn how to write unit tests in C and this project seems to me like the perfect place to start. I picked [Check](https://libcheck.github.io/check/) framework because it is: - installable via `sudo apt install check` (see the [Installing Check](https://libcheck.github.io/check/web/install.html) section for more details); - simple enough and has some documentation and even tutorials: [basic](https://libcheck.github.io/check/doc/check_html/check_3.html) and [advanced](https://libcheck.github.io/check/doc/check_html/check_4.html#Advanced-Features) + not so-long [API reference](https://libcheck.github.io/check/doc/doxygen/html/check_8h.html); -- advised/required (?) at School 21. +- recommended or even required (?) at School 21. -So, if you are a student or an "intern" at School 21 or 42, you might want to use the "Check" framework in your C projects. Tests are available in the [c/tests](./c/tests) directory. Tests are not organised according to best practices, so please don't expect them to be idiomatic and well-structured. **Disclaimer**: I had and have no eagerness to provide full coverage for this project in C, only functions or data structures of interest that was enough **for me** to master "Check" to some basic level. +So, if you are a student or an "intern" at School 21 or 42, you might want to use the "Check" framework in your C projects. Tests are available in the [c/tests](./c/tests) directory. The tests are not written following best practices, please don’t expect idiomatic or well‑structured code. **Disclaimer**: I had and have no eagerness to provide full coverage for this project in C, only functions or data structures of interest that was enough **for me** to master "Check" to some basic level. + +### Libft in Rust + +Libft is a library, and I wanted to learn how to build a proper Rust crate—something I could publish and reuse in other projects. This seemed like a good first challenge. + +- `cargo new --lib %my-fancy-lib%` - creates a library project/directory with ***%my-fancy-lib%*** name; +- `cargo fmt` - format the code; +- `cargo clippy` - built-in linter; +- `cargo build` - build/compile the project; +- `cargo test` - run tests. ### References -Some of my other 42cursus projects in C: +#### C +Some of my other 42cursus projects in C: - [get-next-line](https://github.com/stankudrow/get_next_line) - [ft_printf](https://github.com/stankudrow/ft_printf) Resources on C programming language: - - [CodeVault playlist](https://www.youtube.com/@CodeVault/playlists) - must watch, practically all videos. -The reason why I won't learn C++ and practice it on the libft -> [have fun](https://www.youtube.com/watch?v=7fGB-hjc2Gc). +#### Rust + +- [The Cargo Book][cargo_book_ref] - Cargo is the Rust package manager: dependencies, package compilation, building, uploading to [crates.io package registry](https://crates.io/) etc. + +#### C++ + +No way, thanx. The reason why I won't learn C++ and practice it on the libft -> have fun (or any other emotions) watching [this video](https://www.youtube.com/watch?v=7fGB-hjc2Gc). + + +[cargo_book_ref]: https://doc.rust-lang.org/cargo/index.html diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..e4bdfe5 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libft" +version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..cbac016 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "libft" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/rust/src/ft_chr.rs b/rust/src/ft_chr.rs new file mode 100644 index 0000000..bebe61e --- /dev/null +++ b/rust/src/ft_chr.rs @@ -0,0 +1,61 @@ +// If my goal had been to reproduce the "original" ft_isascii function, +// its signature would have been `pub fn ft_isascii(c: i32) -> i32`, +pub fn ft_isascii(c: u8) -> bool { + // `c >= 0` is redundant when c is unsigned + c <= 127 // the result of the last expression is returned automatically + // however, `return c <= 127;` is doable, yet not idiomatic +} + +// A function without `pub` is private -> like `static` in C +fn ft_islower(c: u8) -> bool { + // c >= b'a' && c <= b'z' // possible, but `cargo clippy` suggests... + // (b'a'..=b'z').contains(&c) // yet this also does not please clippy, so... + c.is_ascii_lowercase() // and this makes clippy happy +} + +// The following macro will suppress the complain from `cargo clippy` +#[allow(clippy::char_lit_as_u8)] +fn ft_isupper(c: u8) -> bool { + // explicit type casting via `obj as %type%` + // because implicit casting is not ppresumed and for good reason + c >= ('A' as u8) && c <= ('Z' as u8) +} + +// No need to declare/define the underlying function before this one +pub fn ft_isalnum(c: u8) -> bool { + ft_isalpha(c) || ft_isdigit(c) +} + +pub fn ft_isalpha(c: u8) -> bool { + ft_islower(c) || ft_isupper(c) +} + +// several "warnings" are silenced in one macro +#[allow(clippy::char_lit_as_u8, clippy::needless_return)] +pub fn ft_isdigit(c: u8) -> bool { + // b'0' is a byte literal, while '0' is char (Unicode) + // '0' is char, c is u8 -> incompatible types, + // either byte literal will do or explicit type casting + return c >= b'0' && c <= ('9' as u8); +} + +#[allow(clippy::needless_return)] +pub fn ft_isprint(c: u8) -> bool { + return c > 31 && c < 127; // not disallowed, why not `return` redundantly +} + +pub fn ft_tolower(c: u8) -> u8 { + if ft_isupper(c) { + return c + 32; // here return is necessary + // because the expression above is not the very last + } + c // here the `return` statement can be omitted +} + +#[allow(clippy::needless_return)] +pub fn ft_toupper(c: u8) -> u8 { + if ft_islower(c) { + return c - 32; // parentheses are unncecessary + } + return c; +} diff --git a/rust/src/ft_str.rs b/rust/src/ft_str.rs new file mode 100644 index 0000000..95c8b70 --- /dev/null +++ b/rust/src/ft_str.rs @@ -0,0 +1,14 @@ +// `pub` = public -> is visible from outside this module. +// Without pub this function would have been private. +pub fn ft_strlen(s: &str) -> usize { + // &str in Rust is UTF-8 + // For ASCII string, `s.len() == s.chars().count()` + // C-strlen is about chars as bytes, that is why `s.as_bytes()` + + // return s.len(); // return is possible, but redundant + s.len() // no semicolon here!!! + + // In a function, the last expression is returned automatically + // If the last expression is followed by a semicolon, + // then it becomes a statement/instruction that returns nothing (semicolon silences). +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..fe0cd7b --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,15 @@ +// `pub mod XXX;` +// `mod`ule (file) XXX is claimed to exist. +// `pub` makes this module public, i.e. visible from the outside. +pub mod ft_chr; + +pub use ft_chr::ft_isalnum; +pub use ft_chr::ft_isalpha; +pub use ft_chr::ft_isascii; +pub use ft_chr::ft_isdigit; +pub use ft_chr::ft_isprint; +pub use ft_chr::ft_tolower; +pub use ft_chr::ft_toupper; + +pub mod ft_str; +pub use ft_str::ft_strlen; diff --git a/rust/tests/test_ft_chr.rs b/rust/tests/test_ft_chr.rs new file mode 100644 index 0000000..9c72486 --- /dev/null +++ b/rust/tests/test_ft_chr.rs @@ -0,0 +1,78 @@ +use libft::ft_isalnum; +use libft::ft_isalpha; +use libft::ft_isascii; +use libft::ft_isdigit; +use libft::ft_isprint; +use libft::ft_tolower; +use libft::ft_toupper; + +#[test] +fn test_ft_isalnum() { + assert_eq!(ft_isalnum(0), false); + assert_eq!(ft_isalnum(b'0'), true); + assert_eq!(ft_isalnum(b'o'), true); + assert_eq!(ft_isalnum(b'9'), true); + assert_eq!(ft_isalnum(b'A'), true); + assert_eq!(ft_isalnum(b'Z'), true); + assert_eq!(ft_isalnum(b'a'), true); + assert_eq!(ft_isalnum(b'z'), true); + assert_eq!(ft_isalnum(128), false); + assert_eq!(ft_isalnum(255), false); +} + +#[test] +fn test_ft_isalpha() { + assert_eq!(ft_isalpha(0), false); + assert_eq!(ft_isalpha(b'A'), true); + assert_eq!(ft_isalpha(b'Z'), true); + assert_eq!(ft_isalpha(b'a'), true); + assert_eq!(ft_isalpha(b'z'), true); + assert_eq!(ft_isalpha(255), false); +} + +#[test] +fn test_ft_isascii() { + assert_eq!(ft_isascii(0), true); + assert_eq!(ft_isascii(100), true); + assert_eq!(ft_isascii(127), true); + assert_eq!(ft_isascii(128), false); + assert_eq!(ft_isascii(255), false); +} + +#[test] +fn test_ft_isdigit() { + assert_eq!(ft_isdigit(0), false); + assert_eq!(ft_isdigit(b'0'), true); + assert_eq!(ft_isdigit(b'9'), true); + assert_eq!(ft_isdigit(128), false); + assert_eq!(ft_isdigit(255), false); +} + +#[test] +fn test_ft_isprint() { + assert_eq!(ft_isprint(31), false); + assert_eq!(ft_isprint(32), true); + assert_eq!(ft_isprint(100), true); + assert_eq!(ft_isprint(126), true); + assert_eq!(ft_isprint(127), false); +} + +#[test] +fn test_ft_tolower() { + assert_eq!(ft_tolower(b'A' - 1), 64); + assert_eq!(ft_tolower(b'A'), b'a'); + assert_eq!(ft_tolower(b'Z'), b'z'); + assert_eq!(ft_tolower(b'a'), b'a'); + assert_eq!(ft_tolower(b'z'), b'z'); + assert_eq!(ft_tolower(b'z' + 1), 123); +} + +#[test] +fn test_ft_toupper() { + assert_eq!(ft_toupper(b'A' - 1), 64); + assert_eq!(ft_toupper(b'A'), b'A'); + assert_eq!(ft_toupper(b'Z'), b'Z'); + assert_eq!(ft_toupper(b'a'), b'A'); + assert_eq!(ft_toupper(b'z'), b'Z'); + assert_eq!(ft_toupper(b'z' + 1), 123); +} diff --git a/rust/tests/test_ft_str.rs b/rust/tests/test_ft_str.rs new file mode 100644 index 0000000..8073f5d --- /dev/null +++ b/rust/tests/test_ft_str.rs @@ -0,0 +1,17 @@ +use libft::ft_strlen; + +#[test] +fn test_empty() { + assert_eq!(ft_strlen(""), 0); +} + +#[test] +fn test_ascii_only() { + assert_eq!(ft_strlen("hello"), 5); +} + +#[test] +fn test_utf8_byte_length() { + let s = "Привет"; + assert!(ft_strlen(s) > s.chars().count()); +}