diff options
-rw-r--r-- | .envrc | 11 | ||||
-rw-r--r-- | .gitignore | 15 | ||||
-rw-r--r-- | .reuse/templates/default.jinja2 | 26 | ||||
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | Cargo.toml | 25 | ||||
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | cog.toml | 34 | ||||
-rw-r--r-- | docs/quotify.1.md | 57 | ||||
-rw-r--r-- | flake.lock | 139 | ||||
-rw-r--r-- | flake.nix | 134 | ||||
-rwxr-xr-x | scripts/optimize_images.sh | 133 | ||||
-rw-r--r-- | src/main.rs | 39 | ||||
-rw-r--r-- | treefmt.nix | 79 | ||||
-rwxr-xr-x | update.sh | 15 |
14 files changed, 746 insertions, 0 deletions
diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..c8c5665 --- /dev/null +++ b/.envrc @@ -0,0 +1,11 @@ +use flake || use nix +watch_file flake.nix + +PATH_add ./target/debug +PATH_add ./target/release +PATH_add ./scripts + +if on_git_branch; then + echo && git status --short --branch && + echo && git fetch --verbose +fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2dcaa2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +# build +/target +/result + +# dev env +.direnv diff --git a/.reuse/templates/default.jinja2 b/.reuse/templates/default.jinja2 new file mode 100644 index 0000000..10c960a --- /dev/null +++ b/.reuse/templates/default.jinja2 @@ -0,0 +1,26 @@ +{# +Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +SPDX-License-Identifier: AGPL-3.0-or-later + +This file is part of Quotify - A simple CLI utility to shell quote the text +inputted into it. + +You should have received a copy of the License along with this program. +If not, see <https://www.gnu.org/licenses/agpl.txt>. +#} + +{% for copyright_line in copyright_lines %} +{{ copyright_line }} +{% endfor %} +{% for contributor_line in contributor_lines %} +SPDX-FileContributor: {{ contributor_line }} +{% endfor %} +{% for expression in spdx_expressions %} +SPDX-License-Identifier: {{ expression }} +{% endfor %} + +This file is part of Quotify - A simple CLI utility to shell quote the text +inputted into it. + +You should have received a copy of the License along with this program. +If not, see <https://www.gnu.org/licenses/agpl.txt>. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0ee492e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "quotify" +version = "0.1.0" +dependencies = [ + "anyhow", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..724f9e5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +[package] +name = "quotify" +description = "A simple cli utility to shell quote the text inputed into it" +version = "0.1.0" +edition = "2021" +license = "%INIT_SPDX_LICENSE_IDENTIFER" +homepage = "%INIT_APPLICATION_HOMEPAGE" +repository = "https://git.vhack.eu/soispha/tools/quotify" +# TODO +# categories = [""] +# keywords = ["", ""] + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +anyhow = "1.0.89" diff --git a/README.md b/README.md new file mode 100644 index 0000000..31c2a85 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +<!-- +Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +SPDX-License-Identifier: AGPL-3.0-or-later + +This file is part of Quotify - A simple CLI utility to shell quote the text +inputted into it. + +You should have received a copy of the License along with this program. +If not, see <https://www.gnu.org/licenses/agpl.txt>. +--> + +# Quotify + +> A simple cli utility to shell quote the text inputed into it + +## Licensing + +This project complies with the REUSE v3.2 specification. This means that every file +clearly states its copyright. +Please run `./scripts/cprh.sh contributer NAME EMAIL FILES..` after you +contributed to `FILES..` to record your contribution (obviously replacing +the `NAME`, `EMAIL` and `FILES..` placeholders with your name, email, and +the paths to the changed files respectively (see the `--help` output for more)). diff --git a/cog.toml b/cog.toml new file mode 100644 index 0000000..3c6df32 --- /dev/null +++ b/cog.toml @@ -0,0 +1,34 @@ +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +tag_prefix = "v" +branch_whitelist = ["main", "prime"] +ignore_merge_commits = false + +pre_bump_hooks = [ + "reuse lint", # Check licensing status. + "nix flake check", # Verify the project builds. + "cargo set-version {{version}}", # Bump version in Cargo.toml. + "nix fmt", # Format. +] +post_bump_hooks = [ + "git push", + "cargo publish", + "git push origin v{{version}}", # push the new tag to origin +] + +[bump_profiles] + +[changelog] +path = "NEWS.md" +template = "remote" +remote = "git.vhack.eu" +repository = "tools/quotify" +owner = "soispha" +authors = [{ signature = "Benedikt Peetz", username = "soispha" }] diff --git a/docs/quotify.1.md b/docs/quotify.1.md new file mode 100644 index 0000000..546b559 --- /dev/null +++ b/docs/quotify.1.md @@ -0,0 +1,57 @@ +<!-- +Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +SPDX-License-Identifier: AGPL-3.0-or-later + +This file is part of Quotify - A simple CLI utility to shell quote the text +inputted into it. + +You should have received a copy of the License along with this program. +If not, see <https://www.gnu.org/licenses/agpl.txt>. +--> + +% QUOTIFY(1) quotify 0.1.0 +% Benedikt Peetz +% Oct 2024 + +# NAME + +quotify - A simple cli utility to shell quote the text inputed into it + +# SYNOPSIS + +**quotify** \[*--help*|*--version*\] + +# DESCRIPTION + +TODO + +# OPTIONS + +**--help**, **-h** +: Displays a help message and exit. + +**--version**, **-v** +: Displays the software version and exit. + +# EXAMPLES + +**quotify** +: TODO. See the Description section for further details. + +# FILES + +*name.file* +: This file is important because it does x. + +# BUGS + +Report bugs to <mailto://soispha@vhack.eu>. + +# COPYRIGHT + +Copyright (C) 2024 Benedikt Peetz + +This program is free software. + +You should have received a copy of the License +along with this program. If not, see <https://www.gnu.org/licenses/agpl.txt>. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..b491dc4 --- /dev/null +++ b/flake.lock @@ -0,0 +1,139 @@ +{ + "nodes": { + "crane": { + "locked": { + "lastModified": 1727974419, + "narHash": "sha256-WD0//20h+2/yPGkO88d2nYbb23WMWYvnRyDQ9Dx4UHg=", + "owner": "ipetkov", + "repo": "crane", + "rev": "37e4f9f0976cb9281cd3f0c70081e5e0ecaee93f", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1728031656, + "narHash": "sha256-JXumn7X+suKEcehp4rchSvBzIboqyybQ5bLK4wk7gQU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "eeeb90a1dd3c9bea3afdbc76fd34d0fb2a727c7a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", + "systems": "systems", + "treefmt-nix": "treefmt-nix" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1728008962, + "narHash": "sha256-MjGMCVKqafsrqLQYJHHKXJkvocTjkxKjadBfN952/Zw=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "862d0c1e5fe2348a22044f225afef39b75df8cf0", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1680978846, + "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=", + "owner": "nix-systems", + "repo": "x86_64-linux", + "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "x86_64-linux", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1727984844, + "narHash": "sha256-xpRqITAoD8rHlXQafYZOLvUXCF6cnZkPfoq67ThN0Hc=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "4446c7a6fc0775df028c5a3f6727945ba8400e64", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..db88011 --- /dev/null +++ b/flake.nix @@ -0,0 +1,134 @@ +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +{ + description = "A simple cli utility to shell quote the text inputed into it"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + + crane = { + url = "github:ipetkov/crane"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + + # inputs for following + systems = { + url = "github:nix-systems/x86_64-linux"; # only evaluate for this system + }; + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + flake-utils = { + url = "github:numtide/flake-utils"; + inputs = { + systems.follows = "systems"; + }; + }; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + treefmt-nix, + crane, + rust-overlay, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + + nightly = false; + rust_minimal = + if nightly + then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal) + else pkgs.rust-bin.stable.latest.minimal; + rust_default = + if nightly + then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default) + else pkgs.rust-bin.stable.latest.default; + + cargo_toml = craneLib.cleanCargoToml {cargoToml = ./Cargo.toml;}; + pname = cargo_toml.package.name; + + craneLib = (crane.mkLib pkgs).overrideToolchain rust_minimal; + craneBuild = craneLib.buildPackage { + src = craneLib.cleanCargoSource ./.; + + doCheck = true; + }; + + manual = pkgs.stdenv.mkDerivation { + name = "${pname}-manual"; + inherit (cargo_toml.package) version; + + src = ./docs; + nativeBuildInputs = with pkgs; [pandoc]; + + buildPhase = '' + mkdir --parents $out/docs; + + pandoc "./${pname}.1.md" -s -t man > $out/docs/${pname}.1 + ''; + + installPhase = '' + install -D $out/docs/${pname}.1 $out/share/man/man1/${pname}; + ''; + }; + + treefmtEval = import ./treefmt.nix {inherit treefmt-nix pkgs;}; + in { + packages.default = pkgs.symlinkJoin { + inherit (cargo_toml.package) name; + + paths = [manual craneBuild]; + }; + + checks = { + inherit craneBuild; + formatting = treefmtEval.config.build.check self; + }; + + formatter = treefmtEval.config.build.wrapper; + + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + cocogitto + + rust_default + cargo-edit + + reuse + ]; + }; + }); +} +# vim: ts=2 + diff --git a/scripts/optimize_images.sh b/scripts/optimize_images.sh new file mode 100755 index 0000000..75f04af --- /dev/null +++ b/scripts/optimize_images.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env nix +#! nix shell nixpkgs#optipng nixpkgs#jpegoptim nixpkgs#nodePackages.svgo nixpkgs#dash --command dash + +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +# shellcheck shell=dash + +# source: https://github.com/stride-tasks/stride/blob/148d513297c8ae66d79fc287769adfe5e711c93c/scripts/optimize-images + +PROJECT_DIR="$(git rev-parse --show-toplevel)" + +cd "$PROJECT_DIR" || { + echo "No '$PROJECT_DIR' ?!" + exit 1 +} + +PNG_OPITMIZE_COMMAND='optipng --quiet -o7 -preserve' +JPG_OPITMIZE_COMMAND='jpegoptim --quiet --strip-all -m76' +SVG_OPITMIZE_COMMAND='svgo --multipass --quiet --input' + +# The extension is the part after the first dot in the filename: +# +# For example: +# file.png => png +# file.tar.gz => tar.gz +find_files_by_extension() { + wanted_extension="$1" + tmp="$(mktemp)" + + git ls-files --cached --modified --other --exclude-standard --deduplicate | while IFS= read -r file; do + extension="${file#*.}" + if [ "$extension" = "$wanted_extension" ]; then + echo "$file" + else + : + # echo "'$file' with extension: '$extension' does not match filter: '$wanted_extension'" 1>&2 + fi + done >"$tmp" + + echo "$tmp" +} + +size_of() { + du -b "$1" | cut -f1 +} + +bytes_human() { + number="$1" + + numfmt --to=iec-i --suffix=B --format="%9.2f" "$number" +} + +# https://stackoverflow.com/questions/44695878/how-to-calculate-percentage-in-shell-script +# Native POSIX solution using string manipulation (assumes integer inputs). +percent() { + DP="$1" + SDC="$2" + + # Special case when DP is zero. + [ "$DP" = "0" ] && echo "0.00" && return + + # # e.g. round down e.g. round up + # # DP=1 SDC=3 DP=2 SDC=3 + Percent=$((DP * 100000 / SDC + 5)) # Percent=33338 Percent=66671 + Whole=${Percent%???} # Whole=33 Whole=66 + Percent=${Percent#"$Whole"} # Percent=338 Percent=671 + Percent=$Whole.${Percent%?} # Percent=33.33 Percent=66.67 + echo "$Percent" +} + +TOTAL=0 +TOTAL_SAVED=0 + +optimize_files() { + FILTER="$1" + PROGRAM="$2" + + printf "%s" "Processing $FILTER files:" + + FILES="$(find_files_by_extension "$FILTER")" + COUNT=$(wc -l <"$FILES") + + if [ "$COUNT" -eq 0 ]; then + echo " no files found!" + return + fi + + echo "" + + I=1 + + TOTAL_INNER=0 + TOTAL_SAVED_INNER=0 + + while IFS= read -r f; do + printf "%-2s/${COUNT} $f ... " "$I" + + SIZE="$(size_of "$f")" + + $PROGRAM "$f" + + NEW_SIZE="$(size_of "$f")" + DIFF=$((SIZE - NEW_SIZE)) + + echo "saved: $(bytes_human "$DIFF") ($(percent $DIFF "$SIZE")%)" + + TOTAL_INNER=$((TOTAL_INNER + SIZE)) + TOTAL_SAVED_INNER=$((TOTAL_SAVED_INNER + DIFF)) + + I=$((I + 1)) + done <"$FILES" + rm "$FILES" + + echo "Total saved for $FILTER: $(bytes_human "$TOTAL_SAVED_INNER") ($(percent $TOTAL_SAVED_INNER $TOTAL_INNER)%)" + echo "" + + TOTAL=$((TOTAL + TOTAL_INNER)) + TOTAL_SAVED=$((TOTAL_SAVED + TOTAL_SAVED_INNER)) +} + +optimize_files 'png' "$PNG_OPITMIZE_COMMAND" +optimize_files 'jpg' "$JPG_OPITMIZE_COMMAND" +optimize_files 'jpeg' "$JPG_OPITMIZE_COMMAND" +optimize_files 'svg' "$SVG_OPITMIZE_COMMAND" + +echo "Total saved: $(bytes_human "$TOTAL_SAVED") ($(percent $TOTAL_SAVED $TOTAL)%)" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a6b047b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,39 @@ +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This file is part of Quotify - A simple CLI utility to shell quote the text +// inputted into it. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/agpl.txt>. + +use std::{ + env::args, + io::{stdin, Read}, +}; + +use anyhow::{Context, Result}; + +fn main() -> Result<()> { + let text: String = { + if args().count() != 1 { + args().skip(1).collect() + } else { + let mut stdin = stdin(); + let mut buf = vec![]; + stdin + .read_to_end(&mut buf) + .context("Failed to read stdin")?; + + let output = + String::from_utf8(buf).context("Failed to decode stdin as a utf8 string")?; + output + } + }; + + let quoted_text = text.replace('\'', "'\\''"); + + print!("'{}'", quoted_text); + + Ok(()) +} diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 0000000..0015102 --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,79 @@ +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +{ + treefmt-nix, + pkgs, +}: +treefmt-nix.lib.evalModule pkgs ( + {pkgs, ...}: { + # Used to find the project root + projectRootFile = "flake.nix"; + + programs = { + alejandra.enable = true; + rustfmt.enable = true; + clang-format.enable = true; + mdformat.enable = true; + shfmt = { + enable = true; + indent_size = 4; + }; + shellcheck.enable = true; + prettier = { + enable = true; + settings = { + arrowParens = "always"; + bracketSameLine = false; + bracketSpacing = true; + editorconfig = true; + embeddedLanguageFormatting = "auto"; + endOfLine = "lf"; + # experimentalTernaries = false; + htmlWhitespaceSensitivity = "css"; + insertPragma = false; + jsxSingleQuote = true; + printWidth = 80; + proseWrap = "always"; + quoteProps = "consistent"; + requirePragma = false; + semi = true; + singleAttributePerLine = true; + singleQuote = false; + trailingComma = "all"; + useTabs = false; + vueIndentScriptAndStyle = false; + + tabWidth = 2; + }; + }; + stylua.enable = true; + ruff = { + enable = true; + format = true; + }; + taplo.enable = true; + }; + + settings = { + global.excludes = [ + "CHANGELOG.md" + "NEWS.md" + ]; + formatter = { + clang-format = { + options = ["--style" "GNU"]; + }; + shfmt = { + includes = ["*.bash"]; + }; + }; + }; + } +) diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..3c615f4 --- /dev/null +++ b/update.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +# Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of Quotify - A simple CLI utility to shell quote the text +# inputted into it. +# +# You should have received a copy of the License along with this program. +# If not, see <https://www.gnu.org/licenses/agpl.txt>. + +nix flake update + +[ "$1" = "upgrade" ] && cargo upgrade +cargo update |