├── .github └── workflows │ ├── ci-actions.yml │ └── docs-actions.yml ├── .gitignore ├── .gitpod.yml ├── LICENSE ├── README.md ├── dscanner.ini ├── dub.json ├── dub.selections.json └── source └── quirks ├── aggregate.d ├── core.d ├── expression.d ├── functional.d ├── internal └── test.d ├── package.d ├── tuple.d ├── type.d └── utility.d /.github/workflows/ci-actions.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | 15 | strategy: 16 | matrix: 17 | dmd_version: [2.092.0, 2.091.1, 2.090.1, 2.089.1, 2.088.1] 18 | os: [ubuntu-latest, windows-latest] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Install DMD 24 | uses: WebFreak001/setup-dmd@v1 25 | with: 26 | dmd-version: ${{ matrix.dmd_version }} 27 | 28 | - name: Build 29 | run: dub build 30 | 31 | - name: Test 32 | run: dub test 33 | 34 | codecov: 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v2 39 | 40 | - name: Install DMD 41 | uses: WebFreak001/setup-dmd@v1 42 | with: 43 | dmd-version: 2.091.0 44 | 45 | - name: Test 46 | run: dub test -b unittest-cov 47 | 48 | - name: Upload coverage to Codecov 49 | uses: codecov/codecov-action@v1.0.7 50 | with: 51 | token: ${{secrets.CODECOV_TOKEN}} 52 | 53 | -------------------------------------------------------------------------------- /.github/workflows/docs-actions.yml: -------------------------------------------------------------------------------- 1 | name: Publish documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | generate-and-publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly. 14 | with: 15 | persist-credentials: false 16 | 17 | - name: Install DMD 18 | uses: WebFreak001/setup-dmd@v1 19 | with: 20 | dmd-version: 2.091.0 21 | 22 | - name: Build 23 | run: | 24 | dub upgrade 25 | dub build -b ddox 26 | 27 | - name: Deploy 28 | uses: JamesIves/github-pages-deploy-action@releases/v3 29 | with: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | BRANCH: gh-pages # The branch the action should deploy to. 32 | FOLDER: docs # The folder the action should deploy. 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.o 3 | *.obj 4 | 5 | # Compiled Dynamic libraries 6 | *.so 7 | *.dylib 8 | *.dll 9 | 10 | # Compiled Static libraries 11 | *.a 12 | *.lib 13 | 14 | # Executables 15 | *.exe 16 | 17 | # DUB 18 | .dub 19 | docs 20 | docs.json 21 | __dummy.html 22 | 23 | # Code coverage 24 | *.lst 25 | 26 | # Visual Studio 27 | *.sln 28 | .vs 29 | 30 | quirks-test-unittest 31 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | 2 | tasks: 3 | - name: DMD 4 | init: curl -fsS https://dlang.org/install.sh | bash -s dmd 5 | 6 | 7 | vscode: 8 | extensions: 9 | - eamodio.gitlens 10 | - pflannery.vscode-versionlens 11 | - webfreak.code-d 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jochem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quirks 2 | [![DUB](https://img.shields.io/dub/v/quirks)](https://code.dlang.org/packages/quirks) 3 | ![DUB](https://img.shields.io/dub/l/quirks) 4 | 5 | [![Github Actions](https://img.shields.io/github/workflow/status/lljochemll/quirks/ci/master)](https://github.com/lljochemll/quirks/actions?query=workflow%3Aci) 6 | [![codecov](https://codecov.io/gh/llJochemll/quirks/branch/master/graph/badge.svg)](https://codecov.io/gh/llJochemll/quirks) 7 | 8 | 9 | quirks is a small library to simplify programming with traits and mixins. 10 | 11 | See https://lljochemll.github.io/quirks/ for documentation and examples 12 | 13 | For a list of "weird" things, look at the bottom 14 | 15 | ## Features 16 | ### Quirks template 17 | Swiss army knife for getting information about things 18 | ```D 19 | import quirks; 20 | import std.stdio; 21 | 22 | class User { 23 | uint age; 24 | string name; 25 | 26 | void talk(string message) { 27 | import std.stdio : writeln; 28 | 29 | writeln(message); 30 | } 31 | 32 | bool isSitting() { 33 | return false; 34 | } 35 | } 36 | 37 | auto userInstance = new User; 38 | 39 | // can use both type and variable 40 | alias info = Quirks!userInstance; // also works with Quirks!User 41 | 42 | // Works at compile and runtime 43 | pragma(msg, info.methods.length); // 2 44 | pragma(msg, info.fields.length); // 2 45 | pragma(msg, info.members.length); // 4 46 | pragma(msg, info.isAggregate); // true 47 | pragma(msg, info.methods[0].name); // talk 48 | pragma(msg, info.methods[0].parameters[0].name); // message 49 | pragma(msg, info.methods.filter!(m => is(m.returnType == bool)).filter!(m => true)[0].name); // isSitting 50 | pragma(msg, info.methods.filter!(m => m.parameters.filter!(p => p.name == "message").length > 0)[0].name); // talk 51 | ``` 52 | 53 | ### interpolateMixin 54 | Adds string interpolation to mixins 55 | ```D 56 | import quirks; 57 | import std.stdio; 58 | 59 | class Foo { 60 | int id = 1; 61 | uint age = 23; 62 | string name = "foo"; 63 | } 64 | 65 | auto foo = new Foo; 66 | 67 | // version with interpolateMixin 68 | static foreach (member; FieldNameTuple!(Foo)) { 69 | mixin(interpolateMixin(q{ 70 | writeln("Field ${member} has a value of: ", foo.${member}); 71 | })); 72 | } 73 | 74 | // version without 75 | static foreach (member; FieldNameTuple!(Foo)) { 76 | mixin(` 77 | writeln("Field ` ~ member ~ ` has a value of: ", foo.` ~ member ~ `); 78 | `); 79 | } 80 | ``` 81 | 82 | ### hasMember, hasField, hasMethod 83 | ```D 84 | import quirks; 85 | 86 | struct Foo { 87 | long id; 88 | string name() { 89 | return "name"; 90 | } 91 | } 92 | 93 | static assert(hasMember!(S, "id")); 94 | static assert(hasMember!(S, "name")); 95 | static assert(!hasMember!(S, "doesNotExist")); 96 | 97 | static assert(hasField!(S, "id")); 98 | static assert(!hasField!(S, "name")); 99 | static assert(!hasField!(S, "doesNotExist")); 100 | 101 | static assert(!hasMethod!(S, "id")); 102 | static assert(hasMethod!(S, "name")); 103 | static assert(!hasMethod!(S, "doesNotExist")); 104 | static assert(hasMethod!(S, method => is(method.returnType == string))); 105 | ``` 106 | 107 | ### Parameters 108 | ```D 109 | import quirks; 110 | import std.stdio; 111 | 112 | uint calculateAge(long birthYear, string planet = "earth"); 113 | 114 | alias parameters = Parameters!calculateAge; 115 | 116 | static foreach (parameter; parameters) { 117 | write("Parameter " , parameter.name, " has a type of ", parameter.type.stringof); 118 | 119 | static if (parameter.hasDefaultValue) { 120 | writeln(" and a default value of ", parameter.defaultValue); 121 | } else { 122 | writeln(" and no default value"); 123 | } 124 | } 125 | ``` 126 | ## Weird things 127 | ### AliasTuple 128 | AliasTuple has some weird things going on 129 | 130 | TL;DR: use .tuple if you want to be sure it will always work 131 | 132 | While it is possible to do this: 133 | ```D 134 | alias things = AliasTuple!(bool, string, "hi"); 135 | 136 | pragma(msg, things[0]); // displays bool 137 | pragma(msg, things[2].length); // displays 2 138 | ``` 139 | This will not work: 140 | ```D 141 | alias things = AliasTuple!(bool, string, "hi"); 142 | 143 | pragma(msg, is(things[0] == bool); // displays false 144 | ``` 145 | But this will: 146 | ```D 147 | alias things = AliasTuple!(bool, string, "hi"); 148 | 149 | pragma(msg, is(things.tuple[0] == bool)); // displays true 150 | pragma(msg, things[2].length == 2); // displays true 151 | pragma(msg, things.tuple[2].length == 2); // displays true 152 | ``` 153 | -------------------------------------------------------------------------------- /dscanner.ini: -------------------------------------------------------------------------------- 1 | ; Configure which static analysis checks are enabled 2 | [analysis.config.StaticAnalysisConfig] 3 | ; Check variable, class, struct, interface, union, and function names against t 4 | ; he Phobos style guide 5 | style_check="enabled" 6 | ; Check for array literals that cause unnecessary allocation 7 | enum_array_literal_check="enabled" 8 | ; Check for poor exception handling practices 9 | exception_check="enabled" 10 | ; Check for use of the deprecated 'delete' keyword 11 | delete_check="enabled" 12 | ; Check for use of the deprecated floating point operators 13 | float_operator_check="enabled" 14 | ; Check number literals for readability 15 | number_style_check="enabled" 16 | ; Checks that opEquals, opCmp, toHash, and toString are either const, immutable 17 | ; , or inout. 18 | object_const_check="enabled" 19 | ; Checks for .. expressions where the left side is larger than the right. 20 | backwards_range_check="enabled" 21 | ; Checks for if statements whose 'then' block is the same as the 'else' block 22 | if_else_same_check="enabled" 23 | ; Checks for some problems with constructors 24 | constructor_check="enabled" 25 | ; Checks for unused variables 26 | unused_variable_check="enabled" 27 | ; Checks for unused labels 28 | unused_label_check="enabled" 29 | ; Checks for unused function parameters 30 | unused_parameter_check="enabled" 31 | ; Checks for duplicate attributes 32 | duplicate_attribute="enabled" 33 | ; Checks that opEquals and toHash are both defined or neither are defined 34 | opequals_tohash_check="enabled" 35 | ; Checks for subtraction from .length properties 36 | length_subtraction_check="enabled" 37 | ; Checks for methods or properties whose names conflict with built-in propertie 38 | ; s 39 | builtin_property_names_check="enabled" 40 | ; Checks for confusing code in inline asm statements 41 | asm_style_check="enabled" 42 | ; Checks for confusing logical operator precedence 43 | logical_precedence_check="enabled" 44 | ; Checks for undocumented public declarations 45 | undocumented_declaration_check="enabled" 46 | ; Checks for poor placement of function attributes 47 | function_attribute_check="enabled" 48 | ; Checks for use of the comma operator 49 | comma_expression_check="enabled" 50 | ; Checks for local imports that are too broad 51 | local_import_check="disabled" 52 | ; Checks for variables that could be declared immutable 53 | could_be_immutable_check="enabled" 54 | ; Checks for redundant expressions in if statements 55 | redundant_if_check="enabled" 56 | ; Checks for redundant parenthesis 57 | redundant_parens_check="enabled" 58 | ; Checks for mismatched argument and parameter names 59 | mismatched_args_check="enabled" 60 | ; Checks for labels with the same name as variables 61 | label_var_same_name_check="enabled" 62 | ; Checks for lines longer than 120 characters 63 | long_line_check="enabled" 64 | ; Checks for assignment to auto-ref function parameters 65 | auto_ref_assignment_check="enabled" 66 | ; Checks for incorrect infinite range definitions 67 | incorrect_infinite_range_check="enabled" 68 | ; Checks for asserts that are always true 69 | useless_assert_check="enabled" 70 | ; Check for uses of the old-style alias syntax 71 | alias_syntax_check="enabled" 72 | ; Checks for else if that should be else static if 73 | static_if_else_check="enabled" 74 | ; Check for unclear lambda syntax 75 | lambda_return_check="enabled" 76 | ; Check for auto function without return statement 77 | auto_function_check="enabled" 78 | ; Check for sortedness of imports 79 | imports_sortedness="disabled" 80 | ; Check for explicitly annotated unittests 81 | explicitly_annotated_unittests="disabled" 82 | ; Check for properly documented public functions (Returns, Params) 83 | properly_documented_public_functions="disabled" 84 | ; Check for useless usage of the final attribute 85 | final_attribute_check="enabled" 86 | ; Check for virtual calls in the class constructors 87 | vcall_in_ctor="enabled" 88 | ; Check for useless user defined initializers 89 | useless_initializer="disabled" 90 | ; Check allman brace style 91 | allman_braces_check="disabled" 92 | ; Check for redundant attributes 93 | redundant_attributes_check="enabled" 94 | ; Check public declarations without a documented unittest 95 | has_public_example="disabled" 96 | ; Check for asserts without an explanatory message 97 | assert_without_msg="disabled" 98 | ; Check indent of if constraints 99 | if_constraints_indent="disabled" 100 | ; Check for @trusted applied to a bigger scope than a single function 101 | trust_too_much="enabled" 102 | ; Check for redundant storage classes on variable declarations 103 | redundant_storage_classes="enabled" 104 | ; ModuleFilters for selectively enabling (+std) and disabling (-std.internal) i 105 | ; ndividual checks 106 | [analysis.config.ModuleFilters] 107 | ; Exclude/Import modules 108 | style_check="" 109 | ; Exclude/Import modules 110 | enum_array_literal_check="" 111 | ; Exclude/Import modules 112 | exception_check="" 113 | ; Exclude/Import modules 114 | delete_check="" 115 | ; Exclude/Import modules 116 | float_operator_check="" 117 | ; Exclude/Import modules 118 | number_style_check="" 119 | ; Exclude/Import modules 120 | object_const_check="" 121 | ; Exclude/Import modules 122 | backwards_range_check="" 123 | ; Exclude/Import modules 124 | if_else_same_check="" 125 | ; Exclude/Import modules 126 | constructor_check="" 127 | ; Exclude/Import modules 128 | unused_variable_check="" 129 | ; Exclude/Import modules 130 | unused_label_check="" 131 | ; Exclude/Import modules 132 | unused_parameter_check="" 133 | ; Exclude/Import modules 134 | duplicate_attribute="" 135 | ; Exclude/Import modules 136 | opequals_tohash_check="" 137 | ; Exclude/Import modules 138 | length_subtraction_check="" 139 | ; Exclude/Import modules 140 | builtin_property_names_check="" 141 | ; Exclude/Import modules 142 | asm_style_check="" 143 | ; Exclude/Import modules 144 | logical_precedence_check="" 145 | ; Exclude/Import modules 146 | undocumented_declaration_check="" 147 | ; Exclude/Import modules 148 | function_attribute_check="" 149 | ; Exclude/Import modules 150 | comma_expression_check="" 151 | ; Exclude/Import modules 152 | local_import_check="" 153 | ; Exclude/Import modules 154 | could_be_immutable_check="" 155 | ; Exclude/Import modules 156 | redundant_if_check="" 157 | ; Exclude/Import modules 158 | redundant_parens_check="" 159 | ; Exclude/Import modules 160 | mismatched_args_check="" 161 | ; Exclude/Import modules 162 | label_var_same_name_check="" 163 | ; Exclude/Import modules 164 | long_line_check="" 165 | ; Exclude/Import modules 166 | auto_ref_assignment_check="" 167 | ; Exclude/Import modules 168 | incorrect_infinite_range_check="" 169 | ; Exclude/Import modules 170 | useless_assert_check="" 171 | ; Exclude/Import modules 172 | alias_syntax_check="" 173 | ; Exclude/Import modules 174 | static_if_else_check="" 175 | ; Exclude/Import modules 176 | lambda_return_check="" 177 | ; Exclude/Import modules 178 | auto_function_check="" 179 | ; Exclude/Import modules 180 | imports_sortedness="" 181 | ; Exclude/Import modules 182 | explicitly_annotated_unittests="" 183 | ; Exclude/Import modules 184 | properly_documented_public_functions="" 185 | ; Exclude/Import modules 186 | final_attribute_check="" 187 | ; Exclude/Import modules 188 | vcall_in_ctor="" 189 | ; Exclude/Import modules 190 | useless_initializer="" 191 | ; Exclude/Import modules 192 | allman_braces_check="" 193 | ; Exclude/Import modules 194 | redundant_attributes_check="" 195 | ; Exclude/Import modules 196 | has_public_example="" 197 | ; Exclude/Import modules 198 | assert_without_msg="" 199 | ; Exclude/Import modules 200 | if_constraints_indent="" 201 | ; Exclude/Import modules 202 | trust_too_much="" 203 | ; Exclude/Import modules 204 | redundant_storage_classes="" 205 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "-ddoxFilterArgs": [ 3 | "--min-protection=Protected" 4 | ], 5 | "authors": [ 6 | "lljochemll" 7 | ], 8 | "configurations": [ 9 | { 10 | "name": "library", 11 | "targetType": "library" 12 | }, 13 | { 14 | "dependencies": { 15 | "fluent-asserts": "~>0.12.0", 16 | "silly": "~>1.0.2" 17 | }, 18 | "name": "unittest", 19 | "targetType": "library" 20 | } 21 | ], 22 | "copyright": "Copyright © 2019, lljochemll", 23 | "dependencies": { 24 | }, 25 | "description": "Metaprogramming library for the D language", 26 | "license": "MIT", 27 | "name": "quirks", 28 | "targetType": "library", 29 | "toolchainRequirements": { 30 | "frontend": ">=2.87.0" 31 | } 32 | } -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "botan": "1.12.10", 5 | "botan-math": "1.0.3", 6 | "ddmp": "0.0.1-0.dev.3", 7 | "diet-ng": "1.6.0", 8 | "eventcore": "0.8.48", 9 | "fluent-asserts": "0.12.5", 10 | "libasync": "0.8.4", 11 | "libdparse": "0.8.8", 12 | "libevent": "2.0.2+2.0.16", 13 | "memutils": "0.4.13", 14 | "mir-linux-kernel": "1.0.1", 15 | "openssl": "1.1.6+1.0.1g", 16 | "silly": "1.0.2", 17 | "stdx-allocator": "2.77.5", 18 | "taggedalgebraic": "0.11.8", 19 | "vibe-core": "1.8.1", 20 | "vibe-d": "0.8.6" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/quirks/aggregate.d: -------------------------------------------------------------------------------- 1 | module quirks.aggregate; 2 | 3 | import quirks.core : Quirks; 4 | import quirks.functional: Parameters; 5 | import quirks.tuple : AliasTuple, FilterTuple; 6 | import quirks.type : TypeOf, isAggregate, isModule; 7 | import quirks.utility; 8 | import std.algorithm; 9 | import std.array; 10 | import std.conv; 11 | import std.functional : unaryFun; 12 | import std.meta; 13 | import std.traits; 14 | import std.typecons; 15 | 16 | /++ 17 | + Returns a tuple of each aggregate in the form of the `Quirks` template 18 | + 19 | + Example: 20 | + --- 21 | + struct S { 22 | + long id; 23 | + int age; 24 | + string name() { 25 | + return "name"; 26 | + } 27 | + 28 | + struct Nested { 29 | + 30 | + } 31 | + } 32 | + 33 | + alias aggregates = Aggregate!S; 34 | + 35 | + static foreach (aggregate; aggregates) { 36 | + pragma(msg, aggregate.type); 37 | + pragma(msg, aggregate.name); 38 | + } 39 | + --- 40 | +/ 41 | @safe 42 | template Aggregates(alias aggregate) if (isAggregate!aggregate || isModule!aggregate) { 43 | alias Aggregates = Members!aggregate 44 | .filter!(member => member.isAggregate); 45 | } unittest { 46 | import quirks.internal.test; 47 | 48 | TestStruct s; 49 | auto c = new TestClass; 50 | 51 | Aggregates!(TestStruct).length.should.equal(2); 52 | Aggregates!(s).length.should.equal(2); 53 | 54 | Aggregates!(TestClass).length.should.equal(2); 55 | Aggregates!(c).length.should.equal(2); 56 | 57 | Aggregates!(quirks.internal.test).length.should.equal(2); 58 | } 59 | 60 | /++ 61 | + Returns a tuple of each field in the form of the `Quirks` template 62 | + 63 | + Example: 64 | + --- 65 | + struct S { 66 | + long id; 67 | + int age; 68 | + string name() { 69 | + return "name"; 70 | + } 71 | + } 72 | + 73 | + alias fields = Fields!S; 74 | + 75 | + static foreach (field; fields) { 76 | + pragma(msg, field.type); 77 | + pragma(msg, field.name); 78 | + } 79 | + --- 80 | +/ 81 | @safe 82 | template Fields(alias aggregate) if (isAggregate!aggregate || isModule!aggregate) { 83 | alias Fields = Members!aggregate 84 | .filter!(member => !isCallable!(member.type) && !is(member.type == void) && !member.isAggregate); 85 | } unittest { 86 | import quirks.internal.test; 87 | 88 | TestStruct s; 89 | auto c = new TestClass; 90 | 91 | Fields!(TestStruct).length.should.equal(3); 92 | Fields!(s).length.should.equal(3); 93 | 94 | Fields!(TestClass).length.should.equal(3); 95 | Fields!(c).length.should.equal(3); 96 | 97 | Fields!(quirks.internal.test).length.should.equal(3); 98 | } 99 | 100 | /++ 101 | + Returns the same as __traits(allMembers, aggregate), excluding this and all default fields inherited from Object 102 | + 103 | + Example: 104 | + --- 105 | + struct S { 106 | + long id; 107 | + int age; 108 | + string name() { 109 | + return "name"; 110 | + } 111 | + } 112 | + 113 | + MemberNames!(S); // is equal to ("id", "age", "name") 114 | + --- 115 | +/ 116 | @safe 117 | template MemberNames(alias aggregate) if (isAggregate!aggregate || isModule!aggregate) { 118 | alias MemberNames = AliasTuple!(__traits(allMembers, TypeOf!aggregate)) 119 | .filter!(name => ![__traits(allMembers, Object)].canFind(name) && !["this", "object"].canFind(name)); 120 | } unittest { 121 | import quirks.internal.test; 122 | 123 | TestStruct s; 124 | auto c = new TestClass; 125 | 126 | MemberNames!TestStruct.length.should.equal(8); 127 | MemberNames!s.length.should.equal(8); 128 | 129 | MemberNames!TestClass.length.should.equal(8); 130 | MemberNames!c.length.should.equal(8); 131 | 132 | MemberNames!(quirks.internal.test).length.should.equal(9); 133 | } 134 | 135 | /++ 136 | + Returns the same as __traits(allMembers, aggregate) mapped with the `Quirks` template 137 | + 138 | + Example: 139 | + --- 140 | + struct S { 141 | + long id; 142 | + int age; 143 | + string name() { 144 | + return "name"; 145 | + } 146 | + } 147 | + 148 | + Members!S.length; // is 3 149 | + --- 150 | +/ 151 | @safe 152 | template Members(alias aggregate) if (isAggregate!aggregate || isModule!aggregate) { 153 | alias getQuirksFromMemberName(string name) = Quirks!(__traits(getMember, TypeOf!aggregate, name)); 154 | 155 | alias Members = MemberNames!aggregate 156 | .map!(getQuirksFromMemberName) 157 | .filter!(member => is(member.type)); 158 | } unittest { 159 | import quirks.internal.test; 160 | 161 | TestStruct s; 162 | auto c = new TestClass; 163 | 164 | Members!TestStruct.length.should.equal(8); 165 | Members!s.length.should.equal(8); 166 | Members!TestClass.length.should.equal(8); 167 | Members!c.length.should.equal(8); 168 | 169 | Members!(quirks.internal.test).length.should.equal(8); 170 | } 171 | 172 | /++ 173 | + Returns a tuple of each method in the form of the `Quirks` template 174 | + 175 | + Example: 176 | + --- 177 | + struct S { 178 | + long id; 179 | + int age; 180 | + string name() { 181 | + return "name"; 182 | + } 183 | + } 184 | + 185 | + alias fields = Methods!S; 186 | + 187 | + static foreach (method; fields) { 188 | + pragma(msg, method.returnType); 189 | + pragma(msg, method.name); 190 | + } 191 | + --- 192 | +/ 193 | @safe 194 | template Methods(alias aggregate) if (isAggregate!aggregate || isModule!aggregate) { 195 | auto generateNames() { 196 | string[] names; 197 | 198 | static foreach (memberName; MemberNames!aggregate) { 199 | static if (!hasField!(aggregate, memberName)) { 200 | static foreach (i, overload; __traits(getOverloads, TypeOf!aggregate, memberName)) { 201 | static if (is(typeof(overload))) { 202 | mixin(interpolateMixin(q{ 203 | names ~= "method_${memberName}_${i}"; 204 | })); 205 | } 206 | } 207 | } 208 | } 209 | 210 | return names; 211 | } 212 | 213 | static foreach (memberName; MemberNames!aggregate) { 214 | static if (!hasField!(aggregate, memberName)) { 215 | static foreach (i, overload; __traits(getOverloads, TypeOf!aggregate, memberName)) { 216 | static if (is(typeof(overload))) { 217 | mixin(interpolateMixin(q{ 218 | alias method_${memberName}_${i} = overload; 219 | })); 220 | } 221 | } 222 | } 223 | } 224 | 225 | static if (generateNames.length > 0) { 226 | mixin(interpolateMixin(q{ 227 | alias Methods = AliasTuple!(Quirks!(${generateNames.join("),Quirks!(")})); 228 | })); 229 | } else { 230 | alias Methods = AliasTuple!(); 231 | } 232 | } unittest { 233 | import quirks.internal.test; 234 | 235 | TestStruct s; 236 | auto c = new TestClass; 237 | 238 | Methods!TestStruct.length.should.equal(4); 239 | Methods!s.length.should.equal(4); 240 | Methods!TestClass.length.should.equal(4); 241 | Methods!c.length.should.equal(4); 242 | 243 | Methods!(quirks.internal.test).length.should.equal(4); 244 | } 245 | 246 | /++ 247 | + Returns true if a field can be found on aggregate with the given fieldName, false otherwise. 248 | + 249 | + Example: 250 | + --- 251 | + struct S { 252 | + long id; 253 | + int age; 254 | + string name() { 255 | + return "name"; 256 | + } 257 | + } 258 | + 259 | + hasField!(S, "id"); // returns true 260 | + hasField!(S, "name"); // returns false 261 | + --- 262 | +/ 263 | @safe 264 | pure nothrow auto hasField(alias aggregate, string fieldName)() if (isAggregate!aggregate || isModule!aggregate) { 265 | return hasField!(aggregate, field => field.name == fieldName); 266 | } unittest { 267 | import quirks.internal.test; 268 | 269 | TestStruct s; 270 | auto c = new TestClass; 271 | 272 | hasField!(TestStruct, "id").should.equal(true); 273 | hasField!(s, "id").should.equal(true); 274 | hasField!(TestStruct, "name").should.equal(false); 275 | hasField!(s, "name").should.equal(false); 276 | hasField!(TestStruct, "doesNotExist").should.equal(false); 277 | hasField!(s, "doesNotExist").should.equal(false); 278 | 279 | hasField!(TestClass, "id").should.equal(true); 280 | hasField!(c, "id").should.equal(true); 281 | hasField!(TestClass, "name").should.equal(false); 282 | hasField!(c, "name").should.equal(false); 283 | hasField!(TestClass, "doesNotExist").should.equal(false); 284 | hasField!(c, "doesNotExist").should.equal(false); 285 | } 286 | 287 | /++ 288 | + Returns true if a field can be found on aggregate filtered with the given predicate, false otherwise. 289 | + 290 | + Example: 291 | + --- 292 | + struct S { 293 | + long id; 294 | + int age; 295 | + string name() { 296 | + return "name"; 297 | + } 298 | + } 299 | + 300 | + hasField!(S, field => isNumeric!(field.type)); // returns true 301 | + hasField!(S, field => is(field.type == string)); // returns false 302 | + --- 303 | +/ 304 | @safe 305 | pure nothrow auto hasField(alias aggregate, alias predicate)() if ((isAggregate!aggregate || isModule!aggregate) && is(typeof(unaryFun!predicate))) { 306 | return Fields!(aggregate).filter!predicate.length > 0; 307 | } unittest { 308 | import quirks.internal.test; 309 | 310 | TestStruct s; 311 | auto c = new TestClass; 312 | 313 | hasField!(TestStruct, field => is(field.type == long)).should.equal(true); 314 | hasField!(s, field => is(field.type == long)).should.equal(true); 315 | hasField!(TestStruct, field => is(field.type == string)).should.equal(false); 316 | hasField!(s, field => is(field.type == string)).should.equal(false); 317 | hasField!(TestStruct, field => isNumeric!(field.type)).should.equal(true); 318 | hasField!(s, field => isNumeric!(field.type)).should.equal(true); 319 | hasField!(TestStruct, field => isArray!(field.type)).should.equal(false); 320 | hasField!(s, field => isArray!(field.type)).should.equal(false); 321 | hasField!(TestStruct, field => field.name == "id").should.equal(true); 322 | hasField!(s, field => field.name == "id").should.equal(true); 323 | hasField!(TestStruct, field => field.name == "name").should.equal(false); 324 | hasField!(s, field => field.name == "name").should.equal(false); 325 | hasField!(TestStruct, field => field.name == "doesNotExist").should.equal(false); 326 | hasField!(s, field => field.name == "doesNotExist").should.equal(false); 327 | 328 | hasField!(TestClass, field => is(field.type == long)).should.equal(true); 329 | hasField!(c, field => is(field.type == long)).should.equal(true); 330 | hasField!(TestClass, field => is(field.type == string)).should.equal(false); 331 | hasField!(c, field => is(field.type == string)).should.equal(false); 332 | hasField!(TestClass, field => isNumeric!(field.type)).should.equal(true); 333 | hasField!(c, field => isNumeric!(field.type)).should.equal(true); 334 | hasField!(TestClass, field => isArray!(field.type)).should.equal(false); 335 | hasField!(c, field => isArray!(field.type)).should.equal(false); 336 | hasField!(TestClass, field => field.name == "id").should.equal(true); 337 | hasField!(c, field => field.name == "id").should.equal(true); 338 | hasField!(TestClass, field => field.name == "name").should.equal(false); 339 | hasField!(c, field => field.name == "name").should.equal(false); 340 | hasField!(TestClass, field => field.name == "doesNotExist").should.equal(false); 341 | hasField!(c, field => field.name == "doesNotExist").should.equal(false); 342 | } 343 | 344 | /++ 345 | + Returns true if a member can be found on aggregate with the given memberName, false otherwise. 346 | + 347 | + Example: 348 | + --- 349 | + struct S { 350 | + long id; 351 | + int age; 352 | + string name() { 353 | + return "name"; 354 | + } 355 | + } 356 | + 357 | + hasMember!(S, "id"); // returns true 358 | + hasMember!(S, "name"); // returns true 359 | + hasMember!(S, "doesNotExist"); // returns false 360 | + --- 361 | +/ 362 | @safe 363 | pure nothrow auto hasMember(alias aggregate, string memberName)() if (isAggregate!aggregate || isModule!aggregate) { 364 | return [MemberNames!aggregate.tuple].canFind(memberName); 365 | } unittest { 366 | import quirks.internal.test; 367 | 368 | TestStruct s; 369 | auto c = new TestClass; 370 | 371 | hasMember!(TestStruct, "id").should.equal(true); 372 | hasMember!(s, "id").should.equal(true); 373 | hasMember!(TestStruct, "name").should.equal(true); 374 | hasMember!(s, "name").should.equal(true); 375 | hasMember!(TestStruct, "doesNotExist").should.equal(false); 376 | hasMember!(s, "doesNotExist").should.equal(false); 377 | 378 | hasMember!(TestClass, "id").should.equal(true); 379 | hasMember!(c, "id").should.equal(true); 380 | hasMember!(TestClass, "name").should.equal(true); 381 | hasMember!(c, "name").should.equal(true); 382 | hasMember!(TestClass, "doesNotExist").should.equal(false); 383 | hasMember!(c, "doesNotExist").should.equal(false); 384 | } 385 | 386 | /++ 387 | + Returns true if a member can be found on aggregate filtered with the given predicate, false otherwise. 388 | + 389 | + Example: 390 | + --- 391 | + struct S { 392 | + long id; 393 | + int age; 394 | + string name() { 395 | + return "name"; 396 | + } 397 | + } 398 | + 399 | + hasMember!(S, member => is(member.type == long)); // returns true 400 | + hasMember!(S, member => is(member.type == string)); // returns true 401 | + hasMember!(S, member => member.name == "doesNotExist"); // returns false 402 | + --- 403 | +/ 404 | @safe 405 | pure nothrow auto hasMember(alias aggregate, alias predicate)() if ((isAggregate!aggregate || isModule!aggregate) && is(typeof(unaryFun!predicate))) { 406 | return Members!(aggregate).filter!predicate.length > 0; 407 | } unittest { 408 | import quirks.internal.test; 409 | 410 | TestStruct s; 411 | auto c = new TestClass; 412 | 413 | hasMember!(TestStruct, member => is(member.type == long)).should.equal(true); 414 | hasMember!(s, member => is(member.type == long)).should.equal(true); 415 | hasMember!(TestStruct, member => is(member.returnType == string)).should.equal(true); 416 | hasMember!(s, member => is(member.returnType == string)).should.equal(true); 417 | hasMember!(TestStruct, member => isNumeric!(member.type)).should.equal(true); 418 | hasMember!(s, member => isNumeric!(member.type)).should.equal(true); 419 | hasMember!(TestStruct, member => isArray!(member.type)).should.equal(false); 420 | hasMember!(s, member => isArray!(member.type)).should.equal(false); 421 | hasMember!(TestStruct, member => member.name == "id").should.equal(true); 422 | hasMember!(s, member => member.name == "id").should.equal(true); 423 | hasMember!(TestStruct, member => member.name == "name").should.equal(true); 424 | hasMember!(s, member => member.name == "name").should.equal(true); 425 | hasMember!(TestStruct, member => member.name == "doesNotExist").should.equal(false); 426 | hasMember!(s, member => member.name == "doesNotExist").should.equal(false); 427 | 428 | hasMember!(TestClass, member => is(member.type == long)).should.equal(true); 429 | hasMember!(c, member => is(member.type == long)).should.equal(true); 430 | hasMember!(TestClass, member => is(member.returnType == string)).should.equal(true); 431 | hasMember!(c, member => is(member.returnType == string)).should.equal(true); 432 | hasMember!(TestClass, member => isNumeric!(member.type)).should.equal(true); 433 | hasMember!(c, member => isNumeric!(member.type)).should.equal(true); 434 | hasMember!(TestClass, member => isArray!(member.type)).should.equal(false); 435 | hasMember!(c, member => isArray!(member.type)).should.equal(false); 436 | hasMember!(TestClass, member => member.name == "id").should.equal(true); 437 | hasMember!(c, member => member.name == "id").should.equal(true); 438 | hasMember!(TestClass, member => member.name == "name").should.equal(true); 439 | hasMember!(c, member => member.name == "name").should.equal(true); 440 | hasMember!(TestClass, member => member.name == "doesNotExist").should.equal(false); 441 | hasMember!(c, member => member.name == "doesNotExist").should.equal(false); 442 | } 443 | 444 | /++ 445 | + Returns true if a method can be found on aggregate with the given methodName, false otherwise. 446 | + 447 | + Example: 448 | + --- 449 | + struct S { 450 | + long id; 451 | + int age; 452 | + string name() { 453 | + return "name"; 454 | + } 455 | + } 456 | + 457 | + hasField!(TestStruct, "name"); // returns true 458 | + hasField!(TestStruct, "age"); // returns false 459 | + --- 460 | +/ 461 | @safe 462 | pure nothrow auto hasMethod(alias aggregate, string methodName)() if (isAggregate!aggregate || isModule!aggregate) { 463 | return Methods!aggregate.filter!(method => method.name == methodName).length > 0; 464 | } unittest { 465 | import quirks.internal.test; 466 | 467 | TestStruct s; 468 | auto c = new TestClass; 469 | 470 | hasMethod!(TestStruct, "id").should.equal(false); 471 | hasMethod!(s, "id").should.equal(false); 472 | hasMethod!(TestStruct, "name").should.equal(true); 473 | hasMethod!(s, "name").should.equal(true); 474 | hasMethod!(TestStruct, "doesNotExist").should.equal(false); 475 | hasMethod!(s, "doesNotExist").should.equal(false); 476 | 477 | hasMethod!(TestClass, "id").should.equal(false); 478 | hasMethod!(c, "id").should.equal(false); 479 | hasMethod!(TestClass, "name").should.equal(true); 480 | hasMethod!(c, "name").should.equal(true); 481 | hasMethod!(TestClass, "doesNotExist").should.equal(false); 482 | hasMethod!(c, "doesNotExist").should.equal(false); 483 | } 484 | 485 | /++ 486 | + Returns true if a method can be found on aggregate filtered with the given predicate, false otherwise. 487 | + 488 | + Example: 489 | + --- 490 | + struct S { 491 | + long id; 492 | + int age; 493 | + string name() { 494 | + return "name"; 495 | + } 496 | + } 497 | + 498 | + hasField!(TestStruct, method => method.name == "name"); // returns true 499 | + hasField!(TestStruct, method => is(method.returnType == int)); // returns false 500 | + --- 501 | +/ 502 | @safe 503 | pure nothrow auto hasMethod(alias aggregate, alias predicate)() if (isAggregate!aggregate || isModule!aggregate) { 504 | return Methods!aggregate.filter!predicate.length > 0; 505 | } unittest { 506 | import quirks.internal.test; 507 | 508 | TestStruct s; 509 | auto c = new TestClass; 510 | 511 | hasMethod!(TestStruct, method => is(method.returnType == long)).should.equal(false); 512 | hasMethod!(s, method => is(method.returnType == long)).should.equal(false); 513 | hasMethod!(TestStruct, method => is(method.returnType == string)).should.equal(true); 514 | hasMethod!(s, method => is(method.returnType == string)).should.equal(true); 515 | hasMethod!(TestStruct, method => isSomeString!(method.returnType)).should.equal(true); 516 | hasMethod!(s, method => isSomeString!(method.returnType)).should.equal(true); 517 | hasMethod!(TestStruct, method => isNumeric!(method.returnType)).should.equal(false); 518 | hasMethod!(s, method => isNumeric!(method.returnType)).should.equal(false); 519 | hasMethod!(TestStruct, method => method.name == "id").should.equal(false); 520 | hasMethod!(s, method => method.name == "id").should.equal(false); 521 | hasMethod!(TestStruct, method => method.name == "name").should.equal(true); 522 | hasMethod!(s, method => method.name == "name").should.equal(true); 523 | hasMethod!(TestStruct, method => method.name == "doesNotExist").should.equal(false); 524 | hasMethod!(s, method => method.name == "doesNotExist").should.equal(false); 525 | 526 | hasMethod!(TestClass, method => is(method.returnType == long)).should.equal(false); 527 | hasMethod!(c, method => is(method.returnType == long)).should.equal(false); 528 | hasMethod!(TestClass, method => is(method.returnType == string)).should.equal(true); 529 | hasMethod!(c, method => is(method.returnType == string)).should.equal(true); 530 | hasMethod!(TestClass, method => isSomeString!(method.returnType)).should.equal(true); 531 | hasMethod!(c, method => isSomeString!(method.returnType)).should.equal(true); 532 | hasMethod!(TestClass, method => isNumeric!(method.returnType)).should.equal(false); 533 | hasMethod!(c, method => isNumeric!(method.returnType)).should.equal(false); 534 | hasMethod!(TestClass, method => method.name == "id").should.equal(false); 535 | hasMethod!(c, method => method.name == "id").should.equal(false); 536 | hasMethod!(TestClass, method => method.name == "name").should.equal(true); 537 | hasMethod!(c, method => method.name == "name").should.equal(true); 538 | hasMethod!(TestClass, method => method.name == "doesNotExist").should.equal(false); 539 | hasMethod!(c, method => method.name == "doesNotExist").should.equal(false); 540 | } -------------------------------------------------------------------------------- /source/quirks/core.d: -------------------------------------------------------------------------------- 1 | module quirks.core; 2 | 3 | static import quirks.aggregate; 4 | static import quirks.expression; 5 | static import quirks.type; 6 | static import std.traits; 7 | import quirks.aggregate : Aggregates, Fields, MemberNames, Members, Methods; 8 | import quirks.expression : isStatic; 9 | import quirks.functional : Parameters, FunctionAttributes; 10 | import quirks.tuple : AliasTuple; 11 | import quirks.type : SimpleTypeOf, TypeOf; 12 | import quirks.utility : interpolateMixin; 13 | import std.meta; 14 | 15 | /++ 16 | + Swiss army knife for getting information about things. 17 | + 18 | + Takes thing and tries to apply a list of functions and templates to it. All that compile can be accessed using property syntax on the resulting alias. 19 | + 20 | + The code for this is generated during compile-time using traits and mixins. Below is a list of properties that are possible to access (note not all will be available for every instantiation): 21 | + $(UL 22 | + $(LI attributes) 23 | + $(LI fields -> see `Fields`) 24 | + $(LI functionAttributes -> see `FunctionAttributes`) 25 | + $(LI isAggregate -> see `isAggregate`) 26 | + $(LI isArray -> see `isArray`) 27 | + $(LI isAssociativeArray -> see `isAssociativeArray`) 28 | + $(LI isBasic -> see `isBasic`) 29 | + $(LI isModule -> see `isModule`) 30 | + $(LI isNested) 31 | + $(LI isNumeric -> see `isNumeric`) 32 | + $(LI isSomeString -> see `isSomeString`) 33 | + $(LI isStatic -> see `isStatic`) 34 | + $(LI memberNames -> see `MemberNames`) 35 | + $(LI members -> see `Members`) 36 | + $(LI methods -> see `Methods`) 37 | + $(LI name) 38 | + $(LI parameters -> see `Parameters`) 39 | + $(LI qualifiedName) 40 | + $(LI returnType) 41 | + $(LI simpleType -> see `SimpleTypeOf`) 42 | + $(LI type) 43 | + ) 44 | + 45 | + In addition, the following properties that require a template parameter are also available: 46 | + $(UL 47 | + $(LI fieldsFilter(alias predicate) -> returns the fields property filtered with the given predicate) 48 | + $(LI getUDAs(alias uda) -> returns the same as getUDAs from std.traits) 49 | + $(LI getUDA(alias uda) -> returns the first result returned by getUDAs) 50 | + $(LI hasField(alias predicate) -> see `hasField`) 51 | + $(LI hasMember(alias predicate) -> see `hasMember`) 52 | + $(LI hasMethod(alias predicate) -> see `hasMethod`) 53 | + $(LI hasUDA(alias uda) -> return the same as hasUDA from std.traits) 54 | + $(LI isInstanceOf(alias templ) -> see `isInstanceOf`) 55 | + $(LI membersFilter(alias predicate) -> returns the members property filtered with the given predicate) 56 | + $(LI methodsFilter(alias predicate) -> returns the methods property filtered with the given predicate) 57 | + ) 58 | + 59 | + Example: 60 | + --- 61 | + struct S { 62 | + static long id; 63 | + int age; 64 | + static string name() { 65 | + return "name"; 66 | + } 67 | + void update(bool force) { } 68 | + } 69 | + 70 | + Quirks!S.type; // S 71 | + Quirks!S.fields.length; // 2 72 | + Quirks!S.methods[1].name; //update 73 | + Quirks!S.isArray; // false 74 | + Quirks!S.methods[1].parameters[0].type; // bool 75 | + --- 76 | +/ 77 | template Quirks(alias thing_) { 78 | alias quirksAliasTuple = AliasSeq!( 79 | "aggregates", q{Aggregates!thing}, 80 | "attributes", q{AliasTuple!(__traits(getAttributes, thing))}, 81 | "fields", q{Fields!thing}, 82 | "functionAttributes", q{FunctionAttributes!thing}, 83 | "isAggregate", q{quirks.type.isAggregate!type}, 84 | "isArray", q{quirks.type.isArray!type}, 85 | "isAssociativeArray", q{quirks.type.isAssociativeArray!type}, 86 | "isBasic", q{quirks.type.isBasic!type}, 87 | "isModule", q{quirks.type.isModule!type}, 88 | "isNested", q{isNested!type}, 89 | "isNumeric", q{quirks.type.isNumeric!type}, 90 | "isSomeFunction", q{quirks.type.isSomeFunction!type}, 91 | "isSomeString", q{quirks.type.isSomeString!type}, 92 | "isStatic", q{quirks.expression.isStatic!type}, 93 | "memberNames", q{MemberNames!thing}, 94 | "members", q{Members!thing}, 95 | "methods", q{Methods!thing}, 96 | "parameters", q{Parameters!thing}, 97 | "qualifiedName", q{std.traits.fullyQualifiedName!thing}, 98 | "returnType", q{std.traits.ReturnType!type}, 99 | "simpleType", q{SimpleTypeOf!thing}, 100 | q{fieldsFilter(alias predicate)}, q{Fields!(thing, predicate)}, 101 | q{getUDAs(alias uda)}, q{std.traits.getUDAs!(thing, uda)}, 102 | q{getUDA(alias uda)}, q{getUDAs!uda[0]}, 103 | q{hasField(alias predicate)}, q{quirks.aggregate.hasField!(thing, predicate)}, 104 | q{hasMember(alias predicate)}, q{quirks.aggregate.hasMember!(thing, predicate)}, 105 | q{hasMethod(alias predicate)}, q{quirks.aggregate.hasMethod!(thing, predicate)}, 106 | q{hasUDA(alias uda)}, q{std.traits.hasUDA!(thing, uda)}, 107 | q{isInstanceOf(alias templ)}, q{quirks.type.isInstanceOf(templ, thing)}, 108 | ); 109 | 110 | alias quirksEnumTuple = AliasSeq!( 111 | "name", q{__traits(identifier, thing)}, 112 | ); 113 | 114 | alias quirksTemplateTuple = AliasSeq!( 115 | 116 | ); 117 | 118 | struct QuirksStruct { 119 | alias thing = thing_; 120 | alias type = TypeOf!thing; 121 | 122 | static if (quirks.type.isModule!type) { 123 | mixin(interpolateMixin(q{ 124 | static import ${std.traits.fullyQualifiedName!thing}; 125 | })); 126 | } 127 | 128 | static foreach (i, expression; quirksAliasTuple) { 129 | static if (i % 2 == 1) { 130 | mixin(interpolateMixin(q{ 131 | static if (__traits(compiles, {alias ${quirksAliasTuple[i - 1]} = ${expression};})) { 132 | public alias ${quirksAliasTuple[i - 1]} = ${expression}; 133 | } 134 | })); 135 | } 136 | } 137 | 138 | static foreach (i, expression; quirksEnumTuple) { 139 | static if (i % 2 == 1) { 140 | mixin(interpolateMixin(q{ 141 | static if (__traits(compiles, ${expression})) { 142 | public enum ${quirksEnumTuple[i - 1]} = (${expression}); 143 | } 144 | })); 145 | } 146 | } 147 | } 148 | 149 | private enum QuirksStruct value = QuirksStruct(); 150 | 151 | alias Quirks = value; 152 | } -------------------------------------------------------------------------------- /source/quirks/expression.d: -------------------------------------------------------------------------------- 1 | module quirks.expression; 2 | 3 | import std.meta; 4 | import std.traits; 5 | 6 | /++ 7 | + Returns is the given thing is either a static function or a static declaration declaration 8 | + 9 | + Example: 10 | + --- 11 | + struct S { 12 | + static long id; 13 | + int age; 14 | + static string name() { 15 | + return "name"; 16 | + } 17 | + void update(bool force) { } 18 | + } 19 | + 20 | + isStatic!(S.id); // true 21 | + isStatic!(S.age); // false 22 | + isStatic!(S.name); // true 23 | + isStatic!(S.update); // false 24 | + --- 25 | +/ 26 | @safe 27 | template isStatic(alias thing) { 28 | static if (isSomeFunction!thing) { 29 | alias isStatic = Alias!(__traits(isStaticFunction, thing)); 30 | } else { 31 | alias isStatic = Alias!(__traits(compiles, &thing)); 32 | } 33 | } unittest { 34 | import quirks.internal.test; 35 | 36 | TestStruct s; 37 | auto c = new TestClass; 38 | 39 | isStatic!(TestStruct.classifier).should.equal(true); 40 | isStatic!(s.classifier).should.equal(true); 41 | isStatic!(TestStruct.age).should.equal(false); 42 | isStatic!(s.age).should.equal(false); 43 | isStatic!(TestStruct.create).should.equal(true); 44 | isStatic!(s.create).should.equal(true); 45 | isStatic!(TestStruct.update).should.equal(false); 46 | isStatic!(s.update).should.equal(false); 47 | 48 | isStatic!(TestClass.classifier).should.equal(true); 49 | isStatic!(c.classifier).should.equal(true); 50 | isStatic!(TestClass.age).should.equal(false); 51 | isStatic!(c.age).should.equal(false); 52 | isStatic!(TestClass.create).should.equal(true); 53 | isStatic!(c.create).should.equal(true); 54 | isStatic!(TestClass.update).should.equal(false); 55 | isStatic!(c.update).should.equal(false); 56 | } -------------------------------------------------------------------------------- /source/quirks/functional.d: -------------------------------------------------------------------------------- 1 | module quirks.functional; 2 | 3 | import quirks.tuple : AliasTuple; 4 | import quirks.utility : interpolateMixin; 5 | import std.algorithm; 6 | import std.array; 7 | import std.conv; 8 | import std.functional : unaryFun; 9 | import std.meta; 10 | import std.string; 11 | import std.traits; 12 | 13 | /// Alias for ParameterDefaults 14 | alias ParameterDefaultValues = ParameterDefaults; 15 | /// Alias for ParameterIdentifierTuple 16 | alias ParameterNames = ParameterIdentifierTuple; 17 | /// Alias for Parameters 18 | alias ParameterTypes = std.traits.Parameters; 19 | 20 | /++ 21 | + Returns the functionAttributes of func as an array of strings 22 | + 23 | + Example: 24 | + --- 25 | + nothrow pure @trusted @nogc void foo(); 26 | + pure @nogc void bar(); 27 | + @safe 28 | + struct S { 29 | + pure void foo() {} 30 | + } 31 | + 32 | + FunctionAttributes!foo; // ["nothrow", "pure", "@trusted", "@nogc"] 33 | + FunctionAttributes!bar; // ["pure", "@nogc", "@system"] 34 | + FunctionAttributes!(S.foo); // ["pure", "@safe"] 35 | + --- 36 | +/ 37 | @safe 38 | template FunctionAttributes(alias func) if (isCallable!func) { 39 | private auto attributesMixinList() { 40 | return [std.traits.EnumMembers!(std.traits.FunctionAttribute)] 41 | .filter!(attribute => std.traits.functionAttributes!func & attribute) 42 | .map!(attribute => attribute.to!string) 43 | .map!(attribute => attribute.endsWith("_") ? attribute[0 .. $ - 1] : "@" ~ attribute) 44 | .array; 45 | } 46 | 47 | enum attributes = [std.traits.EnumMembers!(std.traits.FunctionAttribute)] 48 | .filter!(attribute => std.traits.functionAttributes!func & attribute) 49 | .map!(attribute => attribute.to!string) 50 | .map!(attribute => attribute.endsWith("_") ? attribute[0 .. $ - 1] : "@" ~ attribute) 51 | .array; 52 | 53 | alias FunctionAttributes = attributes; 54 | } unittest { 55 | import fluent.asserts; 56 | 57 | nothrow pure @trusted @nogc void foo(); 58 | pure @nogc void bar(); 59 | 60 | @safe 61 | struct S { 62 | pure void foo(); 63 | } 64 | 65 | FunctionAttributes!foo.should.containOnly(["nothrow", "pure", "@trusted", "@nogc"]); 66 | FunctionAttributes!bar.should.containOnly(["pure", "@nogc", "@system"]); 67 | FunctionAttributes!(S.foo).should.containOnly(["pure", "@safe"]); 68 | } 69 | 70 | /++ 71 | + Get, as a tuple, a list of all parameters with their type, name and default value 72 | +/ 73 | @safe 74 | template Parameters(alias func) if (isCallable!func) { 75 | alias defaultValues = ParameterDefaultValues!func; 76 | alias names = ParameterNames!func; 77 | alias types = ParameterTypes!func; 78 | 79 | private auto parametersMixinList() { 80 | string[] parameters; 81 | 82 | static foreach (i, name; names) { 83 | static if (is(defaultValues[i] == void)) { 84 | mixin(interpolateMixin(q{ 85 | parameters ~= "Parameter!(types[${i}])(names[${i}])"; 86 | })); 87 | } else { 88 | mixin(interpolateMixin(q{ 89 | parameters ~= "Parameter!(types[${i}], { return defaultValues[${i}]; })(names[${i}], true)"; 90 | })); 91 | } 92 | } 93 | 94 | return parameters; 95 | } 96 | 97 | mixin(interpolateMixin(q{ 98 | alias Parameters = AliasTuple!(${parametersMixinList.join(",")}); 99 | })); 100 | } unittest { 101 | import fluent.asserts; 102 | 103 | void temp(int age, string name = "john"); 104 | 105 | alias parameters = Parameters!temp; 106 | 107 | int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); 108 | static assert(is(ParameterDefaults!foo[0] == void)); 109 | 110 | (is(typeof(parameters[0].defaultValue()) == void)).should.equal(true); 111 | parameters[1].defaultValue().should.equal("john"); 112 | 113 | parameters[0].name.should.equal("age"); 114 | parameters[1].name.should.equal("name"); 115 | 116 | (is(parameters.tuple[0].type == int)).should.equal(true); 117 | (is(parameters.tuple[1].type == string)).should.equal(true); 118 | } 119 | 120 | private { 121 | @safe 122 | struct Parameter(T, alias defaultValueFunction = { return; }) { 123 | alias type = T; 124 | 125 | string name; 126 | bool hasDefaultValue = false; 127 | 128 | auto defaultValue() { 129 | return defaultValueFunction(); 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /source/quirks/internal/test.d: -------------------------------------------------------------------------------- 1 | module quirks.internal.test; 2 | 3 | version (unittest) { 4 | public import fluent.asserts; 5 | 6 | static long classifier; 7 | 8 | long id; 9 | int age; 10 | 11 | static TestStruct create() { 12 | return TestStruct(); 13 | } 14 | 15 | string name() { 16 | return "name"; 17 | } 18 | void update() { } 19 | void update(bool force) { } 20 | 21 | struct TestStruct { 22 | static long classifier; 23 | 24 | long id; 25 | int age; 26 | 27 | static TestStruct create() { 28 | return TestStruct(); 29 | } 30 | 31 | string name() { 32 | return "name"; 33 | } 34 | void update() { } 35 | void update(bool force) { } 36 | 37 | struct NestedStruct { 38 | 39 | } 40 | 41 | class NestedClass { 42 | 43 | } 44 | } unittest { 45 | TestStruct s; 46 | 47 | s.create.should.not.throwAnyException; 48 | s.name.should.equal("name"); 49 | s.update.should.not.throwAnyException; 50 | s.update(false).should.not.throwAnyException; 51 | } 52 | 53 | class TestClass { 54 | static long classifier; 55 | 56 | long id; 57 | int age; 58 | 59 | static TestClass create() { 60 | return new TestClass(); 61 | } 62 | 63 | string name() { 64 | return "name"; 65 | } 66 | void update() { } 67 | void update(bool force) { } 68 | 69 | struct NestedStruct { 70 | 71 | } 72 | 73 | class NestedClass { 74 | 75 | } 76 | } unittest { 77 | auto c = new TestClass; 78 | 79 | c.create.should.not.throwAnyException; 80 | c.name.should.equal("name"); 81 | c.update.should.not.throwAnyException; 82 | c.update(false).should.not.throwAnyException; 83 | } 84 | 85 | unittest { 86 | create.should.not.throwAnyException; 87 | name.should.equal("name"); 88 | update.should.not.throwAnyException; 89 | update(false).should.not.throwAnyException; 90 | } 91 | } -------------------------------------------------------------------------------- /source/quirks/package.d: -------------------------------------------------------------------------------- 1 | module quirks; 2 | 3 | public import quirks.aggregate; 4 | public import quirks.core; 5 | public import quirks.expression; 6 | public import quirks.functional; 7 | public import quirks.tuple; 8 | public import quirks.type; 9 | public import quirks.utility; -------------------------------------------------------------------------------- /source/quirks/tuple.d: -------------------------------------------------------------------------------- 1 | module quirks.tuple; 2 | 3 | static import std.traits; 4 | import quirks.core : Quirks; 5 | import quirks.utility : interpolateMixin; 6 | import std.conv; 7 | import std.functional : unaryFun; 8 | import std.meta; 9 | 10 | /++ 11 | + Wrapper around AliasSeq 12 | + 13 | + Example: 14 | + --- 15 | + alias seq = AliasTuple!(1, "hello", 0.5, [1, 2, 3]); 16 | + 17 | + seq.tuple; // gives the original tuple 18 | + seq.length; // 4 19 | + seq.filter!(a => isNumeric!a); // gives AliasTuple!(1, 0.5) 20 | + --- 21 | +/ 22 | @safe 23 | struct AliasTuple(T...) { 24 | private template Join(T...) { 25 | static if (T.length == 1 && is(typeof(T[0].tuple))) { 26 | alias Join = AliasTuple!(tuple, T[0].tuple); 27 | } else { 28 | alias Join = AliasTuple!(tuple, T); 29 | } 30 | } 31 | 32 | alias tuple = T; 33 | alias tuple this; 34 | 35 | enum length = T.length; 36 | 37 | alias filter(alias predicate) = FilterTuple!(predicate, tuple); 38 | alias join(T...) = Join!T; 39 | alias map(alias predicate) = MapTuple!(predicate, tuple); 40 | } unittest { 41 | import fluent.asserts; 42 | 43 | alias seq = AliasSeq!(bool, false, int, 0, string, "hi"); 44 | alias tuple = AliasTuple!seq; 45 | 46 | tuple.length.should.equal(seq.length); 47 | tuple.join!(seq).length.should.equal(seq.length * 2); 48 | } 49 | 50 | /++ 51 | + Takes a tuple and filters it with the given predicate or template. 52 | + 53 | + Example: 54 | + --- 55 | + alias tuple = AliasSeq!(1, "hello", 0.5, [1, 2, 3]); 56 | + 57 | + FilterTuple!(a => is(typeof(a) == double), tuple); // gives AliasTuple!(0.5) 58 | + FilterTuple!(a => isNumeric!a, tuple); // gives AliasTuple!(1, 0.5) 59 | + FilterTuple!(isNumeric, tuple); // gives AliasTuple!(1, 0.5) 60 | + --- 61 | +/ 62 | @safe 63 | template FilterTuple(alias pred, T...) { 64 | private auto getElementsMixinList() { 65 | string[] elements; 66 | 67 | static foreach (i, element; T) { 68 | static if (__traits(compiles, pred(element))) { 69 | static if (pred(element)) { 70 | elements ~= "T[" ~ i.to!string ~ "]"; 71 | } 72 | } else { 73 | static if (pred!element) { 74 | elements ~= "T[" ~ i.to!string ~ "]"; 75 | } 76 | } 77 | } 78 | 79 | return elements; 80 | } 81 | 82 | mixin(interpolateMixin(q{ 83 | alias FilterTuple = AliasTuple!(${getElementsMixinList.join(",")}); 84 | })); 85 | } unittest { 86 | import fluent.asserts; 87 | import quirks.core : Quirks; 88 | import quirks.type : isNumeric; 89 | 90 | alias tuple = AliasSeq!(1, "hello", 0.5, [1, 2, 3]); 91 | 92 | alias result1 = FilterTuple!(a => is(typeof(a) == double), tuple); 93 | result1.length.should.equal(1); 94 | result1[0].should.equal(0.5); 95 | 96 | alias result2 = FilterTuple!(a => isNumeric!a, tuple); 97 | result2.length.should.equal(2); 98 | result2[0].should.equal(1); 99 | result2[1].should.equal(0.5); 100 | } 101 | 102 | /++ 103 | + Takes a tuple and maps it with the given function or template. 104 | + 105 | + Example: 106 | + --- 107 | + alias tuple = AliasSeq!(1, "hello", 1L, [1, 2, 3]); 108 | + 109 | + MapTuple!(a => a.to!string, tuple); // gives AliasTuple!("1", "hello", "1", "[1, 2, 3]") 110 | + MapTuple!(Quirks, tuple); // gives AliasTuple!(Quirks!(1), Quirks!("hello"), Quirks!(1L), Quirks!([1, 2, 3])) 111 | + --- 112 | +/ 113 | @safe 114 | template MapTuple(alias mapper, T...) { 115 | private auto getElementsMixinList() { 116 | string[] elements; 117 | 118 | static foreach (i, element; T) { 119 | static if (__traits(compiles, mapper(element))) { 120 | elements ~= "mapper(T[" ~ i.to!string ~ "])"; 121 | } else { 122 | elements ~= "mapper!(T[" ~ i.to!string ~ "])"; 123 | } 124 | } 125 | 126 | return elements; 127 | } 128 | 129 | mixin(interpolateMixin(q{ 130 | alias MapTuple = AliasTuple!(${getElementsMixinList.join(",")}); 131 | })); 132 | } unittest { 133 | import fluent.asserts; 134 | import quirks.core : Quirks; 135 | import quirks.type : isNumeric; 136 | 137 | alias tuple = AliasSeq!(1, "hello", 1L, [1, 2, 3]); 138 | 139 | alias result1 = MapTuple!(a => a.to!string, tuple); 140 | result1.length.should.equal(4); 141 | result1[0].should.equal("1"); 142 | 143 | alias result2 = MapTuple!(Quirks, tuple); 144 | result2.length.should.equal(4); 145 | result2[0].isBasic.should.equal(true); 146 | } -------------------------------------------------------------------------------- /source/quirks/type.d: -------------------------------------------------------------------------------- 1 | module quirks.type; 2 | 3 | static import std.traits; 4 | import std.typecons; 5 | 6 | /// Alias for std.traits.isExpressions 7 | alias isExpression = std.traits.isExpressions; 8 | 9 | /++ 10 | + Returns the same as TypeOf, but but does away with pointers 11 | + 12 | + Example: 13 | + --- 14 | + struct S { 15 | + long id; 16 | + int age; 17 | + string name() { 18 | + return "name"; 19 | + } 20 | + } 21 | + int number; 22 | + auto s = new S; 23 | + 24 | + TypeOf!int; // int 25 | + TypeOf!number; // int 26 | + TypeOf!(S**); // S 27 | + TypeOf!s; // S 28 | + --- 29 | +/ 30 | @safe 31 | template SimpleTypeOf(alias thing) { 32 | alias Type = TypeOf!thing; 33 | 34 | static if (isPointer!Type) { 35 | static if (isPointer!(std.traits.PointerTarget!Type)) { 36 | alias SimpleTypeOf = SimpleTypeOf!(std.traits.PointerTarget!Type); 37 | } else { 38 | alias SimpleTypeOf = std.traits.PointerTarget!Type; 39 | } 40 | } else { 41 | alias SimpleTypeOf = Type; 42 | } 43 | } unittest { 44 | import fluent.asserts; 45 | 46 | is(SimpleTypeOf!(void) == void).should.equal(true); 47 | is(SimpleTypeOf!(void*) == void).should.equal(true); 48 | is(SimpleTypeOf!(void**) == void).should.equal(true); 49 | is(SimpleTypeOf!(string) == string).should.equal(true); 50 | is(SimpleTypeOf!(string*) == string).should.equal(true); 51 | is(SimpleTypeOf!(string**) == string).should.equal(true); 52 | } 53 | 54 | /++ 55 | + Returns the type of thing. Accepts both expressions and types. 56 | + 57 | + Example: 58 | + --- 59 | + struct S { 60 | + long id; 61 | + int age; 62 | + string name() { 63 | + return "name"; 64 | + } 65 | + } 66 | + int number; 67 | + S s; 68 | + 69 | + TypeOf!int; // int 70 | + TypeOf!number; // int 71 | + TypeOf!S; // S 72 | + TypeOf!s; // S 73 | + --- 74 | +/ 75 | @safe 76 | template TypeOf(alias thing) { 77 | static if (std.traits.isType!thing || !__traits(compiles, typeof(thing))) { 78 | alias TypeOf = thing; 79 | } else { 80 | alias TypeOf = typeof(thing); 81 | } 82 | } unittest { 83 | import fluent.asserts; 84 | 85 | struct S { } 86 | class C { } 87 | 88 | is(TypeOf!int == int).should.equal(true); 89 | is(TypeOf!0 == int).should.equal(true); 90 | is(TypeOf!string == string).should.equal(true); 91 | is(TypeOf!"text" == string).should.equal(true); 92 | is(TypeOf!S == S).should.equal(true); 93 | is(TypeOf!(S()) == S).should.equal(true); 94 | is(TypeOf!C == C).should.equal(true); 95 | auto c = new C; 96 | is(TypeOf!c == C).should.equal(true); 97 | } 98 | 99 | /// Returns std.traits.isAggregate!(TypeOf!thing) 100 | @safe 101 | pure nothrow auto isAggregate(alias thing)() { 102 | alias Type = TypeOf!thing; 103 | 104 | static if (std.traits.isType!Type) { 105 | return std.traits.isAggregateType!Type; 106 | } else { 107 | return false; 108 | } 109 | } unittest { 110 | import fluent.asserts; 111 | 112 | struct S { } 113 | class C { } 114 | 115 | S s; 116 | auto c = new C; 117 | 118 | isAggregate!int.should.equal(false); 119 | isAggregate!0.should.equal(false); 120 | isAggregate!string.should.equal(false); 121 | isAggregate!"hello".should.equal(false); 122 | isAggregate!S.should.equal(true); 123 | isAggregate!s.should.equal(true); 124 | isAggregate!C.should.equal(true); 125 | isAggregate!c.should.equal(true); 126 | } 127 | 128 | /// Returns std.traits.isArray!(TypeOf!thing) 129 | @safe 130 | pure nothrow auto isArray(alias thing)() { 131 | alias Type = TypeOf!thing; 132 | 133 | static if (std.traits.isType!Type) { 134 | return std.traits.isArray!Type; 135 | } else { 136 | return false; 137 | } 138 | } unittest { 139 | import fluent.asserts; 140 | 141 | struct S { } 142 | 143 | S[] s; 144 | 145 | isArray!int.should.equal(false); 146 | isArray!0.should.equal(false); 147 | isArray!string.should.equal(std.traits.isArray!string); 148 | isArray!"hello".should.equal(std.traits.isArray!string); 149 | isArray!(S[]).should.equal(true); 150 | isArray!s.should.equal(true); 151 | } 152 | 153 | /// Returns std.traits.isAssociativeArray!(TypeOf!thing) 154 | @safe 155 | pure nothrow auto isAssociativeArray(alias thing)() { 156 | alias Type = TypeOf!thing; 157 | 158 | static if (std.traits.isType!Type) { 159 | return std.traits.isAssociativeArray!Type; 160 | } else { 161 | return false; 162 | } 163 | } unittest { 164 | import fluent.asserts; 165 | 166 | struct S { } 167 | 168 | S[string] s1; 169 | S[char] s2; 170 | 171 | isAssociativeArray!int.should.equal(false); 172 | isAssociativeArray!0.should.equal(false); 173 | isAssociativeArray!string.should.equal(false); 174 | isAssociativeArray!"hello".should.equal(false); 175 | isAssociativeArray!(S[string]).should.equal(true); 176 | isAssociativeArray!s1.should.equal(true); 177 | isAssociativeArray!s2.should.equal(true); 178 | } 179 | 180 | /// Returns std.traits.isBasic!(TypeOf!thing) 181 | @safe 182 | pure nothrow auto isBasic(alias thing)() { 183 | alias Type = TypeOf!thing; 184 | 185 | static if (std.traits.isType!Type) { 186 | return std.traits.isBasicType!Type; 187 | } else { 188 | return false; 189 | } 190 | } unittest { 191 | import fluent.asserts; 192 | 193 | struct S { } 194 | 195 | S s; 196 | 197 | isBasic!int.should.equal(true); 198 | isBasic!0.should.equal(true); 199 | isBasic!string.should.equal(false); 200 | isBasic!"hello".should.equal(false); 201 | isBasic!S.should.equal(false); 202 | isBasic!s.should.equal(false); 203 | } 204 | 205 | /// Returns std.traits.isBuiltin!(TypeOf!thing) 206 | @safe 207 | pure nothrow auto isBuiltin(alias thing)() { 208 | alias Type = TypeOf!thing; 209 | 210 | static if (std.traits.isType!Type) { 211 | return std.traits.isBuiltinType!Type; 212 | } else { 213 | return false; 214 | } 215 | } unittest { 216 | import fluent.asserts; 217 | 218 | struct S { } 219 | 220 | S s; 221 | 222 | isBuiltin!int.should.equal(true); 223 | isBuiltin!0.should.equal(true); 224 | isBuiltin!string.should.equal(true); 225 | isBuiltin!"hello".should.equal(true); 226 | isBuiltin!S.should.equal(false); 227 | isBuiltin!s.should.equal(false); 228 | } 229 | 230 | /// Returns std.traits.isInstanceOf!(templ, TypeOf!thing) 231 | @safe 232 | pure nothrow auto isInstanceOf(alias templ, alias thing)() { 233 | alias Type = TypeOf!thing; 234 | 235 | return 236 | (__traits(compiles, std.traits.isInstanceOf!(templ, thing)) ? std.traits.isInstanceOf!(templ, thing) : false) || 237 | (__traits(compiles, std.traits.isInstanceOf!(templ, Type)) ? std.traits.isInstanceOf!(templ, Type) : false); 238 | } unittest { 239 | import fluent.asserts; 240 | 241 | class C(T) { } 242 | template T(alias param) { } 243 | 244 | auto c = new C!long; 245 | alias t = T!false; 246 | 247 | isInstanceOf!(C, C!int).should.equal(true); 248 | isInstanceOf!(C, T!int).should.equal(false); 249 | isInstanceOf!(C, c).should.equal(true); 250 | 251 | isInstanceOf!(T, T!0).should.equal(true); 252 | isInstanceOf!(T, C!int).should.equal(false); 253 | isInstanceOf!(T, t).should.equal(true); 254 | } 255 | 256 | /// Returns __traits(isModule, thing) 257 | @safe 258 | pure nothrow auto isModule(alias thing)() { 259 | return __traits(isModule, thing); 260 | } unittest { 261 | import fluent.asserts; 262 | 263 | isModule!(std.traits).should.equal(true); 264 | isModule!(std.traits.hasNested).should.equal(false); 265 | } 266 | 267 | /// Returns std.traits.isNumeric!(TypeOf!thing) 268 | @safe 269 | pure nothrow auto isNumeric(alias thing)() { 270 | alias Type = TypeOf!thing; 271 | 272 | static if (std.traits.isType!Type) { 273 | return std.traits.isNumeric!Type; 274 | } else { 275 | return false; 276 | } 277 | } unittest { 278 | import fluent.asserts; 279 | 280 | struct S { } 281 | 282 | S s; 283 | 284 | isNumeric!int.should.equal(true); 285 | isNumeric!0.should.equal(true); 286 | isNumeric!double.should.equal(true); 287 | isNumeric!(0.0).should.equal(true); 288 | isNumeric!string.should.equal(false); 289 | isNumeric!"hello".should.equal(false); 290 | isNumeric!S.should.equal(false); 291 | isNumeric!s.should.equal(false); 292 | } 293 | 294 | /// Returns std.traits.isPointer!(TypeOf!thing) 295 | @safe 296 | pure nothrow static auto isPointer(alias thing)() { 297 | alias Type = TypeOf!thing; 298 | 299 | static if (std.traits.isType!Type) { 300 | return std.traits.isPointer!Type; 301 | } else { 302 | return false; 303 | } 304 | } unittest { 305 | import fluent.asserts; 306 | 307 | struct S { } 308 | class C { } 309 | 310 | auto a = 42; 311 | auto b = "hello"; 312 | 313 | auto s = S(); 314 | auto sr = new S; 315 | auto c = new C; 316 | 317 | isPointer!(int*).should.equal(true); 318 | isPointer!(string*).should.equal(true); 319 | isPointer!(S*).should.equal(true); 320 | isPointer!(sr).should.equal(true); 321 | isPointer!(C*).should.equal(true); 322 | 323 | isPointer!(int).should.equal(false); 324 | isPointer!(a).should.equal(false); 325 | isPointer!(string).should.equal(false); 326 | isPointer!(b).should.equal(false); 327 | isPointer!(S).should.equal(false); 328 | isPointer!(s).should.equal(false); 329 | isPointer!(C).should.equal(false); 330 | } 331 | 332 | /// Returns std.traits.isSomeString!(TypeOf!thing) 333 | @safe 334 | pure nothrow static auto isSomeString(alias thing)() { 335 | alias Type = TypeOf!thing; 336 | 337 | static if (std.traits.isType!Type) { 338 | return std.traits.isSomeString!Type; 339 | } else { 340 | return false; 341 | } 342 | } unittest { 343 | import fluent.asserts; 344 | 345 | isSomeString!string.should.equal(true); 346 | isSomeString!(wchar[]).should.equal(true); 347 | isSomeString!(dchar[]).should.equal(true); 348 | isSomeString!"aaa".should.equal(true); 349 | isSomeString!(const(char)[]).should.equal(true); 350 | 351 | isSomeString!int.should.equal(false); 352 | isSomeString!(int[]).should.equal(false); 353 | isSomeString!(byte[]).should.equal(false); 354 | isSomeString!null.should.equal(false); 355 | isSomeString!(char[4]).should.equal(false); 356 | } -------------------------------------------------------------------------------- /source/quirks/utility.d: -------------------------------------------------------------------------------- 1 | module quirks.utility; 2 | 3 | import std.algorithm; 4 | import std.string; 5 | 6 | /++ 7 | + Takes code in the form of a string and interpolates variables defined in the form of ${variableName}. 8 | + Usefull in combination with the q{} string literal, to keep syntax highlighting for mixed in code and avoid string concatenations, which keeps the code readable 9 | + 10 | + Params: 11 | + code = code to be mixed in 12 | + 13 | + Example: 14 | + --- 15 | + class Foo { 16 | + int id = 1; 17 | + uint age = 23; 18 | + string name = "foo"; 19 | + } 20 | + 21 | + auto foo = new Foo; 22 | + 23 | + static foreach (member; FieldNameTuple!(Foo)) { 24 | + mixin(interpolateMixin(q{ 25 | + writeln("Field ${member} has a value of: ", foo.${member}); 26 | + })); 27 | + } 28 | + --- 29 | +/ 30 | @safe 31 | pure nothrow static string interpolateMixin(string code) { 32 | string interpolatedCode = ""; 33 | 34 | auto insideInterpolation = false; 35 | string interpolatedFragment = ""; 36 | string[] interpolatedFragments = []; 37 | auto leftBlockCount = 0; 38 | auto rightBlockCount = 0; 39 | auto paramCount = 0; 40 | 41 | foreach (i, c; code) { 42 | if (insideInterpolation) { 43 | if (c == '{') { 44 | leftBlockCount++; 45 | 46 | if (leftBlockCount == 1) { 47 | continue; 48 | } 49 | } else if (c == '}') { 50 | rightBlockCount++; 51 | 52 | if (leftBlockCount == rightBlockCount) { 53 | interpolatedFragments ~= interpolatedFragment; 54 | 55 | interpolatedFragment = ""; 56 | insideInterpolation = false; 57 | leftBlockCount = 0; 58 | rightBlockCount = 0; 59 | 60 | continue; 61 | } 62 | } 63 | 64 | interpolatedFragment ~= c; 65 | } else if (c == '$' && code[i + 1] == '{') { 66 | insideInterpolation = true; 67 | paramCount++; 68 | 69 | interpolatedCode ~= "%s"; 70 | } else { 71 | if (c == '"') { 72 | interpolatedCode ~= `\`; 73 | } 74 | 75 | interpolatedCode ~= c; 76 | } 77 | } 78 | 79 | return `import std.string : join; static import std.string;mixin(std.string.format("` ~ interpolatedCode ~ `", ` ~ interpolatedFragments.join(",") ~ `));`; 80 | } unittest { 81 | import fluent.asserts; 82 | 83 | enum firstname = "first"; 84 | enum lastname = "last"; 85 | 86 | mixin(interpolateMixin(q{ 87 | string name = "${firstname}" ~ "${lastname}"; 88 | })); 89 | 90 | name.should.equal("firstlast"); 91 | } --------------------------------------------------------------------------------