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 }