1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! # Snowcloud
//!
//! a small library for implementing custom ids based on timestamps, static
//! ids, and sequence counters. the module provides 2 types of generators, a
//! thread safe and non thread safe version. they allow for different types of
//! waiting for ids if you want specific behavior. the snowflakes generated by
//! a generator are composed of 3 sections and are described below. small
//! example of how to create a generator and create a snowflake is shown below.
//!
//! ```rust
//! // 43 bit timestamp, 8 bit primary id, 12 bit sequence
//! type MyCloud = snowcloud::SingleThread<43, 8, 12>;
//!
//! // 2023/03/23 9:00:00 in milliseconds, timestamps will start from this
//! // date
//! const START_TIME: u64 = 1679587200000;
//! // primary id could be a machine/node id for example
//! const PRIMARY_ID: i64 = 1;
//!
//! let mut cloud = MyCloud::new(PRIMARY_ID, START_TIME)
//!     .expect("failed to create MyCloud");
//! let flake = cloud.next_id()
//!     .expect("failed to create snowflake");
//!
//! println!("{}", flake.id());
//! ```
//!
//! ## Behavior
//!
//! [`MultiThread`] is a thread safe implementation for sharing between threads
//! on a system. it uses an [`Arc`](std::sync::Arc) [`Mutex`](std::sync::Mutex)
//! to handle sharing the sequence count and prev_time. the only time it will
//! block is when acquiring the mutex and will not wait if a valid snowflake
//! cannot be acquired. if a generator is unable to create a snowflake because
//! the max sequence number has been reached an error will be returned
//! providing an estimated duration to the next millisecond. how you want to
//! wait can be decided by the user.
//!
//! [`SingleThread`] is similar in most aspects to MultiThread expect next_id
//! is a mutating call and sequence count with prev_time are not stored in an
//! Arc Mutext. THIS IS NOT THREAD SAFE.
//!
//! ## Traits
//!
//! to help with using a generator in other situations, traits are provided and
//! implemented for the base types (more can be added later if
//! necessary/desired).
//!
//! - [`IdGenerator`](crate::traits::IdGenerator) describes the basic layout of
//!   an id generator. requiring an Error, Id, and Output type to be specified
//!   along with the next_id method.
//! - [`IdGeneratorMut`](crate::traits::IdGeneratorMut) is similar to
//!   [`IdGenerator`](crate::traits::IdGenerator) except the next_id call
//!   allows for mutating the object
//! - [`NextAvailId`](crate::traits::NextAvailId) describes an object that is
//!   capable of returing a [`duraiton`](std::time::Duration) to the next 
//!   available millisecond.
//!
//! current use case would be for allowing different types of waiting for the
//! next available id. see [`blocking_next_id`](crate::wait::blocking_next_id)
//! for example implementation.
//!
//! ## Snowflake Bits
//!
//! the format of the snowflake is as follows:
//!
//! ```text
//!  01111111111111111111111111111111111111111111 - 11111111 - 111111111111
//!  |                                          |   |      |   |          |
//! 64                                         21  20     13  12          1
//!                                     timestamp          |              |
//!                                               primary id              |
//!                                                                sequence
//! ```
//!
//! the bit values for each segment can be specified by the user if you
//! need/want certain ranges. there is currently no check to ensure that the
//! values specified equal 63 bits (at least none that I can thing of).
//!
//! ### Timestamp
//!
//! the timestamp is in millisconds and is based from a specific start date
//! that you can specify. the start date must be in the future of
//! [`UNIX_EPOCH`](std::time::SystemTime::UNIX_EPOCH) and cannot be a date in
//! the future, `now >= start_time >= UNIX_EPOCH`. internally, a snowcloud will
//! use [`SystemTime`](std::time::SystemTime) to get the timestamp and the
//! convert to the necessary values.
//!
//! ```rust
//! // the current example date is 2023/03/23 9:00:00.
//! const VALID_START_DATE: u64 =   1679587200000;
//!
//! // if a date that is after the current system time is provided the
//! // snowcloud will return an error. 2077/10/23 9:00:00
//! const INVALID_START_DATE: u64 = 3402205200000;
//! ```
//!
//! below is a table with various bit values and how many years you can get out
//! of a timestamp. you will probably get diminishing returns with lower bit
//! values if this is to be used over a long duration of time.
//!
//! | bits | max value | years |
//! | ---: | --------: | ----: |
//! | 43 | 8796093022207 | 278 |
//! | 42 | 4398046511103 | 139 |
//! | 41 | 2199023255551 | 69 |
//! | 40 | 1099511627775 | 34 |
//! | 39 | 549755813887 | 17 |
//! | 38 | 274877906943 | 8 |
//! | 37 | 137438953471 | 4 |
//! | 36 | 68719476735 | 2 |
//! | 35 | 34359738367 | 1 |
//!
//! ### Primary Id
//!
//! the primary id is static to the instance of a generator. if using a
//! generator across multiple machines then it could be the id of the machine.
//! this would allow for those machines to create unique snowflakes that will
//! not conflict with each other as long as the primary id is different between
//! them.
//!
//! ### Sequence
//!
//! the sequence number is used to indicate how many records can be generated
//! within a single millisecond. when the max sequence value is reached in a
//! generator, the user will have to wait for the next available millisecond to
//! get a new snowflake. the higher the bit value the more records that can be
//! created each millisecond. the sequence number will always start with 1 when
//! creating a generator.
//!
//! ## De/Serialize
//!
//! snowflakes support serde [`Serialize`](serde::Serialize) and
//! [`Deserialize`](serde::Deserialize) to [`i64`](core::primitive::i64) with 
//! an addtional option to de/serailize to a string with
//! [`i64_string_id`](crate::serde_ext::i64_string_id)

pub mod traits;
mod error;

mod flake;
mod cloud;

pub mod wait;

pub use error::Error;
pub use flake::*;
pub use cloud::*;