1 module viva.mistflake.mistflake; 2 3 import viva.types.string; 4 5 import std.datetime; 6 import std.random; 7 8 /++ 9 + The `Mistflake` struct contains information about a mistflake id. The mistflake id consists of the following parts: 10 + 1111111111 1111 11111111111 11 + 10 4 11 12 + Fields: 13 + timestamp = The `timestamp` field is a UNIX timestamp, and consists of 10 bits 14 + workerId = The `workerId` is the ID of the generator that generated the mistflake. This can be user defined or randomly generated. Consists of 4 bits 15 + id = The `id` field is the unique ID. For every ID that is generated in the generator, the number is incremented. Consists of 11 bits 16 + 17 + Example: An example Mistflake ID may look like `1593375000795500000000001` 18 +/ 19 struct Mistflake 20 { 21 /++ 22 + The UNIX timestamp 23 +/ 24 SysTime time; 25 26 /++ 27 + The workers ID 28 +/ 29 ulong worker; 30 31 /++ 32 + The unique ID 33 +/ 34 ulong id; 35 36 /++ 37 + Generates a string representation out of the object 38 + Returns: A Mistflake string representation 39 +/ 40 @property string asString() const @safe 41 { 42 import std.conv : to; 43 44 // TODO: Make sure worker ID isn't more than 4 digits long 45 string workerStr = worker.to!string; 46 const(ulong) workerZerosToAdd = 4 - workerStr.length; 47 workerStr = str("0".repeat(workerZerosToAdd), workerStr); 48 49 string idStr = id.to!string; 50 const(ulong) idZerosToAdd = 11 - idStr.length; 51 idStr = str("0".repeat(idZerosToAdd), idStr); 52 53 return str(time.toUnixTime().to!string, workerStr, idStr); 54 } 55 56 string toString() const @safe 57 { 58 return asString; 59 } 60 61 static Mistflake fromString(string s) @safe 62 { 63 return MistflakeParser().parse(s); 64 } 65 } 66 67 /++ 68 + The `MistflakeGenerator` can generate Mistflakes 69 +/ 70 struct MistflakeGenerator 71 { 72 private { 73 Random random; 74 75 ulong start; 76 ulong worker; 77 } 78 79 /++ 80 + Takes in a starting ID and a worker ID 81 + Params: 82 + start = The starting ID (If value is `0` it will be set to `1`) 83 + worker = The worker ID (If value is `0` it will be set to a random value) 84 +/ 85 this(ulong start, ulong worker) 86 { 87 this.random = Random(unpredictableSeed); 88 89 this.start = start == 0 ? 1 : start; 90 this.worker = worker == 0 ? uniform(1000, 9999, random) : worker; 91 } 92 93 /++ 94 + Takes in a starting Mistflake and a worker ID 95 + Params: 96 + start = The starting Mistflake. The start will be the given Mistflakes ID 97 + worker = The worker ID (If value is `0` it will be set to a random value) 98 +/ 99 this(Mistflake start, ulong worker) 100 { 101 this(start.id, worker); 102 } 103 104 /++ 105 + Takes in a starting Mistflake 106 + Params: 107 + start = The starting Mistflake. The start and worker will be the given Mistflakes ID 108 +/ 109 this(Mistflake start) 110 { 111 this(start.id, start.worker); 112 } 113 114 /++ 115 + Generate the next Mistflake 116 +/ 117 public Mistflake next() @safe 118 { 119 return Mistflake(Clock.currTime(), worker, start++); 120 } 121 } 122 123 /++ 124 + The `MistflakeParser` containes functions for parsing Mistflake string representations 125 +/ 126 struct MistflakeParser 127 { 128 /++ 129 + Parse a mistflake 130 + Params: 131 + mistflake = The Mistflake string representation to be parsed 132 + Returns: The generated Mistflake 133 +/ 134 public Mistflake parse(string mistflake) @safe 135 { 136 import std.conv : to; 137 138 const(string) unix = mistflake[0..10]; 139 const(string) worker = mistflake[10..14]; 140 const(string) id = mistflake[14..$]; 141 return Mistflake(SysTime.fromUnixTime(unix.to!ulong), worker.to!ulong, id.to!ulong); 142 } 143 }