Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tigerros committed Nov 2, 2023
0 parents commit b13233b
Show file tree
Hide file tree
Showing 15 changed files with 396 additions and 0 deletions.
56 changes: 56 additions & 0 deletions .github/workflows/lints.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Lints
on:
push:
branches: [ master ]

pull_request:
branches: [ master ]

jobs:
check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Check
run: cargo check --workspace --examples --tests
check-formatting:
needs: [ check ]
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Format
run: cargo fmt --all -- --check
clippy:
needs: [ check ]
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Clippy
run: cargo clippy --workspace --examples --tests -- --warn clippy::pedantic --warn clippy::nursery --deny warnings
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Tests

on:
push:
branches: [ master ]

pull_request:
branches: [ master ]

jobs:
test:
needs: [ check ]
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Check
run: cargo check --workspace --examples --tests

- name: Test
run: cargo test --workspace
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
/Cargo.lock
/.idea
14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "type-path"
version = "0.1.0"
edition = "2021"
authors = ["Leonard. D <tigerros.gh@gmail.com>"]
description = "Get the string array representation of a Rust type path"
license = "MIT"
repository = "https://github.com/tigerros/type-path"
keywords = ["reflection", "macros", "no-std"]
categories = ["rust-patterns", "no-std"]
include = ["src/lib.rs", "README.md"]

