├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-ZLIB.md ├── changelog.md ├── commit.sh ├── readme.md ├── tstr ├── Cargo.toml ├── LICENSE-ZLIB.md ├── src │ ├── asserts.rs │ ├── for_examples.rs │ ├── for_tupled_reprs.rs │ ├── for_tupled_reprs │ │ ├── classify.rs │ │ └── integers.rs │ ├── lib.rs │ ├── macros.rs │ ├── macros │ │ └── cmp_macros.rs │ ├── make_tstr.rs │ ├── p.rs │ ├── to_uint.rs │ ├── to_uint │ │ └── impl_no_const_generics.rs │ ├── tstr_cmp.rs │ ├── tstr_cmp │ │ └── impl_no_const_generics.rs │ ├── tstr_type.rs │ └── utils.rs └── tests │ ├── modules │ ├── alias_and_tuples.rs │ ├── concat_args.rs │ ├── long_strings.rs │ ├── other_args.rs │ ├── string_args.rs │ ├── string_cmp.rs │ ├── to_uint.rs │ └── utils.rs │ └── modules_.rs └── tstr_proc_macros ├── Cargo.toml ├── LICENSE-ZLIB.md └── src ├── lib.rs ├── min_const_generics.rs ├── nested_tuple_compute.rs ├── nested_tuple_compute └── tests.rs ├── no_const_generics.rs ├── non_syn_parsing.rs ├── use_syn.rs └── utils.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | max-parallel: 2 14 | matrix: 15 | rust: [stable, beta, nightly, 1.40.0] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: ci-all-versions 20 | run: | 21 | rustup override set ${{ matrix.rust }} 22 | cargo update 23 | 24 | cd "${{github.workspace}}/tstr_proc_macros/" 25 | cargo test 26 | 27 | cd "${{github.workspace}}/tstr/" 28 | cargo test --features "testing for_examples" 29 | 30 | - uses: actions/checkout@v2 31 | - name: ci-non-MSRV 32 | if: ${{ matrix.rust != '1.40.0' }} 33 | run: | 34 | rustup override set ${{ matrix.rust }} 35 | cargo update 36 | 37 | cd "${{github.workspace}}/tstr/" 38 | cargo test --features "testing for_examples use_syn" 39 | 40 | 41 | - uses: actions/checkout@v2 42 | - name: ci-nighly 43 | if: ${{ matrix.rust == 'nightly' }} 44 | run: | 45 | rustup override set ${{ matrix.rust }} 46 | cargo update 47 | 48 | cd "${{github.workspace}}/tstr/" 49 | cargo test --features "testing for_examples min_const_generics" 50 | cargo test --features "testing for_examples use_syn min_const_generics" 51 | 52 | cargo test --features "testing for_examples nightly_const_generics" 53 | cargo test --features "testing for_examples use_syn nightly_const_generics" 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | *.7z 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members=[ 3 | "tstr", 4 | "tstr_proc_macros", 5 | ] 6 | -------------------------------------------------------------------------------- /LICENSE-ZLIB.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Matias Rodriguez. 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ### 0.2.1 2 | 3 | Added `"cmp_traits"` feature to enable TStr comparison traits. 4 | 5 | Added `"rust_1_46"` feature to enable const functions to compare `&str` and `&[u8]`. 6 | 7 | Added TStrEq trait for equality comparison between type-level strings, enabled by the `"cmp_traits"` feature. 8 | 9 | Added a utils module, with `str_eq` and `u8_slice_eq` const functions, enabled bt the `"rust_1_46"` feature. 10 | 11 | Added `Assert` type for type-level-string related compile-time assertions, inside `tstr::asserts` module. 12 | 13 | Added `EqualityProof` and `InequalityProof` structs, inside `tstr::asserts` module, 14 | 15 | Added `tstr_eq`, `tstr_ne`, and `tstr_cmp` macros for comparing type-level strings. 16 | 17 | Added `get_two` method to types in `for_examples`, for examples that use `tstr::Assert`. 18 | 19 | ### 0.2.0 20 | 21 | Added support for passing multiple literals/identifiers in macros, outputting a tuple of TStrs. 22 | 23 | Added `"min_const_generics"` feature, which changes the representation of type-level strings to use many `char` const parameters. 24 | 25 | Added `concat!(...)` and `stringify!(...)` syntaxes to the `TS`/`alias`/`ts` macros, 26 | since those macros are not expanded before being passed to other macros. 27 | 28 | Breaking change: removed the `Copy` supertrait of `ToUint`. 29 | 30 | Changed `TStr`s implementation of `ToUInt` to use the `T: ToUInt` bound, allowing easier use of `oUint` in generic functions that take a generic `TStr` . 31 | 32 | Added `MakeTStr` trait to construct `TStr`s and tuples of them. 33 | 34 | Changed the internal representation of type-level strings from singly nested tuples to 35 | recursive tuples, this is only visible to users of the `tstr` crate in error messages. 36 | 37 | ### 0.1.1 38 | 39 | Crated `tstr` crate, and `tstr_proc_macros` proc macro crates. 40 | 41 | Added opt-in `syn` and `proc_macro2` dependencies to `tstr_proc_macros` 42 | 43 | Added `tstr_proc_macros` dependency to `tstr` 44 | 45 | Added these features to `tstr_proc_macro` crate: 46 | 47 | - `"const_generics"`: to use `&'static str` const parameters in the TStr type. 48 | 49 | - `"syn_"`: to parse literals with syn, instead of manually parsing them. 50 | 51 | Added these features to tstr crate: 52 | 53 | - `"const_generics"`: to use `&'static str` const parameters in the TStr type. 54 | 55 | - `"nightly_const_generics"`: the same as `"const_generics"`, for use in Rust nightly. 56 | 57 | - `"use_syn"`: to parse literals with syn, instead of manually parsing them. 58 | 59 | - `"for_examples"`: to enable some types used in documentation examples. 60 | 61 | Added TStr type, to represent type level strings 62 | 63 | Added TS macro, to get the type of the TStr equivalent of a literal/identifier 64 | 65 | Added ts macro, to get the value of the TStr equivalent of a literal/identifier 66 | 67 | Added alias macro, to create const and type aliases of type-level strings 68 | 69 | Added StrValue trait, to get the `&'static str` value of a TStr, only usable with the `"const_generics"` feature enabled. 70 | 71 | Added ToUint trait, to convert a TStr to an unsigned integer 72 | 73 | Added for_examples module, with types used in documentation examples. -------------------------------------------------------------------------------- /commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # You can use this script to format and commit the code all at once 5 | # 6 | # 7 | 8 | cargo fmt 9 | 10 | if [ $? -eq 0 ] 11 | then 12 | echo "ran cargo fmt!!!!" 13 | else 14 | exit 1 15 | fi 16 | 17 | 18 | git update-index --again 19 | 20 | git commit -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Rust](https://github.com/rodrimati1992/tstr_crates/workflows/Rust/badge.svg)](https://github.com/rodrimati1992/tstr_crates/actions) 2 | [![crates-io](https://img.shields.io/crates/v/tstr.svg)](https://crates.io/crates/tstr) 3 | [![api-docs](https://docs.rs/tstr/badge.svg)](https://docs.rs/tstr/*) 4 | 5 | This crate provides an encoding of type-level strings as types. 6 | 7 | # Examples 8 | 9 | ### Indexing 10 | 11 | This example demonstrates how you can use type-level strings, 12 | and the [`Index`] trait, to access fields of generic types by name. 13 | 14 | ```rust 15 | use std::ops::Index; 16 | 17 | use tstr::{TS, ts}; 18 | 19 | fn main(){ 20 | takes_person(&Person::new("Bob".into(), "Marley".into())); 21 | 22 | takes_person(&OtherPerson::new("Bob", "Marley")); 23 | } 24 | 25 | fn takes_person

(pers: &P) 26 | where 27 | P: Index + Index 28 | { 29 | assert_eq!(&pers[ts!(name)], "Bob"); 30 | assert_eq!(&pers[ts!(surname)], "Marley"); 31 | } 32 | 33 | 34 | use person::Person; 35 | mod person { 36 | use std::ops::Index; 37 | 38 | use tstr::TS; 39 | 40 | pub struct Person { 41 | name: String, 42 | surname: String, 43 | } 44 | 45 | impl Person { 46 | pub fn new(name: String, surname: String) -> Self { 47 | Self{name, surname} 48 | } 49 | } 50 | 51 | impl Index for Person { 52 | type Output = str; 53 | 54 | fn index(&self, _: TS!(name)) -> &str { 55 | &self.name 56 | } 57 | } 58 | 59 | impl Index for Person { 60 | type Output = str; 61 | 62 | fn index(&self, _: TS!(surname)) -> &str { 63 | &self.surname 64 | } 65 | } 66 | } 67 | 68 | use other_person::OtherPerson; 69 | mod other_person { 70 | use std::ops::Index; 71 | 72 | use tstr::TS; 73 | 74 | pub struct OtherPerson { 75 | name: &'static str, 76 | surname: &'static str, 77 | } 78 | 79 | impl OtherPerson { 80 | pub fn new(name: &'static str, surname: &'static str) -> Self { 81 | Self{name, surname} 82 | } 83 | } 84 | 85 | impl Index for OtherPerson { 86 | type Output = str; 87 | 88 | fn index(&self, _: TS!(name)) -> &str { 89 | self.name 90 | } 91 | } 92 | 93 | impl Index for OtherPerson { 94 | type Output = str; 95 | 96 | fn index(&self, _: TS!(surname)) -> &str { 97 | self.surname 98 | } 99 | } 100 | } 101 | 102 | ``` 103 | 104 | # Macro expansion 105 | 106 | This library reserves the right to change how it represent type-level strings internally 107 | in every single release, and cargo feature combination. 108 | 109 | This only affects you if you expand the code generated by macros from this crate, 110 | and then use that expanded code instead of going through the macros. 111 | 112 | # Cargo features 113 | 114 | - `"rust_1_46"`: 115 | Enables const functions in [`tstr::utils`] for comparing `&str` and `&[u8]`. 116 | 117 | - `"cmp_traits"`: Enables the traits for comparing type-level strings. 118 | 119 | - `"use_syn"`: 120 | Changes how literals passed to the macros of this crate are parsed to use the `syn` crate. 121 | Use this if there is some literal that could not be 122 | parsed but is a valid str/integer literal. 123 | 124 | - `"min_const_generics"`: 125 | changes the representation of type-level strings to use many `char` const parameter, 126 | making for better compiler errors for non-alphanumeric-ascii strings. 127 | Requires Rust 1.51.0. 128 | 129 | - `"const_generics"`: 130 | Changes the representation of type-level strings to use a `&'static str` const parameter, 131 | making for better compiler errors, and a few more features. 132 | As of 2023-03-17, this feature can't be enabled, because it 133 | requires `&'static str` to be stably usable as const parameters. 134 | Consider using `"nightly_const_generics"` if this feature can't be used. 135 | 136 | - `"nightly_const_generics"`: Equivalent to the `"const_generics"` feature, 137 | and enables the nightly compiler features to use `&'static str` const parameters. 138 | 139 | - `"for_examples"`: Enables the `for_examples` module, 140 | with a few types used in documentation examples. 141 | 142 | # No-std support 143 | 144 | This crate is unconditionally `#![no_std]`, and can be used anywhere that Rust can be. 145 | 146 | # Minimum Supported Rust Version 147 | 148 | This crate supports Rust versions back to Rust 1.40.0. 149 | 150 | [`Index`]: https://doc.rust-lang.org/std/ops/trait.Index.html 151 | [`tstr::utils`]: https://docs.rs/tstr/*/tstr/utils/index.html -------------------------------------------------------------------------------- /tstr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tstr" 3 | version = "0.2.4" 4 | authors = ["rodrimati1992 "] 5 | edition = "2018" 6 | license = "Zlib" 7 | description = "Type-level strings" 8 | readme="../readme.md" 9 | documentation = "https://docs.rs/tstr_proc_macros/" 10 | keywords = [] 11 | categories = ["no-std"] 12 | repository = "https://github.com/rodrimati1992/tstr_crates/" 13 | include = [ 14 | "Cargo.toml", 15 | "src/**/*.rs", 16 | "LICENSE-ZLIB.md", 17 | "../readme.md", 18 | ] 19 | 20 | [features] 21 | cmp_traits = [] 22 | rust_1_46 = [] 23 | 24 | min_const_generics = ["tstr_proc_macros/min_const_generics", "rust_1_46"] 25 | 26 | const_generics = ["tstr_proc_macros/const_generics", "min_const_generics"] 27 | nightly_const_generics = ["const_generics"] 28 | 29 | use_syn = ["tstr_proc_macros/syn_"] 30 | for_examples = [] 31 | 32 | testing = ["for_examples"] 33 | 34 | # private features 35 | docsrs = ["for_examples"] 36 | 37 | [dependencies.tstr_proc_macros] 38 | version = "0.2.2" 39 | path = "../tstr_proc_macros" 40 | 41 | [package.metadata.docs.rs] 42 | features = ["docsrs", "nightly_const_generics", "cmp_traits"] 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /tstr/LICENSE-ZLIB.md: -------------------------------------------------------------------------------- 1 | ../LICENSE-ZLIB.md -------------------------------------------------------------------------------- /tstr/src/asserts.rs: -------------------------------------------------------------------------------- 1 | //! Types for asserting properties of type-level strings. 2 | 3 | use core::marker::PhantomData; 4 | 5 | /// For asserting the (in)equality of two type-level strings. 6 | /// 7 | /// # Warning 8 | /// 9 | /// From testing the associated constants from this type, 10 | /// these assertions might not be evaluated in functions that 11 | /// aren't reachable by public functions. 12 | /// 13 | /// # Examples 14 | /// 15 | /// For examples, you can look at each associated constant below. 16 | /// 17 | /// 18 | /// 19 | pub struct Assert(core::marker::PhantomData<(A, B)>); 20 | 21 | #[cfg(feature = "cmp_traits")] 22 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp_traits")))] 23 | impl Assert 24 | where 25 | A: crate::TStrEq, 26 | { 27 | /// Asserts that the `A` and `B` type-level strings compare equal. 28 | pub const EQUAL: EqualityProof = { 29 | ["Expected the type parameters to be equal"][A::NE as usize]; 30 | EqualityProof(PhantomData) 31 | }; 32 | } 33 | 34 | #[cfg(feature = "cmp_traits")] 35 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp_traits")))] 36 | impl Assert 37 | where 38 | A: crate::TStrEq, 39 | { 40 | /// Asserts that the `A` and `B` type-level strings compare not equal. 41 | /// 42 | /// # Example 43 | /// 44 | /// This uses types from the `for_examples` module, 45 | /// which can be seen in the docs with the "for_examples" feature. 46 | /// 47 | /// ```rust 48 | /// use tstr::for_examples::Foo; 49 | /// use tstr::{Assert, ts}; 50 | /// 51 | /// use std::ops::Index; 52 | /// 53 | /// let this = Foo::new(3, 5, "8"); 54 | /// 55 | /// assert_eq!(this.get_two(ts!(bar), ts!(qux), Assert::NOT_EQUAL), (&3, &"8")) 56 | /// 57 | /// ``` 58 | /// 59 | /// The same method call errors when we try to get two references to the same field. 60 | // For some reason it fails to compile with ```rust 61 | // but compiles without errors with ```compile_fail (causing a cargo test failure) 62 | /// ```ignore 63 | /// use tstr::for_examples::Foo; 64 | /// use tstr::{Assert, ts}; 65 | /// use std::ops::Index; 66 | /// 67 | /// # pub fn main() { 68 | /// 69 | /// let this = Foo::new(3, 5, "8"); 70 | /// 71 | /// assert_eq!(this.get_two(ts!(bar), ts!(bar), Assert::NOT_EQUAL), (&3, &3)) 72 | /// 73 | /// # } 74 | /// ``` 75 | /// 76 | /// Truncated error: 77 | /// ```text 78 | /// error[E0080]: erroneous constant used 79 | /// --> src/asserts.rs:55:45 80 | /// | 81 | /// 11 | assert_eq!(this.get_two(ts!(bar), ts!(bar), Assert::NOT_EQUAL), (&3, &3)) 82 | /// | ^^^^^^^^^^^^^^^^^ referenced constant has errors 83 | /// 84 | /// ``` 85 | /// 86 | pub const NOT_EQUAL: InequalityProof = { 87 | ["Expected the type parameters to not be equal"][A::EQ as usize]; 88 | InequalityProof(PhantomData) 89 | }; 90 | } 91 | 92 | macro_rules! declare_assert_res { 93 | ( 94 | $(#[$meta:meta])* 95 | struct $struct:ident<$L:ident, $R:ident>; 96 | )=> { 97 | $(#[$meta])* 98 | pub struct $struct<$L, $R>(PhantomData<($L, $R)>); 99 | 100 | impl<$L, $R> Copy for $struct<$L, $R> {} 101 | 102 | impl<$L, $R> Clone for $struct<$L, $R> { 103 | fn clone(&self) -> Self { 104 | *self 105 | } 106 | } 107 | 108 | impl<$L, $R> $struct<$L, $R> { 109 | /// Infers the type parameters by passing them as arguments. 110 | pub const fn infer(self, _: &$L, _: &$R){} 111 | } 112 | }; 113 | } 114 | 115 | #[cfg(feature = "cmp_traits")] 116 | declare_assert_res! { 117 | /// Value-level proof that the `L` and `R` type-level strings compared equal. 118 | /// 119 | /// Constructed with [`Àssert::EQUAL`] 120 | /// 121 | /// [`Àssert::EQUAL`]: ./struct.Assert.html#associatedconstant.EQUAL 122 | struct EqualityProof; 123 | } 124 | 125 | #[cfg(feature = "cmp_traits")] 126 | declare_assert_res! { 127 | /// Value-level proof that the `L` and `R` type-level strings compared not equal. 128 | /// 129 | /// Constructed with [`Àssert::NOT_EQUAL`] 130 | /// 131 | /// [`Àssert::NOT_EQUAL`]: ./struct.Assert.html#associatedconstant.NOT_EQUAL 132 | struct InequalityProof; 133 | } 134 | -------------------------------------------------------------------------------- /tstr/src/for_examples.rs: -------------------------------------------------------------------------------- 1 | //! Types used by documentation examples. 2 | 3 | use core::ops::{Index, IndexMut}; 4 | 5 | #[cfg(feature = "cmp_traits")] 6 | use crate::{asserts::InequalityProof, TStr, TStrEq}; 7 | 8 | use crate::TS; 9 | 10 | macro_rules! impl_index_indexmut { 11 | ( 12 | impl $impl_params:tt $self:ty { 13 | $($fields:tt)* 14 | } 15 | ) => ( 16 | $( 17 | impl_index_indexmut!{ 18 | @rep 19 | impl $impl_params $self { $fields } 20 | } 21 | )* 22 | 23 | impl_index_indexmut!{ 24 | @inner 25 | impl $impl_params $self 26 | } 27 | 28 | 29 | ); 30 | (@inner impl[$($impl:tt)*] $self:ty ) => { 31 | #[cfg(feature = "cmp_traits")] 32 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp_traits")))] 33 | impl<$($impl)*> $self { 34 | pub fn get_two( 35 | &self, 36 | field_a: TStr, 37 | field_b: TStr, 38 | _proof: InequalityProof, TStr>, 39 | ) -> (&>>::Output, &>>::Output) 40 | where 41 | Self: Index> + Index>, 42 | A: TStrEq, 43 | { 44 | (&self[field_a], &self[field_b]) 45 | } 46 | } 47 | }; 48 | (@rep 49 | impl[$($impl:tt)*] $self:ty { 50 | ($field_name:ident : $type:ty) 51 | } 52 | ) => { 53 | const _: () = { 54 | type $field_name = TS!($field_name); 55 | 56 | impl<$($impl)*> Index<$field_name> for $self { 57 | type Output = $type; 58 | 59 | fn index(&self, _: $field_name) -> &$type { 60 | &self.$field_name 61 | } 62 | } 63 | 64 | impl<$($impl)*> IndexMut<$field_name> for $self { 65 | fn index_mut(&mut self, _: $field_name) -> &mut $type { 66 | &mut self.$field_name 67 | } 68 | } 69 | }; 70 | }; 71 | } 72 | 73 | /// For examples 74 | #[derive(Debug)] 75 | pub struct Foo { 76 | bar: u32, 77 | baz: u64, 78 | qux: &'static str, 79 | } 80 | 81 | impl Foo { 82 | /// A simple contructor 83 | pub fn new(bar: u32, baz: u64, qux: &'static str) -> Self { 84 | Self { bar, baz, qux } 85 | } 86 | } 87 | 88 | impl_index_indexmut! { 89 | impl[] Foo { 90 | (bar: u32) 91 | (baz: u64) 92 | (qux: &'static str) 93 | } 94 | } 95 | 96 | /// For examples 97 | #[derive(Debug)] 98 | pub struct Bar { 99 | bar: u32, 100 | baz: bool, 101 | boom: Option, 102 | } 103 | 104 | impl Bar { 105 | /// A simple contructor 106 | pub fn new(bar: u32, baz: bool, boom: Option) -> Self { 107 | Self { bar, baz, boom } 108 | } 109 | } 110 | 111 | impl_index_indexmut! { 112 | impl[] Bar { 113 | (bar: u32) 114 | (baz: bool) 115 | (boom: Option) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tstr/src/for_tupled_reprs.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod classify; 2 | pub(crate) mod integers; 3 | 4 | pub struct True; 5 | 6 | pub struct False; 7 | 8 | pub struct Less; 9 | 10 | pub struct Equal; 11 | 12 | pub struct Greater; 13 | -------------------------------------------------------------------------------- /tstr/src/for_tupled_reprs/classify.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | integers::{CmpCarry, Number2_5, D0, D1, D2, D3, D4}, 3 | Equal, 4 | }; 5 | 6 | /// Information about what kind of type-level value this is 7 | #[doc(hidden)] 8 | pub trait Classify { 9 | /// Whether this is a unit struct which represents a byte. 10 | type IsByte; 11 | 12 | /// A number used to encode the kind of thing this is 13 | /// 14 | /// It can be any of the `Chars*` or `Tuple*` type aliases. 15 | /// 16 | /// `Chars1` is used for the unit structs which represent bytes. 17 | /// 18 | /// `Chars*` is for the structs parameterized by char const parameters, to emulate char arrays. 19 | /// 20 | /// `Tuple*` is for tuples 21 | type KindNumber; 22 | } 23 | 24 | /// Helper trait which computes whether Self::KindNumber equals Rhs::KindNumber 25 | #[doc(hidden)] 26 | pub trait HasSameKindNumber { 27 | /// This can be either True or False 28 | type DoesIt; 29 | } 30 | 31 | impl HasSameKindNumber for R 32 | where 33 | L: Classify>, 34 | R: Classify>, 35 | LD1: CmpCarry, 36 | LD0: CmpCarry, 37 | { 38 | type DoesIt = LD0::Eq; 39 | } 40 | 41 | pub(crate) type Chars1 = Number2_5; 42 | 43 | #[allow(dead_code)] 44 | pub(crate) type Chars2 = Number2_5; 45 | 46 | #[allow(dead_code)] 47 | pub(crate) type Chars3 = Number2_5; 48 | 49 | #[allow(dead_code)] 50 | pub(crate) type Chars4 = Number2_5; 51 | 52 | #[allow(dead_code)] 53 | pub(crate) type Chars5 = Number2_5; 54 | 55 | #[allow(dead_code)] 56 | pub(crate) type Chars6 = Number2_5; 57 | 58 | #[allow(dead_code)] 59 | pub(crate) type Chars7 = Number2_5; 60 | 61 | #[allow(dead_code)] 62 | pub(crate) type Chars8 = Number2_5; 63 | 64 | pub(crate) type Tuple0 = Number2_5; 65 | pub(crate) type Tuple1 = Number2_5; 66 | pub(crate) type Tuple2 = Number2_5; 67 | pub(crate) type Tuple3 = Number2_5; 68 | pub(crate) type Tuple4 = Number2_5; 69 | pub(crate) type Tuple5 = Number2_5; 70 | pub(crate) type Tuple6 = Number2_5; 71 | pub(crate) type Tuple7 = Number2_5; 72 | pub(crate) type Tuple8 = Number2_5; 73 | -------------------------------------------------------------------------------- /tstr/src/for_tupled_reprs/integers.rs: -------------------------------------------------------------------------------- 1 | use super::{Equal, False, Greater, Less, True}; 2 | 3 | /// A 2 digit, base 5 number 4 | pub struct Number2_5(D1, D0); 5 | 6 | // Digit for 0 in base 5 7 | pub struct D0; 8 | 9 | // Digit for 1 in base 5 10 | pub struct D1; 11 | 12 | // Digit for 2 in base 5 13 | pub struct D2; 14 | 15 | // Digit for 3 in base 5 16 | pub struct D3; 17 | 18 | // Digit for 4 in base 5 19 | pub struct D4; 20 | 21 | /// For comparing digits, while taking into account the previous digit. 22 | pub trait CmpCarry { 23 | /// Whether Self equals R. Either True or False. 24 | type Eq; 25 | /// The ordering of Self compared to R. 26 | /// 27 | /// Represented using the Less, Equal, and Greater unit structs 28 | /// in the for_tupled_reprs module. 29 | type Ord; 30 | } 31 | 32 | impl CmpCarry for L { 33 | type Eq = False; 34 | type Ord = Less; 35 | } 36 | 37 | impl CmpCarry for L { 38 | type Eq = False; 39 | type Ord = Greater; 40 | } 41 | 42 | /* 43 | use std::cmp::{Ord, Ordering}; 44 | 45 | use itertools::iproduct; 46 | 47 | fn main() { 48 | let prod = iproduct!(0..5, 0..5); 49 | { 50 | let mut prod = prod.clone(); 51 | for (i, (a, b)) in prod.by_ref().take(8).enumerate() { 52 | println!("pub(crate) type Chars{} = Number2_5;", i, a, b); 56 | } 57 | } 58 | println!(); 59 | println!(); 60 | { 61 | for (a, b) in prod.clone() { 62 | let (eq, ord) = match a.cmp(&b) { 63 | Ordering::Less => ("False", "Less"), 64 | Ordering::Equal => ("True", "Equal"), 65 | Ordering::Greater => ("False", "Greater"), 66 | }; 67 | 68 | println!("impl CmpCarry for D{a} {{", a = a, b = b); 69 | println!(" type Eq = {};", eq); 70 | println!(" type Ord = {};", ord); 71 | println!("}}"); 72 | } 73 | } 74 | } 75 | */ 76 | 77 | impl CmpCarry for D0 { 78 | type Eq = True; 79 | type Ord = Equal; 80 | } 81 | impl CmpCarry for D0 { 82 | type Eq = False; 83 | type Ord = Less; 84 | } 85 | impl CmpCarry for D0 { 86 | type Eq = False; 87 | type Ord = Less; 88 | } 89 | impl CmpCarry for D0 { 90 | type Eq = False; 91 | type Ord = Less; 92 | } 93 | impl CmpCarry for D0 { 94 | type Eq = False; 95 | type Ord = Less; 96 | } 97 | impl CmpCarry for D1 { 98 | type Eq = False; 99 | type Ord = Greater; 100 | } 101 | impl CmpCarry for D1 { 102 | type Eq = True; 103 | type Ord = Equal; 104 | } 105 | impl CmpCarry for D1 { 106 | type Eq = False; 107 | type Ord = Less; 108 | } 109 | impl CmpCarry for D1 { 110 | type Eq = False; 111 | type Ord = Less; 112 | } 113 | impl CmpCarry for D1 { 114 | type Eq = False; 115 | type Ord = Less; 116 | } 117 | impl CmpCarry for D2 { 118 | type Eq = False; 119 | type Ord = Greater; 120 | } 121 | impl CmpCarry for D2 { 122 | type Eq = False; 123 | type Ord = Greater; 124 | } 125 | impl CmpCarry for D2 { 126 | type Eq = True; 127 | type Ord = Equal; 128 | } 129 | impl CmpCarry for D2 { 130 | type Eq = False; 131 | type Ord = Less; 132 | } 133 | impl CmpCarry for D2 { 134 | type Eq = False; 135 | type Ord = Less; 136 | } 137 | impl CmpCarry for D3 { 138 | type Eq = False; 139 | type Ord = Greater; 140 | } 141 | impl CmpCarry for D3 { 142 | type Eq = False; 143 | type Ord = Greater; 144 | } 145 | impl CmpCarry for D3 { 146 | type Eq = False; 147 | type Ord = Greater; 148 | } 149 | impl CmpCarry for D3 { 150 | type Eq = True; 151 | type Ord = Equal; 152 | } 153 | impl CmpCarry for D3 { 154 | type Eq = False; 155 | type Ord = Less; 156 | } 157 | impl CmpCarry for D4 { 158 | type Eq = False; 159 | type Ord = Greater; 160 | } 161 | impl CmpCarry for D4 { 162 | type Eq = False; 163 | type Ord = Greater; 164 | } 165 | impl CmpCarry for D4 { 166 | type Eq = False; 167 | type Ord = Greater; 168 | } 169 | impl CmpCarry for D4 { 170 | type Eq = False; 171 | type Ord = Greater; 172 | } 173 | impl CmpCarry for D4 { 174 | type Eq = True; 175 | type Ord = Equal; 176 | } 177 | -------------------------------------------------------------------------------- /tstr/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides an encoding of type-level strings as types. 2 | //! 3 | //! # Examples 4 | //! 5 | //! ### Indexing 6 | //! 7 | //! This example demonstrates how you can use type-level strings, 8 | //! and the [`Index`] trait, to access fields of generic types by name. 9 | //! 10 | //! ``` 11 | //! use std::ops::Index; 12 | //! 13 | //! use tstr::{TS, ts}; 14 | //! 15 | //! fn main(){ 16 | //! takes_person(&Person::new("Bob".into(), "Marley".into())); 17 | //! 18 | //! takes_person(&OtherPerson::new("Bob", "Marley")); 19 | //! } 20 | //! 21 | //! fn takes_person

