1use crate::id::{new_id, CuidVersion, IDError, IDFormat, UuidVersion};
2use crate::inspector::inspect_id;
3use serde::Serialize;
4use std::env;
5use std::process;
6
7pub mod exit_codes {
9 pub const SUCCESS: i32 = 0;
11 pub const ERROR: i32 = 1;
13 pub const USAGE_ERROR: i32 = 2;
15}
16
17#[derive(Serialize)]
18struct IdOutput {
19 value: String,
20}
21
22pub fn parse_n_process() {
23 let args: Vec<String> = env::args().collect();
24 let mut version = UuidVersion::V4;
25 let mut format = IDFormat::Hyphenated(version);
26 let mut count = 1;
27 let mut help = false;
28 let mut show_version = false;
29 let mut len: Option<usize> = None;
30 let mut prefix = "";
31 let mut suffix = "";
32 let mut namespace: Option<String> = None;
33 let mut name: Option<String> = None;
34 let mut show_banner = false;
35 let mut json_output = false;
36 let mut inspect_target: Option<String> = None;
37
38 let mut lastcmd = String::new();
39
40 args.iter().enumerate().for_each(|(_, arg)| {
41 if arg == "-h" || arg == "--help" {
42 help = true;
43 } else if arg == "-v" || arg == "--version" {
44 show_version = true;
45 } else if arg == "--json" {
46 json_output = true;
47 show_banner = false;
48 } else if arg == "-s" || arg == "--simple" {
49 format = IDFormat::Simple(version);
50 } else if arg == "-u" || arg == "--urn" {
51 format = IDFormat::URN(version);
52 } else if arg == "-o" || arg == "--objectid" {
53 format = IDFormat::OID;
54 } else if arg == "-n" || arg == "--nano" {
55 format = IDFormat::NanoID;
56 } else if arg == "-c1" || arg == "--cuid1" {
57 format = IDFormat::Cuid(CuidVersion::V1);
58 } else if arg == "-c2" || arg == "--cuid2" {
59 format = IDFormat::Cuid(CuidVersion::V2);
60 } else if arg == "-l" || arg == "--ulid" {
61 format = IDFormat::Ulid;
62 } else if arg == "-b" || arg == "--banner" {
63 show_banner = true;
64 } else if arg == "-u1" || arg == "--uuid1" {
65 version = UuidVersion::V1;
66 format = match format.clone() {
67 IDFormat::Simple(_) => IDFormat::Simple(version),
68 IDFormat::Hyphenated(_) => IDFormat::Hyphenated(version),
69 IDFormat::URN(_) => IDFormat::URN(version),
70 _ => format.clone(),
71 };
72 } else if arg == "-u3" || arg == "--uuid3" {
73 version = UuidVersion::V3;
74 format = match format.clone() {
75 IDFormat::Simple(_) => IDFormat::Simple(version),
76 IDFormat::Hyphenated(_) => IDFormat::Hyphenated(version),
77 IDFormat::URN(_) => IDFormat::URN(version),
78 _ => format.clone(),
79 };
80 } else if arg == "-u4" || arg == "--uuid4" {
81 version = UuidVersion::V4;
82 format = match format.clone() {
83 IDFormat::Simple(_) => IDFormat::Simple(version),
84 IDFormat::Hyphenated(_) => IDFormat::Hyphenated(version),
85 IDFormat::URN(_) => IDFormat::URN(version),
86 _ => format.clone(),
87 };
88 } else if arg == "-u5" || arg == "--uuid5" {
89 version = UuidVersion::V5;
90 format = match format.clone() {
91 IDFormat::Simple(_) => IDFormat::Simple(version),
92 IDFormat::Hyphenated(_) => IDFormat::Hyphenated(version),
93 IDFormat::URN(_) => IDFormat::URN(version),
94 _ => format.clone(),
95 };
96 }
97
98 if lastcmd == "-c" || lastcmd == "--count" {
99 count = arg.parse::<i32>().unwrap_or(1);
100 } else if lastcmd == "-n" || lastcmd == "--nano" {
101 len = arg.parse::<usize>().ok();
102 } else if lastcmd == "-p" || lastcmd == "--prefix" {
103 prefix = arg;
104 } else if lastcmd == "-f" || lastcmd == "--suffix" {
105 suffix = arg;
106 } else if lastcmd == "--namespace" {
107 namespace = Some(arg.to_string());
108 } else if lastcmd == "--name" {
109 name = Some(arg.to_string());
110 } else if lastcmd == "--inspect" {
111 inspect_target = Some(arg.to_string());
112 }
113
114 lastcmd = arg.clone();
115 });
116
117 if let Some(target) = inspect_target {
118 let result = inspect_id(&target);
119 if json_output {
120 let json = serde_json::to_string_pretty(&result).unwrap();
121 println!("{}", json);
122 } else {
123 println!("ID: {}", target);
124 println!("Valid: {}", result.valid);
125 println!("Type: {}", result.id_type);
126 if let Some(v) = result.version {
127 println!("Version: {}", v);
128 }
129 if let Some(v) = result.variant {
130 println!("Variant: {}", v);
131 }
132 if let Some(ts) = result.timestamp {
133 println!("Timestamp: {}", ts);
134 }
135 }
136 if !result.valid {
138 process::exit(exit_codes::ERROR);
139 }
140 return;
141 }
142
143 if show_banner {
144 print_banner();
145 }
146
147 if help {
148 print_help();
149 return;
150 }
151
152 if show_version {
153 print_version();
154 return;
155 }
156
157 if count < 1 {
159 eprintln!("Error: Count must be at least 1, got {}", count);
160 process::exit(exit_codes::USAGE_ERROR);
161 }
162
163 match print_uuid(
164 format,
165 len,
166 count,
167 prefix,
168 suffix,
169 namespace.as_deref(),
170 name.as_deref(),
171 json_output,
172 ) {
173 Ok(_) => {}
174 Err(err) => {
175 eprintln!("Error: {}", err);
176 let exit_code = if err.is::<IDError>() {
178 exit_codes::USAGE_ERROR
179 } else {
180 exit_codes::ERROR
181 };
182 process::exit(exit_code);
183 }
184 }
185}
186
187fn print_uuid(
188 id_format: IDFormat,
189 len: Option<usize>,
190 count: i32,
191 prefix: &str,
192 suffix: &str,
193 namespace: Option<&str>,
194 name: Option<&str>,
195 json_output: bool,
196) -> Result<(), Box<dyn std::error::Error>> {
197 if json_output {
198 let mut ids = Vec::new();
199 for _ in 0..count {
200 let id = new_id(&id_format, len, namespace, name)?;
201 ids.push(IdOutput {
202 value: format!("{}{}{}", prefix, id, suffix),
203 });
204 }
205 let json = serde_json::to_string_pretty(&ids)?;
206 println!("{}", json);
207 } else {
208 for i in 0..count {
209 let id = new_id(&id_format, len, namespace, name)?;
210 print!("{}{}{}", prefix, id, suffix);
211 if i < count - 1 {
212 print!("\n");
213 }
214 }
215 print!("\n");
216 }
217 Ok(())
218}
219
220fn print_version() {
222 const VERSION: &'static str = env!("CARGO_PKG_VERSION");
223 print!("Version {}", VERSION);
224}
225
226fn print_help() {
228 const VERSION: &'static str = env!("CARGO_PKG_VERSION");
229 let help = format!(
230 "ID Generator Version {}
231 Mohamed Aamir Maniar - https://www.linkedin.com/in/aamironline/
232 Generates and prints UUIDs (all versions), NanoID, and MongoDB ObjectIDs.
233
234 USAGE:
235 idgen [OPTIONS]
236
237 FLAGS:
238 -h --help Prints the help information
239 -v --version Prints the version information
240 -b --banner Show the banner output
241 --json Output as JSON
242 --inspect <ID> Inspect an ID string
243
244 UUID VERSION OPTIONS:
245 -u1 --uuid1 Generates UUID version 1 (Time-based)
246 -u3 --uuid3 Generates UUID version 3 (MD5 hash-based)
247 -u4 --uuid4 Generates UUID version 4 (Random - Default)
248 -u5 --uuid5 Generates UUID version 5 (SHA1 hash-based)
249
250 FORMAT OPTIONS:
251 -s --simple Generates UUID without hyphens
252 -u --urn Generates UUID with URN signature
253 -o --objectid Generates sequential MongoDB ObjectId
254 -d --hyphen Generates hyphened version of UUID (Default)
255 -n --nanoid <num?> Generates nanoid with specified length (Default: 21)
256 -c1 --cuid1 Generates a CUIDv1
257 -c2 --cuid2 Generates a CUIDv2
258 -l --ulid Generates a ULID
259
260 OTHER OPTIONS:
261 -c --count <num> Number of IDs to generate (Default: 1)
262 -p --prefix <str> Prefix for the generated IDs (Default: None)
263 -f --suffix <str> Suffix for the generated IDs (Default: None)
264 --namespace <str> Namespace UUID for v3/v5 (Required for v3/v5)
265 --name <str> Name string for v3/v5 (Required for v3/v5)
266
267 EXAMPLES:
268 idgen -u4 Generate a random UUID v4 (default)
269 idgen -u1 Generate a time-based UUID v1
270 idgen -u3 --namespace DNS --name example.com Generate a v3 UUID
271 idgen -u5 --namespace DNS --name example.com Generate a v5 UUID
272 idgen -s -u4 Generate a simple UUID v4 without hyphens
273 idgen -u -u4 Generate a UUID v4 with URN format
274 idgen -n Generate a NanoID of default length (21)
275 idgen -n 10 Generate a NanoID of length 10
276 idgen -o Generate a MongoDB ObjectID
277 idgen -c1 Generate a version 1 CUID
278 idgen -c2 Generate a version 2 CUID
279 idgen -l Generate a ULID
280 idgen -c 5 Generate 5 UUIDs
281 idgen -p 'test-' -c 3 Generate 3 UUIDs with prefix 'test-'
282 idgen -f '.log' -n Generate a NanoID with suffix '.log'
283 ",
284 VERSION
285 )
286 .replace("\n ", "\n");
287 println!("{}", help);
288}
289
290fn print_banner() {
291 let banner = r#" _ _
293(_) __| | __ _ ___ _ __
294| |/ _` |/ _` |/ _ \ '_ \
295| | (_| | (_| | __/ | | |
296|_|\__,_|\__, |\___|_| |_|
297 |___/"#;
298 print!("{}\n", banner); }