[dev-dependencies]
trybuild = "1.0.0"
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[![tests badge]](https://github.com/tigerros/type-path/actions/workflows/tests.yml)
[![lints badge]](https://github.com/tigerros/type-path/actions/workflows/lints.yml)
[![docs.rs badge]](https://docs.rs/type-path/)
[![crates.io badge]](https://crates.io/crates/type-path)
[![license badge]](https://github.com/tigerros/type-path/blob/master/LICENSE)

A tiny crate for getting the string array representation of a Rust type path, with type validation.
Everything happens at compile-time.

Check out the docs for everything else!

[tests badge]: https://img.shields.io/github/actions/workflow/status/tigerros/type-path/tests.yml?label=tests&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tISBGb250IEF3ZXNvbWUgUHJvIDYuNC4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlIChDb21tZXJjaWFsIExpY2Vuc2UpIENvcHlyaWdodCAyMDIzIEZvbnRpY29ucywgSW5jLiAtLT48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMzQyLjYgOS40Yy0xMi41LTEyLjUtMzIuOC0xMi41LTQ1LjMgMHMtMTIuNSAzMi44IDAgNDUuM2w5LjQgOS40TDI4LjEgMzQyLjZDMTAuMSAzNjAuNiAwIDM4NSAwIDQxMC41VjQxNmMwIDUzIDQzIDk2IDk2IDk2aDUuNWMyNS41IDAgNDkuOS0xMC4xIDY3LjktMjguMUw0NDggMjA1LjNsOS40IDkuNGMxMi41IDEyLjUgMzIuOCAxMi41IDQ1LjMgMHMxMi41LTMyLjggMC00NS4zbC0zMi0zMi05Ni05Ni0zMi0zMnpNMjA1LjMgMjU2TDM1MiAxMDkuMyA0MDIuNyAxNjBsLTk2IDk2SDIwNS4zeiIvPjwvc3ZnPg==
[lints badge]: https://img.shields.io/github/actions/workflow/status/tigerros/type-path/lints.yml?label=lints&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIj48IS0tISBGb250IEF3ZXNvbWUgUHJvIDYuNC4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlIChDb21tZXJjaWFsIExpY2Vuc2UpIENvcHlyaWdodCAyMDIzIEZvbnRpY29ucywgSW5jLiAtLT48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMTEyIDBDOTkuMSAwIDg3LjQgNy44IDgyLjUgMTkuN2wtNjYuNyAxNjAtMTMuMyAzMmMtNi44IDE2LjMgLjkgMzUgMTcuMiA0MS44czM1LS45IDQxLjgtMTcuMkw2Ni43IDIyNGg5MC43bDUuMSAxMi4zYzYuOCAxNi4zIDI1LjUgMjQgNDEuOCAxNy4yczI0LTI1LjUgMTcuMi00MS44bC0xMy4zLTMyLTY2LjctMTYwQzEzNi42IDcuOCAxMjQuOSAwIDExMiAwem0xOC43IDE2MEg5My4zTDExMiAxMTUuMiAxMzAuNyAxNjB6TTI1NiAzMnY5NiA5NmMwIDE3LjcgMTQuMyAzMiAzMiAzMmg4MGM0NC4yIDAgODAtMzUuOCA4MC04MGMwLTIzLjEtOS44LTQzLjgtMjUuNC01OC40YzYtMTEuMiA5LjQtMjQgOS40LTM3LjZjMC00NC4yLTM1LjgtODAtODAtODBIMjg4Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnptOTYgNjRIMzIwVjY0aDMyYzguOCAwIDE2IDcuMiAxNiAxNnMtNy4yIDE2LTE2IDE2em0tMzIgNjRoMzIgMTZjOC44IDAgMTYgNy4yIDE2IDE2cy03LjIgMTYtMTYgMTZIMzIwVjE2MHpNNTY2LjYgMzEwLjZjMTIuNS0xMi41IDEyLjUtMzIuOCAwLTQ1LjNzLTMyLjgtMTIuNS00NS4zIDBMMzUyIDQzNC43bC03My40LTczLjRjLTEyLjUtMTIuNS0zMi44LTEyLjUtNDUuMyAwcy0xMi41IDMyLjggMCA0NS4zbDk2IDk2YzEyLjUgMTIuNSAzMi44IDEyLjUgNDUuMyAwbDE5Mi0xOTJ6Ii8+PC9zdmc+
[docs.rs badge]: https://img.shields.io/docsrs/type-path?logo=docs.rs&label=docs.rs
[crates.io badge]: https://img.shields.io/crates/v/type-path?logo=rust
[license badge]: https://img.shields.io/crates/l/type-path
131 changes: 131 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#![no_std]

//! # Guide
//!
//! The crate has just one macro: [`type_path!`](type_path). Check it out for syntax and returning details.
//!
//! You might be wondering why we're using an absolute path in the following example:
//!
//! ```rust
//! use type_path::type_path;
//! assert_eq!(type_path!(::std::io::BufWriter), ["::", "std", "io", "BufWriter"]);
//! ```
//!
//! It's because macros have no way of knowing the scope of the given item.
//!
//! If it wasn't required, the following would happen, because macros only have access to the literal `Result` tokens,
//! and no type information/scope etc.
//!
//! ```rust,ignore
//! # use type_path::type_path;
//! use std::io::Result;
//! assert_eq!(type_path!(Result), ["Result"]);
//! ```
//!
//! Now, the reliability aspect is gone, because even though the type exists,
//! `"Result"` is extremely generic and almost certainly not what you want.
//!
//! You can also have an "everything" path (although you probably won't use this a lot):
//!
//! ```rust
//! # use type_path::type_path;
//! assert_eq!(type_path!(::std::io::*), ["::", "std", "io", "*"]);
//! ```
//!
//! This doesn't compile, since `BufWrirer` doesn't exist:
//!
//! ```compile_fail,E0432
//! # use type_path::type_path;
//! type_path!(::std::io::BufWrirer);
//! ```
//!
//! # Why?
//! - **Reliability**:
//! The [`type_path!`](type_path) macro will check if the path you entered is valid, at compile time.
//! With other approaches, you can't be sure that the path is valid.
//! - No dependencies.
//! - You can use it in a no-std environment,

const WHITESPACE: u8 = b'\t' | b'\n' | b'\r' | b' ';

const fn bytes_trim_start(mut this: &[u8]) -> &[u8] {
loop {
match this {
[WHITESPACE, rem @ ..] => this = rem,
_ => return this,
}
}
}

const fn bytes_trim_end(mut this: &[u8]) -> &[u8] {
loop {
match this {
[rem @ .., WHITESPACE] => this = rem,
_ => return this,
}
}
}

const fn bytes_trim(this: &[u8]) -> &[u8] {
bytes_trim_start(bytes_trim_end(this))
}

/// **This is not intended to be used outside of this crate!**
/// It's public because [`type_path!`](type_path) includes it in the expansion.
///
/// Trims a string at compile time.
#[doc(hidden)]
#[must_use]
pub const fn trim(this: &str) -> &str {
let trimmed = bytes_trim(this.as_bytes());

// Safety: bytes_trim only removes ascii bytes
unsafe { core::str::from_utf8_unchecked(trimmed) }
}

/// **This is not intended to be used outside of this crate!**
///
/// Macro with one empty pattern `() => {};`.
/// Used for enforcing that a meta variable (e.g., `$foo:tt`) is empty.
#[macro_export]
macro_rules! empty {
() => {};
}

/// # Syntax
///
/// *Ignore the macro source. It might be confusing and not helpful.*
///
/// There's 3 patterns:
///
/// - `::` prefix, with 1+ [identifiers] separated by `::`. E.g., `::path::to::Item`.
/// This is an absolute path and will be resolved from the root of your project, regardless of scope.
/// - Same as the first pattern, but with a `crate` prefix.
/// Use this when you need to use types from the current crate.
/// - Same as the first pattern, but ending in `::*`. E.g., `::path::to::prelude::*`.
///
/// # Returns
///
/// `[&'static str; N]`, where `N` is the amount of segments in the path, and `&str` is the segment.
///
/// The first item in the string array will be `"::"` or `"crate"`, depending on which prefix you use.
///
/// [identifiers]: https://doc.rust-lang.org/reference/identifiers.html "Rust identifier reference"
#[macro_export]
#[allow(clippy::crate_in_macro_def)]
macro_rules! type_path {

(::$($segment:ident):: +$(::*$($empty:tt)? )?) => {{
#[allow(unused_imports)]
use :: $($segment)::+ $(::* $($crate::empty!($empty))?)?;

["::", $($crate::trim(stringify!($segment))),+ $(, "*" $($crate::empty!($empty))?)?]
}};

(crate::$($segment:ident):: +$(::*$($empty:tt)? )?) => {{
#[allow(unused_imports)]
use crate :: $($segment)::+ $(::* $($crate::empty!($empty))?)?;

["crate", $($crate::trim(stringify!($segment))),+ $(, "*" $($crate::empty!($empty))?)?]
}};
}
5 changes: 5 additions & 0 deletions tests/compile-fail/no_root_prefix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use type_path::type_path;

fn main() {
type_path!(std::io::BufWriter);
}
11 changes: 11 additions & 0 deletions tests/compile-fail/no_root_prefix.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: no rules expected the token `std`
--> tests/compile-fail/no_root_prefix.rs:4:16
|
4 | type_path!(std::io::BufWriter);
| ^^^ no rules expected this token in macro call
|
note: while trying to match `::`
--> src/lib.rs
|
| (::$($segment:ident):: +$(::*$($empty:tt)? )?) => {{
| ^^
5 changes: 5 additions & 0 deletions tests/compile-fail/slice_instead_of_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use type_path::type_path;

const ITER_REPEAT_PATH: &[&str; 4] = type_path!(::std::iter::repeat);

fn main() {}
7 changes: 7 additions & 0 deletions tests/compile-fail/slice_instead_of_array.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error[E0308]: mismatched types
--> tests/compile-fail/slice_instead_of_array.rs:3:38
|
3 | const ITER_REPEAT_PATH: &[&str; 4] = type_path!(::std::iter::repeat);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[&str; 4]`, found `[&str; 4]`
|
= note: this error originates in the macro `type_path` (in Nightly builds, run with -Z macro-backtrace for more info)
5 changes: 5 additions & 0 deletions tests/compile-fail/unresolved_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use type_path::type_path;

fn main() {
type_path!(::std::io::BufWrirer);
}
10 changes: 10 additions & 0 deletions tests/compile-fail/unresolved_path.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error[E0432]: unresolved import `std::io::BufWrirer`
--> tests/compile-fail/unresolved_path.rs:4:5
|
4 | type_path!(::std::io::BufWrirer);
| ^^^^^^^^^^^^^^^^^^^^^^---------^
| | |
| | help: a similar name exists in the module: `BufWriter`
| no `BufWrirer` in `io`
|
= note: this error originates in the macro `type_path` (in Nightly builds, run with -Z macro-backtrace for more info)
5 changes: 5 additions & 0 deletions tests/compile-fail/wrong_array_length.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use type_path::type_path;

const ITER_REPEAT_PATH: [&str; 3] = type_path!(::std::iter::repeat);

fn main() {}
9 changes: 9 additions & 0 deletions tests/compile-fail/wrong_array_length.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0308]: mismatched types
--> tests/compile-fail/wrong_array_length.rs:3:37
|
3 | const ITER_REPEAT_PATH: [&str; 3] = type_path!(::std::iter::repeat);
| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 4 elements
| |
| help: consider specifying the actual array length: `4`
|
= note: this error originates in the macro `type_path` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit b13233b

Please sign in to comment.