├── .github ├── FUNDING.yml └── workflows │ ├── check.yml │ └── release.yml ├── Cross.toml ├── tools ├── print_all.sh ├── renderer.typ ├── render_all.sh └── all_logos.txt ├── .gitignore ├── pfetch-logo-parser ├── Cargo.toml ├── LICENSE └── src │ └── lib.rs ├── pfetch-extractor ├── Cargo.toml ├── src │ └── lib.rs └── LICENSE ├── custom_logos_example ├── Cargo.toml ├── LICENSE ├── cliff.toml ├── all_logos.md ├── CHANGELOG.md ├── README.md ├── assets └── logos │ ├── bedrock.svg │ ├── buildroot.svg │ ├── debian.svg │ ├── parabola.svg │ ├── ubuntu.svg │ ├── mageia.svg │ ├── irix.svg │ ├── xeonix.svg │ ├── solaris.svg │ ├── elementary.svg │ └── raspbian.svg └── src ├── main.rs └── lib.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: gobidev 2 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-linux-android] 2 | # Workaround https://github.com/cross-rs/cross/issues/1128 / https://github.com/rust-lang/rust/issues/103673 3 | image = "ghcr.io/cross-rs/aarch64-linux-android:edge" 4 | -------------------------------------------------------------------------------- /tools/print_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # A small script to run pfetch (command specified with arguments) with all available logos 4 | 5 | while read -r logo; do 6 | PF_ASCII=$logo "$@" 7 | done < ./all_logos.txt 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /tools/renderer.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/ansi-render:0.6.1": * 2 | 3 | #set page(width: auto, height: auto, margin: .5cm) 4 | 5 | #ansi-render( 6 | read("./tmp"), 7 | theme: terminal-themes.putty, 8 | bold-is-bright: true, 9 | font: "JetBrains Mono", 10 | inset: 8pt, 11 | radius: 5pt, 12 | ) 13 | -------------------------------------------------------------------------------- /pfetch-logo-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pfetch-logo-parser" 3 | version = "0.1.1" 4 | edition = "2021" 5 | authors = ["Gobidev"] 6 | license = "MIT" 7 | description = "A parser for pfetch logos" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [features] 12 | proc-macro = ["dep:proc-macro2", "dep:quote"] 13 | 14 | [dependencies] 15 | regex = "1.8.4" 16 | proc-macro2 = { version = "1.0.50", optional = true } 17 | quote = { version = "1.0.23", optional = true } 18 | -------------------------------------------------------------------------------- /pfetch-extractor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pfetch-extractor" 3 | version = "0.2.3" 4 | authors = ["Gobidev"] 5 | edition = "2021" 6 | keywords = ["pfetch"] 7 | license = "MIT" 8 | description = "A rust proc-macro to extract pfetch logos at compile time" 9 | 10 | [lib] 11 | proc-macro = true 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | pfetch-logo-parser = { version = "0.1.1", path = "../pfetch-logo-parser", features = ["proc-macro"] } 17 | proc-macro2 = "1.0.50" 18 | quote = "1.0.23" 19 | -------------------------------------------------------------------------------- /tools/render_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "# List Of All Logos" > ../all_logos.md 4 | echo "Generated using \`./tools/render_all.sh\`, powered by [typst](https://typst.app)" >> ../all_logos.md 5 | while read -r logo; do 6 | echo "$logo" 7 | PF_ASCII=$logo "$@" > ./tmp || exit 1 8 | mkdir -p ../assets/logos 9 | typst compile -f svg renderer.typ ../assets/logos/"$logo".svg || exit 1 10 | echo "## $logo" >> ../all_logos.md 11 | echo "" >> ../all_logos.md 12 | echo "" >> ../all_logos.md 13 | done < ./all_logos.txt 14 | rm tmp 15 | -------------------------------------------------------------------------------- /custom_logos_example: -------------------------------------------------------------------------------- 1 | [Aa]rch*) 2 | read_ascii 1 <<- EOF 3 | ${c1} /\\ 4 | ${c1} / \\ 5 | ${c1} /\\ \\ 6 | ${c1} / \\ 7 | ${c1} / ,, \\ 8 | ${c1} / | | -\\ 9 | ${c1} /_-'' ''-_\\ 10 | EOF 11 | ;; 12 | [Dd]ebian*) 13 | read_ascii 4 <<- EOF 14 | ${c4} _____ 15 | ${c4} / __ \\ 16 | ${c4}| / | 17 | ${c4}| \\___- 18 | ${c4}-_ 19 | ${c4} --_ 20 | EOF 21 | ;; 22 | 23 | [Ff]edora*) 24 | read_ascii 3 <<- EOF 25 | ${c3},'''''. 26 | ${c3}| ,. | 27 | ${c3}| | '_' 28 | ${c3} ,....| |.. 29 | ${c3}.' ,_;| ..' 30 | ${c3}| | | | 31 | ${c3}| ',_,' | 32 | ${c3} '. ,' 33 | ${c3}''''' 34 | EOF 35 | -------------------------------------------------------------------------------- /tools/all_logos.txt: -------------------------------------------------------------------------------- 1 | alma 2 | alpine 3 | android 4 | amogos 5 | arch 6 | arco 7 | artix 8 | bazzite 9 | bedrock 10 | buildroot 11 | cachyos 12 | celos 13 | centos 14 | crystallinux 15 | dahlia 16 | debian 17 | devuan 18 | dietpi 19 | dragonfly 20 | elementary 21 | endeavour 22 | fedora 23 | fiwix 24 | freebsd 25 | garuda 26 | gentoo 27 | gnu 28 | guix 29 | haiku 30 | hydroOS 31 | hyperbola 32 | iglunix 33 | instantos 34 | irix 35 | kdeneon 36 | linuxlite 37 | linuxmint 38 | linux 39 | macos 40 | mageia 41 | manjaro 42 | morphos 43 | minix 44 | mx 45 | netbsd 46 | nixos 47 | nobara 48 | openbsd 49 | opensusetumbleweed 50 | opensuse 51 | openwrt 52 | oracle 53 | parabola 54 | pop!_os 55 | pureos 56 | raspbian 57 | rocky 58 | serenityos 59 | slackware 60 | solus 61 | steamos 62 | solaris 63 | ubuntu 64 | vanilla 65 | void 66 | windows 67 | xeonix 68 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["pfetch-extractor", "pfetch-logo-parser"] 3 | 4 | [package] 5 | name = "pfetch" 6 | version = "2.11.1" 7 | edition = "2021" 8 | authors = ["Gobidev"] 9 | description = "A rewrite of the pfetch system information tool" 10 | repository = "https://github.com/Gobidev/pfetch-rs" 11 | license = "MIT" 12 | keywords = ["fetch", "pfetch", "cli", "system"] 13 | categories = ["command-line-utilities"] 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | pfetch-logo-parser = { path = "./pfetch-logo-parser", version = "0.1.1" } 19 | pfetch-extractor = { path = "./pfetch-extractor", version = "0.2.3" } 20 | globset = "0.4.10" 21 | dotenvy = "0.15.6" 22 | glob = "0.3.1" 23 | which = "7.0.1" 24 | libmacchina = "8.0.0" 25 | crossterm = "0.28.1" 26 | os-release = "0.1.0" 27 | unicode-width = "0.2.0" 28 | 29 | [profile.release] 30 | strip = true 31 | -------------------------------------------------------------------------------- /pfetch-extractor/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | 4 | #[proc_macro] 5 | pub fn parse_logos(_input: TokenStream) -> TokenStream { 6 | let raw_logos = include_str!("../logos.sh").replace("\r\n", "\n"); 7 | let raw_logos = raw_logos 8 | .split_once("in\n") 9 | .expect("Invalid logos.sh file") 10 | .1; 11 | let raw_logos = raw_logos 12 | .split_once("\nesac") 13 | .expect("Invalid logos.sh file") 14 | .0; 15 | 16 | let mut tux = None; 17 | let logos = raw_logos 18 | .split(";;\n") 19 | .filter_map(|raw_logo| { 20 | let (is_tux, logo) = pfetch_logo_parser::parse_logo(raw_logo)?; 21 | if is_tux { 22 | tux = Some(logo.clone()); 23 | } 24 | Some(logo) 25 | }) 26 | .collect::>(); 27 | 28 | let tux = tux.unwrap(); 29 | 30 | quote! { (#tux, [#(#logos),*]) }.into() 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-2024 Adrian Groh 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /pfetch-extractor/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Adrian Groh 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /pfetch-logo-parser/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Adrian Groh 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # configuration file for git-cliff 2 | # see https://github.com/orhun/git-cliff#configuration-file 3 | 4 | [changelog] 5 | # changelog header 6 | header = """ 7 | # Changelog\n 8 | """ 9 | # template for the changelog body 10 | # https://tera.netlify.app/docs/#introduction 11 | body = """ 12 | {% if version %}\ 13 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 14 | {% else %}\ 15 | ## [unreleased] 16 | {% endif %}\ 17 | {% for group, commits in commits | group_by(attribute="group") %} 18 | ### {{ group | upper_first }} 19 | {% for commit in commits %} 20 | - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ 21 | {% endfor %} 22 | {% endfor %}\n 23 | """ 24 | # remove the leading and trailing whitespace from the template 25 | trim = true 26 | # changelog footer 27 | footer = """ 28 | 29 | """ 30 | 31 | [git] 32 | # parse the commits based on https://www.conventionalcommits.org 33 | conventional_commits = true 34 | # filter out the commits that are not conventional 35 | filter_unconventional = true 36 | # process each line of a commit as an individual commit 37 | split_commits = false 38 | # regex for preprocessing the commit messages 39 | commit_preprocessors = [ 40 | # { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/orhun/git-cliff/issues/${2}))"}, # replace issue numbers 41 | ] 42 | # regex for parsing and grouping commits 43 | commit_parsers = [ 44 | { message = "^feat", group = "Features"}, 45 | { message = "^fix", group = "Bug Fixes"}, 46 | { message = "^doc", group = "Documentation"}, 47 | { message = "^perf", group = "Performance"}, 48 | { message = "^refactor", group = "Refactor"}, 49 | { message = "^style", group = "Styling"}, 50 | { message = "^test", group = "Testing"}, 51 | { message = "^chore\\(release\\): prepare for", skip = true}, 52 | { message = "^chore\\(logos\\): re-render all logos", skip = true}, 53 | { message = "^chore", group = "Miscellaneous"}, 54 | { body = ".*security", group = "Security"}, 55 | ] 56 | # protect breaking changes from being skipped due to matching a skipping commit_parser 57 | protect_breaking_commits = false 58 | # filter out the commits that are not matched by commit parsers 59 | filter_commits = false 60 | # glob pattern for matching git tags 61 | tag_pattern = "v[0-9]*" 62 | # regex for skipping tags 63 | skip_tags = "v0.1.0-beta.1" 64 | # regex for ignoring tags 65 | ignore_tags = "" 66 | # sort the tags topologically 67 | topo_order = false 68 | # sort the commits inside sections by oldest/newest order 69 | sort_commits = "oldest" 70 | # limit the number of commits included in the changelog. 71 | # limit_commits = 42 72 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | permissions: 3 | contents: write 4 | name: CI 5 | 6 | env: 7 | PF_INFO: "ascii" 8 | PF_PAD1: 0 9 | PF_PAD2: 0 10 | PF_PAD3: 0 11 | 12 | jobs: 13 | checks: 14 | name: ${{ matrix.name }} (${{ matrix.target }}) 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | target: 20 | - x86_64-unknown-linux-gnu 21 | - x86_64-unknown-freebsd 22 | - aarch64-linux-android 23 | - x86_64-pc-windows-gnu 24 | - x86_64-apple-darwin 25 | 26 | include: 27 | - os: ubuntu-latest 28 | name: Linux x86_64 29 | target: x86_64-unknown-linux-gnu 30 | cross: false 31 | strip: true 32 | 33 | - os: ubuntu-latest 34 | name: FreeBSD 35 | target: x86_64-unknown-freebsd 36 | cross: true 37 | strip: true 38 | 39 | - os: ubuntu-latest 40 | name: Android 41 | target: aarch64-linux-android 42 | cross: true 43 | strip: true 44 | 45 | - os: windows-latest 46 | name: Windows x86_64 47 | target: x86_64-pc-windows-gnu 48 | cross: false 49 | strip: true 50 | 51 | - os: macos-latest 52 | name: macOS x86_64 53 | target: x86_64-apple-darwin 54 | cross: false 55 | strip: true 56 | 57 | steps: 58 | - name: Checkout 59 | uses: actions/checkout@v4 60 | 61 | - name: Bootstrap 62 | uses: actions-rs/toolchain@v1 63 | with: 64 | toolchain: stable 65 | components: rustfmt, clippy 66 | target: ${{ matrix.target }} 67 | 68 | - name: Formatting 69 | uses: actions-rs/cargo@v1 70 | with: 71 | command: fmt 72 | use-cross: ${{ matrix.cross }} 73 | continue-on-error: false 74 | 75 | - name: Lints 76 | uses: actions-rs/cargo@v1 77 | with: 78 | command: clippy 79 | args: --target=${{ matrix.target }} -- --no-deps -D clippy::all 80 | use-cross: ${{ matrix.cross }} 81 | continue-on-error: false 82 | 83 | - name: Build 84 | uses: actions-rs/cargo@v1 85 | with: 86 | command: build 87 | args: --target=${{ matrix.target }} --target-dir=/tmp 88 | use-cross: ${{ matrix.cross }} 89 | 90 | - name: Upload Artifact 91 | uses: actions/upload-artifact@v4 92 | with: 93 | name: pfetch-${{ matrix.target }} 94 | path: /tmp/${{ matrix.target }}/debug/pfetch 95 | 96 | - name: Test 97 | uses: actions-rs/cargo@v1 98 | with: 99 | command: test 100 | args: --target=${{ matrix.target }} 101 | if: ${{ !matrix.cross }} 102 | render: 103 | needs: checks 104 | runs-on: ubuntu-latest 105 | steps: 106 | - name: Checkout 107 | uses: actions/checkout@v2 108 | 109 | - name: Install Jetbrains Mono 110 | run: sudo apt update && sudo apt install -y fonts-jetbrains-mono 111 | 112 | - name: Install typst 113 | uses: yusancky/setup-typst@v2 114 | with: 115 | version: 'v0.10.0' 116 | 117 | - name: Download Artifact 118 | uses: actions/download-artifact@v4 119 | with: 120 | name: pfetch-x86_64-unknown-linux-gnu 121 | path: /tmp/ 122 | 123 | - name: Run render script 124 | run: sudo chmod +x /tmp/pfetch && cd ./tools/ && ./render_all.sh /tmp/pfetch 125 | 126 | - name: Commit Files 127 | uses: EndBug/add-and-commit@v9 128 | with: 129 | add: 130 | assets all_logos.md 131 | default_author: github_actions 132 | message: "chore(logos): re-render all logos" 133 | -------------------------------------------------------------------------------- /all_logos.md: -------------------------------------------------------------------------------- 1 | # List Of All Logos 2 | Generated using `./tools/render_all.sh`, powered by [typst](https://typst.app) 3 | ## alma 4 | 5 | 6 | ## alpine 7 | 8 | 9 | ## android 10 | 11 | 12 | ## amogos 13 | 14 | 15 | ## arch 16 | 17 | 18 | ## arco 19 | 20 | 21 | ## artix 22 | 23 | 24 | ## bazzite 25 | 26 | 27 | ## bedrock 28 | 29 | 30 | ## buildroot 31 | 32 | 33 | ## cachyos 34 | 35 | 36 | ## celos 37 | 38 | 39 | ## centos 40 | 41 | 42 | ## crystallinux 43 | 44 | 45 | ## dahlia 46 | 47 | 48 | ## debian 49 | 50 | 51 | ## devuan 52 | 53 | 54 | ## dietpi 55 | 56 | 57 | ## dragonfly 58 | 59 | 60 | ## elementary 61 | 62 | 63 | ## endeavour 64 | 65 | 66 | ## fedora 67 | 68 | 69 | ## fiwix 70 | 71 | 72 | ## freebsd 73 | 74 | 75 | ## garuda 76 | 77 | 78 | ## gentoo 79 | 80 | 81 | ## gnu 82 | 83 | 84 | ## guix 85 | 86 | 87 | ## haiku 88 | 89 | 90 | ## hydroOS 91 | 92 | 93 | ## hyperbola 94 | 95 | 96 | ## iglunix 97 | 98 | 99 | ## instantos 100 | 101 | 102 | ## irix 103 | 104 | 105 | ## kdeneon 106 | 107 | 108 | ## linuxlite 109 | 110 | 111 | ## linuxmint 112 | 113 | 114 | ## linux 115 | 116 | 117 | ## macos 118 | 119 | 120 | ## mageia 121 | 122 | 123 | ## manjaro 124 | 125 | 126 | ## morphos 127 | 128 | 129 | ## minix 130 | 131 | 132 | ## mx 133 | 134 | 135 | ## netbsd 136 | 137 | 138 | ## nixos 139 | 140 | 141 | ## nobara 142 | 143 | 144 | ## openbsd 145 | 146 | 147 | ## opensusetumbleweed 148 | 149 | 150 | ## opensuse 151 | 152 | 153 | ## openwrt 154 | 155 | 156 | ## oracle 157 | 158 | 159 | ## parabola 160 | 161 | 162 | ## pop!_os 163 | 164 | 165 | ## pureos 166 | 167 | 168 | ## raspbian 169 | 170 | 171 | ## rocky 172 | 173 | 174 | ## serenityos 175 | 176 | 177 | ## slackware 178 | 179 | 180 | ## solus 181 | 182 | 183 | ## steamos 184 | 185 | 186 | ## solaris 187 | 188 | 189 | ## ubuntu 190 | 191 | 192 | ## vanilla 193 | 194 | 195 | ## void 196 | 197 | 198 | ## windows 199 | 200 | 201 | ## xeonix 202 | 203 | 204 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | 6 | name: Release 7 | 8 | jobs: 9 | changelog: 10 | permissions: 11 | contents: write 12 | name: Generate Changelog 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | - name: Generate changelog 20 | uses: orhun/git-cliff-action@v2 21 | id: git-cliff 22 | with: 23 | args: -vv --latest --strip header 24 | - name: Create release 25 | uses: softprops/action-gh-release@v1 26 | with: 27 | body: ${{ steps.git-cliff.outputs.content }} 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | 32 | publish: 33 | permissions: 34 | contents: write 35 | name: ${{ matrix.name }} (${{ matrix.target }}) 36 | runs-on: ${{ matrix.os }} 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | target: 41 | - x86_64-unknown-linux-gnu 42 | - x86_64-unknown-linux-musl 43 | - x86_64-apple-darwin 44 | - x86_64-unknown-freebsd 45 | - x86_64-pc-windows-gnu 46 | - aarch64-apple-darwin 47 | - aarch64-linux-android 48 | - aarch64-unknown-linux-musl 49 | - armv7-unknown-linux-gnueabihf 50 | 51 | include: 52 | - os: ubuntu-latest 53 | name: Linux GNU x86_64 54 | target: x86_64-unknown-linux-gnu 55 | artifact_name: target/x86_64-unknown-linux-gnu/release/pfetch 56 | release_name: pfetch-linux-gnu-x86_64.tar.gz 57 | cross: false 58 | strip: true 59 | 60 | - os: ubuntu-latest 61 | name: Linux musl x86_64 62 | target: x86_64-unknown-linux-musl 63 | artifact_name: target/x86_64-unknown-linux-musl/release/pfetch 64 | release_name: pfetch-linux-musl-x86_64.tar.gz 65 | cross: true 66 | strip: true 67 | 68 | - os: macos-latest 69 | name: macOS x86_64 70 | target: x86_64-apple-darwin 71 | artifact_name: target/x86_64-apple-darwin/release/pfetch 72 | release_name: pfetch-macos-x86_64.tar.gz 73 | cross: false 74 | strip: true 75 | 76 | - os: macos-latest 77 | name: macOS aarch64 78 | target: aarch64-apple-darwin 79 | artifact_name: target/aarch64-apple-darwin/release/pfetch 80 | release_name: pfetch-macos-aarch64.tar.gz 81 | cross: false 82 | strip: true 83 | 84 | - os: ubuntu-latest 85 | name: freeBSD x86_64 86 | target: x86_64-unknown-freebsd 87 | artifact_name: target/x86_64-unknown-freebsd/release/pfetch 88 | release_name: pfetch-freebsd-x86_64.tar.gz 89 | cross: true 90 | strip: true 91 | 92 | - os: ubuntu-latest 93 | name: Android 94 | target: aarch64-linux-android 95 | artifact_name: target/aarch64-linux-android/release/pfetch 96 | release_name: pfetch-android-aarch64.tar.gz 97 | cross: true 98 | strip: true 99 | 100 | - os: ubuntu-latest 101 | name: Linux musl aarch64 102 | target: aarch64-unknown-linux-musl 103 | artifact_name: target/aarch64-unknown-linux-musl/release/pfetch 104 | release_name: pfetch-linux-musl-aarch64.tar.gz 105 | cross: true 106 | strip: true 107 | 108 | - os: ubuntu-latest 109 | name: Linux ARMv7 110 | target: armv7-unknown-linux-gnueabihf 111 | artifact_name: target/armv7-unknown-linux-gnueabihf/release/pfetch 112 | release_name: pfetch-linux-gnueabihf-armv7.tar.gz 113 | cross: true 114 | strip: true 115 | 116 | - os: windows-latest 117 | name: Windows x86_64 118 | target: x86_64-pc-windows-gnu 119 | artifact_name: target/x86_64-pc-windows-gnu/release/pfetch.exe 120 | release_name: pfetch-windows-x86_64.exe 121 | cross: false 122 | strip: true 123 | steps: 124 | - name: Checkout 125 | uses: actions/checkout@v2 126 | 127 | - name: Bootstrap 128 | uses: actions-rs/toolchain@v1 129 | with: 130 | toolchain: stable 131 | target: ${{ matrix.target }} 132 | 133 | - name: Build 134 | uses: actions-rs/cargo@v1 135 | with: 136 | command: build 137 | args: --target=${{ matrix.target }} --release 138 | use-cross: ${{ matrix.cross }} 139 | 140 | - name: Compress binaries 141 | run: if [ "$RUNNER_OS" != "Windows" ]; then 142 | tar cfzv ${{ matrix.release_name }} -C "$(dirname ${{ matrix.artifact_name}})" "$(basename ${{ matrix.artifact_name }})"; 143 | else 144 | mv ${{ matrix.artifact_name }} ${{ matrix.release_name }}; 145 | fi 146 | shell: bash 147 | 148 | - name: Upload binaries 149 | uses: softprops/action-gh-release@v1 150 | with: 151 | files: ${{ matrix.release_name }} 152 | env: 153 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 154 | 155 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.11.1] - 2025-01-21 4 | 5 | ### Bug Fixes 6 | 7 | - Use unicode-width to determine logo width 8 | 9 | ### Miscellaneous 10 | 11 | - Update dependencies 12 | 13 | ## [2.11.0] - 2024-08-02 14 | 15 | ### Bug Fixes 16 | 17 | - Fix `PF_COLOR=0` stripping too much 18 | 19 | ### Features 20 | 21 | - Update EndeavourOS logo (#59) 22 | 23 | ### Miscellaneous 24 | 25 | - Update dependencies 26 | 27 | ## [2.10.0] - 2024-06-29 28 | 29 | ### Bug Fixes 30 | 31 | - Improve uptime calculation 32 | - Change primary text color for the GNU Hurd logo (#55) 33 | 34 | ### Features 35 | 36 | - Add CachyOS logo (#56) 37 | - Use faster method for nix package count (#57) 38 | 39 | ### Miscellaneous 40 | 41 | - Update the license year (#53) 42 | - Update dependencies 43 | 44 | ## [2.9.2] - 2024-06-03 45 | 46 | ### Features 47 | 48 | - Update GNU Hurd logo 49 | 50 | ### Miscellaneous 51 | 52 | - Speed up alpine package count (libmacchina 7.2.3) 53 | - Update dependencies 54 | 55 | ## [2.9.1] - 2024-03-11 56 | 57 | ### Bug Fixes 58 | 59 | - Ignore invalid info values instead of crashing 60 | 61 | ### Miscellaneous 62 | 63 | - Bump mio from 0.8.10 to 0.8.11 (#42) 64 | - Update dependencies 65 | 66 | ### Ci 67 | 68 | - Remove NetBSD build 69 | 70 | ## [2.9.0] - 2024-02-06 71 | 72 | ### Bug Fixes 73 | 74 | - Do not display "Unknown" in os name 75 | 76 | ### Features 77 | 78 | - Add Bazzite logo (#39) 79 | 80 | ### Miscellaneous 81 | 82 | - Fix clippy warnings 83 | - Update typst (#40) 84 | - Update dependencies 85 | 86 | ## [2.8.1] - 2023-10-13 87 | 88 | ### Bug Fixes 89 | 90 | - Fix macOS Sonoma "unknown" (libmacchina 7.2) 91 | 92 | ### Miscellaneous 93 | 94 | - Update dependencies 95 | 96 | ### Ci 97 | 98 | - Add workflow to render all logos 99 | 100 | ## [2.8.0] - 2023-07-07 101 | 102 | ### Bug Fixes 103 | 104 | - Use short forms for colors below 8 (#31) 105 | 106 | ### Documentation 107 | 108 | - Add nixpkgs installation instructions 109 | 110 | ### Features 111 | 112 | - Add support for custom logos at runtime 113 | 114 | ### Miscellaneous 115 | 116 | - Update dependencies 117 | 118 | ### Refactor 119 | 120 | - Outsource pfetch logo parser code to separate crate 121 | 122 | ## [2.7.0] - 2023-05-22 123 | 124 | ### Features 125 | 126 | - Add cpu name info option 127 | - Add Oracle Linux logo 128 | 129 | ### Miscellaneous 130 | 131 | - Update dependencies 132 | 133 | ### Refactor 134 | 135 | - Simplify code with `.ok()` 136 | 137 | ## [2.6.1] - 2023-05-16 138 | 139 | ### Miscellaneous 140 | 141 | - Fast RPM package count on openSUSE (libmacchina 7.1.0) 142 | - Update dependencies 143 | 144 | ## [2.6.0] - 2023-05-12 145 | 146 | ### Features 147 | 148 | - Add Rocky Linux logo 149 | - Add Alma Linux logo 150 | 151 | ### Miscellaneous 152 | 153 | - Update dependencies 154 | 155 | ## [2.5.0] - 2023-04-14 156 | 157 | ### Bug Fixes 158 | 159 | - Never show `TEMPLATE_VERSION_ID` in os name 160 | - Fix panic when file specified in `PF_SOURCE` does not exist 161 | 162 | ### Documentation 163 | 164 | - Rewrite notice on slow package counts in README 165 | 166 | ### Features 167 | 168 | - Add help menu and `-v` argument to show version (#24) 169 | - Add alternative `rpm` package count method (for OpenSUSE) 170 | 171 | ### Miscellaneous 172 | 173 | - Bump libmacchina to version 7.0.0 174 | - Update dependencies 175 | 176 | ## [2.4.0] - 2023-03-24 177 | 178 | ### Documentation 179 | 180 | - Update benchmark table content in README 181 | - Add homebrew installation instructions (#20) 182 | 183 | ### Features 184 | 185 | - Add SteamOS logo 186 | - Add Vanilla OS logo (#22) 187 | - Finish FreeBSD and Android support (libmacchina 6.4.0) 188 | 189 | ### Miscellaneous 190 | 191 | - Update dependencies 192 | 193 | ### Ci 194 | 195 | - Add more targets to the check workflow 196 | 197 | ## [2.3.0] - 2023-03-16 198 | 199 | ### Bug Fixes 200 | 201 | - Fix Bedrock Linux not being detected 202 | - Disable line wrap for pfetch output 203 | 204 | ### Features 205 | 206 | - Add option to set title color to info name color (#15) 207 | 208 | ### Miscellaneous 209 | 210 | - Update dependencies 211 | 212 | ## [2.2.0] - 2023-03-09 213 | 214 | ### Bug Fixes 215 | 216 | - Fix `invalid logos.sh file` error when building on Windows 217 | 218 | ### Features 219 | 220 | - Add Nobara Project logo 221 | 222 | ### Miscellaneous 223 | 224 | - Update dependencies 225 | 226 | ### Performance 227 | 228 | - Prefer libmacchina readouts over dotenvy 229 | 230 | ### Ci 231 | 232 | - Add macOS aarch64 release binary (#13) 233 | - Add Windows x86_64 release binary 234 | 235 | ## [2.1.0] - 2023-02-28 236 | 237 | ### Bug Fixes 238 | 239 | - Fix os and host detection on Windows 240 | 241 | ### Documentation 242 | 243 | - Add new logos to README.md 244 | 245 | ### Features 246 | 247 | - Add windows ascii logo 248 | - Use libmacchina package count for Windows and BSD 249 | - Add DietPi logo and support all 256 ANSI colors 250 | 251 | ### Miscellaneous 252 | 253 | - Sort logos alphabetically 254 | 255 | ## [2.0.0] - 2023-02-21 256 | 257 | ### Bug Fixes 258 | 259 | - [**breaking**] Fix OS and host detection on macOS 260 | 261 | ### Miscellaneous 262 | 263 | - Update dependencies 264 | 265 | ## [1.0.0] - 2023-02-19 266 | 267 | ### Documentation 268 | 269 | - Add binary installation option to README.md 270 | - Add `PF_FAST_PKG_COUNT` to available options in README.md 271 | 272 | ### Features 273 | 274 | - [**breaking**] Add option to skip count of nix packages (#2, #10) 275 | 276 | ### Miscellaneous 277 | 278 | - Use dotenvy instead of dotenv 279 | 280 | ## [0.1.1] - 2023-02-17 281 | 282 | ### Bug Fixes 283 | 284 | - Correct wrong target name 285 | - Fix ASCII detection on MacOS (#4) 286 | - Fix macos package count 287 | 288 | ### Miscellaneous 289 | 290 | - Add git-cliff configuration 291 | - Change linux x86 target to gnu 292 | 293 | ### Ci 294 | 295 | - Add github workflows for tests and releases 296 | 297 | 298 | -------------------------------------------------------------------------------- /pfetch-logo-parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | 3 | use std::{borrow::Cow, fmt::Display, str::FromStr}; 4 | 5 | #[cfg(feature = "proc-macro")] 6 | use proc_macro2::TokenStream; 7 | #[cfg(feature = "proc-macro")] 8 | use quote::{quote, ToTokens, TokenStreamExt}; 9 | 10 | #[derive(Clone, Copy, Debug)] 11 | pub struct Color(pub Option); 12 | 13 | #[cfg(feature = "proc-macro")] 14 | impl ToTokens for Color { 15 | fn to_tokens(&self, tokens: &mut TokenStream) { 16 | let value = match &self.0 { 17 | Some(val) => quote! { Some(#val) }, 18 | None => quote! { None }, 19 | }; 20 | tokens.append_all(quote! { 21 | ::pfetch_logo_parser::Color(#value) 22 | }); 23 | } 24 | } 25 | 26 | impl Display for Color { 27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 28 | match self.0 { 29 | Some(color @ 0..=7) => write!(f, "\x1b[3{color}m"), 30 | Some(color) => write!(f, "\x1b[38;5;{color}m"), 31 | None => write!(f, "\x1b[39m"), 32 | } 33 | } 34 | } 35 | 36 | impl FromStr for Color { 37 | type Err = String; 38 | 39 | fn from_str(s: &str) -> std::result::Result { 40 | Ok(Color(s.parse::().ok())) 41 | } 42 | } 43 | 44 | #[derive(Clone, Debug)] 45 | pub struct LogoPart { 46 | pub color: Color, 47 | pub content: Cow<'static, str>, 48 | } 49 | 50 | #[cfg(feature = "proc-macro")] 51 | impl ToTokens for LogoPart { 52 | fn to_tokens(&self, tokens: &mut TokenStream) { 53 | let color = &self.color; 54 | let content = &self.content; 55 | tokens.append_all(quote! { 56 | ::pfetch_logo_parser::LogoPart { 57 | color: #color, 58 | content: ::std::borrow::Cow::Borrowed(#content), 59 | } 60 | }); 61 | } 62 | } 63 | 64 | #[derive(Clone, Debug)] 65 | pub struct Logo { 66 | pub primary_color: Color, 67 | pub secondary_color: Color, 68 | pub pattern: Cow<'static, str>, 69 | pub logo_parts: Cow<'static, [LogoPart]>, 70 | } 71 | 72 | #[cfg(feature = "proc-macro")] 73 | impl ToTokens for Logo { 74 | fn to_tokens(&self, tokens: &mut TokenStream) { 75 | let primary_color = &self.primary_color; 76 | let secondary_color = &self.secondary_color; 77 | let pattern = &self.pattern; 78 | let logo_parts = &self.logo_parts; 79 | 80 | tokens.append_all(quote! { 81 | ::pfetch_logo_parser::Logo { 82 | primary_color: #primary_color, 83 | secondary_color: #secondary_color, 84 | pattern: ::std::borrow::Cow::Borrowed(#pattern), 85 | logo_parts: ::std::borrow::Cow::Borrowed(&[#(#logo_parts),*]), 86 | } 87 | }); 88 | } 89 | } 90 | 91 | impl Display for Logo { 92 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 93 | write!( 94 | f, 95 | "{}", 96 | self.logo_parts 97 | .iter() 98 | .fold("".to_string(), |a, LogoPart { color, content }| a 99 | + &if !f.alternate() { 100 | format!("{color}{content}") 101 | } else { 102 | format!("{content}") 103 | }) 104 | ) 105 | } 106 | } 107 | 108 | /// Parses a logo in pfetch formant and returns wether it is the linux (tux) logo and the logo itself 109 | pub fn parse_logo(input: &str) -> Option<(bool, Logo)> { 110 | let input = input.trim().replace('\t', ""); 111 | if input.is_empty() { 112 | return None; 113 | } 114 | let regex = Regex::new(r"^\(?(.*)\)[\s\S]*read_ascii *(\d)?").unwrap(); 115 | 116 | let groups = regex.captures(&input).expect("Error while parsing logo"); 117 | 118 | let pattern = &groups[1]; 119 | let primary_color = match groups.get(2) { 120 | Some(color) => color.as_str().parse::().unwrap(), 121 | None => 7, 122 | }; 123 | let secondary_color = (primary_color + 1) % 8; 124 | let logo = input 125 | .split_once("EOF\n") 126 | .expect("Could not find start of logo, make sure to include the `<<- EOF` and to use tabs for indentation") 127 | .1 128 | .split_once("\nEOF") 129 | .expect("Could not find end of logo, make sure to include the closing EOF and to use tabs for indentation") 130 | .0; 131 | 132 | let mut logo_parts = vec![]; 133 | for logo_part in logo.split("${") { 134 | if let Some((new_color, rest)) = logo_part.split_once('}') { 135 | let new_color: u8 = new_color 136 | .get(1..) 137 | .and_then(|num| num.parse().ok()) 138 | .unwrap_or_else(|| panic!("Invalid color: {new_color}")); 139 | let rest = rest.replace("\\\\", "\\"); 140 | let rest = rest.replace("\\`", "`"); 141 | let lines = rest.split('\n').collect::>(); 142 | let last_index = lines.len() - 1; 143 | for (index, line) in lines.into_iter().enumerate() { 144 | let mut line = line.to_owned(); 145 | if index != last_index { 146 | line += "\n"; 147 | } 148 | logo_parts.push(LogoPart { 149 | color: Color(Some(new_color)), 150 | content: line.into(), 151 | }); 152 | } 153 | } else if !logo_part.is_empty() { 154 | let logo_part = logo_part.replace("\\\\", "\\"); 155 | logo_parts.push(LogoPart { 156 | color: Color(None), 157 | content: logo_part.into(), 158 | }); 159 | } 160 | } 161 | 162 | Some(( 163 | pattern == "[Ll]inux*", 164 | Logo { 165 | primary_color: Color(Some(primary_color)), 166 | secondary_color: Color(Some(secondary_color)), 167 | pattern: pattern.to_owned().into(), 168 | logo_parts: logo_parts.into(), 169 | }, 170 | )) 171 | } 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