(pers: &P) 22 | //! where 23 | //! P: Index + Index 24 | //! { 25 | //! assert_eq!(&pers[ts!(name)], "Bob"); 26 | //! assert_eq!(&pers[ts!(surname)], "Marley"); 27 | //! } 28 | //! 29 | //! 30 | //! use person::Person; 31 | //! mod person { 32 | //! use std::ops::Index; 33 | //! 34 | //! use tstr::TS; 35 | //! 36 | //! pub struct Person { 37 | //! name: String, 38 | //! surname: String, 39 | //! } 40 | //! 41 | //! impl Person { 42 | //! pub fn new(name: String, surname: String) -> Self { 43 | //! Self{name, surname} 44 | //! } 45 | //! } 46 | //! 47 | //! impl Index for Person { 48 | //! type Output = str; 49 | //! 50 | //! fn index(&self, _: TS!(name)) -> &str { 51 | //! &self.name 52 | //! } 53 | //! } 54 | //! 55 | //! impl Index for Person { 56 | //! type Output = str; 57 | //! 58 | //! fn index(&self, _: TS!(surname)) -> &str { 59 | //! &self.surname 60 | //! } 61 | //! } 62 | //! } 63 | //! 64 | //! use other_person::OtherPerson; 65 | //! mod other_person { 66 | //! use std::ops::Index; 67 | //! 68 | //! use tstr::TS; 69 | //! 70 | //! pub struct OtherPerson { 71 | //! name: &'static str, 72 | //! surname: &'static str, 73 | //! } 74 | //! 75 | //! impl OtherPerson { 76 | //! pub fn new(name: &'static str, surname: &'static str) -> Self { 77 | //! Self{name, surname} 78 | //! } 79 | //! } 80 | //! 81 | //! impl Index for OtherPerson { 82 | //! type Output = str; 83 | //! 84 | //! fn index(&self, _: TS!(name)) -> &str { 85 | //! self.name 86 | //! } 87 | //! } 88 | //! 89 | //! impl Index for OtherPerson { 90 | //! type Output = str; 91 | //! 92 | //! fn index(&self, _: TS!(surname)) -> &str { 93 | //! self.surname 94 | //! } 95 | //! } 96 | //! } 97 | //! 98 | //! ``` 99 | //! 100 | //! # Macro expansion 101 | //! 102 | //! This library reserves the right to change how it represent type-level strings internally 103 | //! in every single release, and cargo feature combination. 104 | //! 105 | //! This only affects you if you expand the code generated by macros from this crate, 106 | //! and then use that expanded code instead of going through the macros. 107 | //! 108 | //! # Cargo features 109 | //! 110 | //! - `"rust_1_46"`: 111 | //! Enables const functions in [`tstr::utils`] for comparing `&str` and `&[u8]`. 112 | //! 113 | //! - `"cmp_traits"`: Enables the traits for comparing type-level strings. 114 | //! 115 | //! - `"use_syn"`: 116 | //! Changes how literals passed to the macros of this crate are parsed to use the `syn` crate. 117 | //! Use this if there is some literal that could not be 118 | //! parsed but is a valid str/integer literal. 119 | //! 120 | //! - `"min_const_generics"`: 121 | //! changes the representation of type-level strings to use many `char` const parameter, 122 | //! making for better compiler errors for non-alphanumeric-ascii strings. 123 | //! Requires Rust 1.51.0. 124 | //! 125 | //! - `"const_generics"`: 126 | //! Changes the representation of type-level strings to use a `&'static str` const parameter, 127 | //! making for better compiler errors, and a few more features. 128 | //! As of 2023-03-17, this feature can't be enabled, because it 129 | //! requires `&'static str` to be stably usable as const parameters. 130 | //! Consider using `"nightly_const_generics"` if this feature can't be used. 131 | //! 132 | //! - `"nightly_const_generics"`: Equivalent to the `"const_generics"` feature, 133 | //! and enables the nightly compiler features to use `&'static str` const parameters.//! 134 | //! 135 | //! - `"for_examples"`: Enables the `for_examples` module, 136 | //! with a few types used in documentation examples. 137 | //! 138 | //! # No-std support 139 | //! 140 | //! This crate is unconditionally `#![no_std]`, and can be used anywhere that Rust can be. 141 | //! 142 | //! # Minimum Supported Rust Version 143 | //! 144 | //! This crate supports Rust versions back to Rust 1.40.0. 145 | //! 146 | //! [`Index`]: https://doc.rust-lang.org/std/ops/trait.Index.html 147 | //! [`tstr::utils`]: ./utils/index.html 148 | #![no_std] 149 | #![cfg_attr(feature = "nightly_const_generics", feature(adt_const_params))] 150 | #![cfg_attr(feature = "docsrs", feature(doc_cfg, doc_auto_cfg))] 151 | #![allow(non_camel_case_types)] 152 | #![cfg_attr(feature = "nightly_const_generics", allow(incomplete_features))] 153 | 154 | #[cfg(feature = "for_examples")] 155 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "for_examples")))] 156 | pub mod for_examples; 157 | 158 | #[cfg(not(feature = "const_generics"))] 159 | #[cfg(feature = "cmp_traits")] 160 | mod for_tupled_reprs; 161 | 162 | pub mod asserts; 163 | 164 | mod macros; 165 | mod make_tstr; 166 | mod to_uint; 167 | mod tstr_type; 168 | 169 | #[cfg(feature = "cmp_traits")] 170 | mod tstr_cmp; 171 | 172 | pub mod utils; 173 | 174 | #[doc(hidden)] 175 | extern crate self as tstr; 176 | 177 | #[doc(hidden)] 178 | pub use tstr_proc_macros::__ts_impl; 179 | 180 | pub use crate::{asserts::Assert, make_tstr::MakeTStr, to_uint::ToUint, tstr_type::TStr}; 181 | 182 | #[cfg(feature = "cmp_traits")] 183 | pub use tstr_cmp::TStrEq; 184 | 185 | #[cfg(all(feature = "cmp_traits", feature = "const_generics"))] 186 | pub use tstr_cmp::TStrOrd; 187 | 188 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "const_generics")))] 189 | #[cfg(feature = "const_generics")] 190 | pub use crate::tstr_type::StrValue; 191 | 192 | include! {"./p.rs"} 193 | -------------------------------------------------------------------------------- /tstr/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod cmp_macros; 3 | 4 | /// The type of a type-level string, always a [`TStr`]. 5 | /// 6 | /// # Arguments 7 | /// 8 | /// You can use any of these as arguments: 9 | /// 10 | /// - String literals (eg: `TS!("hello")`, `TS!(r#"world"#)`) 11 | /// 12 | /// - Integers (eg: `TS!(0)`, `TS!(100)`): 13 | /// converting the integer to decimal, then stringifying it. 14 | /// 15 | /// - Single identifiers (eg: `TS!(foo)`, `TS!(bar)`): stringifying the identifier. 16 | /// 17 | /// - A comma separated list of the other valid arguments to this macro 18 | /// (eg: `TS!(foo, "bar", 0)`), this evaluates to a tuple of `TStr`s. 19 | /// 20 | /// - `concat!(...)`-like syntax: concatenates its arguments, 21 | /// accepting the same arguments as this macro. 22 | /// 23 | /// - `stringify!(...)`-like syntax: stringifies its arguments. 24 | /// 25 | /// # Examples 26 | /// 27 | /// ### ToVariant 28 | /// 29 | /// This example demonstrates how you can use type-level strings to create a 30 | /// `GetVariant` trait which gets the data in a variant if the enum is that variant. 31 | /// 32 | /// ```rust 33 | /// use tstr::TS; 34 | /// 35 | /// fn main(){ 36 | /// let foo = Enum::Foo(3, 5); 37 | /// let bar = Enum::Bar("hello".to_string()); 38 | /// 39 | /// assert_eq!(foo.to_variant(Foo::NEW), Some((3, 5))); 40 | /// assert_eq!(foo.to_variant(Bar::NEW), None); 41 | /// 42 | /// assert_eq!(bar.to_variant(Foo::NEW), None); 43 | /// assert_eq!(bar.to_variant(Bar::NEW), Some("hello".to_string())); 44 | /// } 45 | /// 46 | /// type Foo = TS!(Foo); 47 | /// 48 | /// type Bar = TS!(Bar); 49 | /// 50 | /// trait ToVariant { 51 | /// type Output; 52 | /// 53 | /// fn to_variant(&self, variant: V) -> Option; 54 | /// } 55 | /// 56 | /// enum Enum { 57 | /// Foo(u32, u64), 58 | /// Bar(String), 59 | /// } 60 | /// 61 | /// impl ToVariant for Enum { 62 | /// type Output = (u32, u64); 63 | /// 64 | /// fn to_variant(&self, variant: TS!(Foo)) -> Option { 65 | /// match self { 66 | /// Self::Foo(l, r) => Some((*l, *r)), 67 | /// _ => None, 68 | /// } 69 | /// } 70 | /// } 71 | /// 72 | /// impl ToVariant for Enum { 73 | /// type Output = String; 74 | /// 75 | /// fn to_variant(&self, variant: TS!(Bar)) -> Option { 76 | /// match self { 77 | /// Self::Bar(s) => Some(s.clone()), 78 | /// _ => None, 79 | /// } 80 | /// } 81 | /// } 82 | /// 83 | /// 84 | /// ``` 85 | /// 86 | /// ### Equivalences 87 | /// 88 | /// This example demonstrates which invocations of `TS` produce the same type. 89 | /// 90 | /// ```rust 91 | /// use tstr::TS; 92 | /// 93 | /// type Hello1 = TS!("hello"); 94 | /// type Hello2 = TS!(hello); // This is equivalent to `TS!("hello")` 95 | /// 96 | /// type HundredA = TS!("100"); 97 | /// type HundredB = TS!(100); // equivalent to `TS!("100")` 98 | /// type HundredC = TS!(0x64); // equivalent to `TS!("100")` 99 | /// type HundredD = TS!(0b1100100); // equivalent to `TS!("100")` 100 | /// 101 | /// type Tup = TS!(foo, 1, "bar"); // equivalent to `(TS!(foo), TS!(1), TS!(bar))` 102 | /// 103 | /// // Equivalent to TS!("foo4bar200") 104 | /// type Conc = TS!(concat!(foo, 0b100, "bar", 200)); 105 | /// 106 | /// ``` 107 | /// 108 | /// [`TStr`]: ./struct.TStr.html 109 | #[macro_export] 110 | macro_rules! TS { 111 | ($($expr:expr),* $(,)* ) => { 112 | $crate::__ts_impl!(($crate) $($expr)*) 113 | }; 114 | } 115 | 116 | /// A type-level string [`TStr`] value. 117 | /// 118 | /// # Arguments 119 | /// 120 | /// You can use anything that the [`tstr::TS`] macro accepts 121 | /// 122 | /// # Examples 123 | /// 124 | /// ### Indexing 125 | /// 126 | /// This uses types from the `for_examples` module, 127 | /// which can be seen in the docs with the "for_examples" feature. 128 | /// 129 | /// ```rust 130 | /// use tstr::ts; 131 | /// use tstr::for_examples::{Foo, Bar}; 132 | /// 133 | /// let this = Foo::new(3, 5, "8"); 134 | /// assert_eq!(this[ts!(bar)], 3); 135 | /// assert_eq!(this[ts!(baz)], 5); 136 | /// assert_eq!(this[ts!(qux)], "8"); 137 | /// 138 | /// let other = Bar::new(13, false, Some('C')); 139 | /// assert_eq!(other[ts!(bar)], 13); 140 | /// assert_eq!(other[ts!(baz)], false); 141 | /// assert_eq!(other[ts!(boom)], Some('C')); 142 | /// 143 | /// ``` 144 | /// ### Equivalences 145 | /// 146 | /// This example demonstrates which invocations of `ts` produce the same values. 147 | /// 148 | /// ```rust 149 | /// use tstr::ts; 150 | /// 151 | /// let hello1 = ts!("hello"); 152 | /// let hello2 = ts!(hello); // This is equivalent to `ts!("hello")` 153 | /// 154 | /// let hundreda = ts!("100"); 155 | /// let hundredb = ts!(100); // equivalent to `ts!("100")` 156 | /// let hundredc = ts!(0x64); // equivalent to `ts!("100")` 157 | /// let hundredd = ts!(0b1100100); // equivalent to `ts!("100")` 158 | /// 159 | /// let tup = ts!(foo, 1, "bar"); // equivalent to `(ts!(foo), ts!(1), ts!(bar))` 160 | /// 161 | /// // Equivalent to ts!("foo4bar200") 162 | /// let conc = ts!(concat!(foo, 0b100, "bar", 200)); 163 | /// # const _: tstr::TS!("foo4bar200") = ts!(concat!(foo, 0b100, "bar", 200)); 164 | /// ``` 165 | /// 166 | /// 167 | /// [`TStr`]: ./struct.TStr.html 168 | /// [`tstr::TS`]: ./macro.TS.html#arguments 169 | #[macro_export] 170 | macro_rules! ts { 171 | ($($expr:expr),* $(,)* ) => {{ 172 | let __look_at_the_notes__ = 173 | <$crate::__ts_impl!(($crate) $($expr)*) as $crate::MakeTStr>::MAKE; 174 | __look_at_the_notes__ 175 | }}; 176 | } 177 | 178 | /// Declares `const` and `type` aliases for type-level strings. 179 | /// 180 | /// # String Arguments 181 | /// 182 | /// You can alias either one type-level string, or a tuple of type-level strings 183 | /// 184 | /// ```rust 185 | /// tstr::alias!{ 186 | /// // Aliases the "bar" type-level string as both a const and a type, named Bar. 187 | /// pub Bar = bar; 188 | /// 189 | /// // Aliases the "0" type-level string. 190 | /// pub N0 = 0; 191 | /// 192 | /// // Aliases the ("foo", "baz") tuple of type-level strings, 193 | /// // Equivalent to `TS!(foo, baz)` and `ts!("foo", "baz")` 194 | /// pub Tup = (foo, baz); 195 | /// } 196 | /// 197 | /// # const _: (tstr::TS!(foo), tstr::TS!(baz)) = Tup; 198 | /// ``` 199 | /// 200 | /// # Examples 201 | /// 202 | /// ### Indexing 203 | /// 204 | /// This uses types from the `for_examples` module, 205 | /// which can be seen in the docs with the "for_examples" feature. 206 | /// 207 | /// ```rust 208 | /// use std::ops::Index; 209 | /// 210 | /// use tstr::for_examples::{Foo, Bar}; 211 | /// 212 | /// tstr::alias!{ 213 | /// // Declares both an NBar type alias and an NBar constant of that type. 214 | /// pub NBar = bar; 215 | /// 216 | /// // Declares both an NBaz type alias and an NBaz constant of that type. 217 | /// pub NBaz = "baz"; 218 | /// 219 | /// // Declares both an NQux type alias and an NQux constant of that type. 220 | /// pub NQux = "qux"; 221 | /// 222 | /// } 223 | /// 224 | /// // The macro can also be invoked like this 225 | /// tstr::alias!{ pub NBoom = boom } 226 | /// 227 | /// let this = Foo::new(3, 5, "8"); 228 | /// assert_eq!(get_bar_baz(&this), (3, 5)); 229 | /// 230 | /// let other = Bar::new(13, false, Some('C')); 231 | /// assert_eq!(get_bar_baz(&other), (13, false)); 232 | /// 233 | /// 234 | /// type IndexOut = >::Output; 235 | /// 236 | /// fn get_bar_baz(this: &T) -> (IndexOut, IndexOut) 237 | /// where 238 | /// T: Index + Index, 239 | /// IndexOut: Copy, 240 | /// IndexOut: Copy, 241 | /// { 242 | /// (this[NBar], this[NBaz]) 243 | /// } 244 | /// 245 | /// ``` 246 | /// 247 | #[macro_export] 248 | macro_rules! alias { 249 | ( 250 | $( 251 | $(#[$attr:meta])* 252 | $vis:vis $name:ident = $expr:tt 253 | );* 254 | $(;)? 255 | ) => ( 256 | $( 257 | $crate::__priv_alias!{ 258 | @decide-docs 259 | ( 260 | $(#[$attr])* 261 | $vis, 262 | $name, 263 | ) 264 | [$expr] 265 | } 266 | )* 267 | ); 268 | } 269 | 270 | #[doc(hidden)] 271 | #[macro_export] 272 | macro_rules! __priv_alias { 273 | (@decide-docs 274 | $other:tt 275 | [($($expr:expr),* $(,)*)] 276 | )=>{ 277 | $crate::__priv_alias!{ 278 | @inner 279 | $other 280 | [$($expr),*] 281 | concat!( 282 | "An alias for `(", 283 | $(stringify!($expr), ", ",)* 284 | ")` as a tuple of type level strings.\n\n", 285 | "Generated by the [`::tstr::alias`] macro." 286 | ) 287 | } 288 | }; 289 | (@decide-docs 290 | $other:tt 291 | [$expr:expr] 292 | )=>{ 293 | $crate::__priv_alias!{ 294 | @inner 295 | $other 296 | [$expr] 297 | concat!( 298 | "An alias for `", stringify!($expr), "` as a type level string.\n\n", 299 | "Generated by the [`::tstr::alias`] macro." 300 | ) 301 | } 302 | }; 303 | (@inner 304 | ( 305 | $(#[$attr:meta])* 306 | $vis:vis, $name:ident, 307 | ) 308 | [$($expr:expr),*] 309 | $autodoc:expr 310 | )=>{ 311 | $(#[$attr])* 312 | #[allow(broken_intra_doc_links)] 313 | #[doc = $autodoc] 314 | $vis type $name = $crate::TS!($($expr),*); 315 | 316 | $(#[$attr])* 317 | #[allow(non_upper_case_globals, broken_intra_doc_links)] 318 | #[doc = $autodoc] 319 | $vis const $name: $name = <$name as $crate::MakeTStr>::MAKE; 320 | }; 321 | } 322 | -------------------------------------------------------------------------------- /tstr/src/macros/cmp_macros.rs: -------------------------------------------------------------------------------- 1 | /// For comparing [`TStr`] types for equality, using [`TStrEq::EQ`]. 2 | /// 3 | /// # Example 4 | /// 5 | /// ```rust 6 | /// use tstr::{TS, tstr_eq}; 7 | /// 8 | /// assert!( tstr_eq!(TS!("foo"), TS!("foo"))); 9 | /// assert!(!tstr_eq!(TS!("foo"), TS!("bar"))); 10 | /// 11 | /// type Foo = TS!("foo"); 12 | /// type Bar = TS!("bar"); 13 | /// 14 | /// assert!( tstr_eq!(Foo, Foo)); 15 | /// assert!(!tstr_eq!(Foo, Bar)); 16 | /// ``` 17 | /// 18 | /// [`TStrEq::EQ`]: ./trait.TStrEq.html#associatedconstant.EQ 19 | /// [`TStr`]: ./struct.TStr.html 20 | /// 21 | #[macro_export] 22 | #[cfg(feature = "cmp_traits")] 23 | macro_rules! tstr_eq { 24 | ($left:ty, $right:ty $(,)*) => { 25 | <$left as $crate::TStrEq<$right>>::EQ 26 | }; 27 | } 28 | 29 | /// For comparing [`TStr`] types for inequality, using [`TStrEq::NE`] 30 | /// 31 | /// # Example 32 | /// 33 | /// ```rust 34 | /// use tstr::{TS, tstr_ne}; 35 | /// 36 | /// assert!(!tstr_ne!(TS!("foo"), TS!("foo"))); 37 | /// assert!( tstr_ne!(TS!("foo"), TS!("bar"))); 38 | /// 39 | /// type Foo = TS!("foo"); 40 | /// type Bar = TS!("bar"); 41 | /// 42 | /// assert!(!tstr_ne!(Foo, Foo)); 43 | /// assert!( tstr_ne!(Foo, Bar)); 44 | /// ``` 45 | /// 46 | /// [`TStrEq::NE`]: ./trait.TStrEq.html#associatedconstant.NE 47 | /// [`TStr`]: ./struct.TStr.html 48 | #[macro_export] 49 | #[cfg(feature = "cmp_traits")] 50 | macro_rules! tstr_ne { 51 | ($left:ty, $right:ty $(,)*) => { 52 | <$left as $crate::TStrEq<$right>>::NE 53 | }; 54 | } 55 | 56 | /// For comparing [`TStr`] types for ordering, using [`TStrOrd::CMP`] 57 | /// 58 | /// # Example 59 | /// 60 | /// ```rust 61 | /// use tstr::{TS, tstr_cmp}; 62 | /// 63 | /// use std::cmp::Ordering; 64 | /// 65 | /// type Aaa = TS!(Aaa); 66 | /// type Bbb = TS!(Bbb); 67 | /// type Ccc = TS!(Ccc); 68 | /// 69 | /// assert_eq!(tstr_cmp!(Bbb, Ccc), Ordering::Less); 70 | /// assert_eq!(tstr_cmp!(Bbb, Bbb), Ordering::Equal); 71 | /// assert_eq!(tstr_cmp!(Bbb, Aaa), Ordering::Greater); 72 | /// ``` 73 | /// 74 | /// [`TStrOrd::CMP`]: ./trait.TStrOrd.html#associatedconstant.CMP 75 | /// [`TStr`]: ./struct.TStr.html 76 | #[cfg(feature = "const_generics")] 77 | #[macro_export] 78 | #[cfg(feature = "cmp_traits")] 79 | macro_rules! tstr_cmp { 80 | ($left:ty, $right:ty $(,)*) => { 81 | <$left as $crate::TStrOrd<$right>>::CMP 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /tstr/src/make_tstr.rs: -------------------------------------------------------------------------------- 1 | use crate::TStr; 2 | 3 | /// For constructing [`TStr`]s or collections of them. 4 | /// 5 | /// [`TStr`]: ./struct.TStr.html 6 | pub trait MakeTStr: Copy { 7 | /// Gets a value of this type 8 | const MAKE: Self; 9 | } 10 | 11 | impl MakeTStr for TStr { 12 | const MAKE: Self = TStr::NEW; 13 | } 14 | 15 | macro_rules! tuple_impl { 16 | ($($ty:ident)*) => ( 17 | impl<$($ty),*> MakeTStr for ($($ty,)*) 18 | where 19 | $($ty: MakeTStr,)* 20 | { 21 | const MAKE: Self = ( 22 | $($ty::MAKE,)* 23 | ); 24 | } 25 | ) 26 | } 27 | tuple_impl! {} 28 | tuple_impl! {A } 29 | tuple_impl! {A B} 30 | tuple_impl! {A B C} 31 | tuple_impl! {A B C D} 32 | tuple_impl! {A B C D E} 33 | tuple_impl! {A B C D E F} 34 | tuple_impl! {A B C D E F G} 35 | tuple_impl! {A B C D E F G H} 36 | -------------------------------------------------------------------------------- /tstr/src/p.rs: -------------------------------------------------------------------------------- 1 | // This file defines many types that are `#[doc(hidden)] pub` 2 | // and required not to be used by users. 3 | 4 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 5 | macro_rules! declare_min_const { 6 | ( 7 | [$($chars_structs:ident [$($chars:ident),*],)*] 8 | )=>{ 9 | $( 10 | #[doc(hidden)] 11 | pub struct $chars_structs<$(const $chars: char,)*>; 12 | )* 13 | } 14 | } 15 | 16 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 17 | declare_min_const!{ 18 | [ 19 | __a[A], 20 | __b[A,B], 21 | __c[A,B,C], 22 | __d[A,B,C,D], 23 | __e[A,B,C,D,E], 24 | __f[A,B,C,D,E,F], 25 | __g[A,B,C,D,E,F,G], 26 | __[A,B,C,D,E,F,G,H], 27 | ] 28 | } 29 | 30 | 31 | // macros can contain arbitrary syntax, 32 | // which allows this to be defined in this file even if Rust stops parsing `const IDENT:Foo` 33 | #[cfg(feature = "const_generics")] 34 | macro_rules! declare_const_items { 35 | () => { 36 | // `TStr` takes this as a type parameter so that 37 | // this library can start using const generics in the future by replacing the 38 | // `T:?Sized` parameter with `const STR:&'static str`. 39 | #[doc(hidden)] 40 | pub struct ___; 41 | 42 | impl Copy for ___ {} 43 | impl Clone for ___ { 44 | fn clone(&self) -> Self { 45 | ___ 46 | } 47 | } 48 | }; 49 | } 50 | 51 | #[cfg(feature = "const_generics")] 52 | declare_const_items! {} 53 | 54 | /////////////////////////////////////////////////////////////////////////////// 55 | // 56 | // Type Level Characters 57 | // 58 | /////////////////////////////////////////////////////////////////////////////// 59 | 60 | /* 61 | Type-level ascii characters and bytes. 62 | 63 | */ 64 | 65 | /* 66 | 67 | This is code used to generate the macro invocation. 68 | 69 | fn main() { 70 | let mut list=(0..=255u8) 71 | .map(|b|{ 72 | let c=b as char; 73 | if (c.is_alphanumeric() || c=='_') && b<128 { 74 | let for_und = if c=='_' { "_" } else { "" }; 75 | format!("(__{1}{2} = {0},__0x{0:02X}),", b, b as char, for_und) 76 | }else{ 77 | format!("(__0x{0:02X} = {0}),",b) 78 | } 79 | }) 80 | .collect::>(); 81 | for chunk in list.chunks(4) { 82 | for param in chunk { 83 | print!("{}",param); 84 | } 85 | println!(); 86 | } 87 | } 88 | 89 | 90 | */ 91 | 92 | #[cfg(not(feature = "min_const_generics"))] 93 | macro_rules! create_unit_struct { 94 | ($( ($struct_:ident = $value:literal $(,$alias:ident)? ) ),* $(,)*) => { 95 | $( 96 | #[doc(hidden)] 97 | pub struct $struct_; 98 | 99 | #[cfg(feature = "cmp_traits")] 100 | impl crate::tstr_cmp::U8Repr for $struct_ { 101 | const REPR: u8 = $value; 102 | } 103 | 104 | #[cfg(feature = "cmp_traits")] 105 | impl crate::for_tupled_reprs::classify::Classify for $struct_{ 106 | type IsByte = crate::for_tupled_reprs::True; 107 | type KindNumber = crate::for_tupled_reprs::classify::Chars1; 108 | } 109 | 110 | $( 111 | #[doc(hidden)] 112 | pub type $alias=$struct_; 113 | )? 114 | )* 115 | } 116 | } 117 | 118 | #[cfg(not(feature = "min_const_generics"))] 119 | create_unit_struct! { 120 | (__0x00 = 0),(__0x01 = 1),(__0x02 = 2),(__0x03 = 3), 121 | (__0x04 = 4),(__0x05 = 5),(__0x06 = 6),(__0x07 = 7), 122 | (__0x08 = 8),(__0x09 = 9),(__0x0A = 10),(__0x0B = 11), 123 | (__0x0C = 12),(__0x0D = 13),(__0x0E = 14),(__0x0F = 15), 124 | (__0x10 = 16),(__0x11 = 17),(__0x12 = 18),(__0x13 = 19), 125 | (__0x14 = 20),(__0x15 = 21),(__0x16 = 22),(__0x17 = 23), 126 | (__0x18 = 24),(__0x19 = 25),(__0x1A = 26),(__0x1B = 27), 127 | (__0x1C = 28),(__0x1D = 29),(__0x1E = 30),(__0x1F = 31), 128 | (__0x20 = 32),(__0x21 = 33),(__0x22 = 34),(__0x23 = 35), 129 | (__0x24 = 36),(__0x25 = 37),(__0x26 = 38),(__0x27 = 39), 130 | (__0x28 = 40),(__0x29 = 41),(__0x2A = 42),(__0x2B = 43), 131 | (__0x2C = 44),(__0x2D = 45),(__0x2E = 46),(__0x2F = 47), 132 | (__0 = 48,__0x30),(__1 = 49,__0x31),(__2 = 50,__0x32),(__3 = 51,__0x33), 133 | (__4 = 52,__0x34),(__5 = 53,__0x35),(__6 = 54,__0x36),(__7 = 55,__0x37), 134 | (__8 = 56,__0x38),(__9 = 57,__0x39),(__0x3A = 58),(__0x3B = 59), 135 | (__0x3C = 60),(__0x3D = 61),(__0x3E = 62),(__0x3F = 63), 136 | (__0x40 = 64),(__A = 65,__0x41),(__B = 66,__0x42),(__C = 67,__0x43), 137 | (__D = 68,__0x44),(__E = 69,__0x45),(__F = 70,__0x46),(__G = 71,__0x47), 138 | (__H = 72,__0x48),(__I = 73,__0x49),(__J = 74,__0x4A),(__K = 75,__0x4B), 139 | (__L = 76,__0x4C),(__M = 77,__0x4D),(__N = 78,__0x4E),(__O = 79,__0x4F), 140 | (__P = 80,__0x50),(__Q = 81,__0x51),(__R = 82,__0x52),(__S = 83,__0x53), 141 | (__T = 84,__0x54),(__U = 85,__0x55),(__V = 86,__0x56),(__W = 87,__0x57), 142 | (__X = 88,__0x58),(__Y = 89,__0x59),(__Z = 90,__0x5A),(__0x5B = 91), 143 | (__0x5C = 92),(__0x5D = 93),(__0x5E = 94),(____ = 95,__0x5F), 144 | (__0x60 = 96),(__a = 97,__0x61),(__b = 98,__0x62),(__c = 99,__0x63), 145 | (__d = 100,__0x64),(__e = 101,__0x65),(__f = 102,__0x66),(__g = 103,__0x67), 146 | (__h = 104,__0x68),(__i = 105,__0x69),(__j = 106,__0x6A),(__k = 107,__0x6B), 147 | (__l = 108,__0x6C),(__m = 109,__0x6D),(__n = 110,__0x6E),(__o = 111,__0x6F), 148 | (__p = 112,__0x70),(__q = 113,__0x71),(__r = 114,__0x72),(__s = 115,__0x73), 149 | (__t = 116,__0x74),(__u = 117,__0x75),(__v = 118,__0x76),(__w = 119,__0x77), 150 | (__x = 120,__0x78),(__y = 121,__0x79),(__z = 122,__0x7A),(__0x7B = 123), 151 | (__0x7C = 124),(__0x7D = 125),(__0x7E = 126),(__0x7F = 127), 152 | (__0x80 = 128),(__0x81 = 129),(__0x82 = 130),(__0x83 = 131), 153 | (__0x84 = 132),(__0x85 = 133),(__0x86 = 134),(__0x87 = 135), 154 | (__0x88 = 136),(__0x89 = 137),(__0x8A = 138),(__0x8B = 139), 155 | (__0x8C = 140),(__0x8D = 141),(__0x8E = 142),(__0x8F = 143), 156 | (__0x90 = 144),(__0x91 = 145),(__0x92 = 146),(__0x93 = 147), 157 | (__0x94 = 148),(__0x95 = 149),(__0x96 = 150),(__0x97 = 151), 158 | (__0x98 = 152),(__0x99 = 153),(__0x9A = 154),(__0x9B = 155), 159 | (__0x9C = 156),(__0x9D = 157),(__0x9E = 158),(__0x9F = 159), 160 | (__0xA0 = 160),(__0xA1 = 161),(__0xA2 = 162),(__0xA3 = 163), 161 | (__0xA4 = 164),(__0xA5 = 165),(__0xA6 = 166),(__0xA7 = 167), 162 | (__0xA8 = 168),(__0xA9 = 169),(__0xAA = 170),(__0xAB = 171), 163 | (__0xAC = 172),(__0xAD = 173),(__0xAE = 174),(__0xAF = 175), 164 | (__0xB0 = 176),(__0xB1 = 177),(__0xB2 = 178),(__0xB3 = 179), 165 | (__0xB4 = 180),(__0xB5 = 181),(__0xB6 = 182),(__0xB7 = 183), 166 | (__0xB8 = 184),(__0xB9 = 185),(__0xBA = 186),(__0xBB = 187), 167 | (__0xBC = 188),(__0xBD = 189),(__0xBE = 190),(__0xBF = 191), 168 | (__0xC0 = 192),(__0xC1 = 193),(__0xC2 = 194),(__0xC3 = 195), 169 | (__0xC4 = 196),(__0xC5 = 197),(__0xC6 = 198),(__0xC7 = 199), 170 | (__0xC8 = 200),(__0xC9 = 201),(__0xCA = 202),(__0xCB = 203), 171 | (__0xCC = 204),(__0xCD = 205),(__0xCE = 206),(__0xCF = 207), 172 | (__0xD0 = 208),(__0xD1 = 209),(__0xD2 = 210),(__0xD3 = 211), 173 | (__0xD4 = 212),(__0xD5 = 213),(__0xD6 = 214),(__0xD7 = 215), 174 | (__0xD8 = 216),(__0xD9 = 217),(__0xDA = 218),(__0xDB = 219), 175 | (__0xDC = 220),(__0xDD = 221),(__0xDE = 222),(__0xDF = 223), 176 | (__0xE0 = 224),(__0xE1 = 225),(__0xE2 = 226),(__0xE3 = 227), 177 | (__0xE4 = 228),(__0xE5 = 229),(__0xE6 = 230),(__0xE7 = 231), 178 | (__0xE8 = 232),(__0xE9 = 233),(__0xEA = 234),(__0xEB = 235), 179 | (__0xEC = 236),(__0xED = 237),(__0xEE = 238),(__0xEF = 239), 180 | (__0xF0 = 240),(__0xF1 = 241),(__0xF2 = 242),(__0xF3 = 243), 181 | (__0xF4 = 244),(__0xF5 = 245),(__0xF6 = 246),(__0xF7 = 247), 182 | (__0xF8 = 248),(__0xF9 = 249),(__0xFA = 250),(__0xFB = 251), 183 | (__0xFC = 252),(__0xFD = 253),(__0xFE = 254),(__0xFF = 255), 184 | } 185 | 186 | /////////////////////////////////////////////////////////////////////////////// 187 | -------------------------------------------------------------------------------- /tstr/src/to_uint.rs: -------------------------------------------------------------------------------- 1 | mod sealed { 2 | #[doc(hidden)] 3 | pub trait Sealed: Sized {} 4 | } 5 | use sealed::Sealed; 6 | 7 | /// Converts a [`TStr`] to unsigned integers. 8 | /// 9 | /// # Example 10 | /// 11 | /// ```rust 12 | /// use tstr::{ToUint, TS, ts}; 13 | /// 14 | /// type Zero = TS!(0); 15 | /// type N8 = TS!(8); 16 | /// type N13 = TS!(13); 17 | /// type N15 = TS!(0xF); 18 | /// type N16 = TS!(0b10000); 19 | /// 20 | /// assert_eq!(Zero::USIZE, 0); 21 | /// assert_eq!(N8::USIZE, 8); 22 | /// assert_eq!(N13::USIZE, 13); 23 | /// assert_eq!(N15::USIZE, 15); 24 | /// assert_eq!(N16::USIZE, 16); 25 | /// 26 | /// assert_eq!(ts!(0).to_u128(), 0); 27 | /// assert_eq!(ts!(8).to_u128(), 8); 28 | /// assert_eq!(ts!(13).to_u128(), 13); 29 | /// assert_eq!(ts!(0xF).to_u128(), 15); 30 | /// assert_eq!(ts!(0b10000).to_u128(), 16); 31 | /// 32 | /// ``` 33 | /// 34 | /// [`TStr`]: ./struct.TStr.html 35 | pub trait ToUint: Sized { 36 | /// The `usize` value of the type. 37 | /// 38 | /// By default this value is a saturated cast from `Self::U128`. 39 | const USIZE: usize = u128_as_usize(Self::U128); 40 | 41 | /// The `u128` value of the type. 42 | const U128: u128; 43 | 44 | #[doc(hidden)] 45 | const DIGITS: u32; 46 | 47 | /// Gets the usize value of this type 48 | /// 49 | /// By default this value is a saturated cast from `Self::U128`. 50 | fn to_usize(&self) -> usize { 51 | Self::USIZE 52 | } 53 | 54 | /// Gets the u128 value of this type 55 | fn to_u128(&self) -> u128 { 56 | Self::U128 57 | } 58 | } 59 | 60 | #[cfg(feature = "const_generics")] 61 | macro_rules! impl_for_const { 62 | () => { 63 | const fn str_to_u128(s: &str) -> u128 { 64 | let s = s.as_bytes(); 65 | let mut out = 0u128; 66 | let mut index = 0usize; 67 | 68 | while index < s.len() { 69 | let digit = s[index]; 70 | 71 | // This has the effect of panicking on non to '0' to '9' characters. 72 | #[allow(clippy::no_effect)] 73 | ["Expected all characters to be digits"] 74 | [!(b'0' <= digit && digit <= b'9') as usize]; 75 | 76 | let digit = (digit - b'0') as u128; 77 | out = out * 10 + digit; 78 | 79 | index += 1; 80 | } 81 | out 82 | } 83 | 84 | impl Sealed for crate::___ {} 85 | 86 | impl ToUint for crate::___ { 87 | const U128: u128 = str_to_u128(N); 88 | const DIGITS: u32 = N.len() as u32; 89 | } 90 | }; 91 | } 92 | 93 | impl Sealed for crate::TStr where T: Sealed {} 94 | 95 | impl ToUint for crate::TStr 96 | where 97 | T: ToUint, 98 | { 99 | // Intentionally not setting this. 100 | // const USIZE: usize = T::USIZE; 101 | const U128: u128 = T::U128; 102 | const DIGITS: u32 = T::DIGITS; 103 | } 104 | 105 | #[cfg(feature = "const_generics")] 106 | impl_for_const! {} 107 | 108 | #[cfg(not(feature = "const_generics"))] 109 | mod impl_no_const_generics; 110 | 111 | const fn u128_as_usize(n: u128) -> usize { 112 | const MAXU: u128 = usize::max_value() as u128; 113 | [n, MAXU][(n > MAXU) as usize] as usize 114 | } 115 | -------------------------------------------------------------------------------- /tstr/src/to_uint/impl_no_const_generics.rs: -------------------------------------------------------------------------------- 1 | use super::{Sealed, ToUint}; 2 | 3 | #[cfg(not(feature = "min_const_generics"))] 4 | macro_rules! impl_to_digit { 5 | ($($ty:ident = $val:tt,)*) => ( 6 | $( 7 | impl Sealed for crate::$ty {} 8 | 9 | impl ToUint for crate::$ty { 10 | const U128: u128 = $val; 11 | const USIZE: usize = $val; 12 | const DIGITS: u32 = 1; 13 | } 14 | )* 15 | ) 16 | } 17 | 18 | #[cfg(not(feature = "min_const_generics"))] 19 | impl_to_digit! { 20 | __0 = 0, 21 | __1 = 1, 22 | __2 = 2, 23 | __3 = 3, 24 | __4 = 4, 25 | __5 = 5, 26 | __6 = 6, 27 | __7 = 7, 28 | __8 = 8, 29 | __9 = 9, 30 | } 31 | 32 | #[cfg(feature = "min_const_generics")] 33 | macro_rules! impl_to_digits_const { 34 | ( 35 | [$($digit:literal => $value:literal,)*] 36 | [ 37 | $( ($chars_structs:ident, [$($chars:ident),*], $len:expr) ,)* 38 | ] 39 | )=>{ 40 | use crate::__a; 41 | 42 | $( 43 | impl Sealed for __a<$digit> {} 44 | 45 | impl ToUint for __a<$digit> { 46 | const USIZE: usize = $value; 47 | const U128: u128 = $value; 48 | const DIGITS: u32 = 1; 49 | } 50 | )* 51 | 52 | $( 53 | impl<$(const $chars: char,)*> Sealed for crate::$chars_structs<$($chars,)*> {} 54 | 55 | impl<$(const $chars: char,)*> ToUint for crate::$chars_structs<$($chars,)*> 56 | where 57 | $(__a<$chars>: ToUint,)* 58 | { 59 | const U128: u128 = { 60 | #[allow(unused_mut)] 61 | let mut sum = 0u128; 62 | $( 63 | sum = __a::<$chars>::U128 + sum * ten_pow(__a::<$chars>::DIGITS); 64 | )* 65 | sum 66 | }; 67 | const DIGITS: u32 = $len; 68 | } 69 | )* 70 | } 71 | } 72 | 73 | #[cfg(feature = "min_const_generics")] 74 | impl_to_digits_const! { 75 | [ 76 | '0' => 0, 77 | '1' => 1, 78 | '2' => 2, 79 | '3' => 3, 80 | '4' => 4, 81 | '5' => 5, 82 | '6' => 6, 83 | '7' => 7, 84 | '8' => 8, 85 | '9' => 9, 86 | ] 87 | [ 88 | (__b, [A,B], 2), 89 | (__c, [A,B,C], 3), 90 | (__d, [A,B,C,D], 4), 91 | (__e, [A,B,C,D,E], 5), 92 | (__f, [A,B,C,D,E,F], 6), 93 | (__g, [A,B,C,D,E,F,G], 7), 94 | (__ , [A,B,C,D,E,F,G,H], 8), 95 | ] 96 | } 97 | 98 | /* 99 | fn main(){ 100 | let mut accum = 1u128; 101 | println!(" {},", accum); 102 | while let Some(next) = accum.checked_mul(10) { 103 | println!(" {},", next); 104 | accum = next; 105 | } 106 | } 107 | */ 108 | const POW_TEN: &[u128; 39] = &[ 109 | 1, 110 | 10, 111 | 100, 112 | 1000, 113 | 10000, 114 | 100000, 115 | 1000000, 116 | 10000000, 117 | 100000000, 118 | 1000000000, 119 | 10000000000, 120 | 100000000000, 121 | 1000000000000, 122 | 10000000000000, 123 | 100000000000000, 124 | 1000000000000000, 125 | 10000000000000000, 126 | 100000000000000000, 127 | 1000000000000000000, 128 | 10000000000000000000, 129 | 100000000000000000000, 130 | 1000000000000000000000, 131 | 10000000000000000000000, 132 | 100000000000000000000000, 133 | 1000000000000000000000000, 134 | 10000000000000000000000000, 135 | 100000000000000000000000000, 136 | 1000000000000000000000000000, 137 | 10000000000000000000000000000, 138 | 100000000000000000000000000000, 139 | 1000000000000000000000000000000, 140 | 10000000000000000000000000000000, 141 | 100000000000000000000000000000000, 142 | 1000000000000000000000000000000000, 143 | 10000000000000000000000000000000000, 144 | 100000000000000000000000000000000000, 145 | 1000000000000000000000000000000000000, 146 | 10000000000000000000000000000000000000, 147 | 100000000000000000000000000000000000000, 148 | ]; 149 | 150 | const fn ten_pow(power: u32) -> u128 { 151 | POW_TEN[power as usize] 152 | } 153 | 154 | macro_rules! tuple_impl { 155 | ($($ty:ident)*) => ( 156 | impl<$($ty,)*> Sealed for ($($ty,)*) 157 | where 158 | $($ty: Sealed,)* 159 | {} 160 | 161 | #[doc(hidden)] 162 | impl<$($ty,)*> ToUint for ($($ty,)*) 163 | where 164 | $($ty: ToUint,)* 165 | { 166 | const U128: u128 = { 167 | #[allow(unused_mut)] 168 | let mut sum = 0u128; 169 | $( 170 | sum = $ty::U128 + sum * ten_pow($ty::DIGITS); 171 | )* 172 | sum 173 | }; 174 | const DIGITS: u32 = 0 $( + $ty::DIGITS )*; 175 | } 176 | ) 177 | } 178 | 179 | tuple_impl! {} 180 | tuple_impl! {A } 181 | tuple_impl! {A B} 182 | tuple_impl! {A B C} 183 | tuple_impl! {A B C D} 184 | tuple_impl! {A B C D E} 185 | tuple_impl! {A B C D E F} 186 | tuple_impl! {A B C D E F G} 187 | tuple_impl! {A B C D E F G H} 188 | -------------------------------------------------------------------------------- /tstr/src/tstr_cmp.rs: -------------------------------------------------------------------------------- 1 | use crate::TStr; 2 | 3 | #[cfg(not(feature = "const_generics"))] 4 | mod impl_no_const_generics; 5 | 6 | /// For equality comparison between type-level strings. 7 | /// 8 | /// # Examples 9 | /// 10 | /// # Basic 11 | /// 12 | /// ```rust 13 | /// use tstr::{TS, TStrEq, tstr_eq, tstr_ne, ts}; 14 | /// 15 | /// use std::cmp::Ordering; 16 | /// 17 | /// // tstr_eq uses this trait for comparing its TStr arguments for equality 18 | /// assert!(tstr_eq!(TS!("foo"), TS!("foo"))); 19 | /// // what tstr_eq expands into 20 | /// assert!(>::EQ); 21 | /// 22 | /// // tstr_ne uses this trait for comparing its TStr arguments for inequality 23 | /// assert!(tstr_ne!(TS!("foo"), TS!("bar"))); 24 | /// // what tstr_ne expands into 25 | /// assert!(>::NE); 26 | /// 27 | /// // You can also compare TStrs using the `tstr_eq` and `tstr_ne` methods. 28 | /// assert!(ts!("foo").tstr_eq(&ts!("foo"))); 29 | /// 30 | /// assert!(ts!("foo").tstr_ne(&ts!("bar"))); 31 | /// 32 | /// ``` 33 | /// 34 | /// 35 | /// # Advanced 36 | /// 37 | /// This uses types from the `for_examples` module, 38 | /// which can be seen in the docs with the "for_examples" feature. 39 | /// 40 | /// ```rust 41 | /// use tstr::for_examples::Foo; 42 | /// use tstr::{TStr, TStrEq, TS, ts}; 43 | /// 44 | /// use std::ops::Index; 45 | /// 46 | /// let this = Foo::new(3, 5, "8"); 47 | /// assert_eq!(get_two(&this, ts!(bar), ts!(qux)), (&3, &"8")); 48 | /// 49 | /// // Doesn't compile 50 | /// // assert_eq!(get_two(&this, ts!(bar), ts!(bar)), (&3, &3)); 51 | /// 52 | /// fn get_two( 53 | /// this: &T, 54 | /// field_a: TStr, 55 | /// field_b: TStr, 56 | /// ) -> (&IndexOut>, &IndexOut>) 57 | /// where 58 | /// T: Index> + Index>, 59 | /// A: TStrEq, 60 | /// { 61 | /// tstr::Assert::::NOT_EQUAL; 62 | /// (&this[field_a], &this[field_b]) 63 | /// } 64 | /// 65 | /// 66 | /// type IndexOut = >::Output; 67 | /// 68 | /// ``` 69 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp_traits")))] 70 | pub trait TStrEq: Sized { 71 | /// Whether `Self` equals `Rhs` 72 | const EQ: bool; 73 | 74 | /// Whether `Self` is not equal to `Rhs` 75 | const NE: bool = !Self::EQ; 76 | 77 | /// Returns whether `self` is equal to `other`. 78 | #[inline(always)] 79 | fn tstr_eq(&self, _other: &Rhs) -> bool { 80 | Self::EQ 81 | } 82 | 83 | /// Returns whether `self` is not equal to `other`. 84 | #[inline(always)] 85 | fn tstr_ne(&self, _other: &Rhs) -> bool { 86 | Self::NE 87 | } 88 | } 89 | 90 | #[cfg(not(feature = "min_const_generics"))] 91 | pub trait U8Repr { 92 | const REPR: u8; 93 | } 94 | 95 | impl TStrEq> for TStr 96 | where 97 | T: TStrEq, 98 | { 99 | const EQ: bool = T::EQ; 100 | } 101 | 102 | /// For comparison between two type-level strings, 103 | /// getting the `Ordering` of `Self` relative to `Rhs`. 104 | /// 105 | /// This is only available with the `"const_generics"` feature, 106 | /// since it's only implemented for the `&'static str`-const-parameter-based representation. 107 | /// 108 | /// # Example 109 | /// 110 | /// ```rust 111 | /// use tstr::{TS, TStrOrd, tstr_cmp, ts}; 112 | /// 113 | /// use std::cmp::Ordering; 114 | /// 115 | /// const FOO_CMP_FOO: Ordering = { 116 | /// // tstr_cmp uses this trait for comparing its TStr arguments for ordering 117 | /// tstr_cmp!(TS!("foo"), TS!("foo")) 118 | /// }; 119 | /// assert_eq!(FOO_CMP_FOO, Ordering::Equal); 120 | /// 121 | /// const FOO_CMP_FOOOOO: Ordering = { 122 | /// // what tstr_cmp expands into 123 | /// >::CMP 124 | /// }; 125 | /// assert_eq!(FOO_CMP_FOOOOO, Ordering::Less); 126 | /// 127 | /// // You can also compare TStrs using the `tstr_cmp` method. 128 | /// assert_eq!(ts!("foo").tstr_cmp(&ts!("bar")), Ordering::Greater); 129 | /// 130 | /// // short strings can be greater than longer strings 131 | /// assert_eq!(ts!("foo").tstr_cmp(&ts!("aaaaaa")), Ordering::Greater); 132 | /// 133 | /// ``` 134 | /// 135 | #[cfg(feature = "const_generics")] 136 | #[cfg_attr( 137 | feature = "docsrs", 138 | doc(cfg(all(feature = "const_generics", feature = "cmp_traits"))) 139 | )] 140 | pub trait TStrOrd: Sized { 141 | /// The `Ordering` of `Self` relative to `Rhs`. 142 | const CMP: core::cmp::Ordering; 143 | 144 | /// Compares `self` and `other` for ordering. 145 | #[inline(always)] 146 | fn tstr_cmp(&self, _other: &Rhs) -> core::cmp::Ordering { 147 | Self::CMP 148 | } 149 | } 150 | 151 | #[cfg(feature = "const_generics")] 152 | impl TStrOrd> for TStr 153 | where 154 | T: TStrOrd, 155 | { 156 | const CMP: core::cmp::Ordering = T::CMP; 157 | } 158 | 159 | #[cfg(feature = "const_generics")] 160 | macro_rules! impl_const_generics { 161 | () => { 162 | impl TStrEq> for crate::___ { 163 | const EQ: bool = crate::utils::str_eq(S, Z); 164 | } 165 | 166 | impl TStrOrd> 167 | for crate::___ 168 | { 169 | const CMP: core::cmp::Ordering = crate::utils::str_cmp(S, Z); 170 | } 171 | }; 172 | } 173 | 174 | #[cfg(feature = "const_generics")] 175 | impl_const_generics! {} 176 | -------------------------------------------------------------------------------- /tstr/src/tstr_cmp/impl_no_const_generics.rs: -------------------------------------------------------------------------------- 1 | use crate::for_tupled_reprs::{ 2 | classify::{ 3 | Classify, HasSameKindNumber, Tuple0, Tuple1, Tuple2, Tuple3, Tuple4, Tuple5, Tuple6, 4 | Tuple7, Tuple8, 5 | }, 6 | False, True, 7 | }; 8 | 9 | #[allow(unused_imports)] 10 | use crate::for_tupled_reprs::classify::{ 11 | Chars1, Chars2, Chars3, Chars4, Chars5, Chars6, Chars7, Chars8, 12 | }; 13 | 14 | #[cfg(not(feature = "min_const_generics"))] 15 | use super::U8Repr; 16 | 17 | use super::TStrEq; 18 | 19 | pub trait PrivTStrEq { 20 | const EQ: bool; 21 | } 22 | impl PrivTStrEq for L { 23 | const EQ: bool = false; 24 | } 25 | 26 | pub trait TStrEqTupInner { 27 | const EQ_INNER: bool; 28 | } 29 | impl TStrEqTupInner for L { 30 | const EQ_INNER: bool = false; 31 | } 32 | impl TStrEqTupInner for L { 33 | const EQ_INNER: bool = false; 34 | } 35 | 36 | #[cfg(feature = "min_const_generics")] 37 | macro_rules! char_array_impls { 38 | ( 39 | [$fch:ident $($ch:ident)*], 40 | [$ofch:ident $($och:ident)*], 41 | $chars_structs:ident, 42 | $char_kind_num:ident 43 | )=>{ 44 | impl 45 | Classify 46 | for crate::$chars_structs<$fch, $($ch,)*> 47 | { 48 | type IsByte = False; 49 | type KindNumber = $char_kind_num; 50 | } 51 | 52 | impl 53 | TStrEq 54 | for crate::$chars_structs<$fch, $($ch,)*> 55 | where 56 | Self: HasSameKindNumber, 57 | Self: PrivTStrEq, 58 | { 59 | const EQ: bool = >::EQ; 60 | } 61 | 62 | impl 63 | PrivTStrEq, True> 64 | for crate::$chars_structs<$fch, $($ch,)*> 65 | { 66 | const EQ: bool = { 67 | $fch as u32 == $ofch as u32 68 | $( && $ch as u32 == $och as u32 )* 69 | }; 70 | } 71 | } 72 | } 73 | 74 | macro_rules! tuple_impl { 75 | ( 76 | [$fty:ident $($ty:ident)*], 77 | [$ofty:ident $($oty:ident)*], 78 | $tup_kind_num:ident, 79 | $char_arr_struct:ident, 80 | $char_kind_num:ident 81 | ) => ( 82 | #[cfg(feature = "min_const_generics")] 83 | char_array_impls!{ 84 | [$fty $($ty)*], 85 | [$ofty $($oty)*], 86 | $char_arr_struct, 87 | $char_kind_num 88 | } 89 | 90 | impl<$fty, $($ty,)*> Classify for ($fty, $($ty,)*) { 91 | type IsByte = False; 92 | type KindNumber = $tup_kind_num; 93 | } 94 | 95 | impl<$fty, $($ty,)* DI, Rhs> TStrEq for ($fty, $($ty,)*) 96 | where 97 | Self: HasSameKindNumber, 98 | Self: PrivTStrEq, 99 | { 100 | const EQ: bool = >::EQ; 101 | } 102 | 103 | impl<$fty, $($ty,)* $ofty, $($oty,)*> 104 | PrivTStrEq<($ofty, $($oty,)*), True> 105 | for ($fty, $($ty,)*) 106 | where 107 | $fty: Classify, 108 | $ofty: Classify, 109 | Self: TStrEqTupInner<($ofty, $($oty,)*), $fty::IsByte, $ofty::IsByte> 110 | { 111 | const EQ: bool = Self::EQ_INNER; 112 | } 113 | 114 | #[cfg(not(feature = "min_const_generics"))] 115 | impl<$fty, $($ty,)* $ofty, $($oty,)*> 116 | TStrEqTupInner<($ofty, $($oty,)*), True, True> 117 | for ($fty, $($ty,)*) 118 | where 119 | $fty: U8Repr, 120 | $ofty: U8Repr, 121 | $($ty: U8Repr,)* 122 | $($oty: U8Repr,)* 123 | { 124 | const EQ_INNER: bool = { 125 | $fty::REPR == $ofty::REPR 126 | $( && $ty::REPR == $oty::REPR )* 127 | }; 128 | } 129 | 130 | impl<$fty, $($ty,)* $ofty, $($oty,)*> 131 | TStrEqTupInner<($ofty, $($oty,)*), False, False> 132 | for ($fty, $($ty,)*) 133 | where 134 | $fty: TStrEq<$ofty>, 135 | $($ty: TStrEq<$oty>,)* 136 | { 137 | const EQ_INNER: bool = { 138 | $fty::EQ 139 | $( && $ty::EQ )* 140 | }; 141 | } 142 | ) 143 | } 144 | 145 | /* 146 | fn main(){ 147 | for len in 1..=8 { 148 | print!("tuple_impl! {{ ["); 149 | for i in 0..len { 150 | print!("L{} ", i) 151 | } 152 | print!("], ["); 153 | for i in 0..len { 154 | print!("R{} ", i); 155 | } 156 | println!("], Tuple{} }}", len); 157 | } 158 | } 159 | */ 160 | 161 | tuple_impl! { [L0], [R0], Tuple1, __a, Chars1 } 162 | tuple_impl! { [L0 L1 ], [R0 R1 ], Tuple2, __b, Chars2 } 163 | tuple_impl! { [L0 L1 L2 ], [R0 R1 R2 ], Tuple3, __c, Chars3 } 164 | tuple_impl! { [L0 L1 L2 L3 ], [R0 R1 R2 R3 ], Tuple4, __d, Chars4 } 165 | tuple_impl! { [L0 L1 L2 L3 L4 ], [R0 R1 R2 R3 R4 ], Tuple5, __e, Chars5 } 166 | tuple_impl! { [L0 L1 L2 L3 L4 L5 ], [R0 R1 R2 R3 R4 R5 ], Tuple6, __f, Chars6 } 167 | tuple_impl! { [L0 L1 L2 L3 L4 L5 L6 ], [R0 R1 R2 R3 R4 R5 R6 ], Tuple7, __g, Chars7 } 168 | tuple_impl! { [L0 L1 L2 L3 L4 L5 L6 L7 ], [R0 R1 R2 R3 R4 R5 R6 R7 ], Tuple8, __, Chars8 } 169 | 170 | impl Classify for () { 171 | type IsByte = False; 172 | type KindNumber = Tuple0; 173 | } 174 | 175 | impl TStrEq for () 176 | where 177 | Self: HasSameKindNumber, 178 | Self: PrivTStrEq, 179 | { 180 | const EQ: bool = >::EQ; 181 | } 182 | 183 | impl PrivTStrEq<(), True> for () { 184 | const EQ: bool = true; 185 | } 186 | -------------------------------------------------------------------------------- /tstr/src/tstr_type.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{self, Debug}, 3 | marker::PhantomData, 4 | }; 5 | 6 | /// A type-level string type, similar to a `&'static str` const parameter. 7 | /// 8 | /// # Examples 9 | /// 10 | /// ### Accessing Fields 11 | /// 12 | /// This example demonstrates how you can use `TStr` to implement a generic accessor trait. 13 | /// 14 | /// ```rust 15 | /// use tstr::TStr; 16 | /// use tstr::{TS, ts}; 17 | /// 18 | /// fn main() { 19 | /// let mut tup = (3, 5, 8); 20 | /// 21 | /// assert_eq!(tup.get(ts!(0)), &3); 22 | /// assert_eq!(tup.get(ts!(1)), &5); 23 | /// assert_eq!(tup.get(ts!(2)), &8); 24 | /// 25 | /// let old_0 = replace(&mut tup, ts!(0), 333); 26 | /// let old_1 = replace(&mut tup, ts!(1), 555); 27 | /// let old_2 = replace(&mut tup, ts!(2), 888); 28 | /// 29 | /// assert_eq!(tup.get(ts!(0)), &333); 30 | /// assert_eq!(tup.get(ts!(1)), &555); 31 | /// assert_eq!(tup.get(ts!(2)), &888); 32 | /// 33 | /// assert_eq!(old_0, 3); 34 | /// assert_eq!(old_1, 5); 35 | /// assert_eq!(old_2, 8); 36 | /// 37 | /// } 38 | /// 39 | /// fn replace(this: &mut T, name: TStr, replacement: T::Field) -> T::Field 40 | /// where 41 | /// T: Access>, 42 | /// T::Field: Clone, 43 | /// { 44 | /// let ret = this.get(name).clone(); 45 | /// this.set(name, replacement); 46 | /// ret 47 | /// } 48 | /// 49 | /// 50 | /// trait Access { 51 | /// type Field; 52 | /// 53 | /// fn get(&self, _field_name: N) -> &Self::Field; 54 | /// fn set(&mut self, _field_name: N, val: Self::Field); 55 | /// } 56 | /// 57 | /// impl Access for (A, B, C) { 58 | /// type Field = A; 59 | /// 60 | /// fn get(&self, _field_name: TS!(0)) -> &A { 61 | /// &self.0 62 | /// } 63 | /// fn set(&mut self, _field_name: TS!(0), val: A){ 64 | /// self.0 = val; 65 | /// } 66 | /// } 67 | /// 68 | /// impl Access for (A, B, C) { 69 | /// type Field = B; 70 | /// 71 | /// fn get(&self, _field_name: TS!(1)) -> &B { 72 | /// &self.1 73 | /// } 74 | /// fn set(&mut self, _field_name: TS!(1), val: B){ 75 | /// self.1 = val; 76 | /// } 77 | /// } 78 | /// 79 | /// impl Access for (A, B, C) { 80 | /// type Field = C; 81 | /// 82 | /// fn get(&self, _field_name: TS!(2)) -> &C { 83 | /// &self.2 84 | /// } 85 | /// fn set(&mut self, _field_name: TS!(2), val: C){ 86 | /// self.2 = val; 87 | /// } 88 | /// } 89 | /// 90 | /// ``` 91 | /// 92 | pub struct TStr(pub(crate) PhantomData T>); 93 | 94 | impl TStr { 95 | /// Constructs the TStr. 96 | /// 97 | /// # Example 98 | /// 99 | /// ```rust 100 | /// use tstr::{TS, TStr}; 101 | /// 102 | /// type FOO = TS!(foo); 103 | /// 104 | /// let foo_1: FOO = TStr::NEW; 105 | /// let foo_2 = FOO::NEW; // The same as the previous statement 106 | /// 107 | /// ``` 108 | pub const NEW: Self = TStr(PhantomData); 109 | } 110 | 111 | #[cfg(feature = "const_generics")] 112 | macro_rules! const_generics_using { 113 | () => { 114 | /// For getting the `&'static str` value of this [`TStr`]. 115 | /// 116 | /// You can use this as the bound for a generic [`TStr`] parameter. 117 | /// 118 | /// # Example 119 | /// 120 | /// ```rust 121 | /// use tstr::{StrValue, ts}; 122 | /// 123 | /// asserts(ts!(foo), ts!(bar), ts!(baz)); 124 | /// 125 | /// fn asserts(foo: A, bar: B, baz: C) 126 | /// where 127 | /// A: StrValue, 128 | /// B: StrValue, 129 | /// C: StrValue, 130 | /// { 131 | /// assert_eq!(A::STR, "foo"); 132 | /// assert_eq!(foo.to_str(), "foo"); 133 | /// 134 | /// assert_eq!(B::STR, "bar"); 135 | /// assert_eq!(bar.to_str(), "bar"); 136 | /// 137 | /// assert_eq!(C::STR, "baz"); 138 | /// assert_eq!(baz.to_str(), "baz"); 139 | /// 140 | /// } 141 | /// 142 | /// ``` 143 | /// 144 | /// [`TStr`]: ./struct.TStr.html 145 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "const_generics")))] 146 | pub trait StrValue: Debug + Copy + Default + 'static { 147 | /// The `&'static str` value of this `TStr`. 148 | const STR: &'static str; 149 | 150 | /// Gets the `&'static str` value of this `TStr`. 151 | fn to_str(self) -> &'static str { 152 | Self::STR 153 | } 154 | } 155 | 156 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "const_generics")))] 157 | impl StrValue for TStr> { 158 | const STR: &'static str = S; 159 | } 160 | 161 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "const_generics")))] 162 | impl TStr 163 | where 164 | Self: StrValue, 165 | { 166 | /// The `&'static str` value of this `TStr`. 167 | /// 168 | /// # Example 169 | /// 170 | /// ```rust 171 | /// use tstr::TS; 172 | /// 173 | /// type FOO = TS!(foo); 174 | /// type BAR = TS!(bar); 175 | /// 176 | /// assert_eq!(FOO::STR, "foo"); 177 | /// assert_eq!(BAR::STR, "bar"); 178 | /// 179 | /// ``` 180 | pub const STR: &'static str = ::STR; 181 | } 182 | }; 183 | } 184 | #[cfg(feature = "const_generics")] 185 | const_generics_using! {} 186 | 187 | impl Copy for TStr {} 188 | 189 | impl Clone for TStr { 190 | #[inline(always)] 191 | fn clone(&self) -> Self { 192 | *self 193 | } 194 | } 195 | 196 | impl Default for TStr { 197 | #[inline(always)] 198 | fn default() -> Self { 199 | Self::NEW 200 | } 201 | } 202 | 203 | impl Debug for TStr { 204 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 205 | f.debug_struct("TStr").finish() 206 | } 207 | } 208 | 209 | impl core::cmp::PartialEq for TStr { 210 | #[inline(always)] 211 | fn eq(&self, _other: &Self) -> bool { 212 | true 213 | } 214 | } 215 | 216 | impl core::cmp::Eq for TStr {} 217 | 218 | impl core::cmp::PartialOrd for TStr { 219 | #[inline(always)] 220 | fn partial_cmp(&self, _other: &Self) -> Option { 221 | Some(core::cmp::Ordering::Equal) 222 | } 223 | } 224 | 225 | impl core::cmp::Ord for TStr { 226 | #[inline(always)] 227 | fn cmp(&self, _other: &Self) -> core::cmp::Ordering { 228 | core::cmp::Ordering::Equal 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /tstr/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions 2 | 3 | /// A const equivalent of `&str` equality comparison. 4 | /// 5 | /// # Example 6 | /// 7 | /// ```rust 8 | /// use tstr::utils::str_eq; 9 | /// 10 | /// const FOO: &str = "foo"; 11 | /// const BAR: &str = "fooooo"; 12 | /// const BAZ: &str = "bar"; 13 | /// 14 | /// 15 | /// const FOO_EQ_FOO: bool = str_eq(FOO, FOO); 16 | /// assert!( FOO_EQ_FOO ); 17 | /// 18 | /// const FOO_EQ_BAR: bool = str_eq(FOO, BAR); 19 | /// assert!( !FOO_EQ_BAR ); 20 | /// 21 | /// const FOO_EQ_BAZ: bool = str_eq(FOO, BAZ); 22 | /// assert!( !FOO_EQ_BAZ ); 23 | /// 24 | /// ``` 25 | /// 26 | #[cfg(feature = "rust_1_46")] 27 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))] 28 | #[inline] 29 | pub const fn str_eq(left: &str, right: &str) -> bool { 30 | u8_slice_eq(left.as_bytes(), right.as_bytes()) 31 | } 32 | 33 | /// A const equivalent of `&[u8]` equality comparison. 34 | /// 35 | /// # Example 36 | /// 37 | /// ```rust 38 | /// use tstr::utils::u8_slice_eq; 39 | /// 40 | /// const FOO: &[u8] = &[10, 20]; 41 | /// const BAR: &[u8] = &[10, 20, 30, 40]; 42 | /// const BAZ: &[u8] = &[3, 5, 8, 13]; 43 | /// 44 | /// const FOO_EQ_FOO: bool = u8_slice_eq(FOO, FOO); 45 | /// assert!( FOO_EQ_FOO ); 46 | /// 47 | /// const FOO_EQ_BAR: bool = u8_slice_eq(FOO, BAR); 48 | /// assert!( !FOO_EQ_BAR ); 49 | /// 50 | /// const FOO_EQ_BAZ: bool = u8_slice_eq(FOO, BAZ); 51 | /// assert!( !FOO_EQ_BAZ ); 52 | /// 53 | /// 54 | /// ``` 55 | /// 56 | #[cfg(feature = "rust_1_46")] 57 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))] 58 | #[inline] 59 | pub const fn u8_slice_eq(left: &[u8], right: &[u8]) -> bool { 60 | if left.len() != right.len() { 61 | return false; 62 | } 63 | 64 | let mut i = 0; 65 | while i != left.len() { 66 | if left[i] != right[i] { 67 | return false; 68 | } 69 | i += 1; 70 | } 71 | 72 | true 73 | } 74 | 75 | #[cfg(feature = "rust_1_46")] 76 | pub use slice_cmp::{str_cmp, u8_slice_cmp}; 77 | 78 | #[cfg(feature = "rust_1_46")] 79 | mod slice_cmp { 80 | use core::cmp::Ordering; 81 | 82 | const LESS: u8 = 0; 83 | const GREATER: u8 = 1; 84 | const EQUAL: u8 = 2; 85 | 86 | macro_rules! ret_if_ne { 87 | ($left:expr, $right:expr) => {{ 88 | let l = $left; 89 | let r = $right; 90 | if l != r { 91 | return (l > r) as u8; 92 | } 93 | }}; 94 | } 95 | 96 | const fn to_ordering(n: u8) -> Ordering { 97 | match n { 98 | LESS => Ordering::Less, 99 | GREATER => Ordering::Greater, 100 | _ => Ordering::Equal, 101 | } 102 | } 103 | 104 | /// A const equivalent of `str::cmp`. 105 | /// 106 | /// # Example 107 | /// 108 | /// ```rust 109 | /// use tstr::utils::str_cmp; 110 | /// 111 | /// use std::cmp::Ordering; 112 | /// 113 | /// const FOO: &str = "foo"; 114 | /// const BAR: &str = "fooooo"; 115 | /// const BAZ: &str = "bar"; 116 | /// 117 | /// 118 | /// const FOO_CMP_FOO: Ordering = str_cmp(FOO, FOO); 119 | /// assert_eq!(FOO_CMP_FOO, Ordering::Equal); 120 | /// 121 | /// const FOO_CMP_BAR: Ordering = str_cmp(FOO, BAR); 122 | /// assert_eq!(FOO_CMP_BAR, Ordering::Less); 123 | /// 124 | /// const FOO_CMP_BAZ: Ordering = str_cmp(FOO, BAZ); 125 | /// assert_eq!(FOO_CMP_BAZ, Ordering::Greater); 126 | /// 127 | /// ``` 128 | /// 129 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))] 130 | #[inline] 131 | pub const fn str_cmp(left: &str, right: &str) -> Ordering { 132 | const fn str_cmp_inner(left: &[u8], right: &[u8]) -> u8 { 133 | let left_len = left.len(); 134 | let right_len = right.len(); 135 | let (min_len, on_ne) = if left_len < right_len { 136 | (left_len, LESS) 137 | } else { 138 | (right_len, GREATER) 139 | }; 140 | 141 | let mut i = 0; 142 | while i < min_len { 143 | ret_if_ne! {left[i], right[i]} 144 | i += 1; 145 | } 146 | 147 | if left_len == right_len { 148 | EQUAL 149 | } else { 150 | on_ne 151 | } 152 | } 153 | 154 | to_ordering(str_cmp_inner(left.as_bytes(), right.as_bytes())) 155 | } 156 | 157 | /// A const equivalent of `<[u8]>::cmp`. 158 | /// 159 | /// # Example 160 | /// 161 | /// ```rust 162 | /// use tstr::utils::u8_slice_cmp; 163 | /// 164 | /// use std::cmp::Ordering; 165 | /// 166 | /// const FOO: &[u8] = &[10, 20]; 167 | /// const BAR: &[u8] = &[10, 20, 30, 40]; 168 | /// const BAZ: &[u8] = &[3, 5]; 169 | /// 170 | /// const FOO_CMP_FOO: Ordering = u8_slice_cmp(FOO, FOO); 171 | /// assert_eq!(FOO_CMP_FOO, Ordering::Equal); 172 | /// 173 | /// const FOO_CMP_BAR: Ordering = u8_slice_cmp(FOO, BAR); 174 | /// assert_eq!(FOO_CMP_BAR, Ordering::Less); 175 | /// 176 | /// const FOO_CMP_BAZ: Ordering = u8_slice_cmp(FOO, BAZ); 177 | /// assert_eq!(FOO_CMP_BAZ, Ordering::Greater); 178 | /// 179 | /// ``` 180 | /// 181 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))] 182 | #[inline] 183 | pub const fn u8_slice_cmp(left: &[u8], right: &[u8]) -> Ordering { 184 | const fn u8_slice_cmp_inner(left: &[u8], right: &[u8]) -> u8 { 185 | let left_len = left.len(); 186 | 187 | ret_if_ne! {left_len, right.len()} 188 | 189 | let mut i = 0; 190 | while i < left_len { 191 | ret_if_ne! {left[i], right[i]} 192 | i += 1; 193 | } 194 | 195 | EQUAL 196 | } 197 | 198 | to_ordering(u8_slice_cmp_inner(left, right)) 199 | } 200 | } 201 | 202 | #[cfg(test)] 203 | mod tests { 204 | use super::*; 205 | 206 | #[test] 207 | #[cfg(feature = "rust_1_46")] 208 | fn slice_eq_test() { 209 | assert!(u8_slice_eq(&[], &[])); 210 | assert!(!u8_slice_eq(&[], &[0])); 211 | assert!(!u8_slice_eq(&[0], &[])); 212 | assert!(u8_slice_eq(&[0], &[0])); 213 | assert!(!u8_slice_eq(&[0], &[1])); 214 | assert!(!u8_slice_eq(&[1], &[0])); 215 | assert!(!u8_slice_eq(&[0], &[0, 1])); 216 | assert!(!u8_slice_eq(&[0, 1], &[0])); 217 | assert!(u8_slice_eq(&[0, 1], &[0, 1])); 218 | assert!(!u8_slice_eq(&[0, 1], &[0, 2])); 219 | } 220 | 221 | #[test] 222 | #[cfg(feature = "rust_1_46")] 223 | fn str_eq_test() { 224 | assert!(str_eq("", "")); 225 | assert!(!str_eq("", "0")); 226 | assert!(!str_eq("0", "")); 227 | assert!(str_eq("0", "0")); 228 | assert!(!str_eq("0", "1")); 229 | assert!(!str_eq("1", "0")); 230 | assert!(!str_eq("0", "0, 1")); 231 | assert!(!str_eq("0, 1", "0")); 232 | assert!(!str_eq("0, 1", "1")); 233 | assert!(str_eq("0, 1", "0, 1")); 234 | assert!(!str_eq("0, 1", "0, 2")); 235 | } 236 | 237 | #[test] 238 | #[cfg(feature = "rust_1_46")] 239 | fn slice_cmp_test() { 240 | use core::cmp::{ 241 | Ord, 242 | Ordering::{Equal, Greater, Less}, 243 | }; 244 | 245 | macro_rules! assert_s_cmp { 246 | ($left:expr, $right:expr, $expected:expr) => { 247 | assert_eq!(u8_slice_cmp($left, $right), $expected); 248 | assert_eq!(<[u8]>::cmp($left, $right), $expected); 249 | 250 | assert_eq!(u8_slice_cmp($right, $left), $expected.reverse()); 251 | assert_eq!(<[u8]>::cmp($right, $left), $expected.reverse()); 252 | }; 253 | } 254 | 255 | assert_s_cmp!(&[], &[], Equal); 256 | assert_s_cmp!(&[], &[0], Less); 257 | assert_s_cmp!(&[0], &[], Greater); 258 | assert_s_cmp!(&[0], &[0], Equal); 259 | assert_s_cmp!(&[0], &[1], Less); 260 | assert_s_cmp!(&[0], &[0, 1], Less); 261 | assert_s_cmp!(&[0, 1], &[0, 1], Equal); 262 | assert_s_cmp!(&[0, 1], &[0, 2], Less); 263 | } 264 | 265 | #[test] 266 | #[cfg(feature = "rust_1_46")] 267 | fn str_cmp_test() { 268 | use core::cmp::{ 269 | Ord, 270 | Ordering::{Equal, Greater, Less}, 271 | }; 272 | 273 | macro_rules! assert_s_cmp { 274 | ($left:expr, $right:expr, $expected:expr) => { 275 | assert_eq!(str_cmp($left, $right), $expected, "A"); 276 | assert_eq!($left.cmp($right), $expected, "B"); 277 | 278 | assert_eq!(str_cmp($left, $left), Equal); 279 | assert_eq!(str_cmp($right, $right), Equal); 280 | 281 | assert_eq!(str_cmp($right, $left), $expected.reverse(), "cmp"); 282 | assert_eq!($right.cmp($left), $expected.reverse(), "cmp"); 283 | }; 284 | } 285 | 286 | assert_s_cmp!("0", "", Greater); 287 | assert_s_cmp!("0", "1", Less); 288 | assert_s_cmp!("0", "01", Less); 289 | assert_s_cmp!("1", "01", Greater); 290 | assert_s_cmp!("099999", "12", Less); 291 | assert_s_cmp!("111111", "12", Less); 292 | assert_s_cmp!("120", "12", Greater); 293 | assert_s_cmp!("199999", "12", Greater); 294 | assert_s_cmp!("299999", "12", Greater); 295 | assert_s_cmp!("01", "02", Less); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /tstr/tests/modules/alias_and_tuples.rs: -------------------------------------------------------------------------------- 1 | use tstr::{alias, ts, TS}; 2 | 3 | alias! { 4 | A = aaa; 5 | B = bbb; 6 | C = ccc; 7 | D = ddd; 8 | 9 | Tup2 = (aaa, bbb); 10 | Tup3 = (bbb, ccc, ddd); 11 | } 12 | 13 | #[test] 14 | fn alias_and_tups() { 15 | let aa: TS!(aaa) = A; 16 | let bb: TS!(bbb) = B; 17 | let cc: TS!(ccc) = C; 18 | let dd: TS!(ddd) = D; 19 | 20 | let _: TS!(aaa, bbb) = Tup2; 21 | let _: TS!(bbb, ccc, ddd) = Tup3; 22 | 23 | let _: Tup2 = ts!(aaa, bbb); 24 | let _: Tup3 = ts!(bbb, ccc, ddd); 25 | 26 | let _: A = aa; 27 | let _: B = bb; 28 | let _: C = cc; 29 | let _: D = dd; 30 | 31 | let _: (A, B) = Tup2; 32 | let _: (B, C, D) = Tup3; 33 | } 34 | -------------------------------------------------------------------------------- /tstr/tests/modules/concat_args.rs: -------------------------------------------------------------------------------- 1 | use tstr::{ts, TS}; 2 | 3 | #[allow(dead_code)] 4 | type ConcatIdents = TS!("foobarbazqux"); 5 | 6 | #[allow(dead_code)] 7 | type ConcatWithNum = TS!("3,7,15,31"); 8 | 9 | #[test] 10 | fn test_concat() { 11 | const _: ConcatIdents = ts!(concat!(foo, bar, baz, qux)); 12 | const _: ConcatIdents = ts!(concat!(concat!(foo, bar), baz, qux)); 13 | const _: ConcatIdents = ts!(concat!(foo, concat!(bar, baz,), qux)); 14 | const _: ConcatIdents = ts!(concat!(foo, bar, concat!(baz, qux))); 15 | 16 | const _: ConcatIdents = ts!(concat!("foo", bar, r##"baz"##, qux)); 17 | const _: ConcatIdents = ts!(concat!(concat!("foo", bar), r##"baz"##, qux)); 18 | const _: ConcatIdents = ts!(concat!("foo", concat!(bar, r##"baz"##,), qux)); 19 | const _: ConcatIdents = ts!(concat!("foo", bar, concat!(r##"baz"##, qux))); 20 | 21 | const _: ConcatWithNum = ts!(concat!(0b11, ",", 0o7, ",", 15, ",", 0x1F)); 22 | const _: ConcatWithNum = ts!(concat!(0b1_1, ",", 0o_7, ",", 1_5, ",", 0x1_F)); 23 | const _: ConcatWithNum = ts!(concat!(0b1_1, ",", 0o_7, ",15,31")); 24 | } 25 | 26 | #[test] 27 | fn test_stringify() { 28 | let _: TS!("0b11") = ts!(stringify!(0b11)); 29 | let _: TS!("0o7") = ts!(stringify!(0o7)); 30 | let _: TS!("15") = ts!(stringify!(15)); 31 | let _: TS!("0x1F") = ts!(stringify!(0x1F)); 32 | let _: TS!(r#""hello""#) = ts!(stringify!("hello")); 33 | } 34 | -------------------------------------------------------------------------------- /tstr/tests/modules/long_strings.rs: -------------------------------------------------------------------------------- 1 | use tstr::*; 2 | 3 | macro_rules! long_str_test { 4 | ($string:tt, $tuple:ty, $chars:ty $(,)*) => { 5 | test_case!($string, $tuple, $chars, $string); 6 | }; 7 | } 8 | 9 | #[cfg(not(feature = "min_const_generics"))] 10 | #[allow(dead_code)] 11 | type ZeroToSeven = (__0, __1, __2, __3, __4, __5, __6, __7); 12 | 13 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 14 | #[allow(dead_code)] 15 | type ZeroToSeven = __<'0', '1', '2', '3', '4', '5', '6', '7'>; 16 | 17 | #[cfg(not(feature = "min_const_generics"))] 18 | #[allow(dead_code)] 19 | type ZeroToSix = (__0, __1, __2, __3, __4, __5, __6); 20 | 21 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 22 | #[allow(dead_code)] 23 | type ZeroToSix = __g<'0', '1', '2', '3', '4', '5', '6'>; 24 | 25 | #[cfg(not(feature = "min_const_generics"))] 26 | #[allow(dead_code)] 27 | type AToG = (__a, __b, __c, __d, __e, __f, __g); 28 | 29 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 30 | #[allow(dead_code)] 31 | type AToG = __g<'a', 'b', 'c', 'd', 'e', 'f', 'g'>; 32 | 33 | #[cfg(not(feature = "min_const_generics"))] 34 | #[allow(dead_code)] 35 | type AToH = (__a, __b, __c, __d, __e, __f, __g, __h); 36 | 37 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 38 | #[allow(dead_code)] 39 | type AToH = __<'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'>; 40 | 41 | str_test_case! {"abcdefgh", AToH} 42 | str_test_case! {"01234567", ZeroToSeven} 43 | 44 | long_str_test! {"abcdefghi", (AToH, (__i,)), (AToH, __a<'i'>)} 45 | 46 | #[cfg(not(feature = "const_generics"))] 47 | #[allow(dead_code)] 48 | type Len56Plus = (AToH, AToH, AToH, AToH, AToH, AToH, AToH, T); 49 | 50 | str_test_case! { 51 | "abcdefgh\ 52 | abcdefgh\ 53 | abcdefgh\ 54 | abcdefgh\ 55 | abcdefgh\ 56 | abcdefgh\ 57 | abcdefgh\ 58 | abcdefg", 59 | Len56Plus, 60 | } 61 | 62 | str_test_case! { 63 | "abcdefgh\ 64 | abcdefgh\ 65 | abcdefgh\ 66 | abcdefgh\ 67 | abcdefgh\ 68 | abcdefgh\ 69 | abcdefgh\ 70 | 01234567", 71 | Len56Plus, 72 | } 73 | 74 | #[cfg(not(feature = "const_generics"))] 75 | #[allow(dead_code)] 76 | type Len504Plus = ( 77 | Len56Plus, 78 | Len56Plus, 79 | Len56Plus, 80 | Len56Plus, 81 | Len56Plus, 82 | Len56Plus, 83 | Len56Plus, 84 | Len56Plus, 85 | ); 86 | 87 | str_test_case! { 88 | "abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 89 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 90 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 91 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 92 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 93 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 94 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 95 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh0123456\ 96 | ", 97 | Len504Plus 98 | } 99 | 100 | str_test_case! { 101 | "abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 102 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 103 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 104 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 105 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 106 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 107 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 108 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 109 | ", 110 | Len504Plus 111 | } 112 | 113 | long_str_test! { 114 | "abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 115 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 116 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 117 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 118 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 119 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 120 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 121 | abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh01234567\ 122 | 9", 123 | (Len504Plus, (__9,)), 124 | (Len504Plus, __a<'9'>), 125 | } 126 | 127 | // Just making sure that this module is compiled. 128 | #[test] 129 | fn testing_long_strings() {} 130 | -------------------------------------------------------------------------------- /tstr/tests/modules/other_args.rs: -------------------------------------------------------------------------------- 1 | use tstr::*; 2 | 3 | test_case! {0b0, (__0x30, ), __a<'0'>, "0"} 4 | test_case! {0b1, (__0x31, ), __a<'1'>, "1"} 5 | test_case! {0b10, (__0x32, ), __a<'2'>, "2"} 6 | test_case! {0b100, (__0x34, ), __a<'4'>, "4"} 7 | test_case! {0b1000, (__0x38, ), __a<'8'>, "8"} 8 | test_case! {0b1100, (__0x31, __0x32), __b<'1', '2'>, "12"} 9 | test_case! {0b1_100, (__0x31, __0x32), __b<'1', '2'>, "12"} 10 | test_case! {0b10_00__00_01, (__0x31, __0x32, __0x39), __c<'1', '2', '9'>, "129"} 11 | 12 | test_case! {0o0, (__0x30, ), __a<'0'>, "0"} 13 | test_case! {0o1, (__0x31, ), __a<'1'>, "1"} 14 | test_case! {0o2, (__0x32, ), __a<'2'>, "2"} 15 | test_case! {0o4, (__0x34, ), __a<'4'>, "4"} 16 | test_case! {0o10, (__0x38, ), __a<'8'>, "8"} 17 | test_case! {0o14, (__0x31, __0x32), __b<'1', '2'>, "12"} 18 | test_case! {0o1_4, (__0x31, __0x32), __b<'1', '2'>, "12"} 19 | test_case! {0o201, (__0x31, __0x32, __0x39), __c<'1', '2', '9'>, "129"} 20 | test_case! {0o2__0_1, (__0x31, __0x32, __0x39), __c<'1', '2', '9'>, "129"} 21 | 22 | test_case! {0x0, (__0x30, ), __a<'0'>, "0"} 23 | test_case! {0x1, (__0x31, ), __a<'1'>, "1"} 24 | test_case! {0x2, (__0x32, ), __a<'2'>, "2"} 25 | test_case! {0x4, (__0x34, ), __a<'4'>, "4"} 26 | test_case! {0x8, (__0x38, ), __a<'8'>, "8"} 27 | test_case! {0xC, (__0x31, __0x32), __b<'1', '2'>, "12"} 28 | test_case! {0x12, (__0x31, __0x38), __b<'1', '8'>, "18"} 29 | test_case! {0x1_2, (__0x31, __0x38), __b<'1', '8'>, "18"} 30 | test_case! {0x41, (__0x36, __0x35), __b<'6', '5'>, "65"} 31 | test_case! {0x4_1, (__0x36, __0x35), __b<'6', '5'>, "65"} 32 | test_case! {0x100, (__0x32, __0x35, __0x36), __c<'2', '5', '6'>, "256"} 33 | test_case! {0x103, (__0x32, __0x35, __0x39), __c<'2', '5', '9'>, "259"} 34 | test_case! {0x1__0_3, (__0x32, __0x35, __0x39), __c<'2', '5', '9'>, "259"} 35 | 36 | test_case! {0, (__0x30, ), __a<'0'>, "0"} 37 | test_case! {3, (__0x33, ), __a<'3'>, "3"} 38 | test_case! {10, (__0x31, __0x30), __b<'1', '0'>, "10"} 39 | test_case! {1_0, (__0x31, __0x30), __b<'1', '0'>, "10"} 40 | test_case! {16, (__0x31, __0x36), __b<'1', '6'>, "16"} 41 | test_case! {1_6, (__0x31, __0x36), __b<'1', '6'>, "16"} 42 | test_case! {128, (__0x31, __0x32, __0x38), __c<'1', '2', '8'>, "128"} 43 | test_case! {1__2_8, (__0x31, __0x32, __0x38), __c<'1', '2', '8'>, "128"} 44 | 45 | test_case! { 46 | foo_bar_baz, 47 | ((__0x66, __0x6F, __0x6F, __0x5F, __0x62, __0x61, __0x72, __0x5F, ), (__0x62, __0x61, __0x7A, ), ), 48 | (__<'f', 'o', 'o', '_', 'b', 'a', 'r', '_'>, __c<'b', 'a', 'z'>), 49 | "foo_bar_baz", 50 | } 51 | 52 | test_case! { 53 | r#foo_bar_baz, 54 | ((__0x66, __0x6F, __0x6F, __0x5F, __0x62, __0x61, __0x72, __0x5F, ), (__0x62, __0x61, __0x7A, ), ), 55 | (__<'f', 'o', 'o', '_', 'b', 'a', 'r', '_'>, __c<'b', 'a', 'z'>), 56 | "foo_bar_baz", 57 | } 58 | 59 | test_case! {_0, (__0x5F, __0x30, ), __b<'_', '0'>, "_0"} 60 | 61 | test_case! {r#_0, (__0x5F, __0x30, ), __b<'_', '0'>, "_0"} 62 | 63 | test_case! { 64 | r#async, (__0x61, __0x73, __0x79, __0x6E, __0x63, ), 65 | __e<'a', 's', 'y', 'n', 'c'>, 66 | "async", 67 | } 68 | 69 | // Just making sure that this module is compiled. 70 | #[test] 71 | fn testing_other_args() {} 72 | -------------------------------------------------------------------------------- /tstr/tests/modules/string_args.rs: -------------------------------------------------------------------------------- 1 | use tstr::*; 2 | 3 | // Use this to generate the tuple for a particular string: 4 | /* 5 | fn foo(){ 6 | let string = r"\u{2D}\u{61}A\u{3C}>?{\u{7D}¢¤§©ߨࡕৰ\u{9F0}ⓩ蓭𐂶𣏦"; 7 | 8 | print!("("); 9 | for chunk in string.as_bytes().chunks(8) { 10 | print!("("); 11 | for b in chunk { 12 | print!("__0x{:02X}, ", b); 13 | } 14 | print!("), "); 15 | } 16 | print!(")"); 17 | println!(); 18 | } 19 | */ 20 | 21 | // Testing empty strings 22 | test_case!("", (), (), ""); 23 | 24 | // Testing single char strings 25 | test_case!("b", (__b,), __a<'b'>, "b"); 26 | 27 | // Testing two char strings 28 | test_case!("ab", (__a, __b), __b<'a','b'>, "ab"); 29 | 30 | #[cfg(not(feature = "min_const_generics"))] 31 | #[allow(dead_code)] 32 | type AllCharLengths = ( 33 | ( 34 | __0x2B, 35 | __0x2D, 36 | __0x61, 37 | __0x41, 38 | __0x3C, 39 | __0x3E, 40 | __0x3F, 41 | __0x7B, 42 | ), 43 | ( 44 | __0x7D, 45 | __0xC2, 46 | __0xA2, 47 | __0xC2, 48 | __0xA4, 49 | __0xC2, 50 | __0xA7, 51 | __0xC2, 52 | ), 53 | ( 54 | __0xA9, 55 | __0xDF, 56 | __0xA8, 57 | __0xE0, 58 | __0xA1, 59 | __0x95, 60 | __0xE0, 61 | __0xA7, 62 | ), 63 | ( 64 | __0xB0, 65 | __0xE0, 66 | __0xA7, 67 | __0xB0, 68 | __0xE2, 69 | __0x93, 70 | __0xA9, 71 | __0xE8, 72 | ), 73 | ( 74 | __0x93, 75 | __0xAD, 76 | __0xF0, 77 | __0x90, 78 | __0x82, 79 | __0xB6, 80 | __0xF0, 81 | __0xA3, 82 | ), 83 | (__0x8F, __0xA6), 84 | ); 85 | 86 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 87 | #[allow(dead_code)] 88 | type AllCharLengths = ( 89 | __<'+', '-', 'a', 'A', '<', '>', '?', '{'>, 90 | __<'}', '¢', '¤', '§', '©', 'ߨ', 'ࡕ', 'ৰ'>, 91 | __e<'ৰ', 'ⓩ', '蓭', '𐂶', '𣏦'>, 92 | ); 93 | 94 | // Using characters of all utf8 lengths, and no escapes. 95 | str_test_case!("+-aA<>?{}¢¤§©ߨࡕৰৰⓩ蓭𐂶𣏦", AllCharLengths); 96 | 97 | str_test_case!(r"+-aA<>?{}¢¤§©ߨࡕৰৰⓩ蓭𐂶𣏦", AllCharLengths); 98 | 99 | str_test_case!(r#"+-aA<>?{}¢¤§©ߨࡕৰৰⓩ蓭𐂶𣏦"#, AllCharLengths); 100 | 101 | str_test_case!(r##"+-aA<>?{}¢¤§©ߨࡕৰৰⓩ蓭𐂶𣏦"##, AllCharLengths); 102 | 103 | // Using unicode escapes 104 | str_test_case!( 105 | "+\u{2D}\u{61}A\u{3C}\u{3E}?\u{7B}\u{7D}¢\u{A4}\u{A7}©\u{7E8}\u{855}ৰ\u{9F0}\u{24E9}蓭\u{100B6}\u{233E6}", 106 | AllCharLengths 107 | ); 108 | 109 | //////////////////////////////////////////////////////////////////////////////////// 110 | 111 | #[cfg(not(feature = "min_const_generics"))] 112 | #[allow(dead_code)] 113 | type IntermittentUnicodeEscapes = ( 114 | ( 115 | __0x2D, 116 | __0x61, 117 | __0x41, 118 | __0x3C, 119 | __0x3E, 120 | __0x3F, 121 | __0x7B, 122 | __0x7D, 123 | ), 124 | ( 125 | __0xC2, 126 | __0xA2, 127 | __0xC2, 128 | __0xA4, 129 | __0xC2, 130 | __0xA7, 131 | __0xC2, 132 | __0xA9, 133 | ), 134 | ( 135 | __0xDF, 136 | __0xA8, 137 | __0xE0, 138 | __0xA1, 139 | __0x95, 140 | __0xE0, 141 | __0xA7, 142 | __0xB0, 143 | ), 144 | ( 145 | __0xE0, 146 | __0xA7, 147 | __0xB0, 148 | __0xE2, 149 | __0x93, 150 | __0xA9, 151 | __0xE8, 152 | __0x93, 153 | ), 154 | ( 155 | __0xAD, 156 | __0xF0, 157 | __0x90, 158 | __0x82, 159 | __0xB6, 160 | __0xF0, 161 | __0xA3, 162 | __0x8F, 163 | ), 164 | (__0xA6,), 165 | ); 166 | 167 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 168 | #[allow(dead_code)] 169 | type IntermittentUnicodeEscapes = ( 170 | __<'-', 'a', 'A', '<', '>', '?', '{', '}'>, 171 | __<'¢', '¤', '§', '©', 'ߨ', 'ࡕ', 'ৰ', 'ৰ'>, 172 | __d<'ⓩ', '蓭', '𐂶', '𣏦'>, 173 | ); 174 | 175 | #[cfg(not(feature = "min_const_generics"))] 176 | #[allow(dead_code)] 177 | type IntermittentUnicodeEscapesRaw = ( 178 | ( 179 | ( 180 | __0x5C, 181 | __0x75, 182 | __0x7B, 183 | __0x32, 184 | __0x44, 185 | __0x7D, 186 | __0x5C, 187 | __0x75, 188 | ), 189 | ( 190 | __0x7B, 191 | __0x36, 192 | __0x31, 193 | __0x7D, 194 | __0x41, 195 | __0x5C, 196 | __0x75, 197 | __0x7B, 198 | ), 199 | ( 200 | __0x33, 201 | __0x43, 202 | __0x7D, 203 | __0x3E, 204 | __0x3F, 205 | __0x7B, 206 | __0x5C, 207 | __0x75, 208 | ), 209 | ( 210 | __0x7B, 211 | __0x37, 212 | __0x44, 213 | __0x7D, 214 | __0xC2, 215 | __0xA2, 216 | __0xC2, 217 | __0xA4, 218 | ), 219 | ( 220 | __0xC2, 221 | __0xA7, 222 | __0xC2, 223 | __0xA9, 224 | __0xDF, 225 | __0xA8, 226 | __0xE0, 227 | __0xA1, 228 | ), 229 | ( 230 | __0x95, 231 | __0xE0, 232 | __0xA7, 233 | __0xB0, 234 | __0x5C, 235 | __0x75, 236 | __0x7B, 237 | __0x39, 238 | ), 239 | ( 240 | __0x46, 241 | __0x30, 242 | __0x7D, 243 | __0xE2, 244 | __0x93, 245 | __0xA9, 246 | __0xE8, 247 | __0x93, 248 | ), 249 | ( 250 | __0xAD, 251 | __0xF0, 252 | __0x90, 253 | __0x82, 254 | __0xB6, 255 | __0xF0, 256 | __0xA3, 257 | __0x8F, 258 | ), 259 | ), 260 | (__0xA6,), 261 | ); 262 | 263 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 264 | #[allow(dead_code)] 265 | type IntermittentUnicodeEscapesRaw = ( 266 | __<'\\', 'u', '{', '2', 'D', '}', '\\', 'u'>, 267 | __<'{', '6', '1', '}', 'A', '\\', 'u', '{'>, 268 | __<'3', 'C', '}', '>', '?', '{', '\\', 'u'>, 269 | __<'{', '7', 'D', '}', '¢', '¤', '§', '©'>, 270 | __<'ߨ', 'ࡕ', 'ৰ', '\\', 'u', '{', '9', 'F'>, 271 | __f<'0', '}', 'ⓩ', '蓭', '𐂶', '𣏦'>, 272 | ); 273 | 274 | // Using characters of all utf8 lengths, and with some intermittent escapes. 275 | str_test_case!( 276 | "\u{2D}\u{61}A\u{3C}>?{\u{7D}¢¤§©ߨࡕৰ\u{9F0}ⓩ蓭𐂶𣏦", 277 | IntermittentUnicodeEscapes 278 | ); 279 | 280 | str_test_case!( 281 | r"\u{2D}\u{61}A\u{3C}>?{\u{7D}¢¤§©ߨࡕৰ\u{9F0}ⓩ蓭𐂶𣏦", 282 | IntermittentUnicodeEscapesRaw 283 | ); 284 | 285 | str_test_case!( 286 | r#"\u{2D}\u{61}A\u{3C}>?{\u{7D}¢¤§©ߨࡕৰ\u{9F0}ⓩ蓭𐂶𣏦"#, 287 | IntermittentUnicodeEscapesRaw 288 | ); 289 | 290 | str_test_case!( 291 | r##"\u{2D}\u{61}A\u{3C}>?{\u{7D}¢¤§©ߨࡕৰ\u{9F0}ⓩ蓭𐂶𣏦"##, 292 | IntermittentUnicodeEscapesRaw 293 | ); 294 | 295 | //////////////////////////////////////////////////////////////////////////////////// 296 | 297 | #[cfg(not(feature = "min_const_generics"))] 298 | #[allow(dead_code)] 299 | type AsciiEscapes = ( 300 | ( 301 | __0x41, 302 | __0x00, 303 | __0x42, 304 | __0x20, 305 | __0x43, 306 | __0x31, 307 | __0x44, 308 | __0x42, 309 | ), 310 | (__0x45, __0x53, __0x46, __0x7A, __0x47, __0x7F, __0x48), 311 | ); 312 | 313 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 314 | #[allow(dead_code)] 315 | type AsciiEscapes = ( 316 | __<'A', '\u{0}', 'B', ' ', 'C', '1', 'D', 'B'>, 317 | __g<'E', 'S', 'F', 'z', 'G', '\u{7f}', 'H'>, 318 | ); 319 | 320 | #[cfg(not(feature = "min_const_generics"))] 321 | #[allow(dead_code)] 322 | type AsciiEscapesRaw = ( 323 | ( 324 | __0x41, 325 | __0x5C, 326 | __0x78, 327 | __0x30, 328 | __0x30, 329 | __0x42, 330 | __0x5C, 331 | __0x78, 332 | ), 333 | ( 334 | __0x32, 335 | __0x30, 336 | __0x43, 337 | __0x5C, 338 | __0x78, 339 | __0x33, 340 | __0x31, 341 | __0x44, 342 | ), 343 | ( 344 | __0x5C, 345 | __0x78, 346 | __0x34, 347 | __0x32, 348 | __0x45, 349 | __0x5C, 350 | __0x78, 351 | __0x35, 352 | ), 353 | ( 354 | __0x33, 355 | __0x46, 356 | __0x5C, 357 | __0x78, 358 | __0x37, 359 | __0x61, 360 | __0x47, 361 | __0x5C, 362 | ), 363 | (__0x78, __0x37, __0x46, __0x48), 364 | ); 365 | 366 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 367 | #[allow(dead_code)] 368 | type AsciiEscapesRaw = ( 369 | __<'A', '\\', 'x', '0', '0', 'B', '\\', 'x'>, 370 | __<'2', '0', 'C', '\\', 'x', '3', '1', 'D'>, 371 | __<'\\', 'x', '4', '2', 'E', '\\', 'x', '5'>, 372 | __<'3', 'F', '\\', 'x', '7', 'a', 'G', '\\'>, 373 | __d<'x', '7', 'F', 'H'>, 374 | ); 375 | 376 | // Testing the ascii escapes 377 | str_test_case! {"A\x00B\x20C\x31D\x42E\x53F\x7aG\x7FH", AsciiEscapes} 378 | 379 | str_test_case! {r"A\x00B\x20C\x31D\x42E\x53F\x7aG\x7FH", AsciiEscapesRaw} 380 | str_test_case! {r#"A\x00B\x20C\x31D\x42E\x53F\x7aG\x7FH"#, AsciiEscapesRaw} 381 | str_test_case! {r##"A\x00B\x20C\x31D\x42E\x53F\x7aG\x7FH"##, AsciiEscapesRaw} 382 | 383 | //////////////////////////////////////////////////////////////////////////////////// 384 | 385 | #[cfg(not(feature = "min_const_generics"))] 386 | #[allow(dead_code)] 387 | type SingleCharEscapes = ( 388 | ( 389 | __0x41, 390 | __0x0A, 391 | __0x42, 392 | __0x0D, 393 | __0x43, 394 | __0x09, 395 | __0x44, 396 | __0x5C, 397 | ), 398 | ( 399 | __0x45, 400 | __0x00, 401 | __0x46, 402 | __0x46, 403 | __0x27, 404 | __0x47, 405 | __0x47, 406 | __0x22, 407 | ), 408 | (__0x48, __0x48, __0x0A), 409 | ); 410 | 411 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 412 | #[allow(dead_code)] 413 | type SingleCharEscapes = ( 414 | __<'A', '\n', 'B', '\r', 'C', '\t', 'D', '\\'>, 415 | __<'E', '\u{0}', 'F', 'F', '\'', 'G', 'G', '\"'>, 416 | __c<'H', 'H', '\n'>, 417 | ); 418 | 419 | #[cfg(not(feature = "min_const_generics"))] 420 | #[allow(dead_code)] 421 | type SingleCharEscapesRaw = ( 422 | ( 423 | __0x41, 424 | __0x5C, 425 | __0x6E, 426 | __0x42, 427 | __0x5C, 428 | __0x72, 429 | __0x43, 430 | __0x5C, 431 | ), 432 | ( 433 | __0x74, 434 | __0x44, 435 | __0x5C, 436 | __0x5C, 437 | __0x45, 438 | __0x5C, 439 | __0x30, 440 | __0x46, 441 | ), 442 | ( 443 | __0x46, 444 | __0x5C, 445 | __0x27, 446 | __0x47, 447 | __0x47, 448 | __0x5C, 449 | __0x22, 450 | __0x48, 451 | ), 452 | (__0x48, __0x0A), 453 | ); 454 | 455 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 456 | #[allow(dead_code)] 457 | type SingleCharEscapesRaw = ( 458 | __<'A', '\\', 'n', 'B', '\\', 'r', 'C', '\\'>, 459 | __<'t', 'D', '\\', '\\', 'E', '\\', '0', 'F'>, 460 | __<'F', '\\', '\'', 'G', 'G', '\\', '\"', 'H'>, 461 | __b<'H', '\n'>, 462 | ); 463 | 464 | // Testing single char escapes 465 | str_test_case! { 466 | "A\nB\rC\tD\\E\0FF\'GG\"HH 467 | ", 468 | SingleCharEscapes, 469 | } 470 | 471 | str_test_case! { 472 | r#"A\nB\rC\tD\\E\0FF\'GG\"HH 473 | "#, 474 | SingleCharEscapesRaw, 475 | } 476 | 477 | str_test_case! { 478 | r##"A\nB\rC\tD\\E\0FF\'GG\"HH 479 | "##, 480 | SingleCharEscapesRaw, 481 | } 482 | 483 | //////////////////////////////////////////////////////////////////////////////////// 484 | 485 | #[cfg(not(feature = "min_const_generics"))] 486 | #[allow(dead_code)] 487 | type BackSlashNewline = (__f, __o, __o, __b, __a, __r); 488 | 489 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 490 | #[allow(dead_code)] 491 | type BackSlashNewline = __f<'f', 'o', 'o', 'b', 'a', 'r'>; 492 | 493 | #[cfg(not(feature = "min_const_generics"))] 494 | #[allow(dead_code)] 495 | type BackSlashNewlineRaw = ( 496 | (__f, __o, __o, __0x5C, __0x0A, __0x20, __0x20, __0x20), 497 | (__0x20, __b, __a, __r), 498 | ); 499 | 500 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 501 | #[allow(dead_code)] 502 | type BackSlashNewlineRaw = ( 503 | __<'f', 'o', 'o', '\\', '\n', ' ', ' ', ' '>, 504 | __d<' ', 'b', 'a', 'r'>, 505 | ); 506 | 507 | str_test_case! { 508 | "foo\ 509 | bar", 510 | BackSlashNewline, 511 | } 512 | 513 | str_test_case! { 514 | r"foo\ 515 | bar", 516 | BackSlashNewlineRaw, 517 | } 518 | 519 | //////////////////////////////////////////////////////////////////////////////////// 520 | 521 | #[cfg(not(feature = "min_const_generics"))] 522 | #[allow(dead_code)] 523 | type Quotes = (__0x22, __0x22); 524 | 525 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 526 | #[allow(dead_code)] 527 | type Quotes = __b<'"', '"'>; 528 | 529 | #[cfg(not(feature = "min_const_generics"))] 530 | #[allow(dead_code)] 531 | type QuoteHash = (__0x22, __0x23); 532 | 533 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 534 | #[allow(dead_code)] 535 | type QuoteHash = __b<'"', '#'>; 536 | 537 | str_test_case! {r#""""#, Quotes} 538 | str_test_case! {r##""""## , Quotes} 539 | 540 | str_test_case! {r##""#"##, QuoteHash} 541 | str_test_case! {r###""#"###, QuoteHash} 542 | 543 | // Just making sure that this module is compiled. 544 | #[test] 545 | fn testing_string_args() {} 546 | -------------------------------------------------------------------------------- /tstr/tests/modules/string_cmp.rs: -------------------------------------------------------------------------------- 1 | use tstr::{TStrEq, TS}; 2 | 3 | #[cfg(feature = "const_generics")] 4 | use std::cmp::{Ord, Ordering}; 5 | 6 | #[cfg(feature = "const_generics")] 7 | use tstr::{StrValue, TStrOrd}; 8 | 9 | macro_rules! assert_str_eq { 10 | ($left:ty, $right:ty) => { 11 | assert!(<$left as TStrEq<$right>>::EQ); 12 | 13 | #[cfg(feature = "const_generics")] 14 | assert_eq!(<$left as TStrOrd<$right>>::CMP, Ordering::Equal); 15 | }; 16 | } 17 | 18 | macro_rules! assert_str_ne { 19 | ($left:ty, [$($right:ty),* $(,)*]) => { 20 | $(assert!(<$left as TStrEq<$right>>::NE);)* 21 | 22 | #[cfg(feature = "const_generics")] 23 | { 24 | $( 25 | assert_eq!( 26 | <$left as TStrOrd<$right>>::CMP, 27 | <$left as StrValue>::STR.cmp(<$right as StrValue>::STR) 28 | ); 29 | )* 30 | } 31 | }; 32 | } 33 | 34 | type Len0 = TS!(""); 35 | 36 | type Len1A = TS!("a"); 37 | type Len1B = TS!("b"); 38 | 39 | type Len2A = TS!("aa"); 40 | type Len2B = TS!("ab"); 41 | 42 | type Len3A = TS!("aaa"); 43 | type Len3B = TS!("aab"); 44 | 45 | type Len4A = TS!("aaaa"); 46 | type Len4B = TS!("aaab"); 47 | 48 | type Len5A = TS!("aaaaa"); 49 | type Len5B = TS!("aaaab"); 50 | 51 | type Len6A = TS!("aaaaaa"); 52 | type Len6B = TS!("aaabaa"); 53 | 54 | type Len7A = TS!("aaaaaaa"); 55 | type Len7B = TS!("abaaaaa"); 56 | 57 | type Len8A = TS!("aaaaaaaa"); 58 | type Len8B = TS!("aabaaaaa"); 59 | 60 | type Len9A = TS!("aaaaaaaaa"); 61 | type Len9B = TS!("aabaaaaaa"); 62 | 63 | type Len17A = TS!("-aaaaaaa-aaaaaaa-"); 64 | type Len17B = TS!("-aaaaaaa-aaaabaa-"); 65 | 66 | type Len25A = TS!("-aaaaaaa-____aaa-aaaaaaa-"); 67 | type Len25B = TS!("-aaaaaaa-aaaaaaa-aaaaaaa-"); 68 | 69 | type Len63A = TS!("aaaaaaa-aaaaaaa-aaaaaaa-_______-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa"); 70 | type Len63B = TS!("aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa"); 71 | 72 | type Len64A = TS!("-aaaaaaa-aaaaaaa-_______-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa"); 73 | type Len64B = TS!("-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa"); 74 | 75 | type Len65A = TS!("-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-"); 76 | type Len65B = TS!("-aaaaaaa-aaaaaaa-aaaaaaa-_______-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-"); 77 | 78 | #[test] 79 | fn equal_strs() { 80 | assert_str_eq!(Len0, Len0); 81 | assert_str_eq!(Len1A, Len1A); 82 | assert_str_eq!(Len1B, Len1B); 83 | assert_str_eq!(Len2A, Len2A); 84 | assert_str_eq!(Len2B, Len2B); 85 | assert_str_eq!(Len3A, Len3A); 86 | assert_str_eq!(Len3B, Len3B); 87 | assert_str_eq!(Len4A, Len4A); 88 | assert_str_eq!(Len4B, Len4B); 89 | assert_str_eq!(Len5A, Len5A); 90 | assert_str_eq!(Len5B, Len5B); 91 | assert_str_eq!(Len6A, Len6A); 92 | assert_str_eq!(Len6B, Len6B); 93 | assert_str_eq!(Len7A, Len7A); 94 | assert_str_eq!(Len7B, Len7B); 95 | assert_str_eq!(Len8A, Len8A); 96 | assert_str_eq!(Len8B, Len8B); 97 | assert_str_eq!(Len9A, Len9A); 98 | assert_str_eq!(Len9B, Len9B); 99 | assert_str_eq!(Len17A, Len17A); 100 | assert_str_eq!(Len17B, Len17B); 101 | assert_str_eq!(Len25A, Len25A); 102 | assert_str_eq!(Len25B, Len25B); 103 | assert_str_eq!(Len63A, Len63A); 104 | assert_str_eq!(Len63B, Len63B); 105 | assert_str_eq!(Len64A, Len64A); 106 | assert_str_eq!(Len64B, Len64B); 107 | assert_str_eq!(Len65A, Len65A); 108 | assert_str_eq!(Len65B, Len65B); 109 | } 110 | 111 | #[test] 112 | fn short_strs() { 113 | // assert_str_ne!(Len3A, [ 114 | // Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, 115 | // Len9A, Len17A, Len25A, Len63A, Len64A, Len65A, 116 | // ]); 117 | 118 | assert_str_ne!( 119 | Len0, 120 | [ 121 | Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, Len63A, 122 | Len64A, Len65A, 123 | ] 124 | ); 125 | 126 | assert_str_ne!( 127 | Len1A, 128 | [ 129 | Len0, Len1B, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 130 | Len63A, Len64A, Len65A, 131 | ] 132 | ); 133 | 134 | assert_str_ne!( 135 | Len2A, 136 | [ 137 | Len0, Len1A, Len2B, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 138 | Len63A, Len64A, Len65A, 139 | ] 140 | ); 141 | 142 | assert_str_ne!( 143 | Len3A, 144 | [ 145 | Len0, Len1A, Len2A, Len3B, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 146 | Len63A, Len64A, Len65A, 147 | ] 148 | ); 149 | 150 | assert_str_ne!( 151 | Len4A, 152 | [ 153 | Len0, Len1A, Len2A, Len3A, Len4B, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 154 | Len63A, Len64A, Len65A, 155 | ] 156 | ); 157 | 158 | assert_str_ne!( 159 | Len5A, 160 | [ 161 | Len0, Len1A, Len2A, Len3A, Len4A, Len5B, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 162 | Len63A, Len64A, Len65A, 163 | ] 164 | ); 165 | 166 | assert_str_ne!( 167 | Len6A, 168 | [ 169 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6B, Len7A, Len8A, Len9A, Len17A, Len25A, 170 | Len63A, Len64A, Len65A, 171 | ] 172 | ); 173 | 174 | assert_str_ne!( 175 | Len7A, 176 | [ 177 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7B, Len8A, Len9A, Len17A, Len25A, 178 | Len63A, Len64A, Len65A, 179 | ] 180 | ); 181 | 182 | assert_str_ne!( 183 | Len8A, 184 | [ 185 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8B, Len9A, Len17A, Len25A, 186 | Len63A, Len64A, Len65A, 187 | ] 188 | ); 189 | 190 | assert_str_ne!( 191 | Len9A, 192 | [ 193 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9B, Len17A, Len25A, 194 | Len63A, Len64A, Len65A, 195 | ] 196 | ); 197 | 198 | assert_str_ne!( 199 | Len17A, 200 | [ 201 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17B, Len25A, 202 | Len63A, Len64A, Len65A, 203 | ] 204 | ); 205 | 206 | assert_str_ne!( 207 | Len25A, 208 | [ 209 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25B, 210 | Len63A, Len64A, Len65A, 211 | ] 212 | ); 213 | 214 | assert_str_ne!( 215 | Len63A, 216 | [ 217 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 218 | Len63B, Len64A, Len65A, 219 | ] 220 | ); 221 | 222 | assert_str_ne!( 223 | Len64A, 224 | [ 225 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 226 | Len63A, Len64B, Len65A, 227 | ] 228 | ); 229 | 230 | assert_str_ne!( 231 | Len65A, 232 | [ 233 | Len0, Len1A, Len2A, Len3A, Len4A, Len5A, Len6A, Len7A, Len8A, Len9A, Len17A, Len25A, 234 | Len63A, Len64A, Len65B, 235 | ] 236 | ); 237 | } 238 | -------------------------------------------------------------------------------- /tstr/tests/modules/to_uint.rs: -------------------------------------------------------------------------------- 1 | use tstr::{ts, ToUint}; 2 | 3 | fn same(val: T) -> usize { 4 | let v128 = val.to_u128(); 5 | let vusize = val.to_usize(); 6 | assert_eq!(vusize as u128, v128); 7 | vusize 8 | } 9 | 10 | #[test] 11 | fn to_uint() { 12 | assert_eq!(same(ts!(0)), 0); 13 | assert_eq!(same(ts!(5)), 5); 14 | assert_eq!(same(ts!(23)), 23); 15 | assert_eq!(same(ts!(46)), 46); 16 | assert_eq!(same(ts!(50)), 50); 17 | assert_eq!(same(ts!(63)), 63); 18 | assert_eq!(same(ts!(64)), 64); 19 | assert_eq!(same(ts!(65)), 65); 20 | assert_eq!(same(ts!(511)), 511); 21 | assert_eq!(same(ts!(512)), 512); 22 | assert_eq!(same(ts!(513)), 513); 23 | } 24 | 25 | const UMAX: usize = std::usize::MAX; 26 | 27 | #[test] 28 | #[cfg(target_pointer_width = "128")] 29 | fn to_usize() { 30 | assert_eq!(same(ts!(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF)), UMAX); 31 | assert_eq!( 32 | same(ts!(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE)), 33 | UMAX - 1 34 | ); 35 | } 36 | 37 | #[test] 38 | #[cfg(target_pointer_width = "64")] 39 | fn to_usize() { 40 | assert_eq!(same(ts!(0xFFFF_FFFF_FFFF_FFFF)), UMAX); 41 | assert_eq!(same(ts!(0xFFFF_FFFF_FFFF_FFFE)), UMAX - 1); 42 | 43 | assert_eq!(ts!(0x1_0000_0000_0000_0000).to_usize(), UMAX); 44 | assert_eq!(ts!(0x1_0000_0000_0000_0001).to_usize(), UMAX); 45 | } 46 | 47 | #[test] 48 | #[cfg(target_pointer_width = "32")] 49 | fn to_usize() { 50 | assert_eq!(same(ts!(0xFFFF_FFFF)), UMAX); 51 | assert_eq!(same(ts!(0xFFFF_FFFE)), UMAX - 1); 52 | 53 | assert_eq!(ts!(0x1_0000_0000).to_usize(), UMAX); 54 | assert_eq!(ts!(0x1_0000_0001).to_usize(), UMAX); 55 | } 56 | 57 | #[test] 58 | #[cfg(target_pointer_width = "16")] 59 | fn to_usize() { 60 | assert_eq!(same(ts!(0xFFFF)), UMAX); 61 | assert_eq!(same(ts!(0xFFFE)), UMAX - 1); 62 | 63 | assert_eq!(ts!(0x1_0000).to_usize(), UMAX); 64 | assert_eq!(ts!(0x1_0001).to_usize(), UMAX); 65 | } 66 | -------------------------------------------------------------------------------- /tstr/tests/modules/utils.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "const_generics")] 2 | macro_rules! test_case { 3 | ($input:tt, $tytup:ty, $chars:ty, $string:expr $(,)*) => { 4 | const _: tstr::TStr> = ts!($input); 5 | }; 6 | } 7 | 8 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 9 | macro_rules! test_case { 10 | ($input:tt, $tytup:ty, $chars:ty, $string:expr $(,)*) => { 11 | const _: tstr::TStr<$chars> = ts!($input); 12 | }; 13 | } 14 | 15 | #[cfg(not(feature = "min_const_generics"))] 16 | macro_rules! test_case { 17 | ($input:tt, $tytup:ty, $chars:ty, $string:expr $(,)*) => { 18 | const _: tstr::TStr<$tytup> = ts!($input); 19 | }; 20 | } 21 | 22 | macro_rules! str_test_case { 23 | ($string:tt, $tuple:ty $(,)*) => { 24 | test_case!($string, $tuple, $tuple, $string); 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /tstr/tests/modules_.rs: -------------------------------------------------------------------------------- 1 | mod modules { 2 | #[macro_use] 3 | mod utils; 4 | 5 | mod alias_and_tuples; 6 | 7 | mod concat_args; 8 | 9 | mod long_strings; 10 | 11 | mod string_args; 12 | 13 | #[cfg(feature = "cmp_traits")] 14 | mod string_cmp; 15 | 16 | mod other_args; 17 | 18 | mod to_uint; 19 | } 20 | -------------------------------------------------------------------------------- /tstr_proc_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tstr_proc_macros" 3 | version = "0.2.2" 4 | authors = ["rodrimati1992 "] 5 | edition = "2018" 6 | license = "Zlib" 7 | description = "Implementation detail of tstr." 8 | documentation = "https://docs.rs/tstr_proc_macros/" 9 | keywords = [] 10 | categories = ["no-std"] 11 | repository = "https://github.com/rodrimati1992/tstr_crates/" 12 | include = [ 13 | "Cargo.toml", 14 | "src/**/*.rs", 15 | "LICENSE-ZLIB.md", 16 | ] 17 | 18 | [lib] 19 | proc-macro = true 20 | 21 | [features] 22 | default = [] 23 | syn_ = ["syn", "proc_macro2_"] 24 | proc_macro2_ = ["proc-macro2"] 25 | 26 | const_generics = ["min_const_generics"] 27 | min_const_generics = [] 28 | 29 | [dependencies] 30 | proc-macro2 = {version = "1.0", optional = true} 31 | 32 | [dependencies.syn] 33 | version = "1.0.38" 34 | default_features = false 35 | features = ["parsing"] 36 | optional = true 37 | -------------------------------------------------------------------------------- /tstr_proc_macros/LICENSE-ZLIB.md: -------------------------------------------------------------------------------- 1 | ../LICENSE-ZLIB.md -------------------------------------------------------------------------------- /tstr_proc_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::or_fun_call)] 2 | #![allow(clippy::useless_conversion)] 3 | 4 | extern crate proc_macro; 5 | 6 | #[cfg(not(feature = "proc_macro2_"))] 7 | use proc_macro as used_proc_macro; 8 | 9 | #[cfg(feature = "proc_macro2_")] 10 | use proc_macro2 as used_proc_macro; 11 | 12 | use std::iter; 13 | 14 | #[allow(unused_imports)] 15 | use used_proc_macro::{ 16 | Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, 17 | }; 18 | 19 | #[cfg(feature = "syn_")] 20 | mod use_syn; 21 | 22 | #[cfg(not(feature = "syn_"))] 23 | mod non_syn_parsing; 24 | 25 | #[cfg(not(feature = "const_generics"))] 26 | mod nested_tuple_compute; 27 | 28 | mod utils; 29 | 30 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 31 | mod min_const_generics; 32 | 33 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 34 | use min_const_generics::output_tstr_param; 35 | 36 | #[cfg(not(feature = "min_const_generics"))] 37 | mod no_const_generics; 38 | 39 | #[cfg(not(feature = "min_const_generics"))] 40 | use no_const_generics::output_tstr_param; 41 | 42 | #[doc(hidden)] 43 | #[proc_macro] 44 | pub fn __ts_impl(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream { 45 | use crate::utils::{paren, punct_token}; 46 | 47 | let input_tokens = TokenStream::from(input_tokens); 48 | 49 | #[cfg(feature = "syn_")] 50 | let parsed = syn::parse2::(input_tokens); 51 | 52 | #[cfg(not(feature = "syn_"))] 53 | let parsed = non_syn_parsing::parse_inputs(input_tokens); 54 | 55 | match parsed { 56 | Ok(Inputs { 57 | crate_path, 58 | strings, 59 | }) => { 60 | let mut out = TokenStream::new(); 61 | if strings.len() == 1 { 62 | output_tstr(&crate_path, &strings[0], &mut out); 63 | } else { 64 | let tt = paren(Span::call_site(), |out| { 65 | for tstr in &strings { 66 | output_tstr(&crate_path, tstr, out); 67 | out.extend(punct_token(',', tstr.span)); 68 | } 69 | }); 70 | out.extend(iter::once(tt)); 71 | } 72 | out 73 | } 74 | Err(e) => e.to_compile_error(), 75 | } 76 | .into() 77 | } 78 | 79 | fn output_tstr(crate_path: &TokenStream, tstr: &TStr, out: &mut TokenStream) { 80 | use crate::utils::{colon2_token, ident_token, punct_token}; 81 | 82 | let span = tstr.span; 83 | out.extend(crate_path.clone()); 84 | out.extend(colon2_token(span)); 85 | out.extend(ident_token("TStr", span)); 86 | out.extend(punct_token('<', span)); 87 | 88 | #[cfg(feature = "const_generics")] 89 | { 90 | out.extend(crate_path.clone()); 91 | out.extend(colon2_token(span)); 92 | out.extend(ident_token("___", span)); 93 | out.extend(punct_token('<', span)); 94 | } 95 | 96 | output_tstr_param(crate_path, tstr, out); 97 | 98 | #[cfg(feature = "const_generics")] 99 | { 100 | out.extend(punct_token('>', span)); 101 | } 102 | 103 | out.extend(punct_token('>', span)); 104 | } 105 | 106 | #[cfg(feature = "const_generics")] 107 | fn output_tstr_param(_crate_path: &TokenStream, tstr: &TStr, out: &mut TokenStream) { 108 | let string = tstr.string.as_str(); 109 | let span = tstr.span; 110 | 111 | let mut lit = Literal::string(&string); 112 | lit.set_span(span); 113 | out.extend(iter::once(TokenTree::from(lit))); 114 | } 115 | 116 | struct Inputs { 117 | crate_path: TokenStream, 118 | strings: Vec, 119 | } 120 | 121 | struct TStr { 122 | string: String, 123 | span: Span, 124 | } 125 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/min_const_generics.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | used_proc_macro::{Span, TokenStream}, 3 | utils::{char_token, colon2_token, ident_token, punct_token}, 4 | TStr, 5 | }; 6 | 7 | pub(crate) fn output_tstr_param(crate_path: &TokenStream, tstr: &TStr, out: &mut TokenStream) { 8 | let string = tstr.string.chars().collect::>(); 9 | let span = tstr.span; 10 | 11 | out.extend(crate::nested_tuple_compute::compute( 12 | &string, 13 | span, 14 | &mut |string, ts| { 15 | write_chars(ts, string, crate_path, span); 16 | }, 17 | )); 18 | } 19 | 20 | fn write_chars(ts: &mut TokenStream, string: &[char], crate_path: &TokenStream, span: Span) { 21 | const TY: &[&str; 9] = &["", "__a", "__b", "__c", "__d", "__e", "__f", "__g", "__"]; 22 | 23 | ts.extend(crate_path.clone()); 24 | ts.extend(colon2_token(span)); 25 | ts.extend(ident_token(TY[string.len()], span)); 26 | ts.extend(punct_token('<', span)); 27 | for &c in string { 28 | ts.extend(char_token(c, span)); 29 | ts.extend(punct_token(',', span)); 30 | } 31 | ts.extend(punct_token('>', span)); 32 | } 33 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/nested_tuple_compute.rs: -------------------------------------------------------------------------------- 1 | //! Wraps the input type in nested tuples 2 | 3 | use crate::{ 4 | used_proc_macro::{Span, TokenStream}, 5 | utils::{paren, punct_token}, 6 | }; 7 | 8 | use std::iter; 9 | 10 | #[cfg(test)] 11 | mod tests; 12 | 13 | #[must_use] 14 | pub(crate) fn compute(input: &[T], span: Span, func: &mut F) -> TokenStream 15 | where 16 | F: FnMut(&[T], &mut TokenStream), 17 | { 18 | let mut out = TokenStream::new(); 19 | 20 | if input.is_empty() { 21 | out.extend(std::iter::once(paren(span, |_| ()))); 22 | } else if input.len() <= CHUNK_SIZE { 23 | func(input, &mut out); 24 | } else { 25 | let tt = paren(span, |out| { 26 | let lower_power = find_smaller_power(input.len()); 27 | 28 | for chunk in input.chunks(lower_power) { 29 | out.extend(compute(chunk, span, func)); 30 | out.extend(punct_token(',', span)); 31 | } 32 | }); 33 | out.extend(iter::once(tt)); 34 | } 35 | 36 | out 37 | } 38 | 39 | const CHUNK_SIZE: usize = 8; 40 | 41 | fn find_smaller_power(than: usize) -> usize { 42 | let mut curr = 1; 43 | loop { 44 | let next = curr * 8; 45 | if next >= than { 46 | break curr; 47 | } else { 48 | curr = next; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/nested_tuple_compute/tests.rs: -------------------------------------------------------------------------------- 1 | use super::find_smaller_power; 2 | 3 | #[test] 4 | fn test_find_smaller_power() { 5 | assert_eq!(find_smaller_power(1), 1); 6 | assert_eq!(find_smaller_power(5), 1); 7 | assert_eq!(find_smaller_power(7), 1); 8 | assert_eq!(find_smaller_power(8), 1); 9 | assert_eq!(find_smaller_power(9), 8); 10 | assert_eq!(find_smaller_power(63), 8); 11 | assert_eq!(find_smaller_power(64), 8); 12 | assert_eq!(find_smaller_power(65), 64); 13 | assert_eq!(find_smaller_power(511), 64); 14 | assert_eq!(find_smaller_power(512), 64); 15 | assert_eq!(find_smaller_power(513), 512); 16 | assert_eq!(find_smaller_power(4095), 512); 17 | assert_eq!(find_smaller_power(4096), 512); 18 | assert_eq!(find_smaller_power(4097), 4096); 19 | } 20 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/no_const_generics.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use crate::{ 4 | used_proc_macro::{Span, TokenStream}, 5 | utils::{colon2_token, ident_token, paren, punct_token}, 6 | TStr, 7 | }; 8 | 9 | pub(super) fn output_tstr_param(crate_path: &TokenStream, tstr: &TStr, out: &mut TokenStream) { 10 | let string = tstr.string.as_str(); 11 | let span = tstr.span; 12 | let string = string.as_bytes(); 13 | 14 | out.extend(crate::nested_tuple_compute::compute( 15 | string, 16 | span, 17 | &mut |string, ts| write_bytes(ts, string, crate_path, span), 18 | )); 19 | } 20 | 21 | fn write_bytes(ts: &mut TokenStream, string: &[u8], crate_path: &TokenStream, span: Span) { 22 | let tt = paren(span, |ts| { 23 | for &b in string { 24 | ts.extend(crate_path.clone()); 25 | ts.extend(colon2_token(span)); 26 | ts.extend(ident_token(BYTE_NAME[b as usize], span)); 27 | ts.extend(punct_token(',', span)); 28 | } 29 | }); 30 | 31 | ts.extend(iter::once(tt)); 32 | } 33 | 34 | const BYTE_NAME: [&str; 256] = [ 35 | "__0x00", "__0x01", "__0x02", "__0x03", "__0x04", "__0x05", "__0x06", "__0x07", "__0x08", 36 | "__0x09", "__0x0A", "__0x0B", "__0x0C", "__0x0D", "__0x0E", "__0x0F", "__0x10", "__0x11", 37 | "__0x12", "__0x13", "__0x14", "__0x15", "__0x16", "__0x17", "__0x18", "__0x19", "__0x1A", 38 | "__0x1B", "__0x1C", "__0x1D", "__0x1E", "__0x1F", "__0x20", "__0x21", "__0x22", "__0x23", 39 | "__0x24", "__0x25", "__0x26", "__0x27", "__0x28", "__0x29", "__0x2A", "__0x2B", "__0x2C", 40 | "__0x2D", "__0x2E", "__0x2F", "__0", "__1", "__2", "__3", "__4", "__5", "__6", "__7", "__8", 41 | "__9", "__0x3A", "__0x3B", "__0x3C", "__0x3D", "__0x3E", "__0x3F", "__0x40", "__A", "__B", 42 | "__C", "__D", "__E", "__F", "__G", "__H", "__I", "__J", "__K", "__L", "__M", "__N", "__O", 43 | "__P", "__Q", "__R", "__S", "__T", "__U", "__V", "__W", "__X", "__Y", "__Z", "__0x5B", 44 | "__0x5C", "__0x5D", "__0x5E", "____", "__0x60", "__a", "__b", "__c", "__d", "__e", "__f", 45 | "__g", "__h", "__i", "__j", "__k", "__l", "__m", "__n", "__o", "__p", "__q", "__r", "__s", 46 | "__t", "__u", "__v", "__w", "__x", "__y", "__z", "__0x7B", "__0x7C", "__0x7D", "__0x7E", 47 | "__0x7F", "__0x80", "__0x81", "__0x82", "__0x83", "__0x84", "__0x85", "__0x86", "__0x87", 48 | "__0x88", "__0x89", "__0x8A", "__0x8B", "__0x8C", "__0x8D", "__0x8E", "__0x8F", "__0x90", 49 | "__0x91", "__0x92", "__0x93", "__0x94", "__0x95", "__0x96", "__0x97", "__0x98", "__0x99", 50 | "__0x9A", "__0x9B", "__0x9C", "__0x9D", "__0x9E", "__0x9F", "__0xA0", "__0xA1", "__0xA2", 51 | "__0xA3", "__0xA4", "__0xA5", "__0xA6", "__0xA7", "__0xA8", "__0xA9", "__0xAA", "__0xAB", 52 | "__0xAC", "__0xAD", "__0xAE", "__0xAF", "__0xB0", "__0xB1", "__0xB2", "__0xB3", "__0xB4", 53 | "__0xB5", "__0xB6", "__0xB7", "__0xB8", "__0xB9", "__0xBA", "__0xBB", "__0xBC", "__0xBD", 54 | "__0xBE", "__0xBF", "__0xC0", "__0xC1", "__0xC2", "__0xC3", "__0xC4", "__0xC5", "__0xC6", 55 | "__0xC7", "__0xC8", "__0xC9", "__0xCA", "__0xCB", "__0xCC", "__0xCD", "__0xCE", "__0xCF", 56 | "__0xD0", "__0xD1", "__0xD2", "__0xD3", "__0xD4", "__0xD5", "__0xD6", "__0xD7", "__0xD8", 57 | "__0xD9", "__0xDA", "__0xDB", "__0xDC", "__0xDD", "__0xDE", "__0xDF", "__0xE0", "__0xE1", 58 | "__0xE2", "__0xE3", "__0xE4", "__0xE5", "__0xE6", "__0xE7", "__0xE8", "__0xE9", "__0xEA", 59 | "__0xEB", "__0xEC", "__0xED", "__0xEE", "__0xEF", "__0xF0", "__0xF1", "__0xF2", "__0xF3", 60 | "__0xF4", "__0xF5", "__0xF6", "__0xF7", "__0xF8", "__0xF9", "__0xFA", "__0xFB", "__0xFC", 61 | "__0xFD", "__0xFE", "__0xFF", 62 | ]; 63 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/non_syn_parsing.rs: -------------------------------------------------------------------------------- 1 | use std::iter::once; 2 | 3 | #[allow(unused_imports)] 4 | use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; 5 | 6 | use proc_macro::token_stream::IntoIter as TSIterator; 7 | 8 | use super::{Inputs, TStr}; 9 | 10 | pub(crate) fn parse_inputs(ts: TokenStream) -> Result { 11 | let iter = &mut ts.into_iter(); 12 | 13 | let crate_path = match iter.next() { 14 | Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Parenthesis => { 15 | group.stream() 16 | } 17 | Some(x) => { 18 | return Err(Error::new( 19 | x.span(), 20 | &format!("Expected parentheses: found {}", x), 21 | )) 22 | } 23 | None => { 24 | return Err(Error::new( 25 | Span::call_site(), 26 | "Expected parentheses, found nothing", 27 | )) 28 | } 29 | }; 30 | 31 | let mut strings = Vec::::with_capacity(1); 32 | 33 | while let Some(x) = parse_tstr(iter)? { 34 | strings.push(x); 35 | } 36 | 37 | Ok(Inputs { 38 | crate_path, 39 | strings, 40 | }) 41 | } 42 | 43 | fn parse_tstr(iter: &mut TSIterator) -> Result, Error> { 44 | const IN_MSG: &str = "Expected one of: string literal, integer literal, identifier"; 45 | match iter.next() { 46 | Some(TokenTree::Ident(ident)) => { 47 | let mut string = ident.to_string(); 48 | if string == "concat" { 49 | let (span, ts) = parse_post_macro_name(iter)?; 50 | 51 | let mut string = String::new(); 52 | let iter = &mut ts.into_iter(); 53 | 54 | while let Some(tstr) = parse_tstr(iter)? { 55 | string.push_str(&tstr.string); 56 | 57 | if let sep @ Some(_) = iter.next() { 58 | assert_punct(sep, ',')?; 59 | } 60 | } 61 | 62 | Ok(Some(TStr { string, span })) 63 | } else if string == "stringify" { 64 | let (span, ts) = parse_post_macro_name(iter)?; 65 | 66 | let string = ts.to_string(); 67 | 68 | Ok(Some(TStr { string, span })) 69 | } else { 70 | let trimmed = string.trim_start_matches("r#"); 71 | if trimmed.len() != string.len() { 72 | string = trimmed.to_string(); 73 | } 74 | 75 | Ok(Some(TStr { 76 | string, 77 | span: ident.span(), 78 | })) 79 | } 80 | } 81 | Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => { 82 | parse_tstr(&mut group.stream().into_iter()) 83 | } 84 | Some(TokenTree::Literal(lit)) => parse_literal(lit).map(Some), 85 | Some(x) => Err(Error::new(x.span(), &format!("{}\nFound: {}", IN_MSG, x))), 86 | None => Ok(None), 87 | } 88 | } 89 | 90 | fn parse_post_macro_name(iter: &mut TSIterator) -> Result<(Span, TokenStream), Error> { 91 | let bang_span = assert_punct(iter.next(), '!')?; 92 | match iter.next() { 93 | Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => { 94 | Ok((g.span(), g.stream())) 95 | } 96 | _ => Err(Error::new(bang_span, "Expected `( ..... )` after `!`")), 97 | } 98 | } 99 | 100 | fn parse_literal(lit: Literal) -> Result { 101 | let span = lit.span(); 102 | let string = lit.to_string(); 103 | 104 | let string = if string.starts_with('"') { 105 | parse_string(&string, span)? 106 | } else if string.starts_with('r') { 107 | parse_raw_string(&string, span)? 108 | } else { 109 | parse_integer(&string, span)? 110 | }; 111 | 112 | Ok(TStr { string, span }) 113 | } 114 | 115 | fn parse_string(input: &str, span: Span) -> Result { 116 | if !input.ends_with('"') { 117 | return Err(Error::new( 118 | span, 119 | "Somehow there's no terminating quote character?", 120 | )); 121 | } 122 | 123 | let make_err = |rem: &str, error: &str| -> Error { 124 | let pos = rem.as_ptr() as usize - input.as_ptr() as usize; 125 | 126 | let upto = input[..pos] 127 | .chars() 128 | .rev() 129 | .take(10) 130 | .collect::() 131 | .chars() 132 | .rev() 133 | .collect::(); 134 | 135 | Error::new(span, &format!("Error: {} After: {}", error, upto,)) 136 | }; 137 | 138 | let mut rem = &input[1..input.len() - 1]; 139 | let mut out = String::new(); 140 | 141 | loop { 142 | let end_copied = rem.find('\\').unwrap_or(rem.len()); 143 | out.push_str(&rem[..end_copied]); 144 | 145 | rem = &rem[end_copied..]; 146 | 147 | if rem.is_empty() { 148 | break; 149 | } 150 | 151 | // The byte after the '\\' character 152 | let b = get_byte(rem, 1); 153 | 154 | // Now we're at the character right after the matched one. 155 | rem = &rem[2..]; 156 | 157 | out.push(match b { 158 | b'x' => { 159 | if let Some(hex) = rem.get(..2) { 160 | let num = u8::from_str_radix(hex, 16) 161 | .ok() 162 | .filter(|&x| x < 128) 163 | .ok_or_else(|| { 164 | make_err( 165 | rem, 166 | &format!("expected values from \\x00 to \\x7F, found: {}", hex), 167 | ) 168 | })?; 169 | out.push(num as char); 170 | } else { 171 | return Err(make_err(rem, "invalid ascii escape")); 172 | } 173 | rem = &rem[2..]; 174 | continue; 175 | } 176 | b'u' => { 177 | if let Some(end_brace) = rem.bytes().position(|b| b == b'}') { 178 | let c: char = u32::from_str_radix(&rem[1..end_brace], 16) 179 | .ok() 180 | .and_then(std::char::from_u32) 181 | .ok_or_else(|| { 182 | make_err( 183 | rem, 184 | &format!("Invalid unicode escape: {}", &rem[..end_brace]), 185 | ) 186 | })?; 187 | out.push(c); 188 | 189 | rem = &rem[end_brace + 1..]; 190 | } else { 191 | return Err(make_err(rem, "Expected closing brace for unicode escape")); 192 | } 193 | continue; 194 | } 195 | b'n' => '\n', 196 | b'r' => '\r', 197 | b't' => '\t', 198 | b'\\' => '\\', 199 | b'0' => '\0', 200 | b'\'' => '\'', 201 | b'"' => '"', 202 | b'\r' | b'\n' => { 203 | rem = rem.trim_start(); 204 | continue; 205 | } 206 | _ => return Err(make_err(rem, "invalid escape")), 207 | }); 208 | } 209 | 210 | Ok(out) 211 | } 212 | 213 | fn get_byte(s: &str, at: usize) -> u8 { 214 | match s.as_bytes().get(at) { 215 | Some(&x) => x, 216 | None => 0, 217 | } 218 | } 219 | 220 | fn parse_raw_string(input: &str, span: Span) -> Result { 221 | let input = &input[1..]; 222 | 223 | let hash_count = input 224 | .bytes() 225 | .position(|b| b != b'#') 226 | .filter(|&p| input.as_bytes()[p] == b'"') 227 | .ok_or_else(|| { 228 | Error::new( 229 | span, 230 | "Couldn't find initial '\"' character in raw string literal.", 231 | ) 232 | })?; 233 | 234 | let end_quote = input 235 | .bytes() 236 | .rev() 237 | .position(|b| b != b'#') 238 | .map(|p| input.len() - 1 - p) 239 | .filter(|&p| input.as_bytes()[p] == b'"') 240 | .ok_or_else(|| { 241 | Error::new( 242 | span, 243 | "Couldn't find final '\"' character in raw string literal.", 244 | ) 245 | })?; 246 | 247 | Ok(input[hash_count + 1..end_quote].to_string()) 248 | } 249 | 250 | fn parse_integer(input: &str, span: Span) -> Result { 251 | fn make_err(input: &str, span: Span) -> Error { 252 | Error::new( 253 | span, 254 | &format!("could not parse as integer literal: {}", input), 255 | ) 256 | } 257 | 258 | let input = input.replace('_', ""); 259 | let input = input.as_str(); 260 | 261 | let input_bytes = input.as_bytes(); 262 | if input_bytes.get(0) == Some(&b'0') { 263 | let radix = match input_bytes.get(1) { 264 | Some(b'x') => 16, 265 | Some(b'o') => 8, 266 | Some(b'b') => 2, 267 | Some(_) => { 268 | return Err(Error::new( 269 | span, 270 | &format!("Unknown integer prefix: {}", &input[..2]), 271 | )) 272 | } 273 | None => return Ok(String::from("0")), 274 | }; 275 | u128::from_str_radix(&input[2..], radix).map_err(|_| make_err(input, span)) 276 | } else { 277 | input.parse::().map_err(|_| make_err(input, span)) 278 | } 279 | .map(|i| i.to_string()) 280 | } 281 | 282 | fn assert_punct(tt: Option, c: char) -> Result { 283 | match tt { 284 | Some(TokenTree::Punct(p)) if p.as_char() == c => Ok(p.span()), 285 | Some(x) => Err(Error::new( 286 | x.span(), 287 | &format!("Expected `{}`, found `{}`", c, x), 288 | )), 289 | None => Err(Error::new(Span::call_site(), "Expected some token")), 290 | } 291 | } 292 | 293 | pub(crate) struct Error { 294 | span: Span, 295 | message: String, 296 | } 297 | 298 | impl Error { 299 | fn new(span: Span, message: &str) -> Self { 300 | Self { 301 | span, 302 | message: message.to_string(), 303 | } 304 | } 305 | 306 | pub(crate) fn to_compile_error(&self) -> TokenStream { 307 | let Error { ref message, span } = *self; 308 | 309 | let mut out = TokenStream::new(); 310 | 311 | out.extend(crate::utils::ident_token("compile_error", span)); 312 | 313 | out.extend(crate::utils::punct_token('!', span)); 314 | 315 | let msg_paren = crate::utils::paren(span, |ts| { 316 | let mut msg = Literal::string(message); 317 | msg.set_span(self.span); 318 | let msg = TokenTree::from(msg); 319 | ts.extend(once(msg)) 320 | }); 321 | out.extend(once(msg_paren)); 322 | 323 | out 324 | } 325 | } 326 | 327 | trait TokenTreeExt: Sized { 328 | fn into_token_tree(self) -> TokenTree; 329 | 330 | fn set_span_recursive(self, span: Span) -> TokenTree { 331 | let mut tt = self.into_token_tree(); 332 | 333 | tt.set_span(span); 334 | if let TokenTree::Group(group) = tt { 335 | let delim = group.delimiter(); 336 | let stream = group.stream().set_span_recursive(span); 337 | tt = TokenTree::Group(Group::new(delim, stream)); 338 | } 339 | tt.set_span(span); 340 | tt 341 | } 342 | } 343 | 344 | impl TokenTreeExt for TokenTree { 345 | fn into_token_tree(self) -> TokenTree { 346 | self 347 | } 348 | } 349 | 350 | pub trait TokenStreamExt: Sized { 351 | fn into_token_stream(self) -> TokenStream; 352 | 353 | fn set_span_recursive(self, span: Span) -> TokenStream { 354 | self.into_token_stream() 355 | .into_iter() 356 | .map(|tt| tt.set_span_recursive(span)) 357 | .collect() 358 | } 359 | } 360 | 361 | impl TokenStreamExt for TokenStream { 362 | fn into_token_stream(self) -> TokenStream { 363 | self 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/use_syn.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | 3 | use syn::{ 4 | ext::IdentExt, 5 | parenthesized, 6 | parse::{Parse, ParseBuffer, ParseStream}, 7 | LitInt, LitStr, 8 | }; 9 | 10 | use super::{Inputs, TStr}; 11 | 12 | impl Parse for Inputs { 13 | fn parse(input: ParseStream<'_>) -> syn::Result { 14 | let content; 15 | let _ = parenthesized!(content in input); 16 | 17 | let crate_path = content.parse::()?; 18 | 19 | let mut strings = Vec::::new(); 20 | while !input.is_empty() { 21 | strings.push(input.parse()?); 22 | } 23 | 24 | Ok(Self { 25 | crate_path, 26 | strings, 27 | }) 28 | } 29 | } 30 | 31 | impl Parse for TStr { 32 | fn parse(input: ParseStream<'_>) -> syn::Result { 33 | let lookahead = input.lookahead1(); 34 | let (string, span) = if lookahead.peek(kw::concat) { 35 | input.parse::()?; 36 | let (span, content) = parse_post_macro_name(input)?; 37 | let mut value = String::new(); 38 | 39 | while !content.is_empty() { 40 | let tstr = content.parse::()?; 41 | 42 | value.push_str(&tstr.string); 43 | 44 | if !content.is_empty() { 45 | content.parse::()?; 46 | } 47 | } 48 | 49 | (value, span) 50 | } else if lookahead.peek(kw::stringify) { 51 | input.parse::()?; 52 | let (span, content) = parse_post_macro_name(input)?; 53 | (content.parse::()?.to_string(), span) 54 | } else if lookahead.peek(syn::Ident::peek_any) { 55 | let ident = input.parse::()?; 56 | let mut value = ident.to_string(); 57 | if value.starts_with("r#") { 58 | value.drain(..2); 59 | } 60 | (value, ident.span()) 61 | } else if lookahead.peek(LitStr) { 62 | let lit = input.parse::()?; 63 | (lit.value(), lit.span()) 64 | } else if lookahead.peek(LitInt) { 65 | let lit = input.parse::()?; 66 | (lit.base10_digits().to_string(), lit.span()) 67 | } else { 68 | return Err(lookahead.error()); 69 | }; 70 | Ok(Self { string, span }) 71 | } 72 | } 73 | 74 | fn parse_post_macro_name(input: ParseStream) -> syn::Result<(Span, ParseBuffer)> { 75 | input.parse::()?; 76 | let content; 77 | let paren = parenthesized!(content in input); 78 | Ok((paren.span, content)) 79 | } 80 | 81 | mod kw { 82 | syn::custom_keyword!(concat); 83 | syn::custom_keyword!(stringify); 84 | } 85 | -------------------------------------------------------------------------------- /tstr_proc_macros/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::iter::{self, FromIterator, Once}; 2 | 3 | #[allow(unused_imports)] 4 | use crate::used_proc_macro::{ 5 | Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, 6 | }; 7 | 8 | pub(crate) fn ident_token(ident: &str, span: Span) -> Once { 9 | let ident = Ident::new(ident, span); 10 | let tt = TokenTree::from(ident); 11 | iter::once(tt) 12 | } 13 | 14 | #[cfg(all(feature = "min_const_generics", not(feature = "const_generics")))] 15 | pub(crate) fn char_token(c: char, span: Span) -> Once { 16 | let mut lit = Literal::character(c); 17 | lit.set_span(span); 18 | let tt = TokenTree::from(lit); 19 | iter::once(tt) 20 | } 21 | 22 | pub(crate) fn punct_token(token: char, span: Span) -> Once { 23 | let mut token = Punct::new(token, Spacing::Alone); 24 | token.set_span(span); 25 | let tt = TokenTree::from(token); 26 | iter::once(tt) 27 | } 28 | pub(crate) fn colon2_token(span: Span) -> TokenStream { 29 | let mut token = Punct::new(':', Spacing::Joint); 30 | token.set_span(span); 31 | TokenStream::from_iter(vec![TokenTree::from(token.clone()), TokenTree::from(token)]) 32 | } 33 | 34 | pub(crate) fn paren(span: Span, f: F) -> TokenTree 35 | where 36 | F: FnOnce(&mut TokenStream), 37 | { 38 | let mut ts = TokenStream::new(); 39 | f(&mut ts); 40 | let mut tt = Group::new(Delimiter::Parenthesis, ts); 41 | tt.set_span(span); 42 | TokenTree::from(tt) 43 | } 44 | --------------------------------------------------------------------------------