From d163c23b9e285eaeff7c3628b7ed299d60ef947e Mon Sep 17 00:00:00 2001 From: GeopJr Date: Wed, 18 May 2022 19:41:41 +0300 Subject: [PATCH] feat: init --- .editorconfig | 9 ++ .github/workflows/ci.yml | 18 ++++ .github/workflows/release.yml | 41 +++++++++ .gitignore | 6 ++ CODE_OF_CONDUCT.md | 132 +++++++++++++++++++++++++++ LICENSE | 25 ++++++ Makefile | 22 +++++ README.md | 142 ++++++++++++++++++++++++++++++ data/ascii.txt | 17 ++++ data/colors.json | 133 ++++++++++++++++++++++++++++ logo.svg | 1 + shard.yml | 13 +++ spec/blahaj_spec.cr | 52 +++++++++++ spec/color_spec.cr | 53 +++++++++++ spec/results/cowsay.txt | 8 ++ spec/results/cowsay_colored.txt | 8 ++ spec/results/flag.txt | 5 ++ spec/results/flag_3x.txt | 15 ++++ spec/results/shark.txt | 17 ++++ spec/results/shark_background.txt | 17 ++++ spec/results/shark_individual.txt | 17 ++++ spec/results/shark_words.txt | 17 ++++ spec/spec_helper.cr | 15 ++++ src/blahaj.cr | 100 +++++++++++++++++++++ src/blahaj/color.cr | 102 +++++++++++++++++++++ src/blahaj/option_parser.cr | 76 ++++++++++++++++ 26 files changed, 1061 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 data/ascii.txt create mode 100644 data/colors.json create mode 100644 logo.svg create mode 100644 shard.yml create mode 100644 spec/blahaj_spec.cr create mode 100644 spec/color_spec.cr create mode 100644 spec/results/cowsay.txt create mode 100644 spec/results/cowsay_colored.txt create mode 100644 spec/results/flag.txt create mode 100644 spec/results/flag_3x.txt create mode 100644 spec/results/shark.txt create mode 100644 spec/results/shark_background.txt create mode 100644 spec/results/shark_individual.txt create mode 100644 spec/results/shark_words.txt create mode 100644 spec/spec_helper.cr create mode 100644 src/blahaj.cr create mode 100644 src/blahaj/color.cr create mode 100644 src/blahaj/option_parser.cr diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..163eb75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.cr] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..66884b3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,18 @@ +name: Specs & Lint +on: [push, pull_request] +jobs: + ci: + runs-on: ubuntu-latest + steps: + - name: Download source + uses: actions/checkout@v2 + - name: Install Crystal + uses: crystal-lang/install-crystal@v1 + - name: Install shards + run: shards install + - name: Run tests + run: make test + - name: Build + run: make debug + - name: Check formatting + run: crystal tool format; git diff --exit-code \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..526bc40 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +name: Release +on: workflow_dispatch +jobs: + build_linux_static: + runs-on: ubuntu-latest + container: + image: crystallang/crystal:latest-alpine + steps: + - name: Download source + uses: actions/checkout@v2 + - name: Retrieve version + run: | + echo "::set-output name=VERSION::$(shards version)" + id: version + - name: Build + run: make static + - name: Make binary executable + run: chmod +x bin/blahaj + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: blahaj-${{ steps.version.outputs.VERSION }}-linux-x86_64-static + path: bin/blahaj + + release: + runs-on: ubuntu-latest + needs: build_linux_static + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2 + with: + path: ./GH_ARTIFACTS + - name: Make all binaries executable + run: chmod +x GH_ARTIFACTS/**/* + - name: Create zips + run: cd GH_ARTIFACTS && find . -maxdepth 1 -mindepth 1 -type d -execdir zip -jr '{}.zip' '{}' \; && cd .. + - uses: softprops/action-gh-release@v1 + with: + draft: true + files: | + GH_ARTIFACTS/*.zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e070556 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/docs/ +/lib/ +/bin/ +/.shards/ +*.dwarf +logo-full.svg diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..95663f4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +coc@geopjr.dev. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7d1a558 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2022, Evangelos "GeopJr" Paterakis +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..37c060d --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +.PHONY: all install uninstall test build debug static +PREFIX ?= /usr + +all: build + +debug: + shards build + +build: + shards build --production --release --no-debug + +static: + shards build --production --release --no-debug --static + +test: + crystal spec --order random + +install: + install -D -m 0755 bin/blahaj $(PREFIX)/bin/blahaj + +uninstall: + rm -f $(PREFIX)/bin/blahaj diff --git a/README.md b/README.md new file mode 100644 index 0000000..9526369 --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +

+ BLÅHAJ ascii art in trans flag colors +

+

BLÅHAJ

+

Gay sharks at your local terminal

+

+
+ Code Of Conduct + BSD-2-Clause + ci action status +

+ +# + +## What is BLÅHAJ? + +Apart from [a cute cuddly shark plushie from IKEA](https://www.ikea.com/us/en/p/blahaj-soft-toy-shark-90373590/), BLÅHAJ is a lolcat-like CLI tool that colorizes your input, shows flags and prints colorful sharks! + +It has a wide variety of flags/colors to choose from and many options from flag size to whether to colorize by line, word or character. + +# + +## Installation + +### Pre-built + +You can download one of the statically-linked pre-built binaries from the [releases page](https://github.com/GeopJr/BLAHAJ/releases/latest). + +They are built & published by our lovely [actions](https://github.com/GeopJr/BLAHAJ/actions/workflows/release.yml). + +### Building + +#### Dependencies + +- `crystal` - `1.4.1` + +#### Makefile + +- `$ make` (or `$ make static` on Alpine Linux for a static build) +- `# make install` + +# + +## FAQ + +### Why are only striped flags allowed? + +Flags with eg. triangles, circles or symbols, need enough horizontal space to determine where and how to display them. BLÅHAJ can't know in advance how long the input is going to be. + +### Why are the stripes repeating instead of showing the flag in full? + +Just like the previous question, BLÅHAJ can't know how long the text is going to be (vertically) as it colorizes the input as it comes in. + +### I would like to add a color scheme or flag + +Great! Follow the [Contributing section](#contributing) and modify [data/colors.json](./data/colors.json). + +```json +{ + "flag/color scheme name": [ + "color", + "per", + "stripe" + ], + "alias for scheme (if any)": "flag/color scheme name" +} +``` + +# + +## Examples + +``` +$ blahaj -h + +BLÅHAJ v1.0.0 + +Usage: + blahaj [arguments] + blahaj [arguments] file + command | blahaj [arguments] + +Examples: + blahaj -f trans ~/.bashrc + blahaj -s -b + neofetch | blahaj -f gay + blahaj -f lesbian -m 4 + blahaj -- -w /etc/os-release + +Arguments: + -b, --background Color the background + -f, --flag Return a flag + -s, --shark Shork + -i, --individual Color individual characters + -w, --words Color individual words + -m MULTIPLIER, --multiplier=MULTIPLIER + Multiplier for the flag size (-f) + -c FLAG, --colors=FLAG Color scheme to use (Default: trans) + --flags List all available flags + -h, --help Show this help +``` + +### Shark (`-s`), flag (`-f`), flag with 2x multiplier (`-f -m 2`) + +![A screenshot of a terminal with the output of the command "blahaj -s -c pride && blahaj -f -c pan && blahaj -f -c aro -m 2". The output first shows a big ascii shark, colorized in the usual rainbow color scheme of the pride flag (red, orange, yellow, green, blue, purple) repeating (one color per line). Under it, a small pansexual flag (pink, yellow, light blue) and under it a big (2x) aromantic flag (green, light green, white, grey, black).](https://i.imgur.com/SPsIcam.png) + +### Pipe, individual characters instead of rows (`-i`) + +![A screenshot of a terminal with the output of the command "cowsay "1312" | blahaj -c gay -i". The output shows some ascii art produced by the "cowsay" command of a cow with a speech bubble of the text "1312". The whole cow ascii art is colored per character using the gay / nwlnw flag colors (Dark mix of blue and green, normal mix of blue and green, light mix of blue and green, white, light blueish-purple, normal blueish-purple, dark blueish-purple).](https://i.imgur.com/M3N82H3.png) + +### Read from file, background instead of foreground (`-b`), individual words instead of rows (`-w`) + +> Notice how the foreground color changes based on the background color. + +![A screenshot of a terminal with the output of the command "blahaj -b -c lesbian ./src/blahaj.cr". The output shows some example code of a crystal-lang http server (you can read it and more on https://crystal-lang.org/) with each word's background colored using the colors of the lesbian flag (Dark orange, orange, light orange, white, light pinkish-purple, pinkish-purple, dark pinkish-purple). The foreground color is white on the darker background colors and black on the rest.](https://i.imgur.com/v1mTUmm.png) + +### Pipe, default color scheme + +![A screenshot of a terminal with the output of the command "echo -e "A\nHAT\nIN\nTIME\nUWU" | blahaj". The output shows the words "A HAT IN TIME UWU" (one per line) colored in the trans flag colors (light blue, light pink, white, light pink, light blue).](https://i.imgur.com/GjY5xP1.png) + +> All images above include ALT text and you are encouraged to read it, if you find parts of them confusing, by using screen readers, hovering over them with your cursor or looking at the source. + +# + +## Contributing + +1. Read the [Code of Conduct](https://github.com/GeopJr/BLAHAJ/blob/main/CODE_OF_CONDUCT.md) +2. Fork it ( https://github.com/GeopJr/BLAHAJ/fork ) +3. Create your feature branch (git checkout -b my-new-feature) +4. Commit your changes (git commit -am 'Add some feature') +5. Push to the branch (git push origin my-new-feature) +6. Create a new Pull Request + +# + +## Sponsors + +

+ +[![GeopJr Sponsors](https://cdn.jsdelivr.net/gh/GeopJr/GeopJr@main/sponsors.svg)](https://github.com/sponsors/GeopJr) + +

diff --git a/data/ascii.txt b/data/ascii.txt new file mode 100644 index 0000000..ab23193 --- /dev/null +++ b/data/ascii.txt @@ -0,0 +1,17 @@ + ,(((/ + /((((( + ((((#(( (// + (((((((. *(((/ + /(######/ *((((/ + *//%#####((/ ((#((/ + ,*/********/////////////////(//* (%* ,((##(( + ,*/((///(//////////((/(///////(/////(////*,(*#((/(/((//////###(###(/( + /(((((((//((///((////((((((/(((((((((((((((((/(((##((#%(##(/((///*(&#(##/ + /#((%(#(((((//#((((((((((((((((((((((((#(((((((((((/##(((((//((//* ####(/ + (((###(###(#(#####(###############((#((((((((/((//(((#/(///// ,, + ,(###%####%&%#############(#(#(####(((((((/(((/////*//, + . .....*#(#######(((###(#(##(##(((/(/(/////, + .. ....,..........,..*#%#######/( + .. .............,*%%%%#%((((/ + **,,,****//*(##((###(#((( + &#(#/#((((((((# \ No newline at end of file diff --git a/data/colors.json b/data/colors.json new file mode 100644 index 0000000..537e97a --- /dev/null +++ b/data/colors.json @@ -0,0 +1,133 @@ +{ + "trans": [ + "5BCEFA", + "F5A9B8", + "FFFFFF", + "F5A9B8", + "5BCEFA" + ], + "agender": [ + "000000", + "B9B9B9", + "FFFFFF", + "B8F483", + "FFFFFF", + "B9B9B9", + "000000" + ], + "nwlnw": [ + "118C70", + "2DCDA9", + "97E7C0", + "EFEFFF", + "7AACE1", + "504BCB", + "3F2077" + ], + "ace": [ + "000000", + "A3A3A3", + "FFFFFF", + "800080" + ], + "aro": [ + "3DA542", + "A7D379", + "FFFFFF", + "A9A9A9", + "000000" + ], + "bi": [ + "D60270", + "9B4F96", + "0038A8" + ], + "genderfluid": [ + "FF75A2", + "FFFFFF", + "BE18D6", + "000000", + "333EBD" + ], + "genderqueer": [ + "b57EDC", + "FFFFFF", + "4A8123" + ], + "nb": [ + "FFF430", + "FFFFFF", + "9C59D1", + "000000" + ], + "pan": [ + "FF218C", + "FFD800", + "21B1FF" + ], + "pride": [ + "E40303", + "FF8C00", + "FFED00", + "008026", + "004DFF", + "750787" + ], + "philadelphia": [ + "000000", + "784F17", + "E40303", + "FF8C00", + "FFED00", + "008026", + "004DFF", + "750787" + ], + "lesbian": [ + "D52D00", + "EF7627", + "FF9A56", + "FFFFFF", + "D162A4", + "B55690", + "A30262" + ], + "demiboy": [ + "7F7F7F", + "C3C3C3", + "9AD9EA", + "FFFFFF", + "9AD9EA", + "C3C3C3", + "7F7F7F" + ], + "demigirl": [ + "7F7F7F", + "C3C3C3", + "FEAEC9", + "FFFFFF", + "FEAEC9", + "C3C3C3", + "7F7F7F" + ], + "abrosexual": [ + "75CA92", + "B4E4C9", + "FFFFFF", + "E795B6", + "D9446E" + ], + "transgender": "trans", + "gay": "nwlnw", + "asexual": "ace", + "aromantic": "aro", + "bisexual": "bi", + "nonbinary": "nb", + "enby": "nb", + "lgbt": "pride", + "lgbtq": "pride", + "pansexual": "pan", + "philly": "philadelphia", + "nmlnm": "lesbian", + "abro": "abrosexual" +} \ No newline at end of file diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..75dfe95 --- /dev/null +++ b/logo.svg @@ -0,0 +1 @@ + diff --git a/shard.yml b/shard.yml new file mode 100644 index 0000000..f71b372 --- /dev/null +++ b/shard.yml @@ -0,0 +1,13 @@ +name: blahaj +version: 1.0.0 + +authors: + - Evangelos "GeopJr" Paterakis + +targets: + blahaj: + main: src/blahaj.cr + +crystal: 1.4.1 + +license: BSD-2-Clauses diff --git a/spec/blahaj_spec.cr b/spec/blahaj_spec.cr new file mode 100644 index 0000000..f38f961 --- /dev/null +++ b/spec/blahaj_spec.cr @@ -0,0 +1,52 @@ +require "./spec_helper" + +describe Blahaj do + it "outputs a trans flag" do + output = {{ run("#{__DIR__}/../src/blahaj", "-f").stringify }} + result = {{ read_file("#{__DIR__}/results/flag.txt") }} + + output.should eq result + end + + it "outputs a trans flag with 3x multiplier" do + output = {{ run("#{__DIR__}/../src/blahaj", "-f", "-m", "3").stringify }} + result = {{ read_file("#{__DIR__}/results/flag_3x.txt") }} + + output.should eq result + end + + it "outputs an agender shark" do + output = {{ run("#{__DIR__}/../src/blahaj", "-s", "-c", "agender").stringify }} + result = {{ read_file("#{__DIR__}/results/shark.txt") }} + + output.should eq result + end + + it "reads from ARGF and outputs a lesbian cowsay" do + output = {{ run("#{__DIR__}/../src/blahaj", "-c", "lesbian", "#{__DIR__}/results/cowsay.txt").stringify }} + result = {{ read_file("#{__DIR__}/results/cowsay_colored.txt") }} + + output.should eq result + end + + it "outputs an ace shark with each character colorized individually" do + output = {{ run("#{__DIR__}/../src/blahaj", "-s", "-c", "ace", "-i").stringify }} + result = {{ read_file("#{__DIR__}/results/shark_individual.txt") }} + + output.should eq result + end + + it "outputs an aro shark with each word colorized individually" do + output = {{ run("#{__DIR__}/../src/blahaj", "-s", "-c", "aro", "-w").stringify }} + result = {{ read_file("#{__DIR__}/results/shark_words.txt") }} + + output.should eq result + end + + it "outputs a gay shark with its background colorized" do + output = {{ run("#{__DIR__}/../src/blahaj", "-s", "-c", "nwlnw", "-b").stringify }} + result = {{ read_file("#{__DIR__}/results/shark_background.txt") }} + + output.should eq result + end +end diff --git a/spec/color_spec.cr b/spec/color_spec.cr new file mode 100644 index 0000000..46d2949 --- /dev/null +++ b/spec/color_spec.cr @@ -0,0 +1,53 @@ +require "./spec_helper" + +describe Blahaj::Color do + it "converts hex to rgb" do + color = Blahaj::Color.new("#FFF275") + + color.r.should eq 255 + color.g.should eq 242 + color.b.should eq 117 + end + + it "converts hex to rgb (short)" do + color = Blahaj::Color.new("#C8A") + + color.r.should eq 204 + color.g.should eq 136 + color.b.should eq 170 + end + + it "converts rgb to hex" do + color = Blahaj::Color.new(255, 242, 117) + + color.hex.should eq "#FFF275" + end + + it "converts short hex to hex" do + color = Blahaj::Color.new("#C8A") + + color.hex.should eq "#CC88AA" + end + + it "returns Colorize::ColorRGB" do + color = Blahaj::Color.new("#FFF275") + + color.color.red.should eq 255 + color.color.green.should eq 242 + color.color.blue.should eq 117 + end + + it "returns an accessible foreground color" do + color = Blahaj::Color.new("#000000") + + color.foreground.red.should eq 255 + color.foreground.green.should eq 255 + color.foreground.blue.should eq 255 + + color = Blahaj::Color.new("#FFFFFF") + + color.foreground.red.should eq 0 + color.foreground.green.should eq 0 + color.foreground.blue.should eq 0 + end +end diff --git a/spec/results/cowsay.txt b/spec/results/cowsay.txt new file mode 100644 index 0000000..87e0f60 --- /dev/null +++ b/spec/results/cowsay.txt @@ -0,0 +1,8 @@ + ____________________ +< I'm inside a spec? > + -------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || diff --git a/spec/results/cowsay_colored.txt b/spec/results/cowsay_colored.txt new file mode 100644 index 0000000..26ff01c --- /dev/null +++ b/spec/results/cowsay_colored.txt @@ -0,0 +1,8 @@ + ____________________  +< I'm inside a spec? > + --------------------  + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || diff --git a/spec/results/flag.txt b/spec/results/flag.txt new file mode 100644 index 0000000..563786f --- /dev/null +++ b/spec/results/flag.txt @@ -0,0 +1,5 @@ +  +  +  +  +  diff --git a/spec/results/flag_3x.txt b/spec/results/flag_3x.txt new file mode 100644 index 0000000..5cedf58 --- /dev/null +++ b/spec/results/flag_3x.txt @@ -0,0 +1,15 @@ +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  diff --git a/spec/results/shark.txt b/spec/results/shark.txt new file mode 100644 index 0000000..5528ea3 --- /dev/null +++ b/spec/results/shark.txt @@ -0,0 +1,17 @@ + ,(((/  + /(((((  + ((((#(( (//  + (((((((. *(((/  + /(######/ *((((/  + *//%#####((/ ((#((/  + ,*/********/////////////////(//* (%* ,((##((  + ,*/((///(//////////((/(///////(/////(////*,(*#((/(/((//////###(###(/(  + /(((((((//((///((////((((((/(((((((((((((((((/(((##((#%(##(/((///*(&#(##/  + /#((%(#(((((//#((((((((((((((((((((((((#(((((((((((/##(((((//((//* ####(/  + (((###(###(#(#####(###############((#((((((((/((//(((#/(///// ,,  + ,(###%####%&%#############(#(#(####(((((((/(((/////*//,  + . .....*#(#######(((###(#(##(##(((/(/(/////,  + .. ....,..........,..*#%#######/(  + .. .............,*%%%%#%((((/  + **,,,****//*(##((###(#(((  + &#(#/#((((((((#  diff --git a/spec/results/shark_background.txt b/spec/results/shark_background.txt new file mode 100644 index 0000000..591fd11 --- /dev/null +++ b/spec/results/shark_background.txt @@ -0,0 +1,17 @@ + ,(((/  + /(((((  + ((((#(( (//  + (((((((. *(((/  + /(######/ *((((/  + *//%#####((/ ((#((/  + ,*/********/////////////////(//* (%* ,((##((  + ,*/((///(//////////((/(///////(/////(////*,(*#((/(/((//////###(###(/(  + /(((((((//((///((////((((((/(((((((((((((((((/(((##((#%(##(/((///*(&#(##/  + /#((%(#(((((//#((((((((((((((((((((((((#(((((((((((/##(((((//((//* ####(/  + (((###(###(#(#####(###############((#((((((((/((//(((#/(///// ,,  + ,(###%####%&%#############(#(#(####(((((((/(((/////*//,  + . .....*#(#######(((###(#(##(##(((/(/(/////,  + .. ....,..........,..*#%#######/(  + .. .............,*%%%%#%((((/  + **,,,****//*(##((###(#(((  + &#(#/#((((((((#  diff --git a/spec/results/shark_individual.txt b/spec/results/shark_individual.txt new file mode 100644 index 0000000..b74f34a --- /dev/null +++ b/spec/results/shark_individual.txt @@ -0,0 +1,17 @@ + ,(((/ + /((((( + ((((#(( (// + (((((((. *(((/ + /(######/ *((((/ + *//%#####((/ ((#((/ + ,*/********/////////////////(//* (%* ,((##(( + ,*/((///(//////////((/(///////(/////(////*,(*#((/(/((//////###(###(/( + /(((((((//((///((////((((((/(((((((((((((((((/(((##((#%(##(/((///*(&#(##/ + /#((%(#(((((//#((((((((((((((((((((((((#(((((((((((/##(((((//((//* ####(/ + (((###(###(#(#####(###############((#((((((((/((//(((#/(///// ,, + ,(###%####%&%#############(#(#(####(((((((/(((/////*//, + . .....*#(#######(((###(#(##(##(((/(/(/////, + .. ....,..........,..*#%#######/( + .. .............,*%%%%#%((((/ + **,,,****//*(##((###(#((( + &#(#/#((((((((# diff --git a/spec/results/shark_words.txt b/spec/results/shark_words.txt new file mode 100644 index 0000000..7224349 --- /dev/null +++ b/spec/results/shark_words.txt @@ -0,0 +1,17 @@ +                                          ,(((/                                  +                                        /(((((                                   +                                       ((((#((                              (//  +                                      (((((((.                           *(((/   +                                    /(######/                          *((((/    +                                 *//%#####((/                         ((#((/     +               ,*/********/////////////////(//*           (%*      ,((##((       +      ,*/((///(//////////((/(///////(/////(////*,(*#((/(/((//////###(###(/(      +   /(((((((//((///((////((((((/(((((((((((((((((/(((##((#%(##(/((///*(&#(##/     +  /#((%(#(((((//#((((((((((((((((((((((((#(((((((((((/##(((((//((//*    ####(/   +   (((###(###(#(#####(###############((#((((((((/((//(((#/(/////            ,,   +     ,(###%####%&%#############(#(#(####(((((((/(((/////*//,                     +         . .....*#(#######(((###(#(##(##(((/(/(/////,                            +          .. ....,..........,..*#%#######/(                                      +               ..  .............,*%%%%#%((((/                                    +                       **,,,****//*(##((###(#(((                                 +                                        &#(#/#((((((((#                          diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr new file mode 100644 index 0000000..5c72058 --- /dev/null +++ b/spec/spec_helper.cr @@ -0,0 +1,15 @@ +require "spec" +require "file_utils" +require "../src/blahaj" + +module Blahaj + CLI = { + "background" => false, + "color" => "trans", + "flag" => false, + "shark" => false, + "individual" => false, + "multiplier" => 1, + "words" => false, + } +end diff --git a/src/blahaj.cr b/src/blahaj.cr new file mode 100644 index 0000000..056c6eb --- /dev/null +++ b/src/blahaj.cr @@ -0,0 +1,100 @@ +require "json" + +module Blahaj + # Hash of all colors from data/colors.json + COLORS = Hash(String, String | Array(Blahaj::Color)).from_json({{read_file("#{__DIR__}/../data/colors.json")}}) + # Chars that are to be ignored during -i or -w + NO_COLOR = {'\n', '\t', ' '} +end + +# Need to be required after the above have been declared +require "./blahaj/*" + +module Blahaj + # Shork + ascii = {{read_file("#{__DIR__}/../data/ascii.txt")}} + + # Selected color + scheme = COLORS[Blahaj::CLI["color"]].as(Array(Blahaj::Color)) + multiplied_cols = 4 * scheme.size * Blahaj::CLI["multiplier"].as(Int32) + i = 0 + + # [MACRO]: Instead of checking the CLI options and colorizing accordingly, pre-generate all available + # colorizers(?) using macros + macro handle_argf(background, flag, ascii, individual, words) + # flag: scheme.each + # ascii: ascii.each_line + # default: ARGF.each_line + {{"#{(flag ? "scheme" : (ascii ? "ascii" : "ARGF")).id}.each#{(flag ? "" : "_line").id}".id}} do |x| + current_color = scheme[i % scheme.size] + {% if flag %} + tmp_i = 0 + while tmp_i < Blahaj::CLI["multiplier"].as(Int32) + STDOUT.puts (" " * multiplied_cols).colorize.back(current_color.color) + tmp_i += 1 + end + {% elsif individual || words %} + # individual: x.chars.map + # words: x.split(' ').map + STDOUT.puts x.{{(words ? "split(' ')" : "chars").id}}.map {|y| + next y if NO_COLOR.includes?(y) + tmp_color = current_color + i += 1 + current_color = scheme[i % scheme.size] + {% if background %} + y.colorize.back(tmp_color.color).fore(tmp_color.foreground) + {% else %} + y.colorize(tmp_color.color) + {% end %} + }.join({{words ? ' ' : ""}}) + i -= 1 + {% elsif background %} + STDOUT.puts x.colorize.back(current_color.color).fore(current_color.foreground) + {% else %} + STDOUT.puts x.colorize(current_color.color) + {% end %} + i += 1 + end + exit + end + + # [MACRO] Skip on spec + {% unless @top_level.has_constant? "Spec" %} + # This is repetitive but they are macros. It saves runtime checks. + if Blahaj::CLI["shark"] + if Blahaj::CLI["background"] + if Blahaj::CLI["individual"] + handle_argf(true, false, true, true, false) + elsif Blahaj::CLI["words"] + handle_argf(true, false, true, false, true) + else + handle_argf(true, false, true, false, false) + end + else + if Blahaj::CLI["individual"] + handle_argf(false, false, true, true, false) + elsif Blahaj::CLI["words"] + handle_argf(false, false, true, false, true) + else + handle_argf(false, false, true, false, false) + end + end + elsif Blahaj::CLI["flag"] + handle_argf(false, true, false, false, false) + elsif Blahaj::CLI["background"] + if Blahaj::CLI["individual"] + handle_argf(true, false, false, true, false) + elsif Blahaj::CLI["words"] + handle_argf(true, false, false, false, true) + else + handle_argf(true, false, false, false, false) + end + elsif Blahaj::CLI["individual"] + handle_argf(false, false, false, true, false) + elsif Blahaj::CLI["words"] + handle_argf(false, false, false, false, true) + else + handle_argf(false, false, false, false, false) + end + {% end %} +end diff --git a/src/blahaj/color.cr b/src/blahaj/color.cr new file mode 100644 index 0000000..6fc80d7 --- /dev/null +++ b/src/blahaj/color.cr @@ -0,0 +1,102 @@ +require "colorize" + +# Removes '#' prefix (if any) and +# transforms 3 character hex colors +# to 6 character ones. +def clean_hex(hex : String) : String + clean_hex = hex.lstrip('#') + + if clean_hex.size == 3 + tmp_hex = "" + clean_hex.each_char do |char| + tmp_hex += "#{char}#{char}" + end + clean_hex = tmp_hex + end + + clean_hex.upcase +end + +# Converts rgb to hex. +def rgb2hex(r : UInt8, g : UInt8, b : UInt8) : String + "#{r.to_s(16).rjust(2, '0')}#{g.to_s(16).rjust(2, '0')}#{b.to_s(16).rjust(2, '0')}" +end + +# Converts hex to rgb. +def hex2rgb(hex : String) : NamedTuple(r: UInt8, g: UInt8, b: UInt8) + clean_hex = clean_hex(hex) + + chars = clean_hex.chars.each + { + r: "#{chars.next}#{chars.next}".to_u8(16), + g: "#{chars.next}#{chars.next}".to_u8(16), + b: "#{chars.next}#{chars.next}".to_u8(16), + } +end + +module Blahaj + class Color + # Initialize `Blahaj::Color` using UInt8 RGB values + def initialize(r : UInt8, g : UInt8, b : UInt8) + @r = r + @g = g + @b = b + @hex = "##{rgb2hex(@r, @g, @b)}" + @color = Colorize::ColorRGB.new(@r, @g, @b) + end + + # Initialize `Blahaj::Color` using Int32 RGB values + def initialize(r : Int32, g : Int32, b : Int32) + @r = r.to_u8 + @g = g.to_u8 + @b = b.to_u8 + @hex = "##{rgb2hex(@r, @g, @b)}" + @color = Colorize::ColorRGB.new(@r, @g, @b) + end + + # Initialize `Blahaj::Color` using hex string + def initialize(hex : String) + rgb = hex2rgb(hex) + @r = rgb[:r] + @g = rgb[:g] + @b = rgb[:b] + @hex = "##{clean_hex(hex)}" + @color = Colorize::ColorRGB.new(@r, @g, @b) + end + + # :nodoc: + def initialize(json : JSON::PullParser) + initialize(json.read_string) + end + + # Red value + def r : UInt8 + @r + end + + # Green value + def g : UInt8 + @g + end + + # Blue value + def b : UInt8 + @b + end + + # Hex string + def hex : String + @hex.upcase + end + + # Color as `Colorize::ColorRGB` + def color : Colorize::ColorRGB + @color + end + + # Returns the foreground color based on whether the color is dark or light + def foreground : Colorize::ColorRGB + ((0.299 * @r + 0.587 * @g + 0.114 * @b)/255) > 0.5 ? Colorize::ColorRGB.new(0, 0, 0) : Colorize::ColorRGB.new(255, 255, 255) + end + end +end diff --git a/src/blahaj/option_parser.cr b/src/blahaj/option_parser.cr new file mode 100644 index 0000000..dbf1b5e --- /dev/null +++ b/src/blahaj/option_parser.cr @@ -0,0 +1,76 @@ +# [MACRO] Skip on spec +{% skip_file if @top_level.has_constant? "Spec" %} + +require "option_parser" + +module Blahaj + Version = {{read_file("#{__DIR__}/../../shard.yml").split("version: ")[1].split("\n")[0]}} # [MACRO] Get blahaj version + + # CLI options / defaults + CLI = { + "background" => false, + "color" => "trans", + "flag" => false, + "shark" => false, + "individual" => false, + "multiplier" => 1, + "words" => false, + } + + OptionParser.parse do |parser| + parser.banner = <<-BANNER + #{"BLÅHAJ".colorize(:blue).bold} v#{Version} + + #{"Usage:".colorize(:light_blue)} + blahaj [arguments] + blahaj [arguments] file + command | blahaj [arguments] + + #{"Examples:".colorize(:light_blue)} + blahaj -f trans ~/.bashrc + blahaj -s -b + neofetch | blahaj -f gay + blahaj -f lesbian -m 4 + blahaj -- -w /etc/os-release + + #{"Arguments:".colorize(:light_blue)} + BANNER + + parser.on("-b", "--background", "Color the background") { CLI["background"] = true } + parser.on("-f", "--flag", "Return a flag") { CLI["flag"] = true } + parser.on("-s", "--shark", "Shork") { CLI["shark"] = true } + parser.on("-i", "--individual", "Color individual characters") { CLI["individual"] = true } + parser.on("-w", "--words", "Color individual words") { CLI["words"] = true } + parser.on("-m MULTIPLIER", "--multiplier=MULTIPLIER", "Multiplier for the flag size (-f)") do |multiplier| + int = multiplier.to_i? + CLI["multiplier"] = int unless int.nil? || int <= 0 + end + parser.on("-c FLAG", "--colors=FLAG", "Color scheme to use (Default: trans)") do |flag| + down_flag = flag.downcase + unless COLORS.has_key?(down_flag) + puts "Unknown flag/color \"#{down_flag}\".\nPlease pass \"--flags\" for a list of all available flags/colors.".colorize(:red) + exit(1) + end + down_flag = COLORS[down_flag].as(String).downcase if COLORS[down_flag].is_a?(String) + CLI["color"] = down_flag + end + parser.on("--flags", "List all available flags") do + puts "Available flags/colors:\n".colorize(:light_blue) + # Skip symlink flags (eg. transgender => trans) + puts COLORS.reject { |_, v| v.is_a?(String) }.keys.sort.map { |x| + colors = COLORS[x].as(Array(Blahaj::Color)).map { |y| " ".colorize.back(y.color) }.join + "#{x.capitalize} #{colors}" + }.join("\n") + exit + end + parser.on("-h", "--help", "Show this help") do + puts parser + exit + end + parser.invalid_option do |flag| + STDERR.puts "ERROR: #{flag} is not a valid option." + STDERR.puts parser + exit(1) + end + end +end