diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..558677b --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,67 @@ +name: release + +on: + workflow_dispatch: + push: + tags: ["v[0-9]+.[0-9]+.[0-9]+*"] + +jobs: + + build-release: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # Linux + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + - os: ubuntu-latest + target: aarch64-unknown-linux-musl + - os: ubuntu-latest + target: arm-unknown-linux-musleabihf + + # Darwin + - os: macos-latest + target: x86_64-apple-darwin + - os: macos-latest + target: aarch64-apple-darwin + + # Windows + - os: windows-latest + target: x86_64-pc-windows-msvc + ext: .exe + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + override: true + target: ${{ matrix.target }} + toolchain: stable + + - name: Build + uses: actions-rs/cargo@v1 + with: + use-cross: true + command: build + args: --release --target ${{ matrix.target }} + + - name: Rename Artifacts + shell: bash + run: | + ver=${GITHUB_REF#refs/tags/} + ASSET_PATH=idgen-$ver-${{ matrix.target }}${{ matrix.ext }} + mv target/${{ matrix.target }}/release/idgen $ASSET_PATH + echo "ASSET_PATH=$ASSET_PATH" >> $GITHUB_ENV + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: ${{ env.ASSET_PATH }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3821191 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,415 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "idgen" +version = "1.0.0" +dependencies = [ + "anyhow", + "clap", + "rs-snowflake", + "string-join", + "uuid", + "xid", +] + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rs-snowflake" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e60ef3b82994702bbe4e134d98aadca4b49ed04440148985678d415c68127666" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "string-join" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c4f3c10e055640562d00f0f03dd36d4db7510730282aacde5563ac41d49286" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sysctl" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" +dependencies = [ + "bitflags", + "byteorder", + "libc", + "thiserror", + "walkdir", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "uuid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d107f8c6e916235c4c01cabb3e8acf7bea8ef6a63ca2e7fa0527c049badfc48c" +dependencies = [ + "winapi", +] + +[[package]] +name = "xid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9671fb9541acc3118f04c4a6768253091d55077653efed3a4b151493abfd4e" +dependencies = [ + "crc32fast", + "hostname", + "md5", + "once_cell", + "rand", + "sysctl", + "thiserror", + "winreg", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f3dec48 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "idgen" +description = "A simple CLI to generate various UIDs in the terminal." +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "^1.0.66" +clap = { version = "^4.0.18", features = ["derive"] } +uuid = { version = "^1.2.1", features = ["v4"] } +rs-snowflake = "*" +xid = "^1" +string-join = "^0.1.2" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..195ff2e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Ringo Hoffmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b086180 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# idgen + +A simple CLI to generate various UID formats in the terminal. + +Currently, the following UID formats are supported: + - [xid](https://github.com/rs/xid) + - [uuid](http://guid.one/guid) + - [snowflake](https://en.wikipedia.org/wiki/Snowflake_ID) + +## Usage + +``` +$ idgen -h +A simple CLI to generate various UIDs in console. + +Usage: idgen [OPTIONS] + +Commands: + xid Generate XIDs (see https://github.com/rs/xid) + uuid Generate UUIDs (aka. GUIDs) + snowflake Generate Snowflake + help Print this message or the help of the given subcommand(s) + +Options: + -n, --number Set to larger than 1 to generate a list of UIDs [default: 1] + -h, --help Print help information + -V, --version Print version information +``` \ No newline at end of file diff --git a/src/generators/mod.rs b/src/generators/mod.rs new file mode 100644 index 0000000..9102060 --- /dev/null +++ b/src/generators/mod.rs @@ -0,0 +1,27 @@ +pub mod snowflake; +pub mod uuid; +pub mod xid; + +use anyhow::Result; + +pub trait Generator { + type Params; + + fn generate(&self, global_params: Self::Params) -> Result; +} + +#[macro_export] +macro_rules! simple_generator { + ($name:ident, $gen:expr) => { + #[derive(clap::Args, Clone)] + pub(crate) struct $name {} + + impl $crate::generators::Generator for $name { + type Params = $crate::Cli; + + fn generate(&self, _: Self::Params) -> anyhow::Result { + $gen + } + } + }; +} diff --git a/src/generators/snowflake.rs b/src/generators/snowflake.rs new file mode 100644 index 0000000..b856df5 --- /dev/null +++ b/src/generators/snowflake.rs @@ -0,0 +1,26 @@ +use super::Generator; +use crate::Cli; +use anyhow::Result; +use clap::Args; + +#[derive(Args, Clone)] +pub(crate) struct Snowflake { + /// The Machine ID + #[arg(long, short, default_value_t = 0)] + machine: i32, + /// The Node ID + #[arg(long, short, default_value_t = 0)] + node: i32, +} + +impl Generator for Snowflake { + type Params = Cli; + + fn generate(&self, _: Self::Params) -> Result { + Ok( + snowflake::SnowflakeIdGenerator::new(self.machine, self.node) + .generate() + .to_string(), + ) + } +} diff --git a/src/generators/uuid.rs b/src/generators/uuid.rs new file mode 100644 index 0000000..18fa838 --- /dev/null +++ b/src/generators/uuid.rs @@ -0,0 +1,3 @@ +use crate::simple_generator; + +simple_generator!(Uuid, Ok(uuid::Uuid::new_v4().to_string())); diff --git a/src/generators/xid.rs b/src/generators/xid.rs new file mode 100644 index 0000000..b55e120 --- /dev/null +++ b/src/generators/xid.rs @@ -0,0 +1,3 @@ +use crate::simple_generator; + +simple_generator!(Xid, Ok(xid::new().to_string())); diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..00222fb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,59 @@ +mod generators; + +use std::{io, ops::Deref}; + +use clap::{Parser, Subcommand}; +use generators::Generator; +use string_join::Join; + +#[derive(Subcommand, Clone)] +enum Commands { + /// Generate XIDs (see https://github.com/rs/xid) + Xid(generators::xid::Xid), + /// Generate UUIDs (aka. GUIDs) + Uuid(generators::uuid::Uuid), + /// Generate Snowflake + Snowflake(generators::snowflake::Snowflake), +} + +impl Deref for Commands { + type Target = dyn Generator; + + fn deref(&self) -> &Self::Target { + match self { + Commands::Xid(xid) => xid, + Commands::Uuid(uuid) => uuid, + Commands::Snowflake(snowflake) => snowflake, + } + } +} + +#[derive(Parser, Clone)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, + + /// Set to larger than 1 to generate a list of UIDs. + #[arg(short, long, default_value_t = 1)] + number: u16, +} + +fn main() { + let cli = Cli::parse(); + + let mut uids = Vec::new(); + + for _ in 0..cli.number { + let res = cli.command.generate(cli.clone()); + if let Err(err) = res { + eprintln!("Failed generating UID: {}", err); + return; + } + uids.push(res.unwrap()); + } + + if let Err(err) = "\n".write_join(&mut io::stdout(), uids) { + eprintln!("Failed printing results to STDOUT: {}", err); + } +}