├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── build.zig.zon ├── gyro.zzz └── src ├── concepts ├── container.zig ├── decl.zig ├── extern.zig ├── field.zig ├── float.zig ├── function.zig ├── integral.zig ├── method.zig ├── packed.zig ├── same.zig ├── signed_integral.zig ├── slice.zig ├── string.zig ├── tuple.zig └── unsigned_integral.zig ├── lib.zig └── traits ├── has_decl.zig ├── has_decls.zig ├── has_field.zig ├── has_fields.zig ├── has_function.zig ├── has_functions.zig ├── is_container.zig ├── is_extern.zig ├── is_float.zig ├── is_function.zig ├── is_integral.zig ├── is_packed.zig ├── is_same.zig ├── is_signed_integral.zig ├── is_slice.zig ├── is_string.zig └── is_tuple.zig /.gitignore: -------------------------------------------------------------------------------- 1 | zig-* 2 | .gyro/ 3 | gyro.lock 4 | deps.zig 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jason Phan 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 | # Concepts 2 | 3 | The Concepts library provides compile-time validation of type constraints. 4 | 5 | ## Contents 6 | 7 | * [Overview](#overview) 8 | * [Installation](#installation) 9 | * [API Reference](#api-reference) 10 | * [License](#license) 11 | 12 | ## Overview 13 | 14 | A trait is a property of a type (e.g., `isIntegral`). A concept is a named set 15 | of traits (e.g., `integral`). 16 | 17 | ```zig 18 | const std = @import("std"); 19 | 20 | const concepts = @import("concepts"); 21 | 22 | pub fn main() anyerror!void { 23 | comptime { 24 | concepts.integral(i32); 25 | concepts.same(i32, i32); 26 | 27 | arrayList(std.ArrayList(u8)); 28 | arrayList(std.ArrayListUnmanaged(u8)); // error: concept `ArrayList` was not satisfied 29 | } 30 | } 31 | 32 | /// arrayList is a concept that requires `T` to be a `std.ArrayList`. 33 | fn arrayList(comptime T: type) void { 34 | comptime concepts.Concept("ArrayList", "")(.{ 35 | concepts.traits.hasDecl(T, "Slice"), 36 | concepts.traits.isSlice(T.Slice), 37 | concepts.traits.isSame(T, std.ArrayList(std.meta.Child(T.Slice))), 38 | }); 39 | } 40 | ``` 41 | 42 | 43 | ## Installation 44 | 45 | ### Manual 46 | 47 | 1. Install the library. 48 | 49 | ``` 50 | git clone https://github.com/ibokuri/concepts lib/concepts 51 | ``` 52 | 53 | 2. Add the library as a package in `build.zig`. 54 | 55 | ```diff 56 | const std = @import("std"); 57 | 58 | pub fn build(b: *std.build.Builder) void { 59 | 60 | ... 61 | 62 | obj.setTarget(target); 63 | obj.setBuildMode(mode); 64 | + obj.addPackagePath("concepts", "lib/concepts/src/lib.zig"); 65 | obj.install(); 66 | } 67 | ``` 68 | 69 | ### Gyro 70 | 71 | 1. Install the library. 72 | 73 | ``` 74 | gyro add -s github ibokuri/concepts 75 | gyro fetch 76 | gyro update 77 | ``` 78 | 79 | 2. Add the library as a package in `build.zig`. 80 | 81 | ```diff 82 | const std = @import("std"); 83 | +const pkgs = @import("deps.zig").pkgs; 84 | 85 | pub fn build(b: *std.build.Builder) void { 86 | 87 | ... 88 | 89 | obj.setTarget(target); 90 | obj.setBuildMode(mode); 91 | + pkgs.addAllTo(obj); 92 | obj.install(); 93 | } 94 | ``` 95 | 96 | ## API Reference 97 | 98 | ### `concepts` 99 | 100 | #### [General](src/lib.zig) 101 | 102 | - `err` - Generates a compile error due to an invariant violation during concept validation. 103 | - `fail` - Generates a compile error due to a concept being unsatisfied. 104 | 105 | #### [Concepts](src/concepts) 106 | 107 | - `container` - Specifies that a type is a container type. 108 | - `decl` - Specifies that a type contains a given declaration. 109 | - `extern` - Specifies that a type has an `extern` in-memory layout. 110 | - `field` - Specifies that a type contains a given field. 111 | - `float` - Specifies that a type is a floating-point type. 112 | - `integral` - Specifies that a type is an integral type. 113 | - `method` - Specifies that a type contains a given function. 114 | - `packed` - Specifies that a type has a `packed` in-memory layout. 115 | - `same` - Specifies that a type is the same as another type. 116 | - `signedIntegral` - Specifies that a type is an integral type that is signed. 117 | - `slice` - Specifies that a type is a slice type. 118 | - `string` - Specifies that a type is a string type. 119 | - `tuple` - Specifies that a type is a tuple type. 120 | - `unsignedIntegral` - Specifies that a type is an integral type that is unsigned. 121 | 122 | ### `concepts.traits` 123 | 124 | #### [Traits](src/traits) 125 | 126 | - `hasDecl` - Checks if a type contains a given declaration. 127 | - `hasDecls` - Checks if a type contains a given set of declarations. 128 | - `hasField` - Checks if a type contains a given field. 129 | - `hasFields` - Checks if a type contains a given set of fields. 130 | - `hasFunction` - Checks if a type contains a given function. 131 | - `hasFunctions` - Checks if a type contains a given set of functions. 132 | - `isContainer` - Checks if a type is a container type. 133 | - `isExtern` - Checks if a type has an `extern` in-memory layout. 134 | - `isFloat` - Checks if a type is a floating-point type. 135 | - `isIntegral` - Checks if a type is an integral type. 136 | - `isPacked` - Checks if a type has a `packed` in-memory layout. 137 | - `isSame` - Checks if two types are equal. 138 | - `isSignedIntegral` - Checks if a type is an integral type that is signed. 139 | - `isSlice` - Checks if a type is a slice type. 140 | - `isString` - Checks if a type is a string type. 141 | - `isTuple` - Checks if a type is a tuple type. 142 | 143 | ## License 144 | 145 | See [LICENSE](LICENSE) for more detail. 146 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const package_name = "concepts"; 4 | const package_path = "src/lib.zig"; 5 | 6 | const tests = [_][]const u8{ 7 | "src/lib.zig", 8 | }; 9 | 10 | pub fn build(b: *std.build.Builder) void { 11 | const target = b.standardTargetOptions(.{}); 12 | const optimize = b.standardOptimizeOption(.{}); 13 | 14 | _ = b.addModule("concepts", .{ 15 | .source_file = .{ .path = "src/lib.zig" }, 16 | }); 17 | 18 | const step = b.step("test", "Run framework tests"); 19 | for (tests) |path| { 20 | const t = b.addTest(.{ 21 | .name = "test", 22 | .root_source_file = .{ .path = path }, 23 | .target = target, 24 | .optimize = optimize, 25 | }); 26 | 27 | step.dependOn(&t.step); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = "concepts", 3 | .version = "0.1.0", 4 | .dependencies = .{}, 5 | } 6 | 7 | -------------------------------------------------------------------------------- /gyro.zzz: -------------------------------------------------------------------------------- 1 | pkgs: 2 | concepts: 3 | version: 0.0.0 4 | description: "Compile-time validation of type constraints" 5 | license: MIT 6 | source_url: "https://github.com/ibokuri/concepts" 7 | root: src/lib.zig 8 | -------------------------------------------------------------------------------- /src/concepts/container.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Container"; 4 | 5 | pub fn container(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isContainer(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/decl.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Decl"; 4 | 5 | pub fn decl(comptime T: type, comptime name: []const u8) void { 6 | comptime { 7 | // Invariants 8 | concepts.container(T); 9 | 10 | // Constraints 11 | if (!concepts.traits.hasDecl(T, name)) { 12 | concepts.fail(concept, ""); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/concepts/extern.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Extern"; 4 | 5 | pub fn @"extern"(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isExtern(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/field.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Field"; 4 | 5 | pub fn field(comptime T: type, comptime name: []const u8) void { 6 | comptime { 7 | // Invariants 8 | concepts.container(T); 9 | 10 | // Constraints 11 | if (!concepts.traits.hasField(T, name)) { 12 | concepts.fail(concept, ""); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/concepts/float.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Float"; 4 | 5 | pub fn float(comptime T: type) void { 6 | comptime { 7 | // Invariants 8 | concepts.same(@TypeOf(T), type); 9 | 10 | // Constraints 11 | if (!concepts.traits.isFloat(T)) { 12 | concepts.fail(concept, ""); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/concepts/function.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Function"; 4 | 5 | pub fn function(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isFunction(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/integral.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Integral"; 4 | 5 | pub fn integral(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isIntegral(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/method.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Method"; 4 | 5 | pub fn method(comptime T: type, comptime name: []const u8) void { 6 | comptime { 7 | // Invariants 8 | concepts.container(T); 9 | 10 | // Constraints 11 | if (!concepts.traits.hasFunction(T, name)) { 12 | concepts.fail(concept, ""); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/concepts/packed.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Packed"; 4 | 5 | pub fn @"packed"(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isPacked(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/same.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Same"; 4 | 5 | pub fn same(comptime A: type, comptime B: type) void { 6 | comptime { 7 | if (!concepts.traits.isSame(A, B)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/signed_integral.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "SignedIntegral"; 4 | 5 | pub fn signedIntegral(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isSignedIntegral(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/slice.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Slice"; 4 | 5 | pub fn slice(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isSlice(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/string.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "String"; 4 | 5 | pub fn string(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isString(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/tuple.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "Tuple"; 4 | 5 | pub fn tuple(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isTuple(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/concepts/unsigned_integral.zig: -------------------------------------------------------------------------------- 1 | const concepts = @import("../lib.zig"); 2 | 3 | const concept = "UnsignedIntegral"; 4 | 5 | pub fn unsignedIntegral(comptime T: type) void { 6 | comptime { 7 | if (!concepts.traits.isIntegral(T) or concepts.traits.isSignedIntegral(T)) { 8 | concepts.fail(concept, ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lib.zig: -------------------------------------------------------------------------------- 1 | pub const traits = struct { 2 | pub usingnamespace @import("traits/has_decl.zig"); 3 | pub usingnamespace @import("traits/has_decls.zig"); 4 | pub usingnamespace @import("traits/has_field.zig"); 5 | pub usingnamespace @import("traits/has_fields.zig"); 6 | pub usingnamespace @import("traits/has_function.zig"); 7 | pub usingnamespace @import("traits/has_functions.zig"); 8 | pub usingnamespace @import("traits/is_container.zig"); 9 | pub usingnamespace @import("traits/is_extern.zig"); 10 | pub usingnamespace @import("traits/is_float.zig"); 11 | pub usingnamespace @import("traits/is_function.zig"); 12 | pub usingnamespace @import("traits/is_integral.zig"); 13 | pub usingnamespace @import("traits/is_packed.zig"); 14 | pub usingnamespace @import("traits/is_same.zig"); 15 | pub usingnamespace @import("traits/is_signed_integral.zig"); 16 | pub usingnamespace @import("traits/is_slice.zig"); 17 | pub usingnamespace @import("traits/is_string.zig"); 18 | pub usingnamespace @import("traits/is_tuple.zig"); 19 | }; 20 | 21 | const concepts = struct { 22 | pub usingnamespace @import("concepts/container.zig"); 23 | pub usingnamespace @import("concepts/decl.zig"); 24 | pub usingnamespace @import("concepts/extern.zig"); 25 | pub usingnamespace @import("concepts/field.zig"); 26 | pub usingnamespace @import("concepts/float.zig"); 27 | pub usingnamespace @import("concepts/integral.zig"); 28 | pub usingnamespace @import("concepts/method.zig"); 29 | pub usingnamespace @import("concepts/packed.zig"); 30 | pub usingnamespace @import("concepts/same.zig"); 31 | pub usingnamespace @import("concepts/signed_integral.zig"); 32 | pub usingnamespace @import("concepts/slice.zig"); 33 | pub usingnamespace @import("concepts/string.zig"); 34 | pub usingnamespace @import("concepts/tuple.zig"); 35 | pub usingnamespace @import("concepts/unsigned_integral.zig"); 36 | }; 37 | 38 | pub usingnamespace concepts; 39 | 40 | pub fn Concept(comptime concept: []const u8, comptime msg: []const u8) fn (anytype) void { 41 | const Closure = struct { 42 | fn f(results: anytype) void { 43 | comptime { 44 | concepts.tuple(@TypeOf(results)); 45 | 46 | for (results) |result| concepts.same(@TypeOf(result), bool); 47 | 48 | for (results) |result| if (!result) fail(concept, msg); 49 | } 50 | } 51 | }; 52 | 53 | return Closure.f; 54 | } 55 | 56 | pub fn err(comptime concept: []const u8, comptime msg: []const u8) void { 57 | const base = "concept `" ++ concept ++ "` could not be satisfied"; 58 | const extra = " (" ++ msg ++ ")"; 59 | 60 | comptime { 61 | switch (msg.len) { 62 | 0 => @compileError(base), 63 | else => @compileError(base ++ extra), 64 | } 65 | } 66 | } 67 | 68 | pub fn fail(comptime concept: []const u8, comptime msg: []const u8) void { 69 | const base = "concept `" ++ concept ++ "` was not satisfied"; 70 | const extra = " (" ++ msg ++ ")"; 71 | 72 | comptime { 73 | switch (msg.len) { 74 | 0 => @compileError(base), 75 | else => @compileError(base ++ extra), 76 | } 77 | } 78 | } 79 | 80 | comptime { 81 | @import("std").testing.refAllDecls(traits); 82 | } 83 | -------------------------------------------------------------------------------- /src/traits/has_decl.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const concepts = @import("../lib.zig"); 4 | 5 | pub fn hasDecl(comptime T: type, comptime name: []const u8) bool { 6 | comptime { 7 | if (!concepts.traits.isContainer(T)) { 8 | return false; 9 | } 10 | 11 | return @hasDecl(T, name); 12 | } 13 | } 14 | 15 | test { 16 | const A = struct {}; 17 | const B = struct { 18 | pub var a: u32 = undefined; 19 | 20 | pub var b: u32 = undefined; 21 | 22 | c: bool, 23 | 24 | pub fn useless() void {} 25 | }; 26 | 27 | try std.testing.expect(!hasDecl(A, "a")); 28 | 29 | try std.testing.expect(hasDecl(B, "a")); 30 | try std.testing.expect(hasDecl(B, "b")); 31 | try std.testing.expect(hasDecl(B, "useless")); 32 | try std.testing.expect(!hasDecl(B, "c")); 33 | 34 | try std.testing.expect(!hasDecl(u8, "a")); 35 | } 36 | -------------------------------------------------------------------------------- /src/traits/has_decls.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const concepts = @import("../lib.zig"); 4 | 5 | pub fn hasDecls(comptime T: type, comptime names: anytype) bool { 6 | comptime { 7 | // Invariants 8 | concepts.tuple(@TypeOf(names)); 9 | 10 | for (std.meta.fields(@TypeOf(names))) |field| { 11 | concepts.string(field.type); 12 | } 13 | 14 | // Constraints 15 | if (names.len == 0) { 16 | return false; 17 | } 18 | 19 | for (names) |name| { 20 | if (!concepts.traits.hasDecl(T, name)) { 21 | return false; 22 | } 23 | } 24 | 25 | return true; 26 | } 27 | } 28 | 29 | test { 30 | const A = struct {}; 31 | const B = struct { 32 | pub var a: u32 = undefined; 33 | 34 | pub var b: u32 = undefined; 35 | 36 | c: bool, 37 | 38 | pub fn useless() void {} 39 | }; 40 | 41 | try std.testing.expect(hasDecls(B, .{ "a", "b", "useless" })); 42 | 43 | try std.testing.expect(!hasDecls(A, .{})); 44 | //try std.testing.expect(!hasDecls(A, .{"a"})); 45 | try std.testing.expect(!hasDecls(B, .{ "a", "b", "c" })); 46 | 47 | //try std.testing.expect(!hasDecls(u8, .{"a"})); 48 | } 49 | -------------------------------------------------------------------------------- /src/traits/has_field.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn hasField(comptime T: type, comptime name: []const u8) bool { 4 | return std.meta.trait.hasField(name)(T); 5 | } 6 | 7 | test { 8 | const T = struct { 9 | value: u32, 10 | }; 11 | 12 | try std.testing.expect(hasField(T, "value")); 13 | 14 | try std.testing.expect(!hasField(*T, "value")); 15 | try std.testing.expect(!hasField(T, "x")); 16 | try std.testing.expect(!hasField(**T, "x")); 17 | try std.testing.expect(!hasField(u8, "value")); 18 | } 19 | -------------------------------------------------------------------------------- /src/traits/has_fields.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const concepts = @import("../lib.zig"); 4 | 5 | pub fn hasFields(comptime T: type, comptime names: anytype) bool { 6 | comptime { 7 | // Invariants 8 | concepts.tuple(@TypeOf(names)); 9 | 10 | for (std.meta.fields(@TypeOf(names))) |field| { 11 | concepts.string(field.type); 12 | } 13 | 14 | // Constraints 15 | if (names.len == 0) { 16 | return false; 17 | } 18 | 19 | for (names) |name| { 20 | if (!concepts.traits.hasField(T, name)) { 21 | return false; 22 | } 23 | } 24 | 25 | return true; 26 | } 27 | } 28 | 29 | test { 30 | //const T = struct { 31 | //a: u32, 32 | //b: u32, 33 | //}; 34 | 35 | //try std.testing.expect(hasFields(T, .{ "a", "b" })); 36 | 37 | //try std.testing.expect(!hasFields(T, .{})); 38 | //try std.testing.expect(!hasFields(T, .{ "a", "c" })); 39 | //try std.testing.expect(!hasFields(*T, .{ "a", "b" })); 40 | //try std.testing.expect(!hasFields(**T, .{ "a", "b" })); 41 | //try std.testing.expect(!hasFields(u8, .{ "a", "b" })); 42 | } 43 | -------------------------------------------------------------------------------- /src/traits/has_function.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const concepts = @import("../lib.zig"); 4 | 5 | pub fn hasFunction(comptime T: type, comptime name: []const u8) bool { 6 | comptime { 7 | if (!concepts.traits.isContainer(T)) { 8 | return false; 9 | } 10 | 11 | return std.meta.trait.hasFn(name)(T); 12 | } 13 | } 14 | 15 | test { 16 | const T = struct { 17 | pub fn f() void {} 18 | }; 19 | 20 | try std.testing.expect(hasFunction(T, "f")); 21 | try std.testing.expect(!hasFunction(T, "non-existent")); 22 | try std.testing.expect(!hasFunction(u8, "f")); 23 | } 24 | -------------------------------------------------------------------------------- /src/traits/has_functions.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const concepts = @import("../lib.zig"); 4 | 5 | pub fn hasFunctions(comptime T: type, comptime names: anytype) bool { 6 | comptime { 7 | // Invariants 8 | concepts.tuple(@TypeOf(names)); 9 | 10 | for (std.meta.fields(@TypeOf(names))) |field| { 11 | concepts.string(field.type); 12 | } 13 | 14 | // Constraints 15 | if (names.len == 0) { 16 | return false; 17 | } 18 | 19 | for (names) |name| { 20 | if (!concepts.traits.hasFunction(T, name)) { 21 | return false; 22 | } 23 | } 24 | 25 | return true; 26 | } 27 | } 28 | 29 | test { 30 | const T = struct { 31 | pub fn a() void {} 32 | pub fn b() void {} 33 | }; 34 | 35 | try std.testing.expect(hasFunctions(T, .{ "a", "b" })); 36 | 37 | try std.testing.expect(!hasFunctions(T, .{})); 38 | try std.testing.expect(!hasFunctions(T, .{ "a", "non-existent" })); 39 | try std.testing.expect(!hasFunctions(u8, .{"a"})); 40 | } 41 | -------------------------------------------------------------------------------- /src/traits/is_container.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isContainer(comptime T: type) bool { 4 | comptime return std.meta.trait.isContainer(T); 5 | } 6 | 7 | test { 8 | const Struct = struct {}; 9 | const Union = union { a: void }; 10 | const Enum = enum { A, B }; 11 | const Opaque = opaque {}; 12 | 13 | try std.testing.expect(isContainer(Struct)); 14 | try std.testing.expect(isContainer(Union)); 15 | try std.testing.expect(isContainer(Enum)); 16 | try std.testing.expect(isContainer(Opaque)); 17 | try std.testing.expect(!isContainer(u8)); 18 | } 19 | -------------------------------------------------------------------------------- /src/traits/is_extern.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isExtern(comptime T: type) bool { 4 | return std.meta.trait.isExtern(T); 5 | } 6 | 7 | test { 8 | const A = extern struct {}; 9 | const B = struct {}; 10 | 11 | try std.testing.expect(isExtern(A)); 12 | try std.testing.expect(!isExtern(B)); 13 | } 14 | -------------------------------------------------------------------------------- /src/traits/is_float.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isFloat(comptime T: type) bool { 4 | return std.meta.trait.isFloat(T); 5 | } 6 | 7 | test { 8 | comptime { 9 | try std.testing.expect(isFloat(comptime_float)); 10 | try std.testing.expect(isFloat(f16)); 11 | try std.testing.expect(isFloat(f32)); 12 | try std.testing.expect(isFloat(f64)); 13 | try std.testing.expect(isFloat(f128)); 14 | 15 | try std.testing.expect(!isFloat(comptime_int)); 16 | try std.testing.expect(!isFloat(i8)); 17 | try std.testing.expect(!isFloat(u8)); 18 | try std.testing.expect(!isFloat(bool)); 19 | try std.testing.expect(!isFloat([]const u8)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/traits/is_function.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isFunction(comptime T: type) bool { 4 | return @typeInfo(T) == .Fn; 5 | } 6 | 7 | test { 8 | comptime { 9 | try std.testing.expect(isFunction(fn () void)); 10 | try std.testing.expect(!isFunction(i32)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/traits/is_integral.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isIntegral(comptime T: type) bool { 4 | return std.meta.trait.isIntegral(T); 5 | } 6 | 7 | test { 8 | comptime { 9 | try std.testing.expect(isIntegral(comptime_int)); 10 | try std.testing.expect(isIntegral(i8)); 11 | try std.testing.expect(isIntegral(u8)); 12 | 13 | try std.testing.expect(!isIntegral(bool)); 14 | try std.testing.expect(!isIntegral(f32)); 15 | try std.testing.expect(!isIntegral([]const u8)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/traits/is_packed.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isPacked(comptime T: type) bool { 4 | return std.meta.trait.isPacked(T); 5 | } 6 | 7 | test { 8 | const A = packed struct {}; 9 | const B = struct {}; 10 | 11 | try std.testing.expect(isPacked(A)); 12 | try std.testing.expect(!isPacked(B)); 13 | } 14 | -------------------------------------------------------------------------------- /src/traits/is_same.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isSame(comptime A: type, comptime B: type) bool { 4 | return A == B; 5 | } 6 | 7 | test { 8 | comptime { 9 | try std.testing.expect(isSame(@TypeOf(1), comptime_int)); 10 | try std.testing.expect(isSame(@TypeOf(1.0), comptime_float)); 11 | try std.testing.expect(isSame(@TypeOf(true), bool)); 12 | 13 | try std.testing.expect(!isSame(@TypeOf(1), i32)); 14 | try std.testing.expect(!isSame(@TypeOf(1.0), f64)); 15 | try std.testing.expect(!isSame(@TypeOf("true"), bool)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/traits/is_signed_integral.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isSignedIntegral(comptime T: type) bool { 4 | return std.meta.trait.isSignedInt(T); 5 | } 6 | 7 | test { 8 | comptime { 9 | try std.testing.expect(isSignedIntegral(comptime_int)); 10 | try std.testing.expect(isSignedIntegral(i8)); 11 | 12 | try std.testing.expect(!isSignedIntegral(u8)); 13 | try std.testing.expect(!isSignedIntegral(bool)); 14 | try std.testing.expect(!isSignedIntegral(f32)); 15 | try std.testing.expect(!isSignedIntegral([]const u8)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/traits/is_slice.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isSlice(comptime T: type) bool { 4 | return std.meta.trait.isSlice(T); 5 | } 6 | 7 | test { 8 | const array = [_]u8{0} ** 10; 9 | var zero: usize = 0; 10 | 11 | try std.testing.expect(isSlice(@TypeOf(array[zero..]))); 12 | try std.testing.expect(!isSlice(@TypeOf(array))); 13 | try std.testing.expect(!isSlice(@TypeOf(&array[0]))); 14 | } 15 | -------------------------------------------------------------------------------- /src/traits/is_string.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isString(comptime T: type) bool { 4 | return std.meta.trait.isZigString(T); 5 | } 6 | 7 | test { 8 | try std.testing.expect(isString([]const u8)); 9 | try std.testing.expect(isString([]u8)); 10 | try std.testing.expect(isString([:0]const u8)); 11 | try std.testing.expect(isString([:0]u8)); 12 | try std.testing.expect(isString([:5]const u8)); 13 | try std.testing.expect(isString([:5]u8)); 14 | try std.testing.expect(isString(*const [0]u8)); 15 | try std.testing.expect(isString(*[0]u8)); 16 | try std.testing.expect(isString(*const [0:0]u8)); 17 | try std.testing.expect(isString(*[0:0]u8)); 18 | try std.testing.expect(isString(*const [0:5]u8)); 19 | try std.testing.expect(isString(*[0:5]u8)); 20 | try std.testing.expect(isString(*const [10]u8)); 21 | try std.testing.expect(isString(*[10]u8)); 22 | try std.testing.expect(isString(*const [10:0]u8)); 23 | try std.testing.expect(isString(*[10:0]u8)); 24 | try std.testing.expect(isString(*const [10:5]u8)); 25 | try std.testing.expect(isString(*[10:5]u8)); 26 | 27 | try std.testing.expect(!isString(u8)); 28 | try std.testing.expect(!isString([4]u8)); 29 | try std.testing.expect(!isString([4:0]u8)); 30 | try std.testing.expect(!isString([*]const u8)); 31 | try std.testing.expect(!isString([*]const [4]u8)); 32 | try std.testing.expect(!isString([*c]const u8)); 33 | try std.testing.expect(!isString([*c]const [4]u8)); 34 | try std.testing.expect(!isString([*:0]const u8)); 35 | try std.testing.expect(!isString([*:0]const u8)); 36 | try std.testing.expect(!isString(*[]const u8)); 37 | try std.testing.expect(!isString(?[]const u8)); 38 | try std.testing.expect(!isString(?*const [4]u8)); 39 | try std.testing.expect(!isString([]allowzero u8)); 40 | try std.testing.expect(!isString([]volatile u8)); 41 | try std.testing.expect(!isString(*allowzero [4]u8)); 42 | try std.testing.expect(!isString(*volatile [4]u8)); 43 | } 44 | -------------------------------------------------------------------------------- /src/traits/is_tuple.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn isTuple(comptime T: type) bool { 4 | return std.meta.trait.isTuple(T); 5 | } 6 | 7 | test { 8 | comptime { 9 | try std.testing.expect(isTuple(std.meta.Tuple(&.{ i8, u8 }))); 10 | 11 | try std.testing.expect(!isTuple(i8)); 12 | try std.testing.expect(!isTuple(u8)); 13 | try std.testing.expect(!isTuple(bool)); 14 | try std.testing.expect(!isTuple(f32)); 15 | try std.testing.expect(!isTuple([]const u8)); 16 | try std.testing.expect(!isTuple(struct { x: i32, y: i32 })); 17 | } 18 | } 19 | --------------------------------------------------------------------------------