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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
//! todo2(a.k.a. todo or die) - A better `todo!` macro inspired from [searls/todo_or_die](https://github.com/searls/todo_or_die)
//!
//! [](https://crates.io/crates/todo2)
//! [](https://docs.rs/todo2)
//! [](https://crates.io/crates/todo2)
//! [](https://github.com/0x61nas/todo2/blob/aurora/LICENSE)
//!
//! This crate provides a better `todo!` macro, which allows you to specify the deadline and the condition when the code should be implemented.
//! and when the condition or the deadline is met, the code will panic or emit a compile error, or just log an error.
//!
//! > Note: this crate is still in the early development, so it may have some bugs, and the API may change in the future.
//! > If you have any suggestions or you found a bug, please open an issue or a pull request.
//!
//! # Usage
//! Add this to your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! todo2 = "0.1.0"
//! ```
//! or just run this command:
//! ```sh
//! cargo add todo2
//! ```
//!
//! ```rust,should_panic
//! #[macro_use]
//! extern crate todo2;
//!
//! fn main() {
//! todo!("Hack NASA", by: 2024-3-26 at 9:00);
//! get_a_hot_gf(true)
//! }
//!
//! fn get_a_hot_gf(single: bool) {
//! todo!("Get a hot girlfriend", if: single);
//! }
//! ```
//!
//! # Features
//! - `log` - Just logs an error instead of panicking or emitting a compile error, this may useful in the serious projects, this feature respects that you have added the `log` crate to your dependencies
//! - `compile-error` - Emits a compile error instead of panicking.
//! - `with-chrono` - Enables the `chrono` this enables you to specify the deadline for the `by` condition using the [`chrono::Utc`](https://docs.rs/chrono/latest/chrono/struct.Utc.html) or [`chrono::DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) types. [not implemented yet](#maybe)
//! - `with-time` - Enables the `time` this enables you to specify the deadline for the `by` condition using the [`time::OffsetDateTime`](https://docs.rs/time/latest/time/struct.OffsetDateTime.html) type or the [`time::macros::datetime`](https://docs.rs/time/0.3.28/time/macros/macro.datetime.html) macro. [not implemented yet](#maybe)
//! - `and-time` - allows you to specify a specific time of the day in the `by` condition
//! - `original-compatibility` - Allows you to use this macro without pass any arguments, or with only the message.
//! - `strict-syntax` - Enables the strict syntax,, just too force you to put a comma or a semicolon after the message.
//! - `chrono-backend` - Use the `chrono` as the backend instead of the default implementation for the `by` condition to calculate the unix time stamp. I prefer to enable this feature if I have chrono in the dependencies, because it's more accurate than the default implementation. [Read more](#backends)
//! - `time-backend` - Use the `time` as the backend instead of the default implementation for the `by` condition to calculate the unix time stamp. I prefer to enable this feature if I have time in the dependencies, because it's more accurate than the default implementation. [Read more](#backends)
//! - `am-cool` - To indicate that you are cool. I love you.
//!
//! The default features are: `original-compatibility`, `strict-syntax`, `and-time`, `time-backend`.
//!
//! # Examples
//! ## Using the `log` feature
//! You have to enable the `log` feature in your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! # You also have to add the `log` crate to your dependencies
//! log = "0.4.20"
//! # Use any implementation you want for the logging, in this example, I will use the `simple_logger` crate
//! simple_logger = "4.2.0"
//! # and of course, our beloved `todo2` crate
//! todo2 = { version = "0.1.0", features = ["log"] }
//! ```
//! ```rust,ignore
//! #[macro_use]
//! extern crate todo2;
//! #[macro_use]
//! extern crate log;
//!
//! use simple_logger::SimpleLogger;
//!
//! fn main() {
//! // Initialize the logger
//! SimpleLogger::new().init().unwrap();
//!
//! todo!("Make a cool crate", by: 2024-02-02)
//! }
//! ```
//! This will log an error like this when the deadline is met:
//! ```log
//! 2024-02-02T17:27:07.013874956Z ERROR [logging_example] src/main.rs:9: Make a cool crate
//! ```
//! consider that you can't enable the `compile-error` feature with the `log` feature. only one of them can be enabled at a time.
//!
//! ## Using the `compile-error` feature
//! First, add the crate to your `Cargo.toml` and enable the `compile-error` feature:
//! ```sh
//! cargo add todo2 --features compile-error
//! ```
//! ```rust,ignore
//! #[macro_use]
//! extern crate todo2;
//!
//! fn main() {
//! todo!("Remove this secret", by: 2024-02-23);
//! let my_little_secret = "very secret";
//! }
//! ```
//! this will emit a compile error like this, when u try to compile the code in release mode.
//!
//! # Time in the `by` condition
//! by default, the `by` condition takes a raw date and parse it with our custom parser, which expects the date in the `YYYY-MM-DD` format and `YYYY-MM-DD at HH:MM` or `YYYY-MM-DD @ HH:MM` format if you have the `and-time` feature enabled.
//! and then it calculates the unix time stamp in UTC, and then compares it with the current time stamp.
//!
//! this for the parsing part, noting interested here. just macros magic. the complexity comes when we want to calculate the unix time stamp from the parsed date.
//! here the time zones and the daylight saving time and the leap seconds come to play.
//! and I don't want to deal with this complexity 'cause I'm lazy and this is a "proc macro" not a normal crate witch means that it runs at compile time, and we all know that the rust compile times is so "fast" :) and I don't want to make it slower.
//! so I implemented a simple algorithm to calculate the unix time stamp, which is not accurate, but it works. hmm, kinda.
//!
//! ## Backends
//! and that's why we have the `chrono-backend` and the `time-backend` features, to use the `chrono` or the `time` crate to calculate the unix time stamp instead of the default implementation.
//!
//! I encourage you to enable one of them if you don't have a problem with adding yet another dependence to your project dependencies tree, or if you already have one of them in your dependencies already.
//! at least until we have a better implementation for the default backend.
//!
//! the backend doesn't affect the parsing part, or the syntax, it only affects the calculation of the unix time stamp, witch is internal thing, so you don't have to worry about it from this perspective.
//!
//! # Maybe?
//! Here some ideas that I may implement in the future releases:
//! - [ ] Implement the `with-chrono` feature, to enable the user to use the [`chrono::Utc`](https://docs.rs/chrono/latest/chrono/struct.Utc.html) or [`chrono::DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) types
//! to specify the deadline for the `by` condition instead of the raw date.
//! example:
//! ```rust,ignore
//! # #[macro_use]
//! # extern crate todo2;
//! todo!("Make a cool crate", by: chrono::Utc.with_ymd_and_hms(2024, 02, 02, 9, 0, 0));
//! ```
//! - [ ] Implement the `with-time` feature, to enable the user to use the [`time::OffsetDateTime`](https://docs.rs/time/latest/time/struct.OffsetDateTime.html) type or the [`time::macros::datetime`](https://docs.rs/time/0.3.28/time/macros/macro.datetime.html) macro to specify the deadline for the `by` condition instead of the raw date.
//! example:
//! ```rust,ignore
//! # #[macro_use]
//! # extern crate todo2;
//! todo!("Make a cool crate", by: time::macros::datetime!(2024-02-02 09:00:00));
//! ```
//! - [ ] Make the `if` condition parser able to evaluate some conditions at compile time, so we can use the `compile-error` feature with the `if` condition.
//! example:
//! ```rust,ignore
//! # #[macro_use]
//! # extern crate todo2;
//! # fn main() {
//! todo!("Remove this secret", if: !cfg!(debug_assertions));
//! let my_little_secret = "i love you";
//!# }
//! ```
//!
//! # Contributing
//! I'm happy to accept any contributions, just consider reading the [CONTRIBUTING.md](https://github.com/0x61nas/todo2/blob/aurora/CONTRIBUTING.md) guide first. to avoid waste waste our time on some unnecessary things.
//!
//! > the main keywords are: **signed commits**, **conventional commits**, **no emojis**, **the PR shouldn't have more then tree commits most of the time**
//!
//! # License
//! This project is licensed under the MIT license. [Read more](https://github.com/0x61nas/todo2/blob/aurora/LICENSE)
//! And you can use it under the Unlicense license if you want. [Read more](https://github.com/0x61nas/todo2/blob/aurora/LICENSE-UNLICENSE)
//!
compile_error!;
compile_error!;
extern crate proc_macro;
use crateparse_date;
use crateparse_if;
use TokenStream;
use IntoIter;
use TokenTree;
use ;
use Peekable;
/// An alias for `Result<T, String>`
pub type Result<T> = Result;
/// The condition type variant
/// Indicates unfinished implementation or the the intention to do something in the future or when the condition is met
///
/// The difference between this macro and the original [`core::todo`] macro is that this macro allows you to specify the deadline
/// and the condition when the code should be implemented. and when the condition or the deadline is met, the code will panic or emit a compile error
///
/// This macro can also be used as the original [`std::todo`](a.k.a. [`core::todo`]) macro,
/// if you have the `original-compatibility` feature enabled, so you don't need to create an alias if you want to use both.
///
/// # Examples
/// This will panic if the 2023-01-01 00:00 UTC is passed
/// however, if you want to emit a compile error instead of panicking, you can enable the `compile-error` feature
/// ```rust,should_panic
/// # use todo2::todo;
/// todo!("Read the API key from the environment variable", by: 2023-01-01);
/// let key = "some key";
/// ```
/// you can specify a specific time of the day, if you have the `and-time` feature enabled
/// ```rust,should_panic
/// # use todo2::todo;
/// todo!("Read the API key from the environment variable", by: 2023-01-01 at 9:00);
/// ```
///
/// This will panic if the condition is met, unfortunately, the `compile-error` feature doesn't change the behavior of `if` condition,
/// because we can't guarantee that all values will be known at compile time.
/// ```rust,should_panic
/// # use todo2::todo;
/// let username = "The Hacker";
/// todo!("Remove the raw sql query", if: username == "The Hacker");
/// ```
///
/// You can also use it as the original [`core::todo`] macro, if you have the `original-compatibility` feature enabled
/// ```rust,should_panic
/// # use todo2::todo;
/// todo!("Implement the thing");
/// ```