pfetch-rs

2 |

A rewrite of the pfetch system information tool by dylanaraps in Rust


3 |

4 | 5 | ## About 6 | 7 | If you are familiar with the [pfetch](https://github.com/dylanaraps/pfetch) 8 | system information tool by [dylanaraps](https://github.com/dylanaraps), this 9 | does the exact same thing, but with an about _10x faster_ runtime. _pfetch_ is 10 | simple by design with some (but not many) configuration options and a 11 | minimalistic look. 12 | 13 | **Supported Platforms:** Linux, Android, macOS, Windows, FreeBSD, NetBSD 14 | 15 | **Included Logos:** Alma Linux _(new)_, Alpine Linux, Android, AmogOS _(new)_, 16 | Arch Linux, ArcoLinux, Artix Linux, Bazzite _(new)_, Bedrock Linux, Buildroot, 17 | CachyOS _(new)_, CelOS, CentOS, Crystal Linux, dahliaOS, Debian, Devuan, DietPi 18 | _(new)_, DragonflyBSD, Elementary OS, EndeavourOS, Fedora, Fiwix _(new)_, 19 | FreeBSD, Garuda Linux, Gentoo Linux, Gnu Hurd _(updated)_, Guix, Haiku, HydroOS, 20 | Hyperbola, instantOS, IRIX, KDE neon, Linux Lite, Linux, Mint, macOS, Mageia, 21 | Manjaro, Minix, MorphOS _(new)_, MX Linux, NetBSD, NixOS, Nobara Project 22 | _(new)_, OpenBSD, openSUSE Tumbleweed, openSUSE Leap, OpenWrt, Oracle Linux 23 | _(new)_, Parabola, Pop!\_OS _(updated)_, PureOS, Raspbian, Rocky Linux _(new)_, 24 | SerenityOS, Slackware, Solus, SteamOS _(new)_, Solaris, Ubuntu, Vanilla OS 25 | _(new)_, Void Linux, Windows _(new)_, Xeonix Linux 26 | 27 | You can check out how they look [here](./all_logos.md). 28 | 29 | For all other distributions, a penguin will be displayed. 30 | 31 | _Credit to [the original pfetch](https://github.com/dylanaraps/pfetch) and 32 | [its contributors](https://github.com/dylanaraps/pfetch/graphs/contributors)._ 33 | 34 | If you want a logo to be added, feel free to open an issue or a PR. 35 | 36 | ## Installation 37 | 38 | _Note: On openSUSE, install the `rpm-devel` package for faster package count._ 39 | 40 | ### Binary 41 | 42 | Download a binary from the 43 | [latest release](https://github.com/Gobidev/pfetch-rs/releases/latest). 44 | 45 | ### Cargo 46 | 47 | ```sh 48 | cargo install pfetch 49 | ``` 50 | 51 | ### Homebrew 52 | 53 | ```sh 54 | brew install pfetch-rs 55 | ``` 56 | 57 | ### Nixpkgs 58 | 59 | Install the 60 | [pfetch-rs](https://search.nixos.org/packages?channel=unstable&show=pfetch-rs) 61 | Nix package. 62 | 63 | ### AUR 64 | 65 | Install the [pfetch-rs](https://aur.archlinux.org/packages/pfetch-rs) or 66 | [pfetch-rs-bin](https://aur.archlinux.org/packages/pfetch-rs-bin) AUR package. 67 | 68 | ## Performance 69 | 70 | Benchmarks performed on an AMD Ryzen 5 3600. Execution time is measured using 71 | [hyperfine](https://github.com/sharkdp/hyperfine) with `-w 4 -m 500 -N` flags. 72 | 73 | | Implementation | Mean [ms] | Min [ms] | Max [ms] | 74 | | :---------------: | :--------: | :------: | :------: | 75 | | POSIX `sh` (bash) | 23.7 ± 0.9 | 22.3 | 29.3 | 76 | | POSIX `sh` (dash) | 15.9 ± 0.3 | 15.1 | 18.2 | 77 | | Rust (v2.3.0) | 2.2 ± 0.2 | 1.8 | 3.9 | 78 | 79 | _Note: This is with `pacman` and `flatpak` being the only installed package 80 | managers. For more info, see [Improving Performance](#imp_perf)._ 81 | 82 | 83 | 84 | ### Improving Performance 85 | 86 | Counting packages of `zypper` can be sped up a lot by installing the `rpm-devel` 87 | package. If the `zypper` package count takes too long, it can be disabled by 88 | setting the `PF_FAST_PKG_COUNT` environment variable to any value. 89 | 90 | ## Configuration 91 | 92 | Like the original `pfetch`, `pfetch-rs` is configured through environment 93 | variables. Your existing config will probably still work, the main difference is 94 | how padding is configured. 95 | 96 | If you want to display a custom logo, use the `PF_CUSTOM_LOGOS` option, an 97 | example for a custom logos file can be found below. 98 | 99 | ```sh 100 | # Which information to display. 101 | # Default: first example below 102 | # Valid: space separated string 103 | # 104 | # OFF by default: shell editor wm de palette cpu 105 | PF_INFO="ascii title os host kernel uptime pkgs memory" 106 | 107 | # Example: Only ASCII. 108 | PF_INFO="ascii" 109 | 110 | # Example: Only Information. 111 | PF_INFO="title os host kernel uptime pkgs memory" 112 | 113 | # A file containing environment variables to source before running pfetch 114 | # Default: unset 115 | # Valid: A shell script 116 | PF_SOURCE="" 117 | 118 | # A file containing pfetch logos to overwrite default logos or add new logos 119 | # Default: unset 120 | # Valid: Path to a file containing pfetch logos (example below) 121 | PF_CUSTOM_LOGOS="~/.config/pfetch_logos" 122 | 123 | # Separator between info name and info data. 124 | # Default: unset 125 | # Valid: string 126 | PF_SEP=":" 127 | 128 | # Enable/Disable colors in output: 129 | # Default: 1 130 | # Valid: 1 (enabled), 0 (disabled) 131 | PF_COLOR=1 132 | 133 | # Color of info names: 134 | # Default: unset (auto) 135 | # Valid: 0-9 136 | PF_COL1=4 137 | 138 | # Color of info data: 139 | # Default: unset (auto) 140 | # Valid: 0-9 141 | PF_COL2=9 142 | 143 | # Color of title data: 144 | # Default: unset (auto) 145 | # Valid: 0-9, COL1 (copies COL1 value) 146 | PF_COL3=1 147 | 148 | # Alignment paddings (this is different to the original version). 149 | # Default: unset (auto) 150 | # Valid: int 151 | PF_PAD1="" 152 | PF_PAD2="" 153 | PF_PAD3="" 154 | 155 | # Which ascii art to use. 156 | # Default: unset (auto) 157 | # Valid: string 158 | PF_ASCII="openbsd" 159 | 160 | # The below environment variables control more 161 | # than just 'pfetch' and can be passed using 162 | # 'HOSTNAME=cool_pc pfetch' to restrict their 163 | # usage solely to 'pfetch'. 164 | 165 | # Which user to display. 166 | USER="" 167 | 168 | # Which hostname to display. 169 | HOSTNAME="" 170 | 171 | # Skip zypper package count if only slow method is available 172 | PF_FAST_PKG_COUNT=1 173 | ``` 174 | 175 | A file containing custom pfetch logos could look like this (also found under 176 | `custom_logos_example`). This will turn the Arch Linux logo red, the Debian Logo 177 | blue and the Fedora logo yellow: 178 | 179 | ``` 180 | [Aa]rch*) 181 | read_ascii 1 <<- EOF 182 | ${c1} /\\ 183 | ${c1} / \\ 184 | ${c1} /\\ \\ 185 | ${c1} / \\ 186 | ${c1} / ,, \\ 187 | ${c1} / | | -\\ 188 | ${c1} /_-'' ''-_\\ 189 | EOF 190 | ;; 191 | [Dd]ebian*) 192 | read_ascii 4 <<- EOF 193 | ${c4} _____ 194 | ${c4} / __ \\ 195 | ${c4}| / | 196 | ${c4}| \\___- 197 | ${c4}-_ 198 | ${c4} --_ 199 | EOF 200 | ;; 201 | [Ff]edora*) 202 | read_ascii 3 <<- EOF 203 | ${c3},'''''. 204 | ${c3}| ,. | 205 | ${c3}| | '_' 206 | ${c3} ,....| |.. 207 | ${c3}.' ,_;| ..' 208 | ${c3}| | | | 209 | ${c3}| ',_,' | 210 | ${c3} '. ,' 211 | ${c3}''''' 212 | EOF 213 | ``` 214 | 215 | _Note: Make sure to use tabs for indentation and separate logos with `;;`, as 216 | seen above. You only need to add the logos you want to overwrite/add, the 217 | default logos will stay available. The included logos can be found at 218 | `./pfetch-extractor/logos.sh`._ 219 | -------------------------------------------------------------------------------- /assets/logos/bedrock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use libmacchina::{ 2 | traits::GeneralReadout as _, traits::KernelReadout as _, traits::MemoryReadout as _, 3 | traits::PackageReadout as _, GeneralReadout, KernelReadout, MemoryReadout, PackageReadout, 4 | }; 5 | use pfetch_logo_parser::{Color, Logo, LogoPart}; 6 | use std::{env, fmt::Display, str::FromStr}; 7 | use unicode_width::UnicodeWidthStr; 8 | 9 | #[derive(Debug, PartialEq)] 10 | enum PfetchInfo { 11 | Ascii, 12 | Title, 13 | Os, 14 | Host, 15 | Kernel, 16 | Uptime, 17 | Pkgs, 18 | Cpu, 19 | Memory, 20 | Shell, 21 | Editor, 22 | Wm, 23 | De, 24 | Palette, 25 | BlankLine, 26 | } 27 | 28 | impl Display for PfetchInfo { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | write!(f, "{}", format!("{self:?}").to_lowercase()) 31 | } 32 | } 33 | 34 | impl FromStr for PfetchInfo { 35 | type Err = String; 36 | 37 | fn from_str(info: &str) -> Result { 38 | match info { 39 | "ascii" => Ok(PfetchInfo::Ascii), 40 | "title" => Ok(PfetchInfo::Title), 41 | "os" => Ok(PfetchInfo::Os), 42 | "host" => Ok(PfetchInfo::Host), 43 | "kernel" => Ok(PfetchInfo::Kernel), 44 | "uptime" => Ok(PfetchInfo::Uptime), 45 | "pkgs" => Ok(PfetchInfo::Pkgs), 46 | "cpu" => Ok(PfetchInfo::Cpu), 47 | "memory" => Ok(PfetchInfo::Memory), 48 | "shell" => Ok(PfetchInfo::Shell), 49 | "editor" => Ok(PfetchInfo::Editor), 50 | "wm" => Ok(PfetchInfo::Wm), 51 | "de" => Ok(PfetchInfo::De), 52 | "palette" => Ok(PfetchInfo::Palette), 53 | unknown_info => Err(format!("Unknown pfetch info: {unknown_info}")), 54 | } 55 | } 56 | } 57 | 58 | fn pfetch(info: Vec<(Color, String, String)>, logo: Logo, logo_enabled: bool) { 59 | let raw_logo = if logo_enabled { 60 | logo.logo_parts 61 | .iter() 62 | .map(|LogoPart { content, .. }| content.as_ref()) 63 | .collect::() 64 | } else { 65 | "".to_string() 66 | }; 67 | let color_enabled = dotenvy::var("PF_COLOR").unwrap_or_default() != "0"; 68 | let logo = if color_enabled { 69 | logo.to_string() 70 | } else { 71 | format!("{:#}", logo) 72 | }; 73 | let mut logo_lines = logo.lines(); 74 | let raw_logo_lines: Vec<_> = raw_logo.lines().collect(); 75 | let logo_width = raw_logo_lines 76 | .iter() 77 | .map(|line| line.width()) 78 | .max() 79 | .unwrap_or(0); 80 | let line_amount = usize::max(raw_logo_lines.len(), info.len()); 81 | 82 | let info1_width = info 83 | .iter() 84 | .skip(1) 85 | .map(|(_, line, _)| { 86 | if line.starts_with("\x1b[4") { 87 | // exclude palette from info1 width 88 | 0 89 | } else { 90 | line.len() 91 | } 92 | }) 93 | .max() 94 | .unwrap_or(0); 95 | 96 | let padding1 = match dotenvy::var("PF_PAD1") { 97 | Ok(padding0) => padding0.parse::().unwrap_or(0), 98 | Err(_) => 0, 99 | }; 100 | let padding2 = match dotenvy::var("PF_PAD2") { 101 | Ok(padding1) => padding1.parse::().unwrap_or(0), 102 | Err(_) => 3, 103 | }; 104 | let padding3 = match dotenvy::var("PF_PAD3") { 105 | Ok(padding2) => padding2.parse::().unwrap_or(0), 106 | Err(_) => 1, 107 | }; 108 | 109 | let mut pfetch_str = String::new(); 110 | 111 | for l in 0..line_amount { 112 | pfetch_str += &format!( 113 | "{padding1}{bold}{logo}{padding2}{color}{info1}{nobold}{separator}{padding3}{color2}{info2}\n", 114 | padding1 = " ".repeat(padding1), 115 | bold = if color_enabled {"\x1b[1m"} else {""}, 116 | logo = if logo_enabled { 117 | logo_lines.next().unwrap_or("") 118 | } else { 119 | "" 120 | }, 121 | padding2 = " ".repeat( 122 | logo_width - raw_logo_lines.get(l).map_or(0, |line| line.width()) 123 | + if logo_enabled { padding2 } else { 0 } 124 | ), 125 | color = if color_enabled {info.get(l).map_or("".to_owned(), |line| line.0.to_string())} else {"".to_string()}, 126 | info1 = info.get(l).map_or("", |line| &line.1), 127 | nobold = if color_enabled {"\x1b[0m"} else {""}, 128 | separator = info.get(l).map_or("".to_string(), |line| 129 | if ! &line.2.is_empty() { 130 | dotenvy::var("PF_SEP").unwrap_or_default() 131 | } else { "".to_string() } 132 | ), 133 | padding3 = " ".repeat( 134 | info1_width.saturating_sub(info.get(l).map_or(0, |(_, line, _)| line.len())) 135 | + padding3 136 | ), 137 | color2 = if color_enabled {match dotenvy::var("PF_COL2") { 138 | Ok(newcolor) => { 139 | match Color::from_str(&newcolor) { 140 | Ok(newcolor) => format!("{newcolor}"), 141 | Err(_) => "".to_string(), 142 | } 143 | }, 144 | Err(_) => "".to_string() 145 | }} else {"".to_string()}, 146 | info2 = info.get(l).map_or("", |line| &line.2) 147 | ) 148 | } 149 | 150 | // if colors are disabled, remove them from string 151 | // if dotenvy::var("PF_COLOR").unwrap_or_default() == "0" { 152 | // pfetch_str = pfetch_str 153 | // .split("\x1b[") 154 | // .map(|chunk| chunk.chars().skip(3).collect::()) 155 | // .collect(); 156 | // } 157 | 158 | // disable line wrap 159 | crossterm::execute!(std::io::stdout(), crossterm::terminal::DisableLineWrap) 160 | .unwrap_or_default(); 161 | 162 | println!("{pfetch_str}"); 163 | 164 | // enable line wrap 165 | crossterm::execute!(std::io::stdout(), crossterm::terminal::EnableLineWrap).unwrap_or_default(); 166 | } 167 | 168 | struct Readouts { 169 | general_readout: GeneralReadout, 170 | package_readout: PackageReadout, 171 | memory_readout: MemoryReadout, 172 | kernel_readout: KernelReadout, 173 | } 174 | 175 | fn get_info( 176 | info: &PfetchInfo, 177 | readouts: &Readouts, 178 | skip_slow_package_managers: bool, 179 | ) -> Option { 180 | match info { 181 | PfetchInfo::Ascii => None, 182 | PfetchInfo::Title => pfetch::user_at_hostname( 183 | &readouts.general_readout, 184 | &dotenvy::var("USER").ok(), 185 | &dotenvy::var("HOSTNAME").ok(), 186 | ), 187 | PfetchInfo::Os => pfetch::os(&readouts.general_readout), 188 | PfetchInfo::Host => pfetch::host(&readouts.general_readout), 189 | PfetchInfo::Kernel => pfetch::kernel(&readouts.kernel_readout), 190 | PfetchInfo::Uptime => pfetch::uptime(&readouts.general_readout), 191 | PfetchInfo::Pkgs => Some( 192 | pfetch::total_packages(&readouts.package_readout, skip_slow_package_managers) 193 | .to_string(), 194 | ), 195 | PfetchInfo::Cpu => pfetch::cpu(&readouts.general_readout), 196 | PfetchInfo::Memory => pfetch::memory(&readouts.memory_readout), 197 | PfetchInfo::Shell => pfetch::shell(&readouts.general_readout), 198 | PfetchInfo::Editor => pfetch::editor(), 199 | PfetchInfo::Wm => pfetch::wm(&readouts.general_readout), 200 | PfetchInfo::De => pfetch::de(&readouts.general_readout), 201 | PfetchInfo::Palette => Some(pfetch::palette()), 202 | PfetchInfo::BlankLine => Some("".to_string()), 203 | } 204 | } 205 | 206 | fn main() { 207 | // parse arguements 208 | if std::env::args().any(|arg| arg.starts_with("-v") || arg.starts_with("--v")) { 209 | println!("pfetch-rs {}", env!("CARGO_PKG_VERSION")); 210 | std::process::exit(0); 211 | } else if std::env::args().len() > 1 { 212 | println!("pfetch show system information"); 213 | println!("pfetch -v show version"); 214 | std::process::exit(0); 215 | } 216 | 217 | // source file specified by env: PF_SOURCE 218 | if let Ok(filepath) = dotenvy::var("PF_SOURCE") { 219 | let _ = dotenvy::from_path(filepath); 220 | } 221 | // Check if SKIP_SLOW is enabled 222 | let skip_slow_package_managers = dotenvy::var("PF_FAST_PKG_COUNT").is_ok(); 223 | 224 | let enabled_pf_info_base: Vec = match dotenvy::var("PF_INFO") { 225 | Ok(pfetch_infos) => pfetch_infos 226 | .trim() 227 | .split(' ') 228 | .map(PfetchInfo::from_str) 229 | .filter_map(|i| i.ok()) 230 | .collect(), 231 | Err(_) => vec![ 232 | PfetchInfo::Ascii, 233 | PfetchInfo::Title, 234 | PfetchInfo::Os, 235 | PfetchInfo::Host, 236 | PfetchInfo::Kernel, 237 | PfetchInfo::Uptime, 238 | PfetchInfo::Pkgs, 239 | PfetchInfo::Memory, 240 | ], 241 | }; 242 | 243 | // insert blank lines before and after palettes 244 | let mut enabled_pf_info: Vec = vec![]; 245 | let mut ascii_enabled: bool = false; 246 | for info in enabled_pf_info_base { 247 | match info { 248 | PfetchInfo::Palette => { 249 | enabled_pf_info.push(PfetchInfo::BlankLine); 250 | enabled_pf_info.push(PfetchInfo::Palette); 251 | enabled_pf_info.push(PfetchInfo::BlankLine); 252 | } 253 | PfetchInfo::Ascii => { 254 | ascii_enabled = true; 255 | } 256 | i => enabled_pf_info.push(i), 257 | } 258 | } 259 | 260 | let readouts = Readouts { 261 | general_readout: GeneralReadout::new(), 262 | package_readout: PackageReadout::new(), 263 | memory_readout: MemoryReadout::new(), 264 | kernel_readout: KernelReadout::new(), 265 | }; 266 | 267 | let os = pfetch::os(&GeneralReadout::new()).unwrap_or_default(); 268 | 269 | let logo_override = env::var("PF_ASCII"); 270 | 271 | let logo_name = logo_override.unwrap_or(match env::consts::OS { 272 | "linux" => os.clone(), 273 | other => other.to_owned(), 274 | }); 275 | 276 | let mut logo = pfetch::logo(&logo_name); 277 | 278 | // color overrides 279 | if let Ok(newcolor) = dotenvy::var("PF_COL1") { 280 | if let Ok(newcolor) = Color::from_str(&newcolor) { 281 | logo.primary_color = newcolor; 282 | } 283 | } 284 | 285 | if let Ok(newcolor) = dotenvy::var("PF_COL3") { 286 | if newcolor == "COL1" { 287 | logo.secondary_color = logo.primary_color; 288 | } else if let Ok(newcolor) = Color::from_str(&newcolor) { 289 | logo.secondary_color = newcolor; 290 | } 291 | } 292 | 293 | let gathered_pfetch_info: Vec<(Color, String, String)> = enabled_pf_info 294 | .iter() 295 | .filter_map(|info| match info { 296 | PfetchInfo::Os => Some((logo.primary_color, info.to_string(), os.clone())), 297 | _ => get_info(info, &readouts, skip_slow_package_managers).map(|info_str| match info { 298 | PfetchInfo::Title => (logo.secondary_color, info_str, "".to_string()), 299 | PfetchInfo::BlankLine => (logo.primary_color, "".to_string(), "".to_string()), 300 | PfetchInfo::Palette => (logo.primary_color, info_str, "".to_string()), 301 | _ => (logo.primary_color, info.to_string(), info_str), 302 | }), 303 | }) 304 | .collect(); 305 | 306 | pfetch(gathered_pfetch_info, logo, ascii_enabled); 307 | } 308 | -------------------------------------------------------------------------------- /assets/logos/buildroot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /assets/logos/debian.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /assets/logos/parabola.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /assets/logos/ubuntu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /assets/logos/mageia.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /assets/logos/irix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /assets/logos/xeonix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /assets/logos/solaris.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::VecDeque, env, fs, io::Result, process::Command}; 2 | 3 | use glob::glob; 4 | use globset::Glob; 5 | use libmacchina::{ 6 | traits::GeneralReadout as _, traits::KernelReadout as _, traits::MemoryReadout as _, 7 | traits::PackageReadout as _, GeneralReadout, KernelReadout, MemoryReadout, PackageReadout, 8 | }; 9 | use pfetch_logo_parser::{parse_logo, Logo}; 10 | 11 | #[derive(Debug)] 12 | pub enum PackageManager { 13 | Pacman, 14 | Dpkg, 15 | Xbps, 16 | Apk, 17 | Rpm, 18 | Flatpak, 19 | Crux, 20 | Guix, 21 | Opkg, 22 | Kiss, 23 | Portage, 24 | Pkgtool, 25 | Nix, 26 | } 27 | 28 | /// Obtain the amount of installed packages on the system by checking all installed supported package 29 | /// managers and adding the amounts 30 | pub fn total_packages(package_readout: &PackageReadout, skip_slow: bool) -> usize { 31 | match env::consts::OS { 32 | "linux" => { 33 | let macchina_package_count: Vec<(String, usize)> = package_readout 34 | .count_pkgs() 35 | .iter() 36 | .map(|(macchina_manager, count)| (macchina_manager.to_string(), *count)) 37 | .collect(); 38 | [ 39 | PackageManager::Pacman, 40 | PackageManager::Dpkg, 41 | PackageManager::Xbps, 42 | PackageManager::Apk, 43 | PackageManager::Rpm, 44 | PackageManager::Flatpak, 45 | PackageManager::Crux, 46 | PackageManager::Guix, 47 | PackageManager::Opkg, 48 | PackageManager::Kiss, 49 | PackageManager::Portage, 50 | PackageManager::Pkgtool, 51 | PackageManager::Nix, 52 | ] 53 | .iter() 54 | .map(|mngr| packages(mngr, &macchina_package_count, skip_slow)) 55 | .sum() 56 | } 57 | _ => package_readout.count_pkgs().iter().map(|elem| elem.1).sum(), 58 | } 59 | } 60 | 61 | fn get_macchina_package_count( 62 | macchina_result: &[(String, usize)], 63 | package_manager_name: &str, 64 | ) -> Option { 65 | macchina_result 66 | .iter() 67 | .find(|entry| entry.0 == package_manager_name) 68 | .map(|entry| entry.1) 69 | } 70 | 71 | /// return the amount of packages installed with a given linux package manager 72 | /// Return `0` if the package manager is not installed 73 | fn packages( 74 | pkg_manager: &PackageManager, 75 | macchina_package_count: &[(String, usize)], 76 | skip_slow: bool, 77 | ) -> usize { 78 | match pkg_manager { 79 | // libmacchina has very fast implementations for most package managers, so we use them 80 | // where we can, otherwise we fall back to method used by dylans version of pfetch 81 | PackageManager::Pacman 82 | | PackageManager::Flatpak 83 | | PackageManager::Dpkg 84 | | PackageManager::Xbps 85 | | PackageManager::Apk 86 | | PackageManager::Portage 87 | | PackageManager::Nix 88 | | PackageManager::Opkg => get_macchina_package_count( 89 | macchina_package_count, 90 | &format!("{pkg_manager:?}").to_lowercase(), 91 | ) 92 | .unwrap_or(0), 93 | PackageManager::Rpm => match get_macchina_package_count( 94 | macchina_package_count, 95 | &format!("{pkg_manager:?}").to_lowercase(), 96 | ) { 97 | Some(count) => count, 98 | None => { 99 | if !skip_slow { 100 | run_and_count_lines("rpm", &["-qa"]) 101 | } else { 102 | 0 103 | } 104 | } 105 | }, 106 | PackageManager::Guix => run_and_count_lines("guix", &["package", "--list-installed"]), 107 | PackageManager::Crux => { 108 | if check_if_command_exists("crux") { 109 | run_and_count_lines("pkginfo", &["-i"]) 110 | } else { 111 | 0 112 | } 113 | } 114 | PackageManager::Kiss => { 115 | if check_if_command_exists("kiss") { 116 | match glob("/var/db/kiss/installed/*/") { 117 | Ok(files) => files.count(), 118 | Err(_) => 0, 119 | } 120 | } else { 121 | 0 122 | } 123 | } 124 | PackageManager::Pkgtool => { 125 | if check_if_command_exists("pkgtool") { 126 | match glob("/var/log/packages/*") { 127 | Ok(files) => files.count(), 128 | Err(_) => 0, 129 | } 130 | } else { 131 | 0 132 | } 133 | } 134 | } 135 | } 136 | 137 | pub fn user_at_hostname( 138 | general_readout: &GeneralReadout, 139 | username_override: &Option, 140 | hostname_override: &Option, 141 | ) -> Option { 142 | let username = match username_override { 143 | Some(username) => Ok(username.to_string()), 144 | None => general_readout.username(), 145 | }; 146 | let hostname = match hostname_override { 147 | Some(hostname) => Ok(hostname.to_string()), 148 | None => general_readout.hostname(), 149 | }; 150 | if username.is_err() || hostname.is_err() { 151 | None 152 | } else { 153 | Some(format!( 154 | "{}@{}", 155 | username.unwrap_or_default(), 156 | hostname.unwrap_or_default() 157 | )) 158 | } 159 | } 160 | 161 | pub fn memory(memory_readout: &MemoryReadout) -> Option { 162 | let total_memory = memory_readout.total(); 163 | let used_memory = memory_readout.used(); 164 | if total_memory.is_err() || used_memory.is_err() { 165 | None 166 | } else { 167 | Some(format!( 168 | "{}M / {}M", 169 | used_memory.unwrap() / 1024, 170 | total_memory.unwrap() / 1024 171 | )) 172 | } 173 | } 174 | 175 | pub fn cpu(general_readout: &GeneralReadout) -> Option { 176 | general_readout.cpu_model_name().ok() 177 | } 178 | 179 | pub fn os(general_readout: &GeneralReadout) -> Option { 180 | match env::consts::OS { 181 | "linux" => { 182 | // check for Bedrock Linux 183 | if dotenvy::var("PATH") 184 | .unwrap_or_default() 185 | .contains("/bedrock/cross/") 186 | { 187 | return Some("Bedrock Linux".to_string()); 188 | } 189 | let content = os_release::OsRelease::new().ok()?; 190 | let version = if !content.version.is_empty() { 191 | content.version 192 | } else { 193 | content.version_id 194 | }; 195 | // check for Bazzite 196 | if content.pretty_name.contains("Bazzite") { 197 | return Some(format!("Bazzite {version}")); 198 | } 199 | if !version.is_empty() { 200 | return Some(format!("{} {}", content.name, version)); 201 | } 202 | Some(content.name) 203 | } 204 | _ => Some(general_readout.os_name().ok()?.replace("Unknown", "")), 205 | } 206 | } 207 | 208 | pub fn kernel(kernel_readout: &KernelReadout) -> Option { 209 | kernel_readout.os_release().ok() 210 | } 211 | 212 | pub fn seconds_to_string(seconds: usize) -> String { 213 | let days = seconds / 86400; 214 | let hours = (seconds % 86400) / 3600; 215 | let minutes = (seconds % 3600) / 60; 216 | 217 | let mut result = String::with_capacity(10); 218 | 219 | if days > 0 { 220 | result.push_str(&format!("{}d", days)); 221 | } 222 | if hours > 0 { 223 | if !result.is_empty() { 224 | result.push(' '); 225 | } 226 | result.push_str(&format!("{}h", hours)); 227 | } 228 | if minutes > 0 || result.is_empty() { 229 | if !result.is_empty() { 230 | result.push(' '); 231 | } 232 | result.push_str(&format!("{}m", minutes)); 233 | } 234 | 235 | result 236 | } 237 | 238 | pub fn uptime(general_readout: &GeneralReadout) -> Option { 239 | Some(seconds_to_string(general_readout.uptime().ok()?)) 240 | } 241 | 242 | pub fn host(general_readout: &GeneralReadout) -> Option { 243 | match env::consts::OS { 244 | "linux" => { 245 | const BLACKLIST: &[&str] = &[ 246 | "To", 247 | "Be", 248 | "be", 249 | "Filled", 250 | "filled", 251 | "By", 252 | "by", 253 | "O.E.M.", 254 | "OEM", 255 | "Not", 256 | "Applicable", 257 | "Specified", 258 | "System", 259 | "Product", 260 | "Name", 261 | "Version", 262 | "Undefined", 263 | "Default", 264 | "string", 265 | "INVALID", 266 | "�", 267 | "os", 268 | "Type1ProductConfigId", 269 | "", 270 | ]; 271 | 272 | // get device from system files 273 | let product_name = 274 | fs::read_to_string("/sys/devices/virtual/dmi/id/product_name").unwrap_or_default(); 275 | let product_name = product_name.trim(); 276 | let product_version = fs::read_to_string("/sys/devices/virtual/dmi/id/product_version") 277 | .unwrap_or_default(); 278 | let product_version = product_version.trim(); 279 | let product_model = 280 | fs::read_to_string("/sys/firmware/devicetree/base/model").unwrap_or_default(); 281 | let product_model = product_model.trim(); 282 | 283 | let final_str = format!("{product_name} {product_version} {product_model}") 284 | .split(' ') 285 | .filter(|word| !BLACKLIST.contains(word)) 286 | .collect::>() 287 | .join(" "); 288 | 289 | // if string is empty, display system architecture instead 290 | let final_str = if final_str.is_empty() { 291 | run_system_command("uname", &["-m"]).unwrap_or("Unknown".to_owned()) 292 | } else { 293 | final_str 294 | }; 295 | if final_str.is_empty() { 296 | None 297 | } else { 298 | Some(final_str) 299 | } 300 | } 301 | // on non-linux systems, try general_readout.machine(), use cpu model name as fallback 302 | _ => general_readout 303 | .machine() 304 | .ok() 305 | .or_else(|| general_readout.cpu_model_name().ok()), 306 | } 307 | } 308 | 309 | fn parse_custom_logos(filename: &str) -> Vec> { 310 | let file_contents = fs::read_to_string(filename).expect("Could not open custom logo file"); 311 | file_contents 312 | .split(";;") 313 | .map(|raw_logo| parse_logo(raw_logo).map(|(_, logo)| logo)) 314 | .collect::>() 315 | } 316 | 317 | pub fn logo(logo_name: &str) -> Logo { 318 | let (tux, included_logos) = pfetch_extractor::parse_logos!(); 319 | let mut logos: VecDeque<_> = included_logos.into(); 320 | if let Ok(filename) = dotenvy::var("PF_CUSTOM_LOGOS") { 321 | // insert custom logos in front of incuded logos 322 | for custom_logo in parse_custom_logos(&filename).into_iter().flatten() { 323 | logos.insert(0, custom_logo.clone()); 324 | } 325 | }; 326 | logos 327 | .into_iter() 328 | .find(|logo| { 329 | logo.pattern.split('|').any(|glob| { 330 | Glob::new(glob.trim()) 331 | .expect("Invalid logo pattern") 332 | .compile_matcher() 333 | .is_match(logo_name) 334 | }) 335 | }) 336 | .unwrap_or(tux) 337 | } 338 | 339 | pub fn shell(general_readout: &GeneralReadout) -> Option { 340 | general_readout 341 | .shell( 342 | libmacchina::traits::ShellFormat::Relative, 343 | libmacchina::traits::ShellKind::Default, 344 | ) 345 | .ok() 346 | .or_else(|| dotenvy::var("SHELL").ok()) 347 | } 348 | 349 | pub fn editor() -> Option { 350 | env::var("VISUAL") 351 | .or_else(|_| env::var("EDITOR")) 352 | .ok() 353 | .map(|editor| editor.trim().to_owned()) 354 | } 355 | 356 | pub fn wm(general_readout: &GeneralReadout) -> Option { 357 | general_readout.window_manager().ok() 358 | } 359 | 360 | pub fn de(general_readout: &GeneralReadout) -> Option { 361 | general_readout 362 | .desktop_environment() 363 | .ok() 364 | .or_else(|| dotenvy::var("XDG_CURRENT_DESKTOP").ok()) 365 | } 366 | 367 | pub fn palette() -> String { 368 | (1..7).fold("".to_string(), |a, e| a + &format!("\x1b[4{e}m ")) + "\x1b[0m" 369 | } 370 | 371 | fn run_system_command(command: &str, args: &[&str]) -> Result { 372 | let mut output = 373 | String::from_utf8_lossy(&Command::new(command).args(args).output()?.stdout).into_owned(); 374 | output.truncate(output.trim_end().len()); 375 | Ok(output) 376 | } 377 | 378 | fn check_if_command_exists(command: &str) -> bool { 379 | which::which(command).is_ok() 380 | } 381 | 382 | fn _system_command_error(command: &str, args: &[&str]) -> Result { 383 | let mut output = 384 | String::from_utf8_lossy(&Command::new(command).args(args).output()?.stderr).into_owned(); 385 | output.truncate(output.trim_end().len()); 386 | Ok(output) 387 | } 388 | 389 | /// Return the amount of line the output of a system command produces 390 | /// Returns `0` if command fails 391 | fn run_and_count_lines(command: &str, args: &[&str]) -> usize { 392 | run_system_command(command, args) 393 | .unwrap_or_default() 394 | .lines() 395 | .count() 396 | } 397 | 398 | #[cfg(test)] 399 | mod tests { 400 | use super::*; 401 | 402 | #[test] 403 | fn test_seconds_to_string_0() { 404 | assert_eq!(seconds_to_string(0), "0m".to_string()); 405 | } 406 | 407 | #[test] 408 | fn test_seconds_to_string_60() { 409 | assert_eq!(seconds_to_string(60), "1m".to_string()); 410 | } 411 | 412 | #[test] 413 | fn test_seconds_to_string_3600() { 414 | assert_eq!(seconds_to_string(3600), "1h".to_string()); 415 | } 416 | 417 | #[test] 418 | fn test_seconds_to_string_3660() { 419 | assert_eq!(seconds_to_string(3660), "1h 1m".to_string()); 420 | } 421 | 422 | #[test] 423 | fn test_seconds_to_string_86400() { 424 | assert_eq!(seconds_to_string(86400), "1d".to_string()); 425 | } 426 | 427 | #[test] 428 | fn test_seconds_to_string_90000() { 429 | assert_eq!(seconds_to_string(90000), "1d 1h".to_string()); 430 | } 431 | 432 | #[test] 433 | fn test_seconds_to_string_86460() { 434 | assert_eq!(seconds_to_string(86460), "1d 1m".to_string()); 435 | } 436 | 437 | #[test] 438 | fn test_seconds_to_string_90060() { 439 | assert_eq!(seconds_to_string(90060), "1d 1h 1m".to_string()); 440 | } 441 | } 442 | 443 | -------------------------------------------------------------------------------- /assets/logos/elementary.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /assets/logos/raspbian.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | --------------------------------------------------------------------------------