mirror of
https://github.com/m-lamonaca/json-to-env.git
synced 2025-04-10 21:06:41 +00:00
Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
|
83972e3b8e | ||
1ab9291899 | |||
0d7b3850b7 | |||
d6eb0faf47 | |||
1585a60ce8 | |||
f25c8f31fd | |||
beed1f613d | |||
53e991875e | |||
610e24729d | |||
d116a49b11 | |||
15d7ee5dcc | |||
8cefe25d62 | |||
|
19843b8d08 | ||
d5dee1191d | |||
|
46d8223136 | ||
77dd32e5d0 | |||
7086225b40 |
10 changed files with 509 additions and 406 deletions
2
.github/workflows/oranda.yml
vendored
2
.github/workflows/oranda.yml
vendored
|
@ -79,7 +79,7 @@ jobs:
|
|||
cp public/index.html /tmp/public
|
||||
|
||||
- name: Check HTML for broken internal links
|
||||
uses: untitaker/hyperlink@0.1.32
|
||||
uses: untitaker/hyperlink@0.1.44
|
||||
with:
|
||||
args: /tmp/public
|
||||
|
||||
|
|
131
.github/workflows/release.yml
vendored
131
.github/workflows/release.yml
vendored
|
@ -1,20 +1,21 @@
|
|||
# Copyright 2022-2023, axodotdev
|
||||
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
|
||||
#
|
||||
# Copyright 2022-2024, axodotdev
|
||||
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||
#
|
||||
# CI that:
|
||||
#
|
||||
# * checks for a Git Tag that looks like a release
|
||||
# * builds artifacts with cargo-dist (archives, installers, hashes)
|
||||
# * builds artifacts with dist (archives, installers, hashes)
|
||||
# * uploads those artifacts to temporary workflow zip
|
||||
# * on success, uploads the artifacts to a Github Release
|
||||
# * on success, uploads the artifacts to a GitHub Release
|
||||
#
|
||||
# Note that the Github Release will be created with a generated
|
||||
# Note that the GitHub Release will be created with a generated
|
||||
# title/body based on your changelogs.
|
||||
|
||||
name: Release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
"contents": "write"
|
||||
|
||||
# This task will run whenever you push a git tag that looks like a version
|
||||
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
|
||||
|
@ -23,15 +24,15 @@ permissions:
|
|||
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
|
||||
#
|
||||
# If PACKAGE_NAME is specified, then the announcement will be for that
|
||||
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
|
||||
# package (erroring out if it doesn't have the given version or isn't dist-able).
|
||||
#
|
||||
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||
# (cargo-dist-able) packages in the workspace with that version (this mode is
|
||||
# (dist-able) packages in the workspace with that version (this mode is
|
||||
# intended for workspaces with only one dist-able package, or with all dist-able
|
||||
# packages versioned/released in lockstep).
|
||||
#
|
||||
# If you push multiple tags at once, separate instances of this workflow will
|
||||
# spin up, creating an independent announcement for each one. However Github
|
||||
# spin up, creating an independent announcement for each one. However, GitHub
|
||||
# will hard limit this to 3 tags per commit, as it will assume more tags is a
|
||||
# mistake.
|
||||
#
|
||||
|
@ -43,9 +44,9 @@ on:
|
|||
- '**[0-9]+.[0-9]+.[0-9]+*'
|
||||
|
||||
jobs:
|
||||
# Run 'cargo dist plan' (or host) to determine what tasks we need to do
|
||||
# Run 'dist plan' (or host) to determine what tasks we need to do
|
||||
plan:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: "ubuntu-20.04"
|
||||
outputs:
|
||||
val: ${{ steps.plan.outputs.manifest }}
|
||||
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
|
||||
|
@ -57,11 +58,16 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
- name: Install dist
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.11.1/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.26.1/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/dist
|
||||
# sure would be cool if github gave us proper conditionals...
|
||||
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||
|
@ -69,8 +75,8 @@ jobs:
|
|||
# but also really annoying to build CI around when it needs secrets to work right.)
|
||||
- id: plan
|
||||
run: |
|
||||
cargo dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
|
@ -88,28 +94,38 @@ jobs:
|
|||
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# Target platforms/runners are computed by cargo-dist in create-release.
|
||||
# Target platforms/runners are computed by dist in create-release.
|
||||
# Each member of the matrix has the following arguments:
|
||||
#
|
||||
# - runner: the github runner
|
||||
# - dist-args: cli flags to pass to cargo dist
|
||||
# - install-dist: expression to run to install cargo-dist on the runner
|
||||
# - dist-args: cli flags to pass to dist
|
||||
# - install-dist: expression to run to install dist on the runner
|
||||
#
|
||||
# Typically there will be:
|
||||
# - 1 "global" task that builds universal installers
|
||||
# - N "local" tasks that build each platform's binaries and platform-specific installers
|
||||
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
container: ${{ matrix.container && matrix.container.image || null }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
|
||||
steps:
|
||||
- name: enable windows longpaths
|
||||
run: |
|
||||
git config --global core.longpaths true
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Install cargo-dist
|
||||
run: ${{ matrix.install_dist }}
|
||||
- name: Install Rust non-interactively if not already installed
|
||||
if: ${{ matrix.container }}
|
||||
run: |
|
||||
if ! command -v cargo > /dev/null 2>&1; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
fi
|
||||
- name: Install dist
|
||||
run: ${{ matrix.install_dist.run }}
|
||||
# Get the dist-manifest
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
@ -123,8 +139,8 @@ jobs:
|
|||
- name: Build artifacts
|
||||
run: |
|
||||
# Actually do builds and make zips and whatnot
|
||||
cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
- id: cargo-dist
|
||||
name: Post-build
|
||||
# We force bash here just because github makes it really hard to get values up
|
||||
|
@ -134,7 +150,7 @@ jobs:
|
|||
run: |
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
|
@ -159,9 +175,12 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.11.1/cargo-dist-installer.sh | sh"
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
@ -172,12 +191,12 @@ jobs:
|
|||
- id: cargo-dist
|
||||
shell: bash
|
||||
run: |
|
||||
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
|
@ -205,8 +224,12 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.11.1/cargo-dist-installer.sh | sh"
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
@ -214,11 +237,10 @@ jobs:
|
|||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
# This is a harmless no-op for Github Releases, hosting for that happens in "announce"
|
||||
- id: host
|
||||
shell: bash
|
||||
run: |
|
||||
cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||
echo "artifacts uploaded and released successfully"
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
|
@ -228,8 +250,29 @@ jobs:
|
|||
# Overwrite the previous copy
|
||||
name: artifacts-dist-manifest
|
||||
path: dist-manifest.json
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
- name: "Download GitHub Artifacts"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
- name: Cleanup
|
||||
run: |
|
||||
# Remove the granular manifests
|
||||
rm -f artifacts/*-dist-manifest.json
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
|
||||
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
|
||||
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
|
||||
RELEASE_COMMIT: "${{ github.sha }}"
|
||||
run: |
|
||||
# Write and read notes from a file to avoid quoting breaking things
|
||||
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
|
||||
|
||||
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
|
||||
|
||||
# Create a Github Release while uploading all files to it
|
||||
announce:
|
||||
needs:
|
||||
- plan
|
||||
|
@ -245,21 +288,3 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: "Download Github Artifacts"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
- name: Cleanup
|
||||
run: |
|
||||
# Remove the granular manifests
|
||||
rm -f artifacts/*-dist-manifest.json
|
||||
- name: Create Github Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ needs.plan.outputs.tag }}
|
||||
name: ${{ fromJson(needs.host.outputs.val).announcement_title }}
|
||||
body: ${{ fromJson(needs.host.outputs.val).announcement_github_body }}
|
||||
prerelease: ${{ fromJson(needs.host.outputs.val).announcement_is_prerelease }}
|
||||
artifacts: "artifacts/*"
|
||||
|
|
248
Cargo.lock
generated
248
Cargo.lock
generated
|
@ -1,66 +1,61 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.11"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.0"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.0"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.0"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -68,9 +63,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.0"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -80,9 +75,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.0"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -92,89 +87,115 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "json2env"
|
||||
version = "0.2.0"
|
||||
version = "0.3.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.163"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.97"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -183,144 +204,85 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.9"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "json2env"
|
||||
version = "0.2.0"
|
||||
version = "0.3.1"
|
||||
edition = "2021"
|
||||
authors = ["Marcello Lamonaca <marcello@lamonaca.eu>"]
|
||||
description = "JSON to Env Var converter"
|
||||
|
@ -10,7 +10,6 @@ license = "MIT"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
clap = { version = "4.5.0", features = ["derive", "color"] }
|
||||
serde_json = "1.0.97"
|
||||
|
||||
|
@ -18,16 +17,3 @@ serde_json = "1.0.97"
|
|||
[profile.dist]
|
||||
inherits = "release"
|
||||
lto = "thin"
|
||||
|
||||
# Config for 'cargo dist'
|
||||
[workspace.metadata.dist]
|
||||
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.11.1"
|
||||
# CI backends to support
|
||||
ci = ["github"]
|
||||
# The installers to generate for each app
|
||||
installers = ["shell", "powershell"]
|
||||
# Target platforms to build apps for (Rust target-triple syntax)
|
||||
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
|
||||
# Publish jobs to run in CI
|
||||
pr-run-mode = "skip"
|
||||
|
|
52
README.md
52
README.md
|
@ -1,32 +1,20 @@
|
|||
# json2env
|
||||
|
||||
Convert valid JSON to environment variables or an `.env`-line file.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
JSON to Env Var converter
|
||||
|
||||
Usage: json2env.exe [OPTIONS]
|
||||
|
||||
Options:
|
||||
-i, --input <FILE> Input file, defaults to STDIN if not specified
|
||||
-o, --output <FILE> Output file, defaults to STDOUT if not specified
|
||||
-s, --separator <STRING> Separator for nested keys
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
You can either install the tool with `cargo`:
|
||||
|
||||
```sh
|
||||
cargo install --path <path/to/repo>
|
||||
```
|
||||
|
||||
or build the executable with (output in `target/release`):
|
||||
|
||||
```sh
|
||||
cargo build --release
|
||||
```
|
||||
# json2env
|
||||
|
||||
Convert valid JSON to environment variables or an `.env`-line file.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
JSON to Env Var converter
|
||||
|
||||
Usage: json2env.exe [OPTIONS]
|
||||
|
||||
Options:
|
||||
-i, --input <FILE> Input file, defaults to STDIN if not specified
|
||||
-o, --output <FILE> Output file, defaults to STDOUT if not specified
|
||||
-s, --key-separator <STRING> Separator for nested keys [default: __]
|
||||
-S, --array-separator <STRING> Separator for array elements [default: ,]
|
||||
-e, --enumerate-array Separate array elements in multiple environment variables
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
```
|
||||
|
|
19
dist-workspace.toml
Normal file
19
dist-workspace.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[workspace]
|
||||
members = ["cargo:."]
|
||||
|
||||
# Config for 'dist'
|
||||
[dist]
|
||||
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.26.1"
|
||||
# CI backends to support
|
||||
ci = "github"
|
||||
# The installers to generate for each app
|
||||
installers = ["shell", "powershell"]
|
||||
# Target platforms to build apps for (Rust target-triple syntax)
|
||||
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-pc-windows-msvc", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
|
||||
# Which actions to run on pull requests
|
||||
pr-run-mode = "skip"
|
||||
# Path that installers should place binaries in
|
||||
install-path = "CARGO_HOME"
|
||||
# Whether to install an updater program
|
||||
install-updater = false
|
18
oranda.json
18
oranda.json
|
@ -1,5 +1,15 @@
|
|||
{
|
||||
"build": {
|
||||
"path_prefix": "json-to-env"
|
||||
}
|
||||
{
|
||||
"$schema": "https://github.com/axodotdev/oranda/releases/latest/download/oranda-config-schema.json",
|
||||
"build": {
|
||||
"path_prefix": "json-to-env"
|
||||
},
|
||||
"styles": {
|
||||
"theme": "hacker"
|
||||
},
|
||||
"components": {
|
||||
"changelog": true,
|
||||
"artifacts": {
|
||||
"cargo_dist": true
|
||||
}
|
||||
}
|
||||
}
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "stable"
|
242
src/lib.rs
Normal file
242
src/lib.rs
Normal file
|
@ -0,0 +1,242 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParseOptions {
|
||||
key_separator: String,
|
||||
array_separator: String,
|
||||
enumerate_array: bool,
|
||||
}
|
||||
|
||||
impl ParseOptions {
|
||||
pub fn new(key_separator: String, array_separator: String, enumerate_array: bool) -> Self {
|
||||
Self {
|
||||
key_separator,
|
||||
array_separator,
|
||||
enumerate_array,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JsonParser {
|
||||
options: ParseOptions,
|
||||
}
|
||||
|
||||
impl JsonParser {
|
||||
pub fn new(options: ParseOptions) -> Self {
|
||||
Self { options }
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, json: &Value) -> Vec<EnvVar> {
|
||||
Self::parse_value("", json, &self.options)
|
||||
}
|
||||
|
||||
fn parse_value(key: &str, value: &Value, options: &ParseOptions) -> Vec<EnvVar> {
|
||||
match value {
|
||||
Value::Array(array) => {
|
||||
let has_complex_values = array
|
||||
.iter()
|
||||
.any(|value| value.is_object() || value.is_array());
|
||||
|
||||
// complex (nested) values cannot be part of an array enumeration, skip just this array
|
||||
if options.enumerate_array || has_complex_values {
|
||||
let mut values = Vec::with_capacity(array.len());
|
||||
|
||||
for (index, item) in array.iter().enumerate() {
|
||||
let key = Self::build_key(key, &index.to_string(), &options.key_separator);
|
||||
values.push(Self::parse_value(&key, item, options));
|
||||
}
|
||||
|
||||
values.into_iter().flatten().collect()
|
||||
} else {
|
||||
let value = array
|
||||
.iter()
|
||||
.map(|value| value.to_string().replace(['\\', '"'], ""))
|
||||
.collect::<Vec<_>>()
|
||||
.join(&options.array_separator);
|
||||
|
||||
let value = serde_json::Value::String(value);
|
||||
Self::parse_value(key, &value, options)
|
||||
}
|
||||
}
|
||||
Value::Object(object) => {
|
||||
let mut values = Vec::with_capacity(object.len());
|
||||
|
||||
for (name, value) in object.iter() {
|
||||
let key = Self::build_key(key, name, &options.key_separator);
|
||||
values.push(Self::parse_value(&key, value, options));
|
||||
}
|
||||
|
||||
values.into_iter().flatten().collect()
|
||||
}
|
||||
_ => vec![EnvVar(key.trim().to_owned(), value.clone())],
|
||||
}
|
||||
}
|
||||
|
||||
fn build_key(prefix: &str, key: &str, separator: &str) -> String {
|
||||
match prefix.is_empty() {
|
||||
true => key.to_string(),
|
||||
false => format!("{prefix}{separator}{key}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnvVar(String, Value);
|
||||
|
||||
impl Display for EnvVar {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.1 {
|
||||
Value::Null => write!(fmt, "{key}=null", key = self.0),
|
||||
Value::Bool(bool) => write!(fmt, "{key}={bool}", key = self.0),
|
||||
Value::Number(ref number) => write!(fmt, "{key}={number}", key = self.0),
|
||||
Value::String(ref string) => write!(
|
||||
fmt,
|
||||
r#"{key}="{value}""#,
|
||||
key = self.0,
|
||||
value = string.replace('"', r#"\""#)
|
||||
),
|
||||
_ => write!(fmt, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{EnvVar, JsonParser, ParseOptions};
|
||||
|
||||
const KEY: &str = r#""key""#;
|
||||
|
||||
#[test]
|
||||
fn build_key_should_leave_key_unchanged_when_prefix_is_empty() {
|
||||
// ARRANGE
|
||||
let separator = "";
|
||||
let input = KEY.to_owned();
|
||||
let expected = KEY;
|
||||
|
||||
// ACT
|
||||
let result = JsonParser::build_key("", &input, separator);
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_key_should_leave_prepend_prefix_with_separator() {
|
||||
// ARRANGE
|
||||
let separator = "_";
|
||||
let input = KEY.to_owned();
|
||||
let expected = format!("prefix{separator}{KEY}");
|
||||
|
||||
// ACT
|
||||
let actual = JsonParser::build_key("prefix", &input, separator);
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!(true));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, r#""key"=true"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!(1.0));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, r#""key"=1.0"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!("hello"));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, r#""key"="hello""#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!([1, 2]));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, "")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!({ "key": "value" }));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, "")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_array_not_enumerated() {
|
||||
// ARRANGE
|
||||
let json = json!({ "array": [1, 2, 3] });
|
||||
let options = ParseOptions::new("__".to_string(), ",".to_string(), false);
|
||||
let mut parser = JsonParser::new(options);
|
||||
|
||||
// ACT
|
||||
let environ = parser.parse(&json);
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(
|
||||
*environ,
|
||||
vec![EnvVar(
|
||||
"array".to_string(),
|
||||
Value::String("1,2,3".to_string())
|
||||
)]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_array_enumerated() {
|
||||
// ARRANGE
|
||||
let json = json!({ "array": [1, 2, 3] });
|
||||
let options = ParseOptions::new("__".to_string(), ",".to_string(), true);
|
||||
let mut parser = JsonParser::new(options);
|
||||
|
||||
// ACT
|
||||
let environ = parser.parse(&json);
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(
|
||||
*environ,
|
||||
vec![
|
||||
EnvVar("array__0".to_string(), Value::Number(1.into())),
|
||||
EnvVar("array__1".to_string(), Value::Number(2.into())),
|
||||
EnvVar("array__2".to_string(), Value::Number(3.into()))
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
185
src/main.rs
185
src/main.rs
|
@ -1,21 +1,21 @@
|
|||
use std::{
|
||||
fmt::Display,
|
||||
error::Error,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader, BufWriter, Read, Write},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use json2env::{JsonParser, ParseOptions};
|
||||
use serde_json::Value;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
let mut reader: Box<dyn BufRead> = match args.input {
|
||||
None => Box::new(std::io::stdin().lock()),
|
||||
Some(ref filename) => {
|
||||
let file = File::open(filename)
|
||||
.with_context(|| format!("Could not open file `{filename}`"))?;
|
||||
.inspect_err(|_| eprintln!("Error: Could not open file `{filename}`"))?;
|
||||
|
||||
Box::new(BufReader::new(file))
|
||||
}
|
||||
|
@ -26,15 +26,21 @@ fn main() -> Result<()> {
|
|||
let input = args.input.unwrap_or("STDIN".to_string());
|
||||
reader
|
||||
.read_to_string(&mut buffer)
|
||||
.with_context(|| format!("Could not read `{input}`"))?;
|
||||
.inspect_err(|_| eprintln!("Error: Could not read `{input}`"))?;
|
||||
|
||||
let json: Value = serde_json::from_str(&buffer)
|
||||
.with_context(|| format!("`{input}` does not contain valid JSON"))?;
|
||||
.inspect_err(|_| eprintln!("Error: `{input}` does not contain valid JSON"))?;
|
||||
|
||||
let mut vars: Vec<EnvVar> = vec![];
|
||||
JsonParser::parse(&mut vars, "", &json, &args.separator);
|
||||
let options = ParseOptions::new(
|
||||
args.key_separator,
|
||||
args.array_separator,
|
||||
args.enumerate_array,
|
||||
);
|
||||
|
||||
let environ = vars
|
||||
let mut parser = JsonParser::new(options);
|
||||
let keys = parser.parse(&json);
|
||||
|
||||
let environ = keys
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
|
@ -44,7 +50,7 @@ fn main() -> Result<()> {
|
|||
None => Box::new(std::io::stdout().lock()),
|
||||
Some(ref filename) => {
|
||||
let file = File::create(filename)
|
||||
.with_context(|| format!("Could not open file `{filename}`"))?;
|
||||
.inspect_err(|_| eprintln!("Error: Could not open file `{filename}`"))?;
|
||||
|
||||
Box::new(BufWriter::new(file))
|
||||
}
|
||||
|
@ -53,7 +59,7 @@ fn main() -> Result<()> {
|
|||
let output = args.output.unwrap_or("STDOUT".to_string());
|
||||
writer
|
||||
.write_all(environ.as_bytes())
|
||||
.with_context(|| format!("Could not write to `{output}`"))?;
|
||||
.inspect_err(|_| eprintln!("Error: Could not write to `{output}`"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -70,151 +76,14 @@ struct Args {
|
|||
output: Option<String>,
|
||||
|
||||
/// Separator for nested keys
|
||||
#[arg(short, long, value_name = "STRING", default_value = "__")]
|
||||
separator: String,
|
||||
}
|
||||
|
||||
struct JsonParser;
|
||||
|
||||
impl JsonParser {
|
||||
fn parse(lines: &mut Vec<EnvVar>, key: &str, value: &Value, separator: &str) {
|
||||
match value {
|
||||
Value::Array(array) => {
|
||||
for (index, item) in array.iter().enumerate() {
|
||||
let key = Self::build_key(key, index.to_string().as_str(), separator);
|
||||
Self::parse(lines, &key, item, separator)
|
||||
}
|
||||
}
|
||||
Value::Object(object) => {
|
||||
for (name, value) in object {
|
||||
let key = Self::build_key(key, name.as_str(), separator);
|
||||
Self::parse(lines, &key, value, separator)
|
||||
}
|
||||
}
|
||||
_ => lines.push(EnvVar(key.trim().to_owned(), value.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_key(prefix: &str, key: &str, separator: &str) -> String {
|
||||
match prefix.is_empty() {
|
||||
true => key.to_string(),
|
||||
false => format!("{prefix}{separator}{key}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EnvVar(String, Value);
|
||||
|
||||
impl Display for EnvVar {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.1 {
|
||||
Value::Null => write!(fmt, "{key}=null", key = self.0),
|
||||
Value::Bool(bool) => write!(fmt, "{key}={bool}", key = self.0),
|
||||
Value::Number(ref number) => write!(fmt, "{key}={number}", key = self.0),
|
||||
Value::String(ref string) => write!(
|
||||
fmt,
|
||||
r#"{key}="{value}""#,
|
||||
key = self.0,
|
||||
value = string.replace('"', r#"\""#)
|
||||
),
|
||||
_ => write!(fmt, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{EnvVar, JsonParser};
|
||||
|
||||
const KEY: &str = r#""key""#;
|
||||
|
||||
#[test]
|
||||
fn build_key_should_leave_key_unchanged_when_prefix_is_empty() {
|
||||
// ARRANGE
|
||||
let separator = "";
|
||||
let input = KEY.to_owned();
|
||||
let expected = KEY;
|
||||
|
||||
// ACT
|
||||
let result = JsonParser::build_key("", &input, separator);
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_key_should_leave_prepend_prefix_with_separator() {
|
||||
// ARRANGE
|
||||
let separator = "_";
|
||||
let input = KEY.to_owned();
|
||||
let expected = format!("prefix{separator}{KEY}");
|
||||
|
||||
// ACT
|
||||
let actual = JsonParser::build_key("prefix", &input, separator);
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!(true));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, r#""key"=true"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!(1.0));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, r#""key"=1.0"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!("hello"));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, r#""key"="hello""#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!([1, 2]));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, "")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_env_var_should_be_formatted_correctly() {
|
||||
// ARRANGE
|
||||
let input = EnvVar(KEY.to_owned(), json!({ "key": "value" }));
|
||||
|
||||
// ACT
|
||||
let result = input.to_string();
|
||||
|
||||
// ASSERT
|
||||
assert_eq!(result, "")
|
||||
}
|
||||
#[arg(short = 's', long, value_name = "STRING", default_value = "__")]
|
||||
key_separator: String,
|
||||
|
||||
/// Separator for array elements
|
||||
#[arg(short = 'S', long, value_name = "STRING", default_value = ",")]
|
||||
array_separator: String,
|
||||
|
||||
/// Separate array elements in multiple environment variables
|
||||
#[arg(short, long)]
|
||||
enumerate_array: bool,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue