idgen_cli/
cli.rs

1use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
2use clap_complete::Shell;
3
4/// A lightweight, powerful CLI tool for generating and inspecting unique identifiers.
5///
6/// Supports UUID (v1-v5), NanoID, CUID (v1/v2), ULID, and MongoDB ObjectID.
7#[derive(Parser, Debug)]
8#[command(name = "idgen")]
9#[command(author = "Mohamed Aamir Maniar <aamir.maniar@maniartech.com>")]
10#[command(version)]
11#[command(about = "Generate and inspect unique identifiers", long_about = None)]
12#[command(after_help = "EXAMPLES:
13    idgen                                       Generate a random UUID v4 (default)
14    idgen -t uuid1                              Generate a time-based UUID v1
15    idgen -t uuid3 --namespace DNS --name example.com
16    idgen -t nanoid -l 10                       Generate a NanoID of length 10
17    idgen -t ulid                               Generate a ULID
18    idgen -c 5                                  Generate 5 UUIDs
19    idgen -p 'test-' -s '.log'                  Add prefix and suffix
20    idgen --json                                Output as JSON
21    idgen inspect 550e8400-e29b-44d4-a716-446655440000
22    idgen completions bash                      Generate bash completions")]
23pub struct Cli {
24    /// Type of ID to generate
25    #[arg(short = 't', long = "type", value_enum, default_value = "uuid4")]
26    pub id_type: IdType,
27
28    /// Output format for UUIDs
29    #[arg(short = 'f', long = "format", value_enum, default_value = "hyphenated")]
30    pub format: UuidFormat,
31
32    /// Number of IDs to generate
33    #[arg(short = 'c', long = "count", default_value = "1")]
34    pub count: u32,
35
36    /// Length for NanoID (default: 21)
37    #[arg(short = 'l', long = "length")]
38    pub length: Option<usize>,
39
40    /// Prefix to add to generated IDs
41    #[arg(short = 'p', long = "prefix", default_value = "")]
42    pub prefix: String,
43
44    /// Suffix to add to generated IDs
45    #[arg(short = 's', long = "suffix", default_value = "")]
46    pub suffix: String,
47
48    /// Namespace UUID for v3/v5 (use DNS, URL, OID, X500, or a custom UUID)
49    #[arg(long = "namespace")]
50    pub namespace: Option<String>,
51
52    /// Name string for UUID v3/v5
53    #[arg(long = "name")]
54    pub name: Option<String>,
55
56    /// Output as JSON
57    #[arg(long = "json")]
58    pub json: bool,
59
60    /// Show banner
61    #[arg(short = 'b', long = "banner")]
62    pub banner: bool,
63
64    #[command(subcommand)]
65    pub command: Option<Commands>,
66}
67
68#[derive(Subcommand, Debug)]
69pub enum Commands {
70    /// Inspect an ID to determine its type and extract metadata
71    Inspect {
72        /// The ID string to inspect
73        id: String,
74
75        /// Output as JSON
76        #[arg(long = "json")]
77        json: bool,
78    },
79
80    /// Generate shell completions
81    Completions {
82        /// Shell to generate completions for
83        #[arg(value_enum)]
84        shell: Shell,
85    },
86
87    /// Generate man page
88    #[command(name = "manpage", hide = true)]
89    ManPage,
90}
91
92#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
93pub enum IdType {
94    /// UUID version 1 (time-based)
95    #[value(name = "uuid1", alias = "u1")]
96    Uuid1,
97
98    /// UUID version 3 (MD5 hash-based, requires --namespace and --name)
99    #[value(name = "uuid3", alias = "u3")]
100    Uuid3,
101
102    /// UUID version 4 (random)
103    #[value(name = "uuid4", alias = "u4")]
104    Uuid4,
105
106    /// UUID version 5 (SHA1 hash-based, requires --namespace and --name)
107    #[value(name = "uuid5", alias = "u5")]
108    Uuid5,
109
110    /// NanoID (URL-safe, configurable length)
111    #[value(name = "nanoid", alias = "nano")]
112    NanoId,
113
114    /// CUID version 1
115    #[value(name = "cuid1", alias = "c1")]
116    Cuid1,
117
118    /// CUID version 2
119    #[value(name = "cuid2", alias = "c2")]
120    Cuid2,
121
122    /// ULID (Universally Unique Lexicographically Sortable Identifier)
123    #[value(name = "ulid")]
124    Ulid,
125
126    /// MongoDB ObjectID
127    #[value(name = "objectid", alias = "oid")]
128    ObjectId,
129}
130
131#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
132pub enum UuidFormat {
133    /// Standard hyphenated format (e.g., 550e8400-e29b-44d4-a716-446655440000)
134    #[value(name = "hyphenated", alias = "h")]
135    Hyphenated,
136
137    /// Simple format without hyphens (e.g., 550e8400e29b44d4a716446655440000)
138    #[value(name = "simple", alias = "s")]
139    Simple,
140
141    /// URN format (e.g., urn:uuid:550e8400-e29b-44d4-a716-446655440000)
142    #[value(name = "urn", alias = "u")]
143    Urn,
144}
145
146/// Well-known namespace UUIDs
147pub fn resolve_namespace(namespace: &str) -> Result<String, String> {
148    match namespace.to_uppercase().as_str() {
149        "DNS" => Ok("6ba7b810-9dad-11d1-80b4-00c04fd430c8".to_string()),
150        "URL" => Ok("6ba7b811-9dad-11d1-80b4-00c04fd430c8".to_string()),
151        "OID" => Ok("6ba7b812-9dad-11d1-80b4-00c04fd430c8".to_string()),
152        "X500" => Ok("6ba7b814-9dad-11d1-80b4-00c04fd430c8".to_string()),
153        _ => {
154            // Assume it's a custom UUID - validate format
155            if namespace.len() >= 32 && (namespace.len() == 32 || namespace.len() == 36) {
156                Ok(namespace.to_string())
157            } else {
158                Err(format!(
159                    "Invalid namespace '{}'. Use DNS, URL, OID, X500, or a valid UUID.",
160                    namespace
161                ))
162            }
163        }
164    }
165}
166
167pub fn build_cli() -> clap::Command {
168    Cli::command()
169}