conv
conv is a small command-line tool that converts data between common binary
text encodings (hex, base32, base58, base64 variants, plain string), performs
letter-case conversion (upper / lower), and applies JavaScript-style URI
encoding (encodeURI / decodeURI / encodeURIComponent /
decodeURIComponent).
It is built with clap for argument parsing and reads
from --input, --file, or stdin, writing to --output or stdout.
Install
cargo install --path .
or build and run directly:
cargo build --release
./target/release/conv --help
Usage
conv <SPEC> [--input|-i TEXT] [--file|-f PATH] [--output|-o PATH]
<SPEC>isfrom:to(e.g.hex:base64), a text operation (upper,lower,encode-uri,decode-uri,encode-uri-component,decode-uri-component), or any alias (see below).- Input is read from
--input(inline text) if given, otherwise--file, otherwise stdin.--inputtakes precedence over--file. - Output goes to
--outputif given, otherwise stdout.
Supported formats
Any pair of the formats below may be combined as from:to.
| Format | Encodes as | Decodes as |
|---|---|---|
hex |
lowercase hex | hex (case-insensitive) |
HEX |
uppercase hex | hex (case-insensitive) |
base32 |
lowercase base32, with = padding |
base32 (case-insensitive, padding optional) |
BASE32 |
uppercase base32, with = padding |
base32 (case-insensitive, padding optional) |
base32-ne |
lowercase base32, no padding | base32 (case-insensitive, padding optional) |
BASE32-NE |
uppercase base32, no padding | base32 (case-insensitive, padding optional) |
base58 |
standard base58 (Bitcoin alphabet) | standard base58 |
base64 |
standard base64 (+/, = padding) |
lenient: standard / url-safe, with or without padding |
base64-uri |
URL-safe base64 (-_, = padding) |
lenient: standard / url-safe, with or without padding |
base64-ne |
standard base64 (+/), no padding |
lenient: standard / url-safe, with or without padding |
base64-uri-ne |
URL-safe base64 (-_), no padding |
lenient: standard / url-safe, with or without padding |
str |
UTF-8 string | UTF-8 string |
Notes:
- Input decoding is case-insensitive. The
hex/HEXandbase32/BASE32distinction only affects the output letter case. - The
-ne(no-equals) suffix means trailing=padding is omitted — this applies to output only; all base64/base32 variants accept missing padding on input. - The
base64family decoders are intentionally lenient: a singlebase64input format accepts standard, URL-safe, padded, and unpadded forms interchangeably.
Aliases
Short aliases are accepted anywhere a format name is. The alias's letter case follows the same upper/lower output rule:
| Alias | Canonical |
|---|---|
s, string |
str |
h |
hex |
H |
HEX |
b32 |
base32 |
B32 |
BASE32 |
b32-ne |
base32-ne |
B32-NE |
BASE32-NE |
b64 |
base64 |
b64-uri |
base64-uri |
b64-ne |
base64-ne |
b64-uri-ne |
base64-uri-ne |
Any name starting with b32 / B32 / b64 expands to the corresponding
base32 / BASE32 / base64 form, so future base32-* / base64-* variants
gain aliases automatically. (base58 has no alias.)
URI operations
Text operations are valid as <SPEC> just like upper / lower. They
implement the JavaScript encodeURI / decodeURI /
encodeURIComponent / decodeURIComponent semantics (ECMA-262):
Operation (SPEC) |
Aliases | Behavior |
|---|---|---|
encode-uri |
en-uri |
encodeURI — escapes non-reserved chars only |
decode-uri |
de-uri |
decodeURI — leaves reserved %XX untouched |
encode-uri-component |
en-uri-component, en-uri-com |
encodeURIComponent — escapes reserved too |
decode-uri-component |
de-uri-component, de-uri-com |
decodeURIComponent — decodes all %XX |
The "reserved" characters that encodeURI does not escape (and that
decodeURI does not decode) are ; / ? : @ & = + $ , #. Malformed %
sequences or non-UTF-8 decoded bytes are an error (exit 1), matching JS
URIError.
Examples
# Convert hex to base64 from an inline argument
conv hex:base64 -i deadbeef
# => 3q2+7w==
# Convert a string to base64 from stdin
echo -n hello | conv str:base64
# => aGVsbG8=
# Encode with no padding
conv str:base64-uri-ne -i 'Hello, World!'
# => SGVsbG8sIFdvcmxkIQ
# Re-add padding when converting to a padded format
conv base32-ne:base32 -i nbuq
# => nbuq====
# Letter case
echo -n hello | conv upper # => HELLO
conv lower -f notes.txt -o notes-lower.txt
# Round-trip through base58
conv str:base58 -i 'Hello, World!' | conv base58:str
# => Hello, World!
# URI encoding
conv encode-uri -i 'https://example.com/a b?q=1'
# => https://example.com/a%20b?q=1
conv encode-uri-component -i 'https://example.com/a b?q=1'
# => https%3A%2F%2Fexample.com%2Fa%20b%3Fq%3D1
conv decode-uri-component -i 'https%3A%2F%2Fexample.com'
# => https://example.com
# decodeURI leaves reserved %3A/%3F/%3D untouched:
conv decode-uri -i 'https%3A//x/path%3Fq%3D1'
# => https%3A//x/path%3Fq%3D1
# aliases
conv en-uri -i 'a b' # => a%20b
conv de-uri-com -i 'a%20b' # => a b
Exit codes & errors
0— success.1— conversion error (invalid hex / base32 / base58 / base64 input, invalid UTF-8 forstr, or an invalidSPEC). The error message is written to stderr.2— CLI usage error (missing or malformed arguments), reported by clap.
Development
Run the test suite:
cargo test
The conversion logic lives in src/lib.rs (a library target, free of any I/O
or argument parsing) and is covered by the integration tests in
tests/conv.rs. The binary entry point (src/main.rs) is a thin wrapper
around the library.