├── .github
└── workflows
│ ├── docs.yml
│ └── release-pipeline.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── banner.png
├── docs
├── assets
│ ├── banner.png
│ └── logo.png
├── contributing.md
├── features
│ ├── bits.md
│ ├── cpu.md
│ ├── csv.md
│ ├── disk.md
│ ├── env.md
│ ├── file.md
│ ├── http.md
│ ├── ip.md
│ ├── json.md
│ ├── math.md
│ ├── mem.md
│ ├── network.md
│ ├── port.md
│ ├── process.md
│ ├── text.md
│ └── time.md
├── index.md
├── quickstart.md
└── support.md
├── install.ps1
├── install.sh
├── mkdocs.yml
├── src
├── bits_commands.rs
├── bits_utils.rs
├── cpu_commands.rs
├── csv_commands.rs
├── csv_utils.rs
├── disk_commands.rs
├── env_commands.rs
├── file_commands.rs
├── format_utils.rs
├── graph_utils.rs
├── http_commands.rs
├── input_utils.rs
├── ip_commands.rs
├── ip_utils.rs
├── json_commands.rs
├── lib.rs
├── main.rs
├── math_commands.rs
├── math_utils.rs
├── mem_commands.rs
├── network_commands.rs
├── port_commands.rs
├── process_commands.rs
├── text_commands.rs
└── time_commands.rs
├── tests
├── csv_sql_search_tests.rs
└── math_exp_tests.rs
└── vgsales.csv
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: deploy mkdocs
2 | on:
3 | push:
4 | branches:
5 | - main
6 | permissions:
7 | contents: write
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - name: Configure Git Credentials
14 | run: |
15 | git config user.name github-actions[bot]
16 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com
17 | - uses: actions/setup-python@v5
18 | with:
19 | python-version: 3.x
20 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
21 | - uses: actions/cache@v4
22 | with:
23 | key: mkdocs-material-${{ env.cache_id }}
24 | path: .cache
25 | restore-keys: |
26 | mkdocs-material-
27 | - run: pip install mkdocs-material
28 | - run: mkdocs gh-deploy --force
--------------------------------------------------------------------------------
/.github/workflows/release-pipeline.yml:
--------------------------------------------------------------------------------
1 | name: Release Pipeline
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: write
8 |
9 | jobs:
10 | # Build for Windows
11 | build_windows:
12 | runs-on: windows-latest # Use Windows runner
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v2
17 |
18 | - name: Set up Rust
19 | uses: actions-rs/toolchain@v1
20 | with:
21 | toolchain: stable
22 | override: true
23 | components: clippy
24 |
25 | - name: Build for Windows
26 | run: |
27 | cargo build --release
28 | mkdir -p output/win
29 | cp target/release/aid.exe output/win/aid-win.exe
30 |
31 | - name: Upload Windows artifact
32 | uses: actions/upload-artifact@v3
33 | with:
34 | name: windows-artifact
35 | path: output/win/aid-win.exe
36 |
37 | # Build for Linux x86_64
38 | build_linux:
39 | runs-on: ubuntu-latest
40 |
41 | steps:
42 | - name: Checkout code
43 | uses: actions/checkout@v2
44 |
45 | - name: Set up Rust
46 | uses: actions-rs/toolchain@v1
47 | with:
48 | toolchain: stable
49 | override: true
50 | components: clippy
51 |
52 | - name: Build for Linux
53 | run: |
54 | cargo build --release
55 | mkdir -p output/linux
56 | cp target/release/aid output/linux/aid-linux
57 |
58 | - name: Upload Linux artifact
59 | uses: actions/upload-artifact@v3
60 | with:
61 | name: linux-artifact
62 | path: output/linux/aid-linux
63 |
64 | # Build for macOS x86_64
65 | build_macos:
66 | runs-on: macos-latest
67 |
68 | steps:
69 | - name: Checkout code
70 | uses: actions/checkout@v2
71 |
72 | - name: Set up Rust
73 | uses: actions-rs/toolchain@v1
74 | with:
75 | toolchain: stable
76 | override: true
77 | components: clippy
78 |
79 | - name: Build for macOS
80 | run: |
81 | cargo build --release
82 | mkdir -p output/osx
83 | cp target/release/aid output/osx/aid-mac
84 |
85 | - name: Upload macOS artifact
86 | uses: actions/upload-artifact@v3
87 | with:
88 | name: macos-artifact
89 | path: output/osx/aid-mac
90 |
91 | # Build for macOS ARM (Apple Silicon)
92 | build_macos_arm:
93 | runs-on: macos-latest
94 |
95 | steps:
96 | - name: Checkout code
97 | uses: actions/checkout@v2
98 |
99 | - name: Set up Rust
100 | uses: actions-rs/toolchain@v1
101 | with:
102 | toolchain: stable
103 | override: true
104 | target: aarch64-apple-darwin # Add macOS ARM target
105 | components: clippy
106 |
107 | - name: Build for macOS ARM
108 | run: |
109 | cargo build --release --target aarch64-apple-darwin
110 | mkdir -p output/osx_arm
111 | cp target/aarch64-apple-darwin/release/aid output/osx_arm/aid-mac-arm
112 |
113 | - name: Upload macOS ARM artifact
114 | uses: actions/upload-artifact@v3
115 | with:
116 | name: macos-arm-artifact
117 | path: output/osx_arm/aid-mac-arm
118 |
119 | # Create a single GitHub release with all artifacts
120 | create_release:
121 | runs-on: ubuntu-latest
122 | needs: [build_windows, build_linux, build_macos, build_macos_arm]
123 |
124 | steps:
125 | - name: Checkout code
126 | uses: actions/checkout@v2
127 |
128 | # Download artifacts from previous jobs
129 | - name: Download Windows artifact
130 | uses: actions/download-artifact@v3
131 | with:
132 | name: windows-artifact
133 |
134 | - name: Download Linux artifact
135 | uses: actions/download-artifact@v3
136 | with:
137 | name: linux-artifact
138 |
139 | - name: Download macOS artifact
140 | uses: actions/download-artifact@v3
141 | with:
142 | name: macos-artifact
143 |
144 | - name: Download macOS ARM artifact
145 | uses: actions/download-artifact@v3
146 | with:
147 | name: macos-arm-artifact
148 |
149 | - name: Generate release tag
150 | id: tag
151 | run: |
152 | VERSION=0.1.11
153 | echo "::set-output name=release_tag::aid-${VERSION}"
154 |
155 | - name: Create GitHub release
156 | uses: softprops/action-gh-release@v1
157 | env:
158 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
159 | with:
160 | tag_name: ${{ steps.tag.outputs.release_tag }}
161 | files: |
162 | aid-win.exe
163 | aid-linux
164 | aid-mac
165 | aid-mac-arm
166 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 | req.json
23 | test.csv
24 | data.csv
25 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aid"
3 | version = "0.1.11"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | clap = { version = "4.5.20", features = ["derive"] }
8 | comfy-table = "7.1.1"
9 | num-traits = "0.2.19"
10 | ping = "0.5.2"
11 | rand = "0.8.5"
12 | reqwest = "0.12.8"
13 | serde = "1.0.210"
14 | serde_derive = "1.0.210"
15 | serde_json = "1.0.129"
16 | socket2 = "0.5.7"
17 | sysinfo = "0.32.0"
18 | tokio = { version = "1", features = ["full"] }
19 | csv = "1.3.0"
20 | nom = "7.1.3"
21 | regex = "1.11.0"
22 | base64 = "0.22.1"
23 | md-5 = "0.10.5"
24 | sha2 = "0.10.6"
25 | sha1 = "0.10.5"
26 | time = { version = "0.3", features = ["macros", "formatting", "local-offset"] }
27 | walkdir = "2.5.0"
28 | zip = "2.2.0"
29 | cron-descriptor = "=0.1.0"
30 | chrono = "0.4.38"
31 | uuid = { version = "1.3", features = ["v4"] }
32 | percent-encoding = "2.3.1"
33 |
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | A CLI toolkit featuring a variety of helpful utilities.
7 |
8 | ```
9 | aid math plot --start -20 --end 20 --step 0.5 --exp "x * sin(1 - x)"
10 | | *
11 | * | **
12 | *** | **
13 | * * | ***
14 | * * | ** * **
15 | * * ** | *** * *
16 | * * *** | * * * *
17 | ** * ** * | *** * * * *
18 | * * * * *** | * * * * * *
19 | * * * ** * ** | ** * * ** * *
20 | --*---*---*---*--**--******-**---*---*---*--*---*-
21 | * * ** * * | *** * * * * **
22 | * * * **** | * * * *
23 | * * * ** | * * * *
24 | ** * * | *** ** *
25 | * *** | ***
26 | * ** | **
27 | * | **
28 | ** |
29 | ** |
30 | ```
31 | ## Read the [docs](https://timmoth.github.io/aid-cli/)
32 | for all supported commands, parameters, and examples
33 |
34 | ## Installation
35 |
36 | Manual installation is simple, just download the release and add it to your PATH environment variable, if you'd like an even easier way i've added scripts to do it for you.
37 |
38 | ### Linux / Mac (apple silicon supported)
39 | ```
40 | wget https://raw.githubusercontent.com/Timmoth/aid-cli/refs/heads/main/install.sh
41 | chmod +x install.sh
42 | sudo ./install.sh
43 | ```
44 | ### Windows (powershell)
45 | ```
46 | Invoke-WebRequest -Uri https://raw.githubusercontent.com/Timmoth/aid-cli/refs/heads/main/install.ps1 -OutFile install.ps1
47 | Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
48 | .\install.ps1
49 | ```
50 |
51 | ### Releases
52 | [Download the latest release](https://github.com/Timmoth/aid-cli/releases)
53 |
54 | ## Top level commands:
55 | ```
56 | | command | description |
57 | | ----------- | --------------------------------------------------------- |
58 | | aid http | HTTP functions |
59 | | aid ip | IP information / scanning |
60 | | aid port | Port information / scanning |
61 | | aid cpu | System cpu information |
62 | | aid mem | System memory information |
63 | | aid disk | System disk information |
64 | | aid network | System network information |
65 | | aid json | JSON parsing / extraction functions |
66 | | aid csv | CSV search / transformation functions |
67 | | aid text | Text manipulation functions |
68 | | aid file | File info functions |
69 | | aid time | Time related functions |
70 | | aid bits | Bit manipulation functions |
71 | | aid math | Math functions |
72 | | aid process | Process monitoring functions |
73 | | aid env | Environment information |
74 | | aid help | Print this message or the help of the given subcommand(s) |
75 | ```
76 | ## All commands:
77 | [More info](https://timmoth.github.io/aid-cli/)
78 | ```
79 | | version | command | description |
80 | | ---------- | ---------------------- | ---------------------------------------------------------------------- |
81 | | [u] 0.1.3 | aid http req | Make a HTTP request |
82 | | [u] 0.1.3 | aid http serve | Start a dummy HTTP server |
83 | | [u] 0.1.3 | aid ip local | Show my local IP address |
84 | | [u] 0.1.3 | aid ip public | Show my public IP address |
85 | | [u] 0.1.3 | aid ip scan | Scan a specified IP address subnet for active ip addresses |
86 | | [u] 0.1.3 | aid ip status | Try to connect to the specified IP address |
87 | | [u] 0.1.3 | aid port status | Check if the specified port is 'open' or 'closed'. |
88 | | [u] 0.1.3 | aid port scan | Scan for open ports on a specified IP address |
89 | | [u] 0.1.3 | aid cpu info | Show CPU information |
90 | | [u] 0.1.6 | aid cpu usage | Monitor CPU usage |
91 | | [u] 0.1.6 | aid mem usage | Monitor memory usage |
92 | | [u] 0.1.3 | aid disk info | Show disk information |
93 | | [u] 0.1.3 | aid network info | Show network information |
94 | | [u] 0.1.3 | aid network usage | Display network usage |
95 | | [a] 0.1.7 | aid process usage | Display process usage |
96 | | [u] 0.1.3 | aid json extract | Extract a property from JSON data |
97 | | [u] 0.1.3 | aid json jwt-decode | Decode a JWT |
98 | | [u] 0.1.3 | aid csv search | Sql search over csv |
99 | | [u] 0.1.3 | aid text base64-encode | encodes a base64 string |
100 | | [u] 0.1.3 | aid text base64-decode | decodes a base64 string |
101 | | [a] 0.1.10 | aid text url-encode | url encodes a string |
102 | | [a] 0.1.10 | aid text url-decode | decodes a url encoded string |
103 | | [u] 0.1.3 | aid text lines | reads and prints lines from a text file |
104 | | [a] 0.1.10 | aid text guid | Generates a random GUID |
105 | | [a] 0.1.11 | aid text replace | Find and replace text using a regex pattern in the given input string. |
106 | | [a] 0.1.11 | aid text count | Count occurrences of a regex pattern in the given input string. |
107 | | [u] 0.1.3 | aid file info | prints file metadata |
108 | | [u] 0.1.3 | aid file md5 | calculates the files Md5 checksum |
109 | | [u] 0.1.3 | aid file sha1 | calculates the files Sha1 checksum |
110 | | [u] 0.1.3 | aid file sha256 | calculates the files Sha256 checksum |
111 | | [a] 0.1.4 | aid file zip | zips the files in the source directory |
112 | | [u] 0.1.9 | aid time unix | Display unix timestamp |
113 | | [u] 0.1.9 | aid time dt | Display the datetime |
114 | | [a] 0.1.9 | aid time chron | Describes a chron job |
115 | | [a] 0.1.9 | aid time count-down | Starts a countdown timer |
116 | | [a] 0.1.8 | aid bits eval | Bitwise expression evaluation / conversion / information |
117 | | [u] 0.1.10 | aid math eval | Evaluates a math expression |
118 | | [u] 0.1.10 | aid math plot | Plot a math expression |
119 | | [a] 0.1.10 | aid env vars | Filter / Display environment variables |
120 |
121 | [a] added in version x.x.x
122 | [u] updated in version x.x.x
123 | [p] patched in version x.x.x
124 | [d] deprecated in version x.x.x
125 | ```
126 |
127 | ### Build
128 | If you'd like to build the latest version from source:
129 | ```
130 | //Install rust https://www.rust-lang.org/tools/install
131 | git clone https://github.com/Timmoth/aid-cli
132 | cd aid-cli
133 | cargo build --release
134 | .\target\release\aid.exe
135 | ```
136 |
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Timmoth/aid-cli/dffe698894e09c95d134ca3db96ca46865d09870/banner.png
--------------------------------------------------------------------------------
/docs/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Timmoth/aid-cli/dffe698894e09c95d134ca3db96ca46865d09870/docs/assets/banner.png
--------------------------------------------------------------------------------
/docs/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Timmoth/aid-cli/dffe698894e09c95d134ca3db96ca46865d09870/docs/assets/logo.png
--------------------------------------------------------------------------------
/docs/contributing.md:
--------------------------------------------------------------------------------
1 | ## Contributing 🙏
2 |
3 | Contributions are welcome! Here’s how you can get involved:
4 |
5 | 1. **Fork the repository**: Click the "Fork" button at the top right of this page.
6 | 2. **Clone your fork**:
7 | ```bash
8 | git clone https://github.com/Timmoth/aid-cli.git
9 | ```
10 | 3. **Create a branch**: Make your changes in a new branch.
11 | ```bash
12 | git checkout -b my-feature-branch
13 | ```
14 | 4. **Commit your changes**:
15 | ```bash
16 | git commit -m 'Add some feature'
17 | ```
18 | 5. **Push to the branch**:
19 | ```bash
20 | git push origin my-feature-branch
21 | ```
22 | 6. **Open a pull request**: Describe your changes and submit your PR.
23 |
--------------------------------------------------------------------------------
/docs/features/bits.md:
--------------------------------------------------------------------------------
1 | ### aid bits eval
2 | ```
3 | aid bits eval Evaluates a bitwise expression, converts base, visualize binary / display info
4 | -i, --info Output the bitboard representation.
5 | -c, --chess Output the chess bitboard representation.
6 | -b, --bin Output the result in binary.
7 | --hex Output the result in hex.
8 |
9 | -----input-----
10 | aid bits eval 0b1001
11 | -----output-----
12 | 9
13 |
14 | -----input-----
15 | aid bits eval --hex 0b10101001
16 | -----output-----
17 | a9
18 |
19 | -----input-----
20 | aid bits eval --bin 0xa9
21 | -----output-----
22 | 10101001
23 |
24 | -----input-----
25 | aid bits eval --bin '0xa9 << 2 | (0b1010 & 7)'
26 | -----output-----
27 | 1010100110
28 |
29 | -----input-----
30 | aid bits eval --info '!((0b11110000 ^ 0b00001111) << 8)'
31 | -----output-----
32 | FF | 1 1 1 1 1 1 1 1
33 | FF | 1 1 1 1 1 1 1 1
34 | FF | 1 1 1 1 1 1 1 1
35 | FF | 1 1 1 1 1 1 1 1
36 | FF | 1 1 1 1 1 1 1 1
37 | FF | 1 1 1 1 1 1 1 1
38 | 00 | 0 0 0 0 0 0 0 0
39 | FF | 1 1 1 1 1 1 1 1
40 | -----------------------
41 | 7 6 5 4 3 2 1 0
42 | dec: 18446744073709486335
43 | hex: FFFFFFFFFFFF00FF
44 | bin: 1111111111111111111111111111111111111111111111110000000011111111
45 | lsb: 0
46 | msb: 63
47 | set bits: 56
48 |
49 | -----input-----
50 | aid bits eval --chess '!(0xFEFEFEFEFEFEFEFE << 8)'
51 | -----output-----
52 | 8 | 1 0 0 0 0 0 0 0
53 | 7 | 1 0 0 0 0 0 0 0
54 | 6 | 1 0 0 0 0 0 0 0
55 | 5 | 1 0 0 0 0 0 0 0
56 | 4 | 1 0 0 0 0 0 0 0
57 | 3 | 1 0 0 0 0 0 0 0
58 | 2 | 1 0 0 0 0 0 0 0
59 | 1 | 1 1 1 1 1 1 1 1
60 | -----------------------
61 | A B C D E F G H
62 | dec: 72340172838076927
63 | hex: 1010101010101FF
64 | bin: 100000001000000010000000100000001000000010000000111111111
65 | lsb: 0
66 | msb: 56
67 | set bits: 15
68 | ```
--------------------------------------------------------------------------------
/docs/features/cpu.md:
--------------------------------------------------------------------------------
1 | ### aid cpu info
2 | ```
3 | aid cpu info Show CPU information
4 | -j, --json Output CPU information in JSON format.
5 |
6 | -----input-----
7 | aid cpu info
8 |
9 | -----output-----
10 | AMD Ryzen 9 5900HX with Radeon Graphics
11 | ```
12 |
13 | ### aid cpu usage
14 | ```
15 | aid cpu usage Monitor CPU usage
16 | -w, --watch Continuously monitor CPU usage.
17 | -j, --json Output CPU usage in JSON format.
18 | -p, --plot Plot total % cpu usage.
19 |
20 | -----input-----
21 | aid cpu usage -w -p
22 |
23 | -----output-----
24 | total: 6.0%, cpus: 18.6%, 2.3%, 12.7%, 1.6%, 11.2%, 7.0%, 8.7%, 1.5%, 6.5%, 1.4%, 5.0%, 1.9%, 6.1%, 1.9%, 3.7%, 5.4%
25 | plot: total cpu %
26 | X range: [27, 56]
27 | Y range: [4.8092117, 23.36589]
28 | *
29 |
30 | *
31 | *
32 | *
33 |
34 | * *
35 | * *
36 | *
37 | * *
38 | *
39 | *** * *
40 | * * * *
41 | ** * *
42 | * * **** *
43 | ** * *****
44 | ***** ** * ** *
45 | * ** * *** * ** *
46 | **** ** ** * * ** ** ***
47 | ** *** **************
48 | ```
--------------------------------------------------------------------------------
/docs/features/csv.md:
--------------------------------------------------------------------------------
1 | ### aid csv search
2 | ```
3 | aid csv search Sql search over csv
4 | -s, --sql Sql query e.g SELECT 'first name',age FROM people.csv WHERE age >= 25 AND age < 30 ORDER BY 'age' ASC.
5 | -o, --output Output file path.
6 |
7 | -----input-----
8 | aid csv search -s "csv search -s "SELECT Platform,COUNT(Name) FROM vgsales.csv GROUP BY Platform ORDER BY COUNT(Name) DESC"
9 | -----output-----
10 | Platform,COUNT(Name)
11 | DS,2163
12 | PS2,2161
13 | PS3,1329
14 | Wii,1325
15 | X360,1265
16 | PSP,1213
17 | PS,1196
18 | PC,960
19 | XB,824
20 | GBA,822
21 | GC,556
22 | 3DS,509
23 | PSV,413
24 | PS4,336
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/features/disk.md:
--------------------------------------------------------------------------------
1 | ### aid disk info
2 | ```
3 | aid disk info Show disk information
4 | -j, --json Output disk information in JSON format.
5 |
6 | -----input-----
7 | aid disk info -j
8 | -----output-----
9 | [
10 | {
11 | "available_gb": 52.2,
12 | "total_gb": 936.2,
13 | "percent_free": 5.6,
14 | "file_system": "NTFS",
15 | "mount_point": "C:\\",
16 | "removable": false,
17 | "kind": "SSD"
18 | }
19 | ]
20 | ```
--------------------------------------------------------------------------------
/docs/features/env.md:
--------------------------------------------------------------------------------
1 | ### aid env vars
2 |
3 | ```
4 | aid env vars Filter / Display environment variables
5 | -k, --kfilter filter the results by a key regex.
6 | -v, --vfilter filter the results by a value regex.
7 |
8 | -----input-----
9 | aid env vars
10 | -----output-----
11 | CommonProgramFiles:
12 | C:\Program Files\Common Files
13 | CommonProgramFiles(x86):
14 | C:\Program Files (x86)\Common Files
15 | CommonProgramW6432:
16 | C:\Program Files\Common Files
17 | JAVA_HOME:
18 | C:\Program Files\Java\jdk-21\bin
19 | ProgramData:
20 | C:\ProgramData
21 | ProgramFiles:
22 | C:\Program Files
23 | ProgramFiles(x86):
24 | C:\Program Files (x86)
25 |
26 | -----input-----
27 | aid env vars -k program
28 | -----output-----
29 | CommonProgramFiles:
30 | C:\Program Files\Common Files
31 | CommonProgramFiles(x86):
32 | C:\Program Files (x86)\Common Files
33 | CommonProgramW6432:
34 | C:\Program Files\Common Files
35 | ProgramData:
36 | C:\ProgramData
37 | ProgramFiles:
38 | C:\Program Files
39 | ProgramFiles(x86):
40 | C:\Program Files (x86)
41 |
42 | -----input-----
43 | aid env vars -k program -v common
44 | -----output-----
45 | CommonProgramFiles:
46 | C:\Program Files\Common Files
47 | CommonProgramFiles(x86):
48 | C:\Program Files (x86)\Common Files
49 | CommonProgramW6432:
50 | C:\Program Files\Common Files
51 | ```
52 |
53 | ### aid time count-down
54 |
55 | ```
56 | aid time count-down Starts a countdown timer
57 |
58 | -----input-----
59 | aid time count-down 10:30
60 | -----output-----
61 | Time left: 10 minutes 30 seconds
62 | ```
63 |
64 | ### aid time unix
65 | ```
66 | aid time unix Display unix timestamp
67 | -d, --dt Use the specified datetime.
68 | -m, --milli Output the timestamp as unix milliseconds.
69 |
70 | -----input-----
71 | aid time unix
72 | -----output-----
73 | 1729546694
74 |
75 | -----input-----
76 | aid time unix -m
77 | -----output-----
78 | 1729715103358
79 |
80 | -----input-----
81 | aid time unix -d 'Fri, 14 Jul 2017 02:40:00 +0000'
82 | -----output-----
83 | 1500000000
84 | ```
85 |
86 | ### aid time dt
87 | ```
88 | aid time dt Display the datetime
89 | -l, --local Use the local datetime.
90 | -u, --unix Use the specified unix second timestamp.
91 | -r, --rfc Output the datetime in Rfc3339 format.
92 |
93 | -----input-----
94 | aid time dt
95 | -----output-----
96 | 2024-10-21 21:38:34
97 |
98 | -----input-----
99 | aid time dt -l
100 | -----output-----
101 | 2024-10-21 22:38:14
102 |
103 | -----input-----
104 | aid time dt -u 1729546694
105 | -----output-----
106 | 2024-10-21 21:38:14
107 |
108 | -----input-----
109 | aid time dt --rfc
110 | -----output-----
111 | 2024-10-23T20:38:00.086663200+00:00
112 | ```
--------------------------------------------------------------------------------
/docs/features/file.md:
--------------------------------------------------------------------------------
1 | ### aid file info
2 | ```
3 | aid file info prints file metadata
4 | -f, --file file path.
5 |
6 | -----input-----
7 | aid file info -f .\README.md
8 | -----output-----
9 | Type: File
10 | Size: 5582 bytes
11 | Permissions: Read-only: false
12 | Modified: 2024-10-21 20:52:25.0 +00:00:00
13 | Accessed: 2024-10-21 20:52:25.0 +00:00:00
14 | Created: 2024-10-18 20:45:22.0 +00:00:00
15 | ```
16 |
17 | ### aid file md5
18 | ```
19 | aid file md5 calculates the files Md5 checksum
20 | -f, --file file path.
21 |
22 | -----input-----
23 | aid file md5 -f .\README.md
24 | -----output-----
25 | 7d417daec5c2663c7a224297d1fc9d95
26 | ```
27 |
28 | ### aid file sha1
29 | ```
30 | aid file sha1 calculates the files Sha1 checksum
31 | -f, --file file path.
32 |
33 | -----input-----
34 | aid file sha1 -f .\README.md
35 | -----output-----
36 | aaaa9aa5119da904c84eca8b0a1db46947732737
37 | ```
38 |
39 | ### aid file sha256
40 | ```
41 | aid file sha256 calculates the files Sha256 checksum
42 | -f, --file file path.
43 |
44 | -----input-----
45 | aid file sha256 -f .\README.md
46 | -----output-----
47 | 06e033f7ca19a5ef5a1be805a41dec6bd5b36cfdd231d6d3959373d6d4fe7ae7
48 | ```
49 |
50 | ### aid file zip
51 | ```
52 | aid file zip zips the files in the source directory
53 | -d, --dir file path.
54 | -f, --file output zip file.
55 |
56 | -----input-----
57 | aid file zip -dir ./src ./src.zip
58 | -----output-----
59 | Successfully zipped directory './src'
60 | ```
61 |
--------------------------------------------------------------------------------
/docs/features/http.md:
--------------------------------------------------------------------------------
1 | ### aid http req
2 | ```
3 | aid http req Make a HTTP request
4 | -m, --method Specify the HTTP method (e.g., GET, POST).
5 | -u, --url Specify the URL for the HTTP request.
6 | -c, --config Path to a configuration file for the request. Specify: method, url, body, headers in json format.
7 | -o, --output If specified saves http response body to a file at the given path.
8 |
9 | -----input-----
10 | aid http req -m GET -u https://dog-api.kinduff.com/api/facts
11 | -----output-----
12 | {
13 | "facts": [
14 | "The U.S. has the highest dog population in the world. France has the second highest."
15 | ],
16 | "success": true
17 | }
18 | ```
19 |
20 | ### aid http serve
21 | ```
22 | aid http serve Start a HTTP server (GET: 0.0.0.0:80 -> 'Hello, World!')
23 | -p, --port Specify the port for the HTTP server (default is 80). [default: 80]
24 |
25 | -----input-----
26 | aid http serve
27 | -----output-----
28 | Server listening on http://0.0.0.0:80
29 | ```
--------------------------------------------------------------------------------
/docs/features/ip.md:
--------------------------------------------------------------------------------
1 | ### aid ip local
2 | ```
3 | aid ip local Show my local IP address
4 | -j, --json Output the local IP address in JSON format.
5 |
6 | -----input-----
7 | aid ip local
8 | -----output-----
9 | 192.168.0.10
10 | ```
11 |
12 | ### aid ip public
13 | ```
14 | aid ip public Show my public IP address
15 | -j, --json Output the local IP address in JSON format.
16 |
17 | -----input-----
18 | aid ip public
19 | -----output-----
20 | 1.2.3.4
21 | ```
22 | ### aid ip scan
23 | ```
24 | aid ip scan Scan a specified IP address subnet for active ip addresses
25 | -i, --ip The IP subnet to scan. If not provided, the local subnet will be used. [default: ]
26 | -j, --json Output scan results in JSON format.
27 |
28 | -----input-----
29 | aid ip scan
30 | -----output-----
31 | 1.2.3.0
32 | 1.2.3.2
33 | 1.2.3.4
34 | ```
35 | ### aid ip status
36 | ```
37 | aid ip status Try to connect to the specified IP address
38 | -i, --ip The IP address to check the status of.
39 | -j, --json Output status in JSON format.
40 |
41 | -----input-----
42 | aid ip status -i 1.2.3.2
43 | -----output-----
44 | online
45 | ```
--------------------------------------------------------------------------------
/docs/features/json.md:
--------------------------------------------------------------------------------
1 | ### aid json jwt-decode
2 | ```
3 | aid json jwt-decode Decode a JWT
4 |
5 | -----input-----
6 | aid json jwt-decode eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjM5OWZkM2E5MmI3YTJiNDZjMzQzMDNiOTViOGNhMmMyIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNzI5NTQ2MzUxfQ.BSvGKZUlSPDWtnVjeJG45eUz1JqYZbBYVKPp4EiV23gs8hE92LvnlxnfnZP-QNfb1JTFCPikQKmkAhp5QInTDg
7 | -----output-----
8 | {
9 | "header": {
10 | "alg": "ES256",
11 | "kid": "399fd3a92b7a2b46c34303b95b8ca2c2",
12 | "typ": "JWT"
13 | },
14 | "payload": {
15 | "iat": 1729546351,
16 | "name": "John Doe",
17 | "sub": "1234567890"
18 | }
19 | }
20 | ```
--------------------------------------------------------------------------------
/docs/features/math.md:
--------------------------------------------------------------------------------
1 | ### aid math eval
2 | ```
3 | aid math eval Evaluates a math expression
4 |
5 | -----input-----
6 | aid math eval "sin(pi / 2) * 8 ^ e"
7 |
8 | -----output-----
9 | 285.0054081607272
10 | ```
11 |
12 | ### aid math plot
13 | ```
14 | aid math plot Plots a math expression
15 | --start Start x coord.
16 | --end End x coord.
17 | -s, --step x step size.
18 |
19 | -----input-----
20 | aid math plot --start -20 --end 20 --step 0.5 "x * sin(1 - x)"
21 |
22 | -----output-----
23 | expression: x * sin(1 - x)
24 | X range: [-20, 20]
25 | Y range: [-19.43818, 18.049082]
26 | | *
27 | * | **
28 | *** | **
29 | * * | ***
30 | * * | ** * **
31 | * * ** | *** * *
32 | * * *** | * * * *
33 | ** * ** * | *** * * * *
34 | * * * * *** | * * * * * *
35 | * * * ** * ** | ** * * ** * *
36 | --*---*---*---*--**--******-**---*---*---*--*---*-
37 | * * ** * * | *** * * * * **
38 | * * * **** | * * * *
39 | * * * ** | * * * *
40 | ** * * | *** ** *
41 | * *** | ***
42 | * ** | **
43 | * | **
44 | ** |
45 | ** |
46 | ```
--------------------------------------------------------------------------------
/docs/features/mem.md:
--------------------------------------------------------------------------------
1 | ### aid mem usage
2 | ```
3 | aid mem usage Monitor memory usage
4 | -w, --watch Continuously monitor memory usage.
5 | -j, --json Output memory usage in JSON format.
6 | -p, --plot Plot total % mem usage.
7 | -----input-----
8 | aid mem usage -w -p
9 | -----output-----
10 | total: 15.4GB, used: 13.3GB (86.6%), free: 2.1GB (13.4%)
11 | plot: used memory %
12 | X range: [15, 44]
13 | Y range: [86.5, 87.1]
14 | * *
15 | * **
16 | *** *
17 | ** *
18 | * *
19 | *
20 | *
21 | ** ** *
22 | * *** **
23 | * * ** ***
24 | ***** ***** **** *
25 | * ** *
26 | ** * *
27 | ****** *******
28 | *
29 | *
30 | * ********
31 | * **
32 | * *
33 | **
34 | ```
--------------------------------------------------------------------------------
/docs/features/network.md:
--------------------------------------------------------------------------------
1 | ### aid network info
2 | ```
3 | aid network info Show network information
4 | -j, --json Output network information in JSON format.
5 |
6 | -----input-----
7 | aid network info -j
8 | -----output-----
9 | [
10 | {
11 | "name": "WiFi",
12 | "transmitted": 0.2,
13 | "received": 1.5,
14 | "mac": "00:00:00:00:00:00"
15 | }
16 | ]
17 | ```
18 |
19 | ### aid network usage
20 | ```
21 | aid network usage Display network usage
22 | -w, --watch Continuously monitor network usage.
23 |
24 | -----input-----
25 | aid network usage -w
26 | -----output-----
27 | WiFi: rx 1.05 kB/s, tx 0 B/s
28 | WiFi-QoS Packet Scheduler-0000: rx 1.05 kB/s, tx 0 B/s
29 | ```
--------------------------------------------------------------------------------
/docs/features/port.md:
--------------------------------------------------------------------------------
1 | ### aid port status
2 | ```
3 | aid port status Check if the specified port is 'open' or 'closed'.
4 | -i, --ip The IP address to check (optional).
5 | -p The port number to check the status of.
6 | -j, --json Output port status in JSON format.
7 |
8 | -----input-----
9 | aid port status -i 192.168.0.1 -p 80
10 | -----output-----
11 | open
12 | ```
13 | ### aid port scan
14 | ```
15 | aid port scan Scan for open ports on a specified IP address
16 | -i, --ip The IP address to scan (optional).
17 | -j, --json Output scan results in JSON format.
18 |
19 | -----input-----
20 | aid port scan -i 192.168.0.1
21 | -----output-----
22 | 80
23 | 443
24 | 22
25 | ```
--------------------------------------------------------------------------------
/docs/features/process.md:
--------------------------------------------------------------------------------
1 | ### aid process usage
2 | ```
3 | aid process usage Display process information.
4 | -f, --filter filter the results by process name regex.
5 | -s, --sort Sort the results by [cpu, mem, disk]
6 | -l, --limit Limit the number of results to display.
7 | -w, --watch Continuously monitor the processes.
8 |
9 | -----input-----
10 | aid process usage -w -s mem -l 10 -f host
11 | -----output-----
12 | [pid] [cpu %] [mem] [disk read] [disk write] name
13 | [5640] 1.20 % 44 MB 0 B/s 0 B/s svchost.exe
14 | [1568] 0.00 % 42 MB 0 B/s 0 B/s svchost.exe
15 | [5632] 0.00 % 30 MB 0 B/s 0 B/s svchost.exe
16 | [9348] 0.00 % 20 MB 0 B/s 0 B/s svchost.exe
17 | [14736] 0.00 % 20 MB 0 B/s 0 B/s svchost.exe
18 | [9388] 0.00 % 19 MB 0 B/s 0 B/s svchost.exe
19 | [9244] 0.00 % 19 MB 0 B/s 0 B/s sihost.exe
20 | [8396] 0.00 % 19 MB 0 B/s 0 B/s svchost.exe
21 | [1736] 0.00 % 17 MB 0 B/s 0 B/s svchost.exe
22 | [37596] 0.00 % 16 MB 0 B/s 0 B/s taskhostw.exe
23 | ```
--------------------------------------------------------------------------------
/docs/features/text.md:
--------------------------------------------------------------------------------
1 | ### aid text base64-encode
2 | ```
3 | aid text base64-encode encodes a base64 string
4 |
5 | -----input-----
6 | aid text base64-encode "Hello, world!"
7 | -----output-----
8 | SGVsbG8sIHdvcmxkIQ==
9 | ```
10 | ### aid text base64-decode
11 | ```
12 | aid text base64-decode decodes a base64 string
13 |
14 | -----input-----
15 | aid text base64-decode SGVsbG8sIHdvcmxkIQ==
16 | -----output-----
17 | Hello, world!
18 | ```
19 | ### aid text guid
20 | ```
21 | aid text guid Generates a random GUID
22 |
23 | -----input-----
24 | aid text guid
25 | -----output-----
26 | af6b8756-32fb-4630-bfe4-264c9a476273
27 | ```
28 |
29 | ### aid text url-encode
30 | ```
31 | aid text url-encode url encodes a string
32 |
33 | -----input-----
34 | aid text url-encode hello, world!
35 | -----output-----
36 | hello%20world%21
37 | ```
38 |
39 | ### aid text url-decode
40 | ```
41 | aid text url-decode decodes a url encoded string
42 |
43 | -----input-----
44 | aid text url-decode hello%20world%21
45 | -----output-----
46 | hello world!
47 | ```
48 |
49 | ### aid text lines
50 | ```
51 | aid text lines reads and prints lines from a text file
52 | -i, --input Input text file to search.
53 | -s, --start first line to print
54 | -e, --end last line to print
55 | -f, --first number of lines from the start of the file to print
56 | -l, --last number of lines from the end of the file to print
57 |
58 | -----input-----
59 | aid text lines -i .\README.md -l 10
60 | -----output-----
61 | | aid file zip | zips the files in the source directory |
62 | | aid time unix | Display unix timestamp |
63 | | aid time dt | Display the datetime |
64 | | aid bits board | Display the number in bitboard representation |
65 | | aid bits to-bin | Converts a number to it's binary representation |
66 | | aid bits to-dec | Converts a number to it's decimal representation |
67 | | aid bits to-hex | Converts a number to it's hex representation |
68 | | aid math eval | Evaluates a math expression |
69 | | aid math plot | Plot a math expression |
70 | ```
71 |
72 | ### aid text replace
73 | ```
74 | aid text replace Find and replace text using a regex pattern in the given input string.
75 | -m, --match Regex pattern to match substrings to replace.
76 | -r, --replace Replacement string for matched substrings.
77 |
78 | -----input-----
79 | aid text replace -m dog -r rock "The quick brown fox jumps over the lazy dog."
80 | -----output-----
81 | The quick brown fox jumps over the lazy rock.
82 | ```
83 |
84 | ### aid text count
85 | ```
86 | aid text count Count occurrences of a regex pattern in the given input string.
87 | -m, --match Regex pattern to match and count in the input.
88 |
89 | -----input-----
90 | aid text count -m in "The rain in Spain stays mainly in the plain."
91 | -----output-----
92 | 6
93 | ```
--------------------------------------------------------------------------------
/docs/features/time.md:
--------------------------------------------------------------------------------
1 | ### aid time chron
2 |
3 | ```
4 | aid time chron Describes a chron job
5 |
6 | -----input-----
7 | aid time chron 0 30 3 ? * 2#3 *
8 | -----output-----
9 | At 3:30 AM, on the third Tuesday of the month
10 | ```
11 |
12 | ### aid time count-down
13 |
14 | ```
15 | aid time count-down Starts a countdown timer
16 |
17 | -----input-----
18 | aid time count-down 10:30
19 | -----output-----
20 | Time left: 10 minutes 30 seconds
21 | ```
22 |
23 | ### aid time unix
24 | ```
25 | aid time unix Display unix timestamp
26 | -d, --dt Use the specified datetime.
27 | -m, --milli Output the timestamp as unix milliseconds.
28 |
29 | -----input-----
30 | aid time unix
31 | -----output-----
32 | 1729546694
33 |
34 | -----input-----
35 | aid time unix -m
36 | -----output-----
37 | 1729715103358
38 |
39 | -----input-----
40 | aid time unix -d 'Fri, 14 Jul 2017 02:40:00 +0000'
41 | -----output-----
42 | 1500000000
43 | ```
44 |
45 | ### aid time dt
46 | ```
47 | aid time dt Display the datetime
48 | -l, --local Use the local datetime.
49 | -u, --unix Use the specified unix second timestamp.
50 | -r, --rfc Output the datetime in Rfc3339 format.
51 |
52 | -----input-----
53 | aid time dt
54 | -----output-----
55 | 2024-10-21 21:38:34
56 |
57 | -----input-----
58 | aid time dt -l
59 | -----output-----
60 | 2024-10-21 22:38:14
61 |
62 | -----input-----
63 | aid time dt -u 1729546694
64 | -----output-----
65 | 2024-10-21 21:38:14
66 |
67 | -----input-----
68 | aid time dt --rfc
69 | -----output-----
70 | 2024-10-23T20:38:00.086663200+00:00
71 | ```
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## Top level commands:
4 | ```
5 | | command | description |
6 | | ----------- | --------------------------------------------------------- |
7 | | aid http | HTTP functions |
8 | | aid ip | IP information / scanning |
9 | | aid port | Port information / scanning |
10 | | aid cpu | System cpu information |
11 | | aid mem | System memory information |
12 | | aid disk | System disk information |
13 | | aid network | System network information |
14 | | aid json | JSON parsing / extraction functions |
15 | | aid csv | CSV search / transformation functions |
16 | | aid text | Text manipulation functions |
17 | | aid file | File info functions |
18 | | aid time | Time related functions |
19 | | aid bits | Bit manipulation functions |
20 | | aid math | Math functions |
21 | | aid process | Process monitoring functions |
22 | | aid env | Environment information |
23 | | aid help | Print this message or the help of the given subcommand(s) |
24 | ```
25 | ## All commands:
26 |
27 | ```
28 | | version | command | description |
29 | | ---------- | ---------------------- | ---------------------------------------------------------------------- |
30 | | [u] 0.1.3 | aid http req | Make a HTTP request |
31 | | [u] 0.1.3 | aid http serve | Start a dummy HTTP server |
32 | | [u] 0.1.3 | aid ip local | Show my local IP address |
33 | | [u] 0.1.3 | aid ip public | Show my public IP address |
34 | | [u] 0.1.3 | aid ip scan | Scan a specified IP address subnet for active ip addresses |
35 | | [u] 0.1.3 | aid ip status | Try to connect to the specified IP address |
36 | | [u] 0.1.3 | aid port status | Check if the specified port is 'open' or 'closed'. |
37 | | [u] 0.1.3 | aid port scan | Scan for open ports on a specified IP address |
38 | | [u] 0.1.3 | aid cpu info | Show CPU information |
39 | | [u] 0.1.6 | aid cpu usage | Monitor CPU usage |
40 | | [u] 0.1.6 | aid mem usage | Monitor memory usage |
41 | | [u] 0.1.3 | aid disk info | Show disk information |
42 | | [u] 0.1.3 | aid network info | Show network information |
43 | | [u] 0.1.3 | aid network usage | Display network usage |
44 | | [a] 0.1.7 | aid process usage | Display process usage |
45 | | [u] 0.1.3 | aid json extract | Extract a property from JSON data |
46 | | [u] 0.1.3 | aid json jwt-decode | Decode a JWT |
47 | | [u] 0.1.3 | aid csv search | Sql search over csv |
48 | | [u] 0.1.3 | aid text base64-encode | encodes a base64 string |
49 | | [u] 0.1.3 | aid text base64-decode | decodes a base64 string |
50 | | [a] 0.1.10 | aid text url-encode | url encodes a string |
51 | | [a] 0.1.10 | aid text url-decode | decodes a url encoded string |
52 | | [u] 0.1.3 | aid text lines | reads and prints lines from a text file |
53 | | [a] 0.1.10 | aid text guid | Generates a random GUID |
54 | | [a] 0.1.11 | aid text replace | Find and replace text using a regex pattern in the given input string. |
55 | | [a] 0.1.11 | aid text count | Count occurrences of a regex pattern in the given input string. |
56 | | [u] 0.1.3 | aid file info | prints file metadata |
57 | | [u] 0.1.3 | aid file md5 | calculates the files Md5 checksum |
58 | | [u] 0.1.3 | aid file sha1 | calculates the files Sha1 checksum |
59 | | [u] 0.1.3 | aid file sha256 | calculates the files Sha256 checksum |
60 | | [a] 0.1.4 | aid file zip | zips the files in the source directory |
61 | | [u] 0.1.9 | aid time unix | Display unix timestamp |
62 | | [u] 0.1.9 | aid time dt | Display the datetime |
63 | | [a] 0.1.9 | aid time chron | Describes a chron job |
64 | | [a] 0.1.9 | aid time count-down | Starts a countdown timer |
65 | | [a] 0.1.8 | aid bits eval | Bitwise expression evaluation / conversion / information |
66 | | [u] 0.1.10 | aid math eval | Evaluates a math expression |
67 | | [u] 0.1.10 | aid math plot | Plot a math expression |
68 | | [a] 0.1.10 | aid env vars | Filter / Display environment variables |
69 |
70 | [a] added in version x.x.x
71 | [u] updated in version x.x.x
72 | [p] patched in version x.x.x
73 | [d] deprecated in version x.x.x
74 | ```
75 |
76 | # Support 🛟
77 |
78 | Need help? Ping me on [linkedin](https://www.linkedin.com/in/timmoth/) and I'd be more then happy to jump on a call to debug, help configure or answer any questions.
79 |
--------------------------------------------------------------------------------
/docs/quickstart.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | Manual installation is simple, just download the release and add it to your PATH environment variable, if you'd like an even easier way i've added scripts to do it for you.
4 |
5 | ### Linux / Mac
6 | ```
7 | wget https://raw.githubusercontent.com/Timmoth/aid-cli/refs/heads/main/install.sh
8 | chmod +x install.sh
9 | sudo ./install.sh
10 | ```
11 | ### Windows (powershell)
12 | ```
13 | Invoke-WebRequest -Uri https://raw.githubusercontent.com/Timmoth/aid-cli/refs/heads/main/install.ps1 -OutFile install.ps1
14 | Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
15 | .\install.ps1
16 | ```
17 |
18 | ### Releases
19 | [Download the latest release](https://github.com/Timmoth/aid-cli/releases)
20 |
21 | ### Build
22 | If you'd like to build the latest version from source:
23 | ```
24 | //Install rust https://www.rust-lang.org/tools/install
25 | git clone https://github.com/Timmoth/aid-cli
26 | cd aid-cli
27 | cargo build --release
28 | .\target\release\aid.exe
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/support.md:
--------------------------------------------------------------------------------
1 | Need help? Ping me on [linkedin](https://www.linkedin.com/in/timmoth/) and I'd be more then happy to jump on a call to debug, help configure or answer any questions.
2 |
--------------------------------------------------------------------------------
/install.ps1:
--------------------------------------------------------------------------------
1 | # Set the download URL and target path
2 | $downloadUrl = "https://github.com/Timmoth/aid-cli/releases/download/aid-0.1.11/aid-win.exe"
3 | $installDir = "C:\Program Files\AidCLI"
4 | $filename = "aid.exe"
5 |
6 | # Create the installation directory if it doesn't exist
7 | if (-not (Test-Path -Path $installDir)) {
8 | Write-Host "Creating install directory..."
9 | New-Item -ItemType Directory -Path $installDir
10 | }
11 |
12 | # Download the file using Invoke-WebRequest
13 | $destination = Join-Path -Path $installDir -ChildPath $filename
14 | Write-Host "Downloading aid-win.exe..."
15 | Invoke-WebRequest -Uri $downloadUrl -OutFile $destination
16 |
17 | if (-not (Test-Path -Path $destination)) {
18 | Write-Host "Download failed! File not found."
19 | exit 1
20 | }
21 |
22 | Write-Host "Download successful! File saved to $destination"
23 |
24 | # Check if the installation directory is already in the PATH
25 | $envPath = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine)
26 | if ($envPath -notlike "*$installDir*") {
27 | Write-Host "Adding $installDir to system PATH..."
28 |
29 | # Add the directory to the PATH
30 | $newPath = "$envPath;$installDir"
31 | [System.Environment]::SetEnvironmentVariable("Path", $newPath, [System.EnvironmentVariableTarget]::Machine)
32 |
33 | Write-Host "$installDir successfully added to system PATH"
34 | } else {
35 | Write-Host "$installDir is already in the system PATH"
36 | }
37 |
38 | Write-Host "Installation complete! You can now use the 'aid' command from any terminal."
39 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Determine the operating system (Linux or macOS) and architecture (x86_64 or ARM64)
4 | OS="$(uname)"
5 | ARCH="$(uname -m)"
6 |
7 | if [[ "$OS" == "Linux" ]]; then
8 | if [[ "$ARCH" == "x86_64" ]]; then
9 | URL="https://github.com/Timmoth/aid-cli/releases/download/aid-0.1.11/aid-linux"
10 | else
11 | echo "Error: Unsupported architecture for Linux."
12 | exit 1
13 | fi
14 | elif [[ "$OS" == "Darwin" ]]; then
15 | if [[ "$ARCH" == "x86_64" ]]; then
16 | URL="https://github.com/Timmoth/aid-cli/releases/download/aid-0.1.11/aid-mac"
17 | # Check if running on an x86 mac with Rosetta
18 | if [[ $(sysctl -n sysctl.proc_translated) == "1" ]]; then
19 | echo "Running on Apple Silicon with Rosetta. Consider downloading the ARM64 version."
20 | fi
21 | elif [[ "$ARCH" == "arm64" ]]; then
22 | URL="https://github.com/Timmoth/aid-cli/releases/download/aid-0.1.11/aid-mac-arm"
23 | else
24 | echo "Error: Unsupported architecture for macOS."
25 | exit 1
26 | fi
27 | else
28 | echo "Error: Unsupported operating system."
29 | exit 1
30 | fi
31 |
32 | INSTALL_DIR="/usr/local/bin"
33 | FILENAME="aid"
34 |
35 | # Check if curl or wget is installed for downloading
36 | if command -v curl >/dev/null 2>&1; then
37 | DOWNLOADER="curl -L -o"
38 | elif command -v wget >/dev/null 2>&1; then
39 | DOWNLOADER="wget -O"
40 | else
41 | echo "Error: Neither curl nor wget is installed."
42 | exit 1
43 | fi
44 |
45 | # Download the file
46 | echo "Downloading aid from $URL..."
47 | $DOWNLOADER "$INSTALL_DIR/$FILENAME" "$URL"
48 |
49 | # Verify if the download was successful
50 | if [ ! -f "$INSTALL_DIR/$FILENAME" ]; then
51 | echo "Error: Download failed."
52 | exit 1
53 | fi
54 |
55 | echo "Download successful! File saved to $INSTALL_DIR/$FILENAME"
56 |
57 | # Make the file executable
58 | echo "Making $FILENAME executable..."
59 | chmod +x "$INSTALL_DIR/$FILENAME"
60 |
61 | # Check if the install directory is already in PATH
62 | if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then
63 | echo "Adding $INSTALL_DIR to PATH..."
64 |
65 | # Add the directory to the shell profile for persistent PATH updates
66 | if [ -f "$HOME/.bashrc" ]; then
67 | echo "export PATH=\$PATH:$INSTALL_DIR" >> "$HOME/.bashrc"
68 | source "$HOME/.bashrc"
69 | elif [ -f "$HOME/.bash_profile" ]; then
70 | echo "export PATH=\$PATH:$INSTALL_DIR" >> "$HOME/.bash_profile"
71 | source "$HOME/.bash_profile"
72 | elif [ -f "$HOME/.zshrc" ]; then
73 | echo "export PATH=\$PATH:$INSTALL_DIR" >> "$HOME/.zshrc"
74 | source "$HOME/.zshrc"
75 | else
76 | echo "Could not find shell profile. Please add $INSTALL_DIR to your PATH manually."
77 | fi
78 | else
79 | echo "$INSTALL_DIR is already in PATH."
80 | fi
81 |
82 | echo "Installation complete! You can now run 'aid' from any terminal."
83 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Aid-CLI Docs
2 | site_url: https://timmoth.github.io/aid-cli/
3 | site_description: A CLI toolkit featuring a variety of helpful utilities.
4 | site_author: Timmoth
5 | repo_url: https://github.com/Timmoth/aid-cli/
6 | edit_uri: edit/main/docs
7 |
8 | theme:
9 | name: readthedocs
10 | highlightjs: true
11 | logo: assets/logo.png
12 | nav:
13 | - Overview ✅: index.md
14 | - Quick start ▶️: quickstart.md
15 | - Support 🛟: support.md
16 | - Contributing 🙏: contributing.md
17 | - Features:
18 | - http: features/http.md
19 | - ip: features/ip.md
20 | - port: features/port.md
21 | - cpu: features/cpu.md
22 | - mem: features/mem.md
23 | - disk: features/disk.md
24 | - network: features/network.md
25 | - process: features/process.md
26 | - json: features/json.md
27 | - csv: features/csv.md
28 | - text: features/text.md
29 | - file: features/file.md
30 | - time: features/time.md
31 | - bits: features/bits.md
32 | - math: features/math.md
33 | markdown_extensions:
34 | - tables
35 | - admonition
36 | - pymdownx.details
37 | - pymdownx.superfences
38 |
--------------------------------------------------------------------------------
/src/bits_commands.rs:
--------------------------------------------------------------------------------
1 | use crate::bits_utils;
2 |
3 | pub fn evaluate(board: bool, chess_board: bool, binary: bool, hex: bool, expression: String) {
4 | let (_, parsed_expr) = bits_utils::parse_expr(&expression).unwrap();
5 | let result = parsed_expr.eval();
6 | if board || chess_board {
7 | bitboard(chess_board, None, Some(result), None)
8 | } else {
9 | if binary {
10 | println!("{:b}", result);
11 | } else if hex {
12 | println!("{:x}", result);
13 | } else {
14 | println!("{}", result);
15 | }
16 | }
17 | }
18 |
19 | fn bitboard(chess_board: bool, binary: Option, decimal: Option, hex: Option) {
20 | let num: u64 = if let Some(v) = decimal {
21 | v
22 | } else if let Some(v) = hex {
23 | u64::from_str_radix(&v, 16).expect("failed parsing hex string")
24 | } else if let Some(b) = binary {
25 | u64::from_str_radix(&b, 2).expect("failed parsing binary string")
26 | } else {
27 | 0
28 | };
29 |
30 | for rank in (0..8).rev() {
31 | let byte = ((num >> (rank * 8)) & 0xFF) as u8;
32 | if chess_board{
33 | print!("{} | ", rank + 1);
34 | }else{
35 | print!("{:02X} | ", byte);
36 | }
37 |
38 | if chess_board {
39 | for file in 0..8 {
40 | let bit = (num >> (rank * 8 + file)) & 1;
41 | print!("{} ", bit);
42 | }
43 | } else {
44 | for file in (0..8).rev() {
45 | let bit = (num >> (rank * 8 + file)) & 1;
46 | print!("{} ", bit);
47 | }
48 | }
49 |
50 | println!();
51 | }
52 |
53 | if chess_board {
54 | println!(" -----------------------");
55 | println!(" A B C D E F G H");
56 | }else{
57 | println!(" -----------------------");
58 | println!(" 7 6 5 4 3 2 1 0");
59 | }
60 |
61 | println!("dec: {}", num);
62 | println!("hex: {:X}", num);
63 | println!("bin: {:b}", num);
64 | println!("lsb: {}", num.trailing_zeros());
65 | if num.leading_zeros() >= 64 {
66 | println!("msb: {}", 64);
67 | } else {
68 | println!("msb: {}", 63 - num.leading_zeros());
69 | }
70 | println!("set bits: {}", num.count_ones());
71 | }
--------------------------------------------------------------------------------
/src/bits_utils.rs:
--------------------------------------------------------------------------------
1 | use nom::{
2 | branch::alt,
3 | bytes::complete::tag,
4 | character::complete::{char, digit1, space0},
5 | combinator::{map, map_res},
6 | sequence::{delimited, preceded},
7 | IResult,
8 | };
9 | use std::str::FromStr;
10 |
11 | #[derive(Debug)]
12 | pub enum Expr {
13 | Num(u64),
14 | BinOp(Box, BinOp, Box),
15 | UnOp(UnOp, Box),
16 | }
17 |
18 | #[derive(Debug)]
19 | pub enum BinOp {
20 | And,
21 | Or,
22 | Xor,
23 | Shl,
24 | Shr,
25 | }
26 |
27 | #[derive(Debug)]
28 | pub enum UnOp {
29 | Not,
30 | }
31 |
32 | // Parsing numbers: decimal, hex, and binary
33 | fn parse_number(input: &str) -> IResult<&str, u64> {
34 | alt((
35 | map_res(preceded(tag("0x"), nom::character::complete::hex_digit1), |s: &str| {
36 | u64::from_str_radix(s, 16)
37 | }),
38 | map_res(preceded(tag("0b"), nom::character::complete::digit1), |s: &str| {
39 | u64::from_str_radix(s, 2)
40 | }),
41 | map_res(digit1, |s: &str| u64::from_str(s)),
42 | ))(input)
43 | }
44 |
45 | // Parsing parenthesized expressions
46 | fn parse_parentheses(input: &str) -> IResult<&str, Expr> {
47 | delimited(
48 | delimited(space0, char('('), space0),
49 | |input| parse_expression(input), // Call the main expression parser
50 | delimited(space0, char(')'), space0),
51 | )(input)
52 | }
53 |
54 | // Primary expressions: either numbers or parentheses
55 | fn parse_primary(input: &str) -> IResult<&str, Expr> {
56 | alt((parse_parentheses, map(parse_number, Expr::Num)))(input)
57 | }
58 |
59 | // Parsing unary operator (NOT)
60 | fn parse_unop(input: &str) -> IResult<&str, UnOp> {
61 | delimited(space0, map(tag("!"), |_| UnOp::Not), space0)(input)
62 | }
63 |
64 | // Function to parse unary expressions
65 | fn parse_unary_expr(input: &str) -> IResult<&str, Expr> {
66 | let (input, op) = parse_unop(input)?;
67 | let (input, expr) = parse_primary(input)?;
68 | Ok((input, Expr::UnOp(op, Box::new(expr))))
69 | }
70 |
71 | // Binary operator precedence levels
72 | fn get_precedence(op: &BinOp) -> u8 {
73 | match op {
74 | BinOp::Shl | BinOp::Shr => 3,
75 | BinOp::And => 2,
76 | BinOp::Xor => 1,
77 | BinOp::Or => 0,
78 | }
79 | }
80 |
81 | // Parsing binary operators
82 | fn parse_binop(input: &str) -> IResult<&str, BinOp> {
83 | alt((
84 | map(tag("&"), |_| BinOp::And),
85 | map(tag("|"), |_| BinOp::Or),
86 | map(tag("^"), |_| BinOp::Xor),
87 | map(tag("<<"), |_| BinOp::Shl),
88 | map(tag(">>"), |_| BinOp::Shr),
89 | ))(input)
90 | }
91 |
92 | // Parse expressions with precedence climbing
93 | fn parse_expression(input: &str) -> IResult<&str, Expr> {
94 | let (input, mut lhs) = alt((parse_unary_expr, parse_primary))(input)?; // Allow unary expressions first
95 |
96 | let mut input = input;
97 | loop {
98 | let (next_input, _) = space0(input)?;
99 |
100 | // Attempt to parse a binary operator
101 | if let Ok((next_input, op)) = parse_binop(next_input) {
102 | let precedence = get_precedence(&op);
103 | let (next_input, _) = space0(next_input)?;
104 | let (next_input, mut rhs) = alt((parse_unary_expr, parse_primary))(next_input)?; // Allow unary expressions on rhs
105 |
106 | // Check for next higher precedence operators
107 | while let Ok((next_next_input, next_op)) = parse_binop(next_input) {
108 | let next_precedence = get_precedence(&next_op);
109 | if next_precedence > precedence {
110 | let (next_next_input, higher_rhs) = parse_expression(next_next_input)?;
111 | rhs = Expr::BinOp(Box::new(rhs), next_op, Box::new(higher_rhs));
112 | input = next_next_input; // Update input
113 | } else {
114 | input = next_next_input; // Exit if no higher precedence
115 | break;
116 | }
117 | }
118 |
119 | lhs = Expr::BinOp(Box::new(lhs), op, Box::new(rhs));
120 | input = next_input; // Update input for the next iteration
121 | } else {
122 | break; // Exit loop if no binary operator found
123 | }
124 | }
125 |
126 | Ok((input, lhs))
127 | }
128 |
129 | // Parsing the expression (with support for unary and binary operators)
130 | pub fn parse_expr(input: &str) -> IResult<&str, Expr> {
131 | parse_expression(input)
132 | }
133 |
134 | // Implement evaluation of the expressions
135 | impl Expr {
136 | pub fn eval(&self) -> u64 {
137 | match self {
138 | Expr::Num(n) => *n,
139 | Expr::BinOp(lhs, op, rhs) => {
140 | let lhs_val = lhs.eval();
141 | let rhs_val = rhs.eval();
142 | match op {
143 | BinOp::And => lhs_val & rhs_val,
144 | BinOp::Or => lhs_val | rhs_val,
145 | BinOp::Xor => lhs_val ^ rhs_val,
146 | BinOp::Shl => lhs_val << rhs_val,
147 | BinOp::Shr => lhs_val >> rhs_val,
148 | }
149 | }
150 | Expr::UnOp(op, expr) => {
151 | let val = expr.eval();
152 | match op {
153 | UnOp::Not => !val,
154 | }
155 | }
156 | }
157 | }
158 | }
--------------------------------------------------------------------------------
/src/cpu_commands.rs:
--------------------------------------------------------------------------------
1 | use serde_derive::Serialize;
2 | use std::{collections::HashSet, time::Duration};
3 | use sysinfo::System;
4 |
5 | use crate::{format_utils, graph_utils};
6 |
7 | #[derive(Serialize, Debug)]
8 | struct CpuInfo {
9 | name: String,
10 | }
11 |
12 | pub fn system_cpu_info(json: bool) {
13 | let s = sysinfo::System::new_all();
14 |
15 | let unique_vendor_ids: HashSet =
16 | s.cpus().iter().map(|c| c.brand().to_string()).collect();
17 | let unique_vendor_ids_vec: Vec = unique_vendor_ids.into_iter().collect();
18 |
19 | if json {
20 | let output: Vec = unique_vendor_ids_vec
21 | .iter()
22 | .map(|v| CpuInfo {
23 | name: v.to_string(),
24 | })
25 | .collect();
26 | format_utils::print_json(&output);
27 | } else {
28 | let joined_vendor_ids = unique_vendor_ids_vec.join(", ");
29 | println!("{}", joined_vendor_ids);
30 | }
31 | }
32 |
33 | #[derive(Serialize, Debug)]
34 | struct CpuUsage {
35 | total: f64,
36 | core_usage: Vec,
37 | }
38 |
39 | pub async fn system_cpu_usage(watch: bool, json: bool, plot: bool) {
40 | let mut system = System::new_all();
41 |
42 | if watch {
43 | let mut points: Vec<(f32, f32)> = Vec::new();
44 | let mut i:f32 = 0.0;
45 | loop {
46 | system.refresh_cpu_all();
47 | let total: f32 = system.global_cpu_usage();
48 | if points.len() >= 30 {
49 | // Remove the oldest point (optional, only if you want a rolling window)
50 | points.remove(0); // or points.drain(..1); for potentially more efficient operation
51 | }
52 | points.push((i, total));
53 | i += 1.0;
54 |
55 | let usage = CpuUsage {
56 | total: format_utils::round_to_one_decimal(total),
57 | core_usage: system
58 | .cpus()
59 | .iter()
60 | .map(|c| format_utils::round_to_one_decimal(c.cpu_usage()))
61 | .collect(),
62 | };
63 |
64 | format_utils::clear_terminal();
65 | if json {
66 | format_utils::print_json(&usage);
67 | }else{
68 | println!(
69 | "total: {}, cpus: {}",
70 | format_utils::format_percent(total),
71 | system
72 | .cpus()
73 | .iter()
74 | .map(|c| format_utils::format_percent(c.cpu_usage()))
75 | .collect::>()
76 | .join(", ")
77 | );
78 | }
79 |
80 | if plot{
81 | // Plot the chart
82 | let title: &str = "total cpu %";
83 | graph_utils::plot_chart(&points, String::from(title));
84 | }
85 |
86 | tokio::time::sleep(Duration::from_millis(500)).await;
87 | }
88 | } else {
89 | system.refresh_cpu_all();
90 | let total: f32 = system.global_cpu_usage();
91 |
92 | println!(
93 | "total: {}, cpus: {}",
94 | format_utils::format_percent(total),
95 | system
96 | .cpus()
97 | .iter()
98 | .map(|c| format_utils::format_percent(c.cpu_usage()))
99 | .collect::>()
100 | .join(", ")
101 | );
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/csv_commands.rs:
--------------------------------------------------------------------------------
1 | use std::{fs::File, time::Instant};
2 |
3 | use csv::Writer;
4 |
5 | use crate::csv_utils;
6 |
7 | pub async fn sql_search(sql: String, output_path: Option) {
8 |
9 | let start = Instant::now();
10 |
11 | // Parse the SQL query
12 | let parsed = match csv_utils::parse_sql(&sql) {
13 | Ok(result) => result,
14 | Err(e) => {
15 | eprintln!("Failed to parse SQL query: {}", e);
16 | return; // Early exit on error
17 | }
18 | };
19 |
20 | let parse_time = start.elapsed();
21 | let start = Instant::now();
22 |
23 | let query = parsed.1;
24 |
25 | // Load the CSV file
26 | let (headers, records) = match csv_utils::load_csv(&query.table) {
27 | Ok(result) => result,
28 | Err(e) => {
29 | eprintln!("Failed to load CSV file '{}': {}", query.table, e);
30 | return; // Early exit on error
31 | }
32 | };
33 |
34 | let rows = records.len();
35 | let load_time = start.elapsed();
36 | let start = Instant::now();
37 |
38 | // Apply the query
39 | let (result_headers, result_rows) = csv_utils::apply_query(&headers, records, &query);
40 | let query_time = start.elapsed();
41 | let result_row_count = result_rows.len();
42 |
43 | output_results(result_rows, &result_headers, &output_path);
44 |
45 | if output_path.is_some() {
46 | // Log the query statistics
47 | println!("Parsed {:?} in {:?}", query, parse_time);
48 | println!("Loaded {} rows in {:?}", rows, load_time);
49 | println!("Found {} rows in {:?}", result_row_count, query_time);
50 | }
51 | }
52 |
53 |
54 | fn output_results(results: Vec>, headers: &Vec, output_path: &Option) {
55 | if results.is_empty() {
56 | println!("No results found.");
57 | return;
58 | }
59 |
60 | match output_path {
61 | Some(path) => {
62 | match File::create(&path) {
63 | Ok(file) => {
64 | let mut wtr = Writer::from_writer(file);
65 |
66 | // Write headers to the CSV if provided
67 | if let Err(e) = wtr.write_record(headers) {
68 | eprintln!("Failed to write headers to file: {}", e);
69 | return;
70 | }
71 |
72 | // Write each row of results to the CSV
73 | for row in results.iter() {
74 | if let Err(e) = wtr.write_record(row) {
75 | eprintln!("Failed to write row to file: {}", e);
76 | return;
77 | }
78 | }
79 |
80 | if let Err(e) = wtr.flush() {
81 | eprintln!("Failed to flush writer: {}", e);
82 | }
83 |
84 | println!("Results successfully written to {}", path);
85 | }
86 | Err(e) => {
87 | eprintln!("Failed to create file {}: {}", path, e);
88 | }
89 | }
90 | }
91 |
92 | None => {
93 | // Print headers if they are available
94 | println!("{}", headers.join(","));
95 |
96 |
97 | // Print each row of results
98 | for row in results.iter() {
99 | println!("{}", row.join(","));
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/disk_commands.rs:
--------------------------------------------------------------------------------
1 | use sysinfo::Disks;
2 | use serde_derive::Serialize;
3 |
4 | use crate::format_utils;
5 |
6 | #[derive(Serialize, Debug)]
7 | struct DiskInfoOutput {
8 | available_gb: f64,
9 | total_gb: f64,
10 | percent_free: f64,
11 | file_system: String,
12 | mount_point: String,
13 | removable: bool,
14 | kind: String,
15 | }
16 |
17 | pub async fn system_disk_info(json: bool) {
18 |
19 | let disks = Disks::new_with_refreshed_list();
20 | let mut outputs: Vec = Vec::new();
21 | for disk in &disks {
22 | outputs.push(DiskInfoOutput{
23 | available_gb: format_utils::bytes_to_gb(disk.available_space()),
24 | total_gb: format_utils::bytes_to_gb(disk.total_space()),
25 | percent_free: format_utils::percent(disk.available_space(), disk.total_space()),
26 | file_system: String::from(disk.file_system().to_str().unwrap_or_default()),
27 | mount_point: String::from(disk.mount_point().to_str().unwrap_or_default()),
28 | removable: disk.is_removable(),
29 | kind: disk.kind().to_string(),
30 | });
31 | }
32 |
33 | if json {
34 | format_utils::print_json(&outputs);
35 | }else{
36 | for o in outputs{
37 | println!("available: {}GB ({}%), total: {}GB, file_system: {}, mount_point: {}, removable: {}, king: {}", o.available_gb, o.percent_free, o.total_gb, o.file_system, o.mount_point, o.removable, o.kind)
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/env_commands.rs:
--------------------------------------------------------------------------------
1 | use std::collections::BTreeMap;
2 | use std::env;
3 | use std::env::consts::OS;
4 |
5 | use regex::Regex;
6 |
7 | pub fn print_env_vars(key_filter: Option, value_filter: Option) {
8 | let mut env_vars: BTreeMap> = BTreeMap::new();
9 | let separator = if OS == "windows" { ';' } else { ':' };
10 |
11 | let key_regex = key_filter.map(|kf| Regex::new(&kf).unwrap());
12 | let value_regex = value_filter.map(|vf| Regex::new(&vf).unwrap());
13 |
14 | for (key, value) in env::vars() {
15 | if let Some(ref kr) = key_regex {
16 | if !kr.is_match(&key.to_lowercase()) {
17 | continue;
18 | }
19 | }
20 |
21 | let split_values: Vec = value
22 | .split(separator)
23 | .map(|s| s.trim().to_string())
24 | .filter(|v| !v.is_empty()) // Filter out empty values
25 | .collect();
26 |
27 | let filtered_values: Vec = if let Some(ref vr) = value_regex {
28 | split_values
29 | .into_iter()
30 | .filter(|v| vr.is_match(&v.to_lowercase()))
31 | .collect()
32 | } else {
33 | split_values
34 | };
35 |
36 | if !filtered_values.is_empty() {
37 | env_vars.insert(key, filtered_values);
38 | }
39 | }
40 |
41 | for (key, values) in env_vars {
42 | println!("{}:", key);
43 | for value in values {
44 | println!("\t{}", value);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/file_commands.rs:
--------------------------------------------------------------------------------
1 | use md5::{Digest as Md5Digest, Md5};
2 | use sha1::Sha1;
3 | use sha2::Sha256;
4 | use std::fs::File;
5 | use std::io::{self, BufReader, Read};
6 | use std::path::Path;
7 | use std::time::UNIX_EPOCH;
8 | use time::OffsetDateTime;
9 | use std::io::Write;
10 | use zip::ZipWriter;
11 | use zip::write::{FileOptions, SimpleFileOptions};
12 | use walkdir::WalkDir;
13 |
14 | #[cfg(unix)]
15 | use std::os::unix::fs::PermissionsExt;
16 |
17 | pub fn zip_directory(dir_path: String, zip_file_path: String) {
18 | // Create a new ZIP file
19 | let zip_file = File::create(&zip_file_path);
20 | if let Err(e) = zip_file {
21 | eprintln!("Error creating zip file '{}': {}", zip_file_path, e);
22 | return;
23 | }
24 | let zip_file = zip_file.unwrap();
25 |
26 | let mut zip_writer = ZipWriter::new(zip_file);
27 | let options = SimpleFileOptions::default()
28 | .compression_method(zip::CompressionMethod::Deflated)
29 | .unix_permissions(0o755);
30 |
31 | // Walk through the directory
32 | for entry in WalkDir::new(&dir_path) {
33 | let entry = entry.unwrap(); // Handle errors with unwrap; you may want to log them instead
34 | let path = entry.path();
35 |
36 | // Only add files to the zip
37 | if path.is_file() {
38 | // Get relative path for the ZIP entry
39 | let relative_path = path.strip_prefix(&dir_path).unwrap_or(path);
40 | if let Err(e) = zip_writer.start_file(relative_path.to_string_lossy(), options) {
41 | eprintln!("Error adding file to zip '{}': {}", relative_path.display(), e);
42 | continue; // Skip to the next file
43 | }
44 |
45 | let file_reader = File::open(path);
46 | match file_reader {
47 | Ok(mut reader) => {
48 | if let Err(e) = io::copy(&mut reader, &mut zip_writer) {
49 | eprintln!("Error writing file to zip '{}': {}", relative_path.display(), e);
50 | }
51 | }
52 | Err(e) => {
53 | eprintln!("Error opening file '{}': {}", path.display(), e);
54 | }
55 | }
56 | }
57 | }
58 |
59 | if let Err(e) = zip_writer.finish() {
60 | eprintln!("Error finalizing zip file: {}", e);
61 | } else {
62 | println!("Successfully zipped directory '{}'", dir_path);
63 | }
64 | }
65 |
66 | pub fn file_info(file_path: String) {
67 | let path = Path::new(&file_path);
68 |
69 | // Check if file exists
70 | if !path.exists() {
71 | println!("File does not exist.");
72 | return;
73 | }
74 |
75 | // Get metadata
76 | let metadata = path.metadata().expect("Failed reading file metadata");
77 |
78 | // File type: Directory or file
79 | if metadata.is_dir() {
80 | println!("Type: Directory");
81 | } else if metadata.is_file() {
82 | println!("Type: File");
83 | } else if metadata.file_type().is_symlink() {
84 | println!("Type: Symlink");
85 | } else {
86 | println!("Type: Other");
87 | }
88 |
89 | println!("Size: {} bytes", metadata.len());
90 |
91 | // Permissions (cross-platform, but more detailed on Unix)
92 | #[cfg(unix)]
93 | println!("Permissions: {:o}", metadata.permissions().mode());
94 | #[cfg(not(unix))]
95 | println!(
96 | "Permissions: Read-only: {}",
97 | metadata.permissions().readonly()
98 | );
99 |
100 | // Timestamps
101 | let modified_time = metadata
102 | .modified()
103 | .expect("Failed reading date modified")
104 | .duration_since(UNIX_EPOCH)
105 | .expect("Failed reading date modified")
106 | .as_secs();
107 | let accessed_time = metadata
108 | .accessed()
109 | .expect("Failed reading date accessed.")
110 | .duration_since(UNIX_EPOCH)
111 | .expect("Failed reading date accessed.")
112 | .as_secs();
113 |
114 | // On some platforms, creation time may not be available
115 | let created_time = metadata.created().ok().map_or_else(
116 | || "Not available".to_string(),
117 | |created| {
118 | let created_secs = OffsetDateTime::from_unix_timestamp(created.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64).expect("Failed converting date accessed.");
119 | format!("{}", created_secs)
120 | },
121 | );
122 |
123 | let modified = OffsetDateTime::from_unix_timestamp(modified_time as i64).expect("Failed converting date modified.");
124 | let accessed = OffsetDateTime::from_unix_timestamp(accessed_time as i64).expect("Failed converting date accessed.");
125 |
126 | println!("Modified: {}", modified);
127 | println!("Accessed: {}", accessed);
128 | println!("Created: {}", created_time);
129 | }
130 |
131 | pub fn md5_checksum(file_path: String) {
132 | let file = File::open(&file_path).expect("Unable to open file");
133 | let mut reader = BufReader::new(file);
134 | let mut md5_hasher = Md5::new();
135 | let mut buffer = [0; 1024]; // 1KB buffer
136 |
137 | loop {
138 | let bytes_read = reader.read(&mut buffer).expect("Failed reading file data");
139 |
140 | if bytes_read == 0 {
141 | break; // EOF reached
142 | }
143 |
144 | md5_hasher.update(&buffer[..bytes_read]);
145 | }
146 |
147 | let md5_result = md5_hasher.finalize();
148 | println!("{:x}", md5_result);
149 | }
150 |
151 | pub fn sha1_checksum(file_path: String) {
152 | let file = File::open(&file_path).expect("Unable to open file");
153 | let mut reader = BufReader::new(file);
154 | let mut sha1_hasher = Sha1::new();
155 | let mut buffer = [0; 1024]; // 1KB buffer
156 |
157 | loop {
158 | let bytes_read = reader.read(&mut buffer).expect("Failed reading file data");
159 |
160 | if bytes_read == 0 {
161 | break; // EOF reached
162 | }
163 |
164 | sha1_hasher.update(&buffer[..bytes_read]);
165 | }
166 |
167 | let sha1_result = sha1_hasher.finalize();
168 | println!("{:x}", sha1_result);
169 | }
170 |
171 | pub fn sha256_checksum(file_path: String) {
172 | let file = File::open(&file_path).expect("Unable to open file");
173 | let mut reader = BufReader::new(file);
174 | let mut sha256_hasher = Sha256::new();
175 | let mut buffer = [0; 1024]; // 1KB buffer
176 |
177 | loop {
178 | let bytes_read = reader.read(&mut buffer).expect("Failed reading file data");
179 |
180 | if bytes_read == 0 {
181 | break; // EOF reached
182 | }
183 |
184 | sha256_hasher.update(&buffer[..bytes_read]);
185 | }
186 |
187 | let sha256_result = sha256_hasher.finalize();
188 | println!("{:x}", sha256_result);
189 | }
--------------------------------------------------------------------------------
/src/format_utils.rs:
--------------------------------------------------------------------------------
1 | use num_traits::ToPrimitive;
2 | use serde::Serialize;
3 |
4 | pub fn round_to_one_decimal(num: T) -> f64 {
5 | if let Some(n) = num.to_f64() {
6 | (n * 10.0).round() / 10.0
7 | } else {
8 | 0.0
9 | }
10 | }
11 |
12 | const GB: u64 = 1 << 30;
13 |
14 | pub fn percent(a: T, b: U) -> f64 {
15 | let a_f64 = a.to_f64().unwrap_or(0.0);
16 | let b_f64 = b.to_f64().unwrap_or(0.0);
17 |
18 | if b_f64 > 0.0 {
19 | round_to_one_decimal((a_f64 / b_f64) * 100.0)
20 | } else {
21 | 0.0 // Avoid division by zero
22 | }
23 | }
24 |
25 | pub fn bytes_to_gb(num: T) -> f64 {
26 | if let Some(n) = num.to_f64() {
27 | let gb = n / GB as f64;
28 | (gb * 10.0).round() / 10.0
29 | } else {
30 | // Handle the case where the conversion fails (e.g., for non-numeric types)
31 | 0.0
32 | }
33 | }
34 |
35 | pub fn format_bytes(bytes: T) -> String {
36 |
37 | let bytes_f64 = bytes.to_f64().unwrap_or(0.0);
38 |
39 | const GB: u64 = 1 << 30; // 1 GiB in bytes
40 | const MB: u64 = 1 << 20; // 1 MiB in bytes
41 |
42 | if bytes_f64 as u64 >= GB {
43 | format!("{:.1}GB", bytes_f64 as f64 / GB as f64)
44 | } else {
45 | format!("{:.1}MB", bytes_f64 as f64 / MB as f64)
46 | }
47 | }
48 |
49 | pub fn format_percent(a: T) -> String {
50 | let a_f64 = a.to_f64().unwrap_or(0.0);
51 | return format!("{:.1}%",a_f64 );// Avoid division by zero
52 | }
53 |
54 | pub fn calculate_format_percent(a: T, b: U) -> String {
55 | let a_f64 = a.to_f64().unwrap_or(0.0);
56 | let b_f64 = b.to_f64().unwrap_or(0.0);
57 | if b_f64 > 0.0 {
58 | return format!("{:.1}%", (a_f64 / b_f64) * 100.0);
59 | } else {
60 | return format!("{:.1}%",0.0 );// Avoid division by zero
61 | };
62 | }
63 |
64 | pub fn clear_terminal(){
65 | print!("\x1B[2J\x1B[3J\x1B[H");
66 | }
67 |
68 | pub fn print_json(body: &T) {
69 | let json = serde_json::to_string_pretty(body).expect("Failed to convert to JSON");
70 | println!("{}", json);
71 | }
--------------------------------------------------------------------------------
/src/graph_utils.rs:
--------------------------------------------------------------------------------
1 | use crate::math_utils;
2 | use std::cmp::{max, min};
3 |
4 | /// Interpolate between two points (x0, y0) and (x1, y1), and generate intermediate points.
5 | fn interpolate(p1: (f32, f32), p2: (f32, f32), num_points: usize) -> Vec<(f32, f32)> {
6 | let mut interpolated_points = Vec::new();
7 |
8 | for i in 0..=num_points {
9 | let t = i as f32 / num_points as f32;
10 | let x = p1.0 * (1.0 - t) + p2.0 * t;
11 | let y = p1.1 * (1.0 - t) + p2.1 * t;
12 | interpolated_points.push((x, y));
13 | }
14 |
15 | interpolated_points
16 | }
17 |
18 | pub fn plot_chart(points: &Vec<(f32, f32)>, expression: String) {
19 | let width = 50;
20 | let height = 20;
21 |
22 | // Find min and max values for x and y to set the chart bounds
23 | let (min_x, max_x) = points.iter().fold(
24 | (f32::INFINITY, f32::NEG_INFINITY),
25 | |(min_x, max_x), &(x, _)| (min_x.min(x), max_x.max(x)),
26 | );
27 |
28 | let (min_y, max_y) = points.iter().fold(
29 | (f32::INFINITY, f32::NEG_INFINITY),
30 | |(min_y, max_y), &(_, y)| (min_y.min(y), max_y.max(y)),
31 | );
32 |
33 | let axis_x = if min_y <= 0.0 && max_y >= 0.0 {
34 | Some((0.0 - min_y) / (max_y - min_y) * (height - 1) as f32)
35 | } else if max_y < 0.0 {
36 | Some(height as f32 - 1.0)
37 | // x-axis at the top
38 | } else {
39 | Some(-1.0)
40 | // x-axis at the bottom
41 | };
42 |
43 | let axis_y = if min_x <= 0.0 && max_x >= 0.0 {
44 | Some(((0.0 - min_x) / (max_x - min_x) * (width - 1) as f32) as f32)
45 | } else if max_x < 0.0 {
46 | // y-axis at the left
47 | Some(width as f32 - 1.0)
48 | } else {
49 | Some(-1.0)
50 | // y-axis at the right
51 | };
52 |
53 | println!("plot: {}", expression);
54 | println!("X range: [{}, {}]", min_x, max_x);
55 | println!("Y range: [{}, {}]", min_y, max_y);
56 | // Create a 2D grid initialized with spaces
57 | let mut grid = vec![vec![' '; width]; height];
58 |
59 | // Draw the axis
60 | if let Some(axis_x) = axis_x {
61 | if axis_x >= 0.0 {
62 | let xaxis = axis_x as usize;
63 | if height > xaxis {
64 | for i in 0..width {
65 | grid[height - 1 - xaxis][i] = '-'; // x-axis (horizontal)
66 | }
67 | }
68 | }
69 | }
70 |
71 | if let Some(axis_y) = axis_y {
72 | if axis_y >= 0.0 {
73 | let axisy = axis_y as usize;
74 | for i in 0..height {
75 | grid[i][axisy] = '|'; // y-axis (vertical)
76 | if let Some(axis_x) = axis_x {
77 | if axis_x >= 0.0 {
78 | let xaxis = axis_x as usize;
79 |
80 | grid[height - 1 - xaxis][axisy] = '+'; // Intersection of x and y axes
81 | }
82 | }
83 | }
84 | }
85 | }
86 |
87 | // Plot interpolated points
88 | for pair in points.windows(2) {
89 | let interpolated_points = interpolate(pair[0], pair[1], 10); // Interpolating with 10 points between each pair
90 | for &(x, y) in &interpolated_points {
91 | let scaled_x = ((x - min_x) / (max_x - min_x) * (width - 1) as f32) as usize;
92 | let scaled_y = ((y - min_y) / (max_y - min_y) * (height - 1) as f32) as usize;
93 |
94 | if height > scaled_y {
95 | grid[height - 1 - scaled_y][scaled_x] = '*';
96 | }
97 | }
98 | }
99 |
100 | // Print the grid
101 | for row in &grid {
102 | println!("{}", row.iter().collect::());
103 | }
104 | }
105 |
106 | pub fn generate_points(start: f32, end: f32, step: f32, expression: &String) -> Vec<(f32, f32)> {
107 | let exp = math_utils::parse_expr(&expression).unwrap();
108 |
109 | let mut points = Vec::new();
110 |
111 | let mut x = start;
112 | while x <= end {
113 | let y = math_utils::evaluate(&exp.1, x as f64) as f32;
114 | points.push((x, y));
115 | x += step;
116 | }
117 |
118 | points
119 | }
120 |
--------------------------------------------------------------------------------
/src/http_commands.rs:
--------------------------------------------------------------------------------
1 | use reqwest::Method;
2 | use tokio::fs::File;
3 | use tokio::io::{AsyncReadExt, AsyncWriteExt};
4 | use serde_derive::Deserialize;
5 | use serde_json::{from_str, Value};
6 | use reqwest::{Client, header::{HeaderMap, HeaderName, HeaderValue}};
7 | use core::str;
8 | use std::str::FromStr;
9 | use tokio::net::TcpListener;
10 |
11 | #[derive(Deserialize, Debug)]
12 | struct HttpRequestConfig {
13 | method: Option,
14 | url: Option,
15 | headers: Option,
16 | body: Option,
17 | }
18 |
19 | async fn read_config_file(file_path: &str) -> Result {
20 | let mut file = File::open(file_path).await.map_err(|e| format!("Failed to open file: {}", e))?;
21 | let mut contents = String::new();
22 | file.read_to_string(&mut contents).await.map_err(|e| format!("Failed to read file: {}", e))?;
23 | serde_json::from_str(&contents).map_err(|e| format!("Failed to deserialize JSON: {}", e))
24 | }
25 |
26 | // Helper function to build request headers
27 | fn build_headers(header_map: Option) -> Result {
28 | let mut headers = HeaderMap::new();
29 | if let Some(headers_object) = header_map.and_then(|h| h.as_object().cloned()) {
30 | for (key, value) in headers_object {
31 | let header_key = HeaderName::from_str(&key).map_err(|_| format!("Invalid header key: {}", key))?;
32 | let value_str = value.as_str().ok_or(format!("Header value for '{}' is not a string", key))?;
33 | let header_value = HeaderValue::from_str(value_str).map_err(|_| format!("Invalid header value for '{}': {}", key, value_str))?;
34 | headers.insert(header_key, header_value);
35 | }
36 | }
37 | Ok(headers)
38 | }
39 |
40 | // Helper function to handle the request method and URL logic
41 | fn parse_method_and_url(config: &mut HttpRequestConfig, method: Option, url: Option) -> Result<(Method, String), String> {
42 | if let Some(u) = url {
43 | if !u.is_empty() {
44 | config.url = Some(u);
45 | }
46 | }
47 |
48 | let url = config.url.clone().ok_or("No valid URL provided.".to_string())?;
49 |
50 | if let Some(m) = method {
51 | if !m.is_empty() {
52 | config.method = Some(m);
53 | }
54 | }
55 |
56 | let method = config.method.clone().ok_or("No valid HTTP method provided.".to_string())?;
57 | let method = Method::from_str(&method).map_err(|_| format!("Invalid HTTP method: {}", method))?;
58 |
59 | Ok((method, url))
60 | }
61 |
62 | pub async fn http_request(
63 | method: Option,
64 | url: Option,
65 | config_path: Option,
66 | output: Option
67 | ) {
68 | let mut config = HttpRequestConfig {
69 | method: None,
70 | url: None,
71 | headers: None,
72 | body: None,
73 | };
74 |
75 | // Load configuration from file, if provided
76 | if let Some(c) = config_path {
77 | if let Err(e) = read_config_file(&c).await.map(|cfg| config = cfg) {
78 | eprintln!("Error reading config: {}", e);
79 | return;
80 | }
81 | }
82 |
83 | // Parse the method and URL
84 | let (method, url) = match parse_method_and_url(&mut config, method, url) {
85 | Ok((method, url)) => (method, url),
86 | Err(e) => {
87 | eprintln!("{}", e);
88 | return;
89 | }
90 | };
91 |
92 | // Build the request
93 | let client = Client::new();
94 | let mut builder = client.request(method, &url);
95 |
96 | // Add headers if provided
97 | if let Ok(headers) = build_headers(config.headers) {
98 | builder = builder.headers(headers);
99 | } else {
100 | eprintln!("Error building headers");
101 | return;
102 | }
103 |
104 | // Add body if provided
105 | if let Some(body) = config.body {
106 | if let Ok(body_str) = serde_json::to_string(&body) {
107 | builder = builder.body(body_str);
108 | } else {
109 | eprintln!("Failed to serialize body");
110 | return;
111 | }
112 | }
113 |
114 | // Send the request and handle the response
115 | match builder.send().await {
116 | Ok(response) => handle_response(response, output).await,
117 | Err(e) => eprintln!("Http request failed: {}", e),
118 | }
119 | }
120 |
121 | // Helper function to handle HTTP response
122 | async fn handle_response(response: reqwest::Response, output: Option) {
123 | let status = response.status();
124 | if status.is_success() {
125 | if let Some(output) = output {
126 | if let Ok(bytes) = response.bytes().await {
127 | if let Ok(mut file) = File::create(&output).await {
128 | let _ = file.write_all(&bytes).await;
129 | println!("Downloaded file to: {}", output);
130 | } else {
131 | eprintln!("Failed to write the output file.");
132 | }
133 | }
134 | } else {
135 | if let Ok(text) = response.text().await {
136 | let payload = from_str(&text).unwrap_or(Value::Null);
137 | let json_output = serde_json::to_string_pretty(&payload).unwrap();
138 | println!("{}", json_output);
139 | } else {
140 | eprintln!("Failed to read response.");
141 | }
142 | }
143 | } else {
144 | eprintln!("Request failed with status: {}", status);
145 |
146 | // Attempt to read and print the error body if available
147 | if let Ok(error_body) = response.text().await {
148 | let payload = from_str(&error_body).unwrap_or(Value::Null);
149 | let json_output = serde_json::to_string_pretty(&payload).unwrap();
150 | println!("{}", json_output);
151 | } else {
152 | eprintln!("Failed to read the error response body.");
153 | }
154 | }
155 | }
156 |
157 |
158 | pub async fn http_serve(port: u16) {
159 | let addr = format!("0.0.0.0:{}", port);
160 | let listener = TcpListener::bind(&addr).await.unwrap();
161 | println!("Server listening on http://{}", addr);
162 |
163 | // Accept incoming connections in a loop
164 | loop {
165 | let (mut stream, _) = listener.accept().await.unwrap();
166 | tokio::spawn(async move {
167 | handle_client(&mut stream).await;
168 | });
169 | }
170 | }
171 |
172 | async fn handle_client(stream: &mut tokio::net::TcpStream) {
173 | let mut buffer = [0; 1024];
174 | match stream.read(&mut buffer).await {
175 | Ok(0) => return, // Connection closed
176 | Ok(_) => {
177 | if let Ok(request) = str::from_utf8(&buffer) {
178 | println!("Received request: {}", request);
179 | let response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!";
180 |
181 | if let Err(e) = stream.write_all(response.as_bytes()).await {
182 | eprintln!("Failed to send response: {}", e);
183 | }
184 |
185 | if let Err(e) = stream.flush().await {
186 | eprintln!("Failed to flush stream: {}", e);
187 | }
188 | }
189 | }
190 | Err(e) => {
191 | eprintln!("Failed to read from stream: {}", e);
192 | }
193 | }
194 |
195 | // Close the connection
196 | if let Err(e) = stream.shutdown().await {
197 | eprintln!("Failed to shutdown stream: {}", e);
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/input_utils.rs:
--------------------------------------------------------------------------------
1 | use std::io::{self, Read};
2 |
3 | pub fn args_or_readline(args: Vec) -> String{
4 | if args.is_empty() {
5 | // If no arguments are provided, read from stdin
6 | let mut input = String::new();
7 | io::stdin().read_line(&mut input).expect("Failed to read from stdin");
8 | input.trim().to_string() // Trim any whitespace from stdin input
9 | } else {
10 | // Concatenate provided arguments with spaces
11 | args.join(" ")
12 | }
13 | }
--------------------------------------------------------------------------------
/src/ip_commands.rs:
--------------------------------------------------------------------------------
1 | use reqwest;
2 | use serde_derive::Serialize;
3 | use tokio::task::JoinHandle;
4 | use std::net::Ipv4Addr;
5 | use std::str::FromStr;
6 | use std::time::Duration;
7 | use std::time::Instant;
8 |
9 | use crate::format_utils;
10 | use crate::ip_utils;
11 |
12 | #[derive(Serialize, Debug)]
13 | struct IpResponse {
14 | ip: String,
15 | }
16 |
17 | #[derive(Serialize, Debug)]
18 | struct MultiIpResponse {
19 | ips: Vec,
20 | }
21 |
22 | #[derive(Serialize, Debug)]
23 | struct OnlineResponse {
24 | online: bool,
25 | }
26 |
27 | pub async fn ip_local(json: bool) {
28 | match ip_utils::get_local_ip().await {
29 | Ok(addr) => {
30 | if json {
31 | format_utils::print_json(&IpResponse {
32 | ip: addr.to_string(),
33 | });
34 | } else {
35 | println!("{}", addr)
36 | }
37 | }
38 | Err(e) => eprintln!("Failed to get local address: {}", e),
39 | }
40 | }
41 |
42 | pub async fn ip_public(json: bool) {
43 | // Send a request to an external service that returns the public IP
44 | let response = match reqwest::get("https://api.ipify.org").await {
45 | Ok(s) => s,
46 | Err(e) => {
47 | eprintln!("Http request failed: {}", e);
48 | return;
49 | }
50 | };
51 |
52 | match response.text().await {
53 | Ok(ip_addr) => {
54 | if json {
55 | format_utils::print_json(&IpResponse { ip: ip_addr });
56 | } else {
57 | println!("{}", ip_addr)
58 | }
59 | }
60 | Err(e) => println!("{}", e),
61 | }
62 | }
63 |
64 | pub async fn ip_scan(ip_str: Option, json: bool) {
65 | let timeout = Duration::from_millis(100);
66 |
67 | let ip_str = ip_utils::to_ip_or_local(ip_str).await;
68 |
69 | let ping_tasks: Vec>> = (0..255)
70 | .filter_map(|i| {
71 | // Split the IP into segments
72 | let mut segments: Vec = ip_str.split('.').map(|s| s.to_string()).collect();
73 |
74 | // Make sure it's a valid IPv4 address (i.e., has 4 segments)
75 | if segments.len() != 4 {
76 | eprintln!("Invalid IP format: {}", ip_str);
77 | return None;
78 | }
79 |
80 | // Replace the last segment with `i`
81 | segments[3] = i.to_string();
82 |
83 | // Join the segments back into a string
84 | let target_ip_str = segments.join(".");
85 |
86 | // Parse the modified string back into an Ipv4Addr
87 | let target_ip = match Ipv4Addr::from_str(&target_ip_str) {
88 | Ok(ip) => ip,
89 | Err(_) => return None, // Skip invalid IPs
90 | };
91 |
92 | let start = Instant::now();
93 |
94 | // Create a task for each ping
95 | Some(tokio::spawn(async move {
96 | match ip_utils::ping(target_ip, timeout).await {
97 | Ok(_) => Some((target_ip, start.elapsed())),
98 | Err(_) => None,
99 | }
100 | }))
101 | })
102 | .collect();
103 |
104 | let mut ips: Vec = Vec::new();
105 | // Await all tasks and filter successful pings
106 | for task in ping_tasks {
107 | if let Ok(Some(result)) = task.await {
108 | ips.push(result.0.to_string());
109 | }
110 | }
111 |
112 | if json {
113 | format_utils::print_json(&MultiIpResponse { ips: ips });
114 | } else {
115 | println!("{}", ips.join("\n\r"))
116 | }
117 | }
118 |
119 | pub async fn ip_status(ip_str: String, json: bool) {
120 | let ip = match Ipv4Addr::from_str(&ip_str) {
121 | Ok(s) => s,
122 | Err(e) => {
123 | eprintln!("Failed to bind udp socket: {}", e);
124 | return;
125 | }
126 | };
127 |
128 | let timeout = Duration::from_millis(100); // Timeout for each port scan
129 |
130 | if json {
131 | match ip_utils::ping(ip, timeout).await {
132 | Ok(_) => format_utils::print_json(&OnlineResponse { online: true }),
133 | Err(_) => format_utils::print_json(&OnlineResponse { online: false }),
134 | }
135 | } else {
136 | match ip_utils::ping(ip, timeout).await {
137 | Ok(_) => println!("online"),
138 | Err(_) => println!("offline"),
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/ip_utils.rs:
--------------------------------------------------------------------------------
1 | use socket2::{Domain, Protocol, Socket, Type};
2 | use std::mem::MaybeUninit;
3 | use std::net::SocketAddr;
4 | use std::net::{IpAddr, Ipv4Addr};
5 | use std::time::Duration;
6 | use tokio::net::{TcpStream, UdpSocket};
7 | use tokio::time::timeout;
8 |
9 | // ICMP Echo Request Type and Code
10 | const ICMP_ECHO_REQUEST: u8 = 8;
11 | const ICMP_ECHO_REPLY: u8 = 0;
12 |
13 | fn checksum(data: &[u8]) -> u16 {
14 | let mut sum = 0u32;
15 | let chunks = data.chunks_exact(2);
16 | for chunk in chunks {
17 | sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
18 | }
19 | if let Some(&byte) = data.chunks_exact(2).remainder().first() {
20 | sum += (byte as u32) << 8;
21 | }
22 | while (sum >> 16) != 0 {
23 | sum = (sum & 0xFFFF) + (sum >> 16);
24 | }
25 | !(sum as u16)
26 | }
27 |
28 | fn build_icmp_packet(sequence: u16) -> Vec {
29 | let mut packet = vec![0u8; 8]; // ICMP Header is 8 bytes
30 | packet[0] = ICMP_ECHO_REQUEST; // Type (Echo Request)
31 | packet[1] = 0; // Code
32 | packet[2] = 0; // Checksum (placeholder)
33 | packet[3] = 0;
34 | packet[4] = (sequence >> 8) as u8; // Identifier
35 | packet[5] = (sequence & 0xff) as u8;
36 | packet[6] = 0; // Sequence number
37 | packet[7] = sequence as u8;
38 |
39 | let checksum_val = checksum(&packet);
40 | packet[2] = (checksum_val >> 8) as u8;
41 | packet[3] = (checksum_val & 0xff) as u8;
42 |
43 | packet
44 | }
45 |
46 | pub async fn ping(target_ip: Ipv4Addr, timeout: Duration) -> Result<(), std::io::Error> {
47 | // Create a raw socket for ICMP (requires root/admin privileges)
48 | let socket = Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))?;
49 |
50 | // Set a timeout for the socket
51 | socket.set_read_timeout(Some(timeout))?;
52 |
53 | // Destination address
54 | let socket_addr = socket2::SockAddr::from(std::net::SocketAddrV4::new(target_ip, 0));
55 |
56 | // Build an ICMP echo request packet
57 | let icmp_packet = build_icmp_packet(1); // Sequence number 1
58 |
59 | // Send the ICMP packet
60 | socket.send_to(&icmp_packet, &socket_addr)?;
61 |
62 | // Buffer to receive the reply
63 | let mut buf = [const { MaybeUninit::::uninit() }; 32];
64 |
65 | // Receive the ICMP echo reply
66 | match socket.recv_from(&mut buf) {
67 | Ok((size, _)) => {
68 | // SAFETY: We know `size` bytes were written to the buffer, so we can safely assume these bytes are initialized
69 | let initialized_buf =
70 | unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const u8, size) };
71 |
72 | if size >= 20 && initialized_buf[20] == ICMP_ECHO_REPLY {
73 | return Ok(());
74 | } else {
75 | return Err(std::io::Error::new(
76 | std::io::ErrorKind::Other,
77 | "Received non-echo reply packet",
78 | ));
79 | }
80 | }
81 | Err(e) => {
82 | // Forward the error instead of printing it
83 | return Err(e);
84 | }
85 | }
86 | }
87 |
88 | pub async fn can_connect(
89 | ip: Ipv4Addr,
90 | port: u16,
91 | timeout_duration: Duration,
92 | ) -> Result {
93 | // Define the socket address (IP + port)
94 | let address = SocketAddr::new(IpAddr::V4(ip), port);
95 |
96 | // Attempt to connect with a timeout
97 | let result = timeout(timeout_duration, TcpStream::connect(address)).await;
98 |
99 | match result {
100 | Ok(Ok(_)) => Ok(true), // Connection succeeded
101 | Ok(Err(e)) => Err(e), // Connection failed
102 | Err(_) => Ok(false), // Timeout error
103 | }
104 | }
105 |
106 | pub async fn get_local_ip() -> Result {
107 | let socket = UdpSocket::bind("0.0.0.0:0").await?;
108 | socket.connect("8.8.8.8:80").await?;
109 | let socket_addr = socket.local_addr()?;
110 | return Ok(socket_addr.ip());
111 | }
112 |
113 | pub async fn to_ip_or_local(ip: Option) -> String {
114 | // Check if the provided IP is Some and non-empty
115 | if let Some(t) = ip {
116 | if !t.is_empty() {
117 | return t; // Return the provided IP
118 | }
119 | }
120 |
121 | // If the provided IP is None or empty, get the local IP
122 | match get_local_ip().await {
123 | Ok(addr) => addr.to_string(), // Return the local address as a string
124 | Err(e) => {
125 | eprintln!("Failed to get local address: {}", e);
126 | String::from("127.0.0.1") // Return a default value if an error occurs
127 | }
128 | }
129 | }
--------------------------------------------------------------------------------
/src/json_commands.rs:
--------------------------------------------------------------------------------
1 | use std::io::{self, Read};
2 | use serde_derive::Serialize;
3 | use serde_json::{from_str, Value};
4 | use base64::prelude::*;
5 | use std::str;
6 | use base64::engine::general_purpose::URL_SAFE;
7 |
8 | pub async fn json_extract(property: String) {
9 | let mut input = String::new();
10 | io::stdin().read_to_string(&mut input).expect("Failed to read from stdin");
11 | let json: Value = serde_json::from_str(&input).expect("Failed to parse JSON");
12 |
13 | let filter: &str = &format!("/{}", property);
14 | match json.pointer(filter) {
15 | Some(value) => match value {
16 | Value::String(s) => println!("{}", s), // Print raw string if it's a JSON string
17 | _ => println!("{}", value), // Print normally for non-string values
18 | },
19 | None => eprintln!("Field not found in the provided JSON"),
20 | }
21 | }
22 |
23 | #[derive(Serialize)]
24 | struct JwtDecoded {
25 | header: Value,
26 | payload: Value,
27 | }
28 |
29 | pub fn json_decode_jwt(token: &str) {
30 | let parts: Vec<&str> = token.split('.').collect();
31 | if parts.len() != 3 {
32 | println!("Invalid token format");
33 | return;
34 | }
35 |
36 | let header = parts[0];
37 | let payload = parts[1];
38 |
39 | // Decode the header
40 | let decoded_header = decode_jwt_part(header).unwrap_or_else(|err| {
41 | println!("Error decoding header: {}", err);
42 | "".to_string()
43 | });
44 |
45 | // Decode the payload
46 | let decoded_payload = decode_jwt_part(payload).unwrap_or_else(|err| {
47 | println!("Error decoding payload: {}", err);
48 | "".to_string()
49 | });
50 |
51 | // Create a struct to hold the decoded values
52 | let decoded_jwt = JwtDecoded {
53 | header: from_str(&decoded_header).unwrap_or(Value::Null),
54 | payload: from_str(&decoded_payload).unwrap_or(Value::Null),
55 | };
56 |
57 | // Serialize the struct to JSON
58 | let json_output = serde_json::to_string_pretty(&decoded_jwt).unwrap();
59 |
60 | // Print the JSON output
61 | println!("{}", json_output);
62 | }
63 |
64 |
65 | fn decode_jwt_part(part: &str) -> Result {
66 | // Ensure correct padding for Base64 URL encoding
67 | let mut padding = String::new();
68 | let missing_padding = 4 - (part.len() % 4);
69 | if missing_padding != 4 {
70 | padding.push_str(&"=".repeat(missing_padding));
71 | }
72 |
73 | // Create the Base64 URL string with proper padding
74 | let base64_url_encoded = format!("{}{}", part, padding);
75 |
76 | // Decode the Base64 string
77 | match URL_SAFE.decode(&base64_url_encoded) {
78 | Ok(decoded_bytes) => {
79 | // Convert bytes to string
80 | str::from_utf8(&decoded_bytes)
81 | .map(|s| s.to_string())
82 | .map_err(|e| e.to_string())
83 | }
84 | Err(e) => Err(format!("Base64 decode error: {}", e)),
85 | }
86 | }
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod csv_utils;
2 | pub mod cpu_commands;
3 | pub mod format_utils;
4 | pub mod ip_commands;
5 | pub mod ip_utils;
6 | pub mod mem_commands;
7 | pub mod port_commands;
8 | pub mod disk_commands;
9 | pub mod network_commands;
10 | pub mod http_commands;
11 | pub mod json_commands;
12 | pub mod csv_commands;
13 | pub mod text_commands;
14 | pub mod file_commands;
15 | pub mod time_commands;
16 | pub mod bits_commands;
17 | pub mod math_utils;
18 | pub mod math_commands;
19 | pub mod graph_utils;
20 | pub mod process_commands;
21 | pub mod bits_utils;
22 | pub mod input_utils;
23 | pub mod env_commands;
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | use aid::{
2 | bits_commands, cpu_commands, csv_commands, disk_commands, env_commands, file_commands,
3 | http_commands, input_utils, ip_commands, json_commands, math_commands, mem_commands,
4 | network_commands, port_commands, process_commands, text_commands, time_commands,
5 | };
6 | use clap::{Parser, Subcommand};
7 |
8 | #[derive(Parser)]
9 | #[command(author, version, about, long_about = None)]
10 | struct Args {
11 | #[command(subcommand)]
12 | cmd: Commands,
13 | }
14 |
15 | #[derive(Subcommand, Debug, Clone)]
16 | enum Commands {
17 | #[command(subcommand, about = "HTTP functions")]
18 | Http(HttpCommands),
19 | #[command(subcommand, about = "IP information / scanning")]
20 | Ip(IpCommands),
21 | #[command(subcommand, about = "Port information / scanning")]
22 | Port(PortCommands),
23 | #[command(subcommand, about = "System cpu information")]
24 | Cpu(CpuCommands),
25 | #[command(subcommand, about = "System memory information")]
26 | Mem(MemoryCommands),
27 | #[command(subcommand, about = "System disk information")]
28 | Disk(DiskCommands),
29 | #[command(subcommand, about = "System network information")]
30 | Network(NetworkCommands),
31 | #[command(subcommand, about = "JSON parsing / extraction functions")]
32 | Json(JsonCommands),
33 | #[command(subcommand, about = "CSV searching / filtering")]
34 | Csv(CsvCommands),
35 | #[command(subcommand, about = "Text manipulation functions")]
36 | Text(TextCommands),
37 | #[command(subcommand, about = "File information")]
38 | File(FileCommands),
39 | #[command(subcommand, about = "Time related functions")]
40 | Time(TimeCommands),
41 | #[command(subcommand, about = "Math functions")]
42 | Math(MathCommands),
43 | #[command(subcommand, about = "Bit manipulation functions")]
44 | Bits(BitsCommands),
45 | #[command(subcommand, about = "Process monitoring functions")]
46 | Process(ProcessCommands),
47 | #[command(subcommand, about = "Environment information")]
48 | Env(EnvCommands),
49 | }
50 | #[derive(Subcommand, Debug, Clone)]
51 | enum IpCommands {
52 | #[command(about = "Show my local IP address")]
53 | Local {
54 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
55 | help = "Output the local IP address in JSON format.")]
56 | json: bool,
57 | },
58 |
59 | #[command(about = "Show my public IP address")]
60 | Public {
61 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
62 | help = "Output the public IP address in JSON format.")]
63 | json: bool,
64 | },
65 |
66 | #[command(about = "Scan a specified IP address subnet for active ip addresses")]
67 | Scan {
68 | #[arg(
69 | short = 'i',
70 | long = "ip",
71 | default_value = "",
72 | help = "The IP subnet to scan. If not provided, the local subnet will be used."
73 | )]
74 | ip: Option,
75 |
76 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
77 | help = "Output scan results in JSON format.")]
78 | json: bool,
79 | },
80 |
81 | #[command(about = "Try to connect to the specified IP address")]
82 | Status {
83 | #[arg(
84 | short = 'i',
85 | long = "ip",
86 | help = "The IP address to check the status of."
87 | )]
88 | ip: String,
89 |
90 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
91 | help = "Output status in JSON format.")]
92 | json: bool,
93 | },
94 | }
95 |
96 | #[derive(Subcommand, Debug, Clone)]
97 | enum PortCommands {
98 | #[command(about = "Check if the specified port is 'open' or 'closed'.")]
99 | Status {
100 | #[arg(short = 'i', long = "ip", help = "The IP address to check (optional).")]
101 | ip: Option,
102 |
103 | #[arg(short = 'p', help = "The port number to check the status of.")]
104 | port: u16,
105 |
106 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
107 | help = "Output port status in JSON format.")]
108 | json: bool,
109 | },
110 |
111 | #[command(about = "Scan for open ports on a specified IP address")]
112 | Scan {
113 | #[arg(short = 'i', long = "ip", help = "The IP address to scan (optional).")]
114 | ip: Option,
115 |
116 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
117 | help = "Output scan results in JSON format.")]
118 | json: bool,
119 | },
120 | }
121 |
122 | #[derive(Subcommand, Debug, Clone)]
123 | enum CpuCommands {
124 | #[command(about = "Show CPU information")]
125 | Info {
126 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
127 | help = "Output CPU information in JSON format.")]
128 | json: bool,
129 | },
130 |
131 | #[command(about = "Monitor CPU usage")]
132 | Usage {
133 | #[arg(short = 'w', long = "watch", action = clap::ArgAction::SetTrue,
134 | help = "Continuously monitor CPU usage.")]
135 | watch: bool,
136 |
137 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
138 | help = "Output CPU usage in JSON format.")]
139 | json: bool,
140 |
141 | #[arg(short = 'p', long = "plot", action = clap::ArgAction::SetTrue,
142 | help = "Plot total % CPU usage.")]
143 | plot: bool,
144 | },
145 | }
146 |
147 | #[derive(Subcommand, Debug, Clone)]
148 | enum MemoryCommands {
149 | #[command(about = "Monitor memory usage")]
150 | Usage {
151 | #[arg(short = 'w', long = "watch", action = clap::ArgAction::SetTrue,
152 | help = "Continuously monitor memory usage.")]
153 | watch: bool,
154 |
155 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
156 | help = "Output memory usage in JSON format.")]
157 | json: bool,
158 |
159 | #[arg(short = 'p', long = "plot", action = clap::ArgAction::SetTrue,
160 | help = "Plot total % mem usage.")]
161 | plot: bool,
162 | },
163 | }
164 |
165 | #[derive(Subcommand, Debug, Clone)]
166 | enum DiskCommands {
167 | #[command(about = "Show disk information")]
168 | Info {
169 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
170 | help = "Output disk information in JSON format.")]
171 | json: bool,
172 | },
173 | }
174 |
175 | #[derive(Subcommand, Debug, Clone)]
176 | enum NetworkCommands {
177 | #[command(about = "Show network information")]
178 | Info {
179 | #[arg(short = 'j', long = "json", action = clap::ArgAction::SetTrue,
180 | help = "Output network information in JSON format.")]
181 | json: bool,
182 | },
183 | #[command(about = "Monitor network usage")]
184 | Usage {
185 | #[arg(short = 'w', long = "watch", action = clap::ArgAction::SetTrue,
186 | help = "Continuously monitor network usage.")]
187 | watch: bool,
188 | },
189 | }
190 |
191 | #[derive(Subcommand, Debug, Clone)]
192 | enum HttpCommands {
193 | #[command(about = "Make a HTTP request")]
194 | Req {
195 | #[arg(
196 | short = 'm',
197 | long = "method",
198 | help = "Specify the HTTP method (e.g., GET, POST)."
199 | )]
200 | method: Option,
201 |
202 | #[arg(
203 | short = 'u',
204 | long = "url",
205 | help = "Specify the URL for the HTTP request."
206 | )]
207 | url: Option,
208 |
209 | #[arg(
210 | short = 'c',
211 | long = "config",
212 | help = "Path to a configuration file for the request. Specify: method, url, body, headers in json format."
213 | )]
214 | config: Option,
215 |
216 | #[arg(
217 | short = 'o',
218 | long = "output",
219 | help = "If specified saves http response body to a file at the given path."
220 | )]
221 | output: Option,
222 | },
223 |
224 | #[command(about = "Start a HTTP server (GET: 0.0.0.0:80 -> 'Hello, World!')")]
225 | Serve {
226 | #[arg(
227 | short = 'p',
228 | long = "port",
229 | default_value = "80",
230 | help = "Specify the port for the HTTP server (default is 80)."
231 | )]
232 | port: u16,
233 | },
234 | }
235 |
236 | #[derive(Subcommand, Debug, Clone)]
237 | enum JsonCommands {
238 | #[command(about = "Extract a property from JSON data")]
239 | Extract {
240 | #[arg(
241 | short = 'p',
242 | long = "prop",
243 | help = "Specify the property to extract from the JSON."
244 | )]
245 | property: String,
246 | },
247 | #[command(about = "Decode a JWT")]
248 | JwtDecode {
249 | #[arg(trailing_var_arg = true)]
250 | c_args: Vec,
251 | },
252 | }
253 |
254 | #[derive(Subcommand, Debug, Clone)]
255 | enum CsvCommands {
256 | #[command(about = "Sql search over csv")]
257 | Search {
258 | #[arg(short = 's', long = "sql", help = "Sql query.")]
259 | sql: String,
260 | #[arg(short = 'o', long = "output", help = "Output file path.")]
261 | output: Option,
262 | },
263 | }
264 |
265 | #[derive(Subcommand, Debug, Clone)]
266 | enum TextCommands {
267 | #[command(about = "base64 encode")]
268 | Base64Encode {
269 | #[arg(trailing_var_arg = true)]
270 | c_args: Vec,
271 | },
272 |
273 | #[command(about = "base64 decode")]
274 | Base64Decode {
275 | #[arg(trailing_var_arg = true)]
276 | c_args: Vec,
277 | },
278 | #[command(about = "url encodes a string")]
279 | UrlEncode {
280 | #[arg(trailing_var_arg = true)]
281 | c_args: Vec,
282 | },
283 | #[command(about = "decodes a url encoded string")]
284 | UrlDecode {
285 | #[arg(trailing_var_arg = true)]
286 | c_args: Vec,
287 | },
288 | #[command(about = "search a text file for lines that match a regex")]
289 | Regex {
290 | #[arg(short = 'f', long = "file", help = "Input text file to search.")]
291 | file: String,
292 | #[arg(short = 'r', long = "regex", help = "regex search pattern.")]
293 | regex: String,
294 | },
295 |
296 | #[command(about = "print the specified range of lines.")]
297 | Lines {
298 | #[arg(short = 'i', long = "input", help = "Input text file to search.")]
299 | file: String,
300 | #[arg(short = 's', long = "start", help = "first line to print")]
301 | start: Option,
302 | #[arg(short = 'e', long = "end", help = "last line to print")]
303 | end: Option,
304 | #[arg(
305 | short = 'f',
306 | long = "first",
307 | help = "number of lines from the start of the file to print"
308 | )]
309 | head: Option,
310 | #[arg(
311 | short = 'l',
312 | long = "last",
313 | help = "number of lines from the end of the file to print"
314 | )]
315 | tail: Option,
316 | },
317 |
318 | #[command(about = "Generates a random guid")]
319 | Guid,
320 | #[command(about = "Find and replace text using a regex pattern in the given input string.")]
321 | Replace {
322 | #[arg(
323 | short = 'm',
324 | long = "match",
325 | help = "Regex pattern to match substrings to replace."
326 | )]
327 | pattern: String,
328 |
329 | #[arg(
330 | short = 'r',
331 | long = "replace",
332 | help = "Replacement string for matched substrings."
333 | )]
334 | replace: String,
335 |
336 | #[arg(
337 | trailing_var_arg = true,
338 | )]
339 | c_args: Vec,
340 | },
341 |
342 | #[command(about = "Count occurrences of a regex pattern in the given input string.")]
343 | Count {
344 | #[arg(
345 | short = 'm',
346 | long = "match",
347 | help = "Regex pattern to match and count in the input."
348 | )]
349 | pattern: String,
350 |
351 | #[arg(
352 | trailing_var_arg = true,
353 | )]
354 | c_args: Vec,
355 | },
356 | }
357 |
358 | #[derive(Subcommand, Debug, Clone)]
359 | enum FileCommands {
360 | #[command(about = "prints file metadata")]
361 | Info {
362 | #[arg(short = 'f', long = "file", help = "Input file.")]
363 | file: String,
364 | },
365 | #[command(about = "calculate a files Md5 checksum")]
366 | Md5 {
367 | #[arg(short = 'f', long = "file", help = "Input file.")]
368 | file: String,
369 | },
370 | #[command(about = "calculate a files Sha1 checksum")]
371 | Sha1 {
372 | #[arg(short = 'f', long = "file", help = "Input file.")]
373 | file: String,
374 | },
375 | #[command(about = "calculate a files Sha256 checksum")]
376 | Sha256 {
377 | #[arg(short = 'f', long = "file", help = "Input file.")]
378 | file: String,
379 | },
380 |
381 | #[command(about = "zips the files in the source directory")]
382 | Zip {
383 | #[arg(short = 'd', long = "dir", help = "source directory.")]
384 | dir: String,
385 | #[arg(short = 'f', long = "file", help = "output zip file.")]
386 | file: String,
387 | },
388 | }
389 |
390 | #[derive(Subcommand, Debug, Clone)]
391 | enum TimeCommands {
392 | #[command(about = "Display unix timestamp")]
393 | Unix {
394 | #[arg(short = 'm', long = "milli", action = clap::ArgAction::SetTrue,
395 | help = "Output the timestamp as unix milliseconds.")]
396 | milli: bool,
397 | #[arg(short = 'd', long = "dt", help = "Use the specified datetime.")]
398 | dt: Option,
399 | },
400 | #[command(about = "Display the datetime")]
401 | Dt {
402 | #[arg(short = 'l', long = "local", action = clap::ArgAction::SetTrue,
403 | help = "Use the local datetime.")]
404 | local: bool,
405 | #[arg(
406 | short = 'u',
407 | long = "unix",
408 | help = "Use the specified unix second timestamp."
409 | )]
410 | unix: Option,
411 | #[arg(short = 'r', long = "rfc", action = clap::ArgAction::SetTrue,
412 | help = "Output the datetime in Rfc3339 format.")]
413 | rfc: bool,
414 | },
415 | #[command(about = "Describes a chron job")]
416 | Chron {
417 | #[arg(trailing_var_arg = true)]
418 | c_args: Vec,
419 | },
420 | #[command(about = "Start a countdown timer for the given minutes / seconds")]
421 | CountDown {
422 | #[arg(trailing_var_arg = true)]
423 | c_args: Vec,
424 | },
425 | }
426 |
427 | #[derive(Subcommand, Debug, Clone)]
428 | enum MathCommands {
429 | #[command(about = "Evaluates a math expression")]
430 | Eval {
431 | #[arg(trailing_var_arg = true)]
432 | c_args: Vec,
433 | },
434 | #[command(about = "Plot a math expression")]
435 | Plot {
436 | #[arg(long = "start", help = "Start x coord.", allow_hyphen_values = true)]
437 | start_x: f32,
438 | #[arg(long = "end", help = "End x coord.", allow_hyphen_values = true)]
439 | end_x: f32,
440 | #[arg(
441 | short = 's',
442 | long = "step",
443 | help = "x step size.",
444 | allow_hyphen_values = true
445 | )]
446 | step_x: f32,
447 | #[arg(trailing_var_arg = true)]
448 | c_args: Vec,
449 | },
450 | }
451 |
452 | #[derive(Subcommand, Debug, Clone)]
453 | enum BitsCommands {
454 | #[command(
455 | about = "Evaluates a bitwise expression, converts base, visualize binary / display info"
456 | )]
457 | Eval {
458 | #[arg(
459 | short = 'i',
460 | long = "info",
461 | help = "Output the bitboard representation."
462 | )]
463 | board: bool,
464 | #[arg(
465 | short = 'c',
466 | long = "chess",
467 | help = "Output the chess bitboard representation."
468 | )]
469 | chess_board: bool,
470 | #[arg(short = 'b', long = "bin", help = "Output the result in binary.")]
471 | binary: bool,
472 | #[arg(long = "hex", help = "Output the result in hex.")]
473 | hex: bool,
474 | #[arg(trailing_var_arg = true)]
475 | c_args: Vec,
476 | },
477 | }
478 |
479 | #[derive(Subcommand, Debug, Clone)]
480 | enum ProcessCommands {
481 | #[command(about = "Display process usage")]
482 | Usage {
483 | #[arg(
484 | short = 'f',
485 | long = "filter",
486 | help = "filter the results by process name regex."
487 | )]
488 | filter: Option,
489 | #[arg(
490 | short = 's',
491 | long = "sort",
492 | help = "Sort the results by [cpu, mem, disk]"
493 | )]
494 | sort_by: Option,
495 | #[arg(
496 | short = 'l',
497 | long = "limit",
498 | help = "Limit the number of results to display."
499 | )]
500 | limit: Option,
501 | #[arg(short = 'w', long = "watch", help = "Continuously monitor the processes.", action = clap::ArgAction::SetTrue)]
502 | watch: bool,
503 | },
504 | }
505 |
506 | #[derive(Subcommand, Debug, Clone)]
507 | enum EnvCommands {
508 | #[command(about = "Filter / Display environment variables")]
509 | Vars {
510 | #[arg(
511 | short = 'k',
512 | long = "kfilter",
513 | help = "filter the results by a key regex."
514 | )]
515 | kfilter: Option,
516 | #[arg(
517 | short = 'v',
518 | long = "vfilter",
519 | help = "filter the results by a value regex."
520 | )]
521 | vfilter: Option,
522 | },
523 | }
524 |
525 | #[tokio::main]
526 | async fn main() {
527 | let args = Args::parse();
528 | match args.cmd {
529 | Commands::Ip(command) => match command {
530 | IpCommands::Local { json } => ip_commands::ip_local(json).await,
531 | IpCommands::Public { json } => ip_commands::ip_public(json).await,
532 | IpCommands::Scan { ip, json } => ip_commands::ip_scan(ip, json).await,
533 | IpCommands::Status { ip, json } => ip_commands::ip_status(ip, json).await,
534 | },
535 | Commands::Port(command) => match command {
536 | PortCommands::Scan { ip, json } => port_commands::port_scan(ip, json).await,
537 | PortCommands::Status { ip, port, json } => {
538 | port_commands::port_status(ip, port, json).await
539 | }
540 | },
541 | Commands::Cpu(sub_command) => match sub_command {
542 | CpuCommands::Info { json } => cpu_commands::system_cpu_info(json),
543 | CpuCommands::Usage { watch, json, plot } => {
544 | cpu_commands::system_cpu_usage(watch, json, plot).await
545 | }
546 | },
547 | Commands::Mem(sub_command) => match sub_command {
548 | MemoryCommands::Usage { watch, json, plot } => {
549 | mem_commands::system_mem_usage(watch, json, plot).await
550 | }
551 | },
552 | Commands::Disk(sub_command) => match sub_command {
553 | DiskCommands::Info { json } => disk_commands::system_disk_info(json).await,
554 | },
555 | Commands::Network(sub_command) => match sub_command {
556 | NetworkCommands::Info { json } => network_commands::system_network_info(json).await,
557 | NetworkCommands::Usage { watch } => network_commands::system_network_usage(watch),
558 | },
559 | Commands::Http(sub_command) => match sub_command {
560 | HttpCommands::Req {
561 | method,
562 | url,
563 | config,
564 | output,
565 | } => http_commands::http_request(method, url, config, output).await,
566 | HttpCommands::Serve { port } => http_commands::http_serve(port).await,
567 | },
568 |
569 | Commands::Json(sub_command) => match sub_command {
570 | JsonCommands::Extract { property } => json_commands::json_extract(property).await,
571 | JsonCommands::JwtDecode { c_args } => {
572 | json_commands::json_decode_jwt(&input_utils::args_or_readline(c_args))
573 | }
574 | },
575 |
576 | Commands::Csv(sub_command) => match sub_command {
577 | CsvCommands::Search { sql, output } => csv_commands::sql_search(sql, output).await,
578 | },
579 |
580 | Commands::Text(sub_command) => match sub_command {
581 | TextCommands::Base64Encode { c_args } => {
582 | text_commands::base64_encode(input_utils::args_or_readline(c_args))
583 | }
584 | TextCommands::Base64Decode { c_args } => {
585 | text_commands::base64_decode(input_utils::args_or_readline(c_args))
586 | }
587 | TextCommands::Regex { file, regex } => text_commands::regex_search(file, regex),
588 | TextCommands::Lines {
589 | file,
590 | start,
591 | end,
592 | head,
593 | tail,
594 | } => text_commands::print_lines(file, start, end, head, tail),
595 | TextCommands::Guid => {
596 | text_commands::guid()
597 | }
598 | TextCommands::UrlEncode {c_args} => {
599 | text_commands::url_encode(&input_utils::args_or_readline(c_args))
600 | }
601 | TextCommands::UrlDecode {c_args} => {
602 | text_commands::url_decode(&input_utils::args_or_readline(c_args))
603 | },
604 | TextCommands::Replace {pattern, replace, c_args} => {
605 | text_commands::replace(input_utils::args_or_readline(c_args), &pattern, &replace)
606 | },
607 | TextCommands::Count {pattern, c_args} => {
608 | text_commands::count(input_utils::args_or_readline(c_args), &pattern)
609 | }
610 | },
611 |
612 | Commands::File(sub_command) => match sub_command {
613 | FileCommands::Info { file } => file_commands::file_info(file),
614 | FileCommands::Md5 { file } => file_commands::md5_checksum(file),
615 | FileCommands::Sha1 { file } => file_commands::sha1_checksum(file),
616 | FileCommands::Sha256 { file } => file_commands::sha256_checksum(file),
617 | FileCommands::Zip { dir, file } => file_commands::zip_directory(dir, file),
618 | },
619 | Commands::Time(sub_command) => match sub_command {
620 | TimeCommands::Unix { milli, dt } => time_commands::unix_timestamp(milli, dt),
621 | TimeCommands::Dt { local, rfc, unix } => time_commands::date_time(local, rfc, unix),
622 | TimeCommands::Chron { c_args } => {
623 | time_commands::chron_tostring(input_utils::args_or_readline(c_args))
624 | }
625 | TimeCommands::CountDown { c_args } => {
626 | time_commands::countdown(input_utils::args_or_readline(c_args))
627 | }
628 | },
629 | Commands::Math(sub_command) => match sub_command {
630 | MathCommands::Eval { c_args } => {
631 | math_commands::evaluate(input_utils::args_or_readline(c_args))
632 | }
633 | MathCommands::Plot {
634 | start_x,
635 | end_x,
636 | step_x,
637 | c_args,
638 | } => math_commands::plot(
639 | start_x,
640 | end_x,
641 | step_x,
642 | input_utils::args_or_readline(c_args),
643 | ),
644 | },
645 | Commands::Bits(sub_command) => match sub_command {
646 | BitsCommands::Eval {
647 | board,
648 | chess_board,
649 | binary,
650 | hex,
651 | c_args,
652 | } => bits_commands::evaluate(
653 | board,
654 | chess_board,
655 | binary,
656 | hex,
657 | input_utils::args_or_readline(c_args),
658 | ),
659 | },
660 | Commands::Process(sub_command) => match sub_command {
661 | ProcessCommands::Usage {
662 | filter,
663 | sort_by,
664 | limit,
665 | watch,
666 | } => process_commands::system_process_info(filter, sort_by, limit, watch),
667 | },
668 | Commands::Env(sub_command) => match sub_command {
669 | EnvCommands::Vars { kfilter, vfilter } => {
670 | env_commands::print_env_vars(kfilter, vfilter)
671 | }
672 | },
673 | }
674 | }
675 |
--------------------------------------------------------------------------------
/src/math_commands.rs:
--------------------------------------------------------------------------------
1 | use crate::{graph_utils, math_utils};
2 | pub fn evaluate(expression: String) {
3 | match math_utils::parse_expr(&expression) {
4 | Ok((_, ast)) => {
5 | println!("{}", math_utils::evaluate(&ast, 0.0));
6 | }
7 | Err(e) => println!("Error parsing expression: {:?}", e),
8 | }
9 | }
10 |
11 | pub fn plot(startx: f32, endx: f32, step: f32, expression: String) {
12 | // Example points
13 | let points = graph_utils::generate_points(startx, endx, step, &expression);
14 | // Plot the chart
15 | graph_utils::plot_chart(&points, expression);
16 | }
--------------------------------------------------------------------------------
/src/math_utils.rs:
--------------------------------------------------------------------------------
1 | use nom::{
2 | branch::alt,
3 | bytes::complete::tag,
4 | character::complete::{char, digit1, multispace0},
5 | combinator::{map, opt, recognize},
6 | sequence::{delimited, preceded, tuple},
7 | IResult,
8 | };
9 | use std::str::FromStr;
10 |
11 | // Define the AST for the expressions
12 | #[derive(Debug)]
13 | pub enum Expr {
14 | Number(f64),
15 | Variable, // New variant for the variable x
16 | NegateOp(Box),
17 | AbsOp(Box),
18 | BinaryOp(Box, Op, Box),
19 | TrigOp(TrigFunc, Box),
20 | Log2Op(Box),
21 | Log10Op(Box),
22 | Ln(Box),
23 | Sqrt(Box),
24 | Pi,
25 | E,
26 | }
27 |
28 | #[derive(Debug)]
29 | pub enum Op {
30 | Add,
31 | Sub,
32 | Mul,
33 | Div,
34 | Pow,
35 | Mod,
36 | }
37 |
38 | #[derive(Debug)]
39 | pub enum TrigFunc {
40 | Sin,
41 | aSin,
42 | Sinh,
43 | Cos,
44 | aCos,
45 | Cosh,
46 | Tan,
47 | aTan,
48 | Tanh,
49 | }
50 |
51 | // A parser for floating-point numbers
52 | fn parse_number(input: &str) -> IResult<&str, Expr> {
53 | let (input, number_str) = recognize(tuple((
54 | preceded(multispace0, digit1), // integer part
55 | opt(preceded(char('.'), digit1)), // optional fractional part
56 | )))(input)?;
57 |
58 | let number = f64::from_str(number_str.trim()).unwrap();
59 | Ok((input, Expr::Number(number)))
60 | }
61 |
62 | // A parser for unary operations (e.g., negation)
63 | fn parse_negate(input: &str) -> IResult<&str, Expr> {
64 | let (input, _) = preceded(multispace0, char('-'))(input)?;
65 | let (input, expr) = preceded(multispace0, parse_factor)(input)?;
66 | Ok((input, Expr::NegateOp(Box::new(expr))))
67 | }
68 |
69 | fn parse_abs(input: &str) -> IResult<&str, Expr> {
70 | let (input, _) = preceded(multispace0, tag("abs"))(input)?;
71 | let (input, expr) = preceded(multispace0, parse_factor)(input)?;
72 | Ok((input, Expr::AbsOp(Box::new(expr))))
73 | }
74 |
75 | // A parser for pi (π)
76 | fn parse_pi(input: &str) -> IResult<&str, Expr> {
77 | preceded(multispace0, map(tag("pi"), |_| Expr::Pi))(input)
78 | }
79 |
80 | // A parser for e (Euler's number)
81 | fn parse_e(input: &str) -> IResult<&str, Expr> {
82 | preceded(multispace0, map(tag("e"), |_| Expr::E))(input)
83 | }
84 |
85 | // A parser for the variable 'x'
86 | fn parse_variable(input: &str) -> IResult<&str, Expr> {
87 | preceded(multispace0, map(tag("x"), |_| Expr::Variable))(input)
88 | }
89 |
90 | // A parser for trigonometric functions
91 | fn parse_trig_func(input: &str) -> IResult<&str, Expr> {
92 | let (input, trig_func) = preceded(
93 | multispace0,
94 | alt((
95 | map(tag("sinh"), |_| TrigFunc::Sinh),
96 | map(tag("sin"), |_| TrigFunc::Sin),
97 | map(tag("asin"), |_| TrigFunc::aSin),
98 | map(tag("cos"), |_| TrigFunc::Cos),
99 | map(tag("cosh"), |_| TrigFunc::Cosh),
100 | map(tag("acos"), |_| TrigFunc::aCos),
101 | map(tag("tanh"), |_| TrigFunc::Tanh),
102 | map(tag("tan"), |_| TrigFunc::Tan),
103 | map(tag("atan"), |_| TrigFunc::aTan),
104 | )),
105 | )(input)?;
106 |
107 | let (input, expr) = preceded(multispace0, delimited(char('('), parse_expr, char(')')))(input)?;
108 | Ok((input, Expr::TrigOp(trig_func, Box::new(expr))))
109 | }
110 |
111 | fn parse_log2_func(input: &str) -> IResult<&str, Expr> {
112 | let (input, _) = preceded(multispace0, tag("log2"))(input)?;
113 |
114 | let (input, expr) = preceded(multispace0, delimited(char('('), parse_expr, char(')')))(input)?;
115 | Ok((input, Expr::Log2Op(Box::new(expr))))
116 | }
117 |
118 | fn parse_log10_func(input: &str) -> IResult<&str, Expr> {
119 | let (input, _) = preceded(multispace0, tag("log10"))(input)?;
120 |
121 | let (input, expr) = preceded(multispace0, delimited(char('('), parse_expr, char(')')))(input)?;
122 | Ok((input, Expr::Log2Op(Box::new(expr))))
123 | }
124 |
125 | fn parse_ln_func(input: &str) -> IResult<&str, Expr> {
126 | let (input, _) = preceded(multispace0, tag("ln"))(input)?;
127 |
128 | let (input, expr) = preceded(multispace0, delimited(char('('), parse_expr, char(')')))(input)?;
129 | Ok((input, Expr::Ln(Box::new(expr))))
130 | }
131 |
132 | fn parse_sqrt_func(input: &str) -> IResult<&str, Expr> {
133 | let (input, _) = preceded(multispace0, tag("sqrt"))(input)?;
134 |
135 | let (input, expr) = preceded(multispace0, delimited(char('('), parse_expr, char(')')))(input)?;
136 | Ok((input, Expr::Ln(Box::new(expr))))
137 | }
138 |
139 | // A parser for multiplication, division, and other operators
140 | fn parse_term(input: &str) -> IResult<&str, Expr> {
141 | let (input, mut left) = parse_factor(input)?; // Start with parsing a factor
142 | let mut input = input;
143 |
144 | while let Ok((next_input, op)) = parse_mul_div_op(input) {
145 | let (next_input, right) = parse_factor(next_input)?; // Continue parsing factors for terms
146 | left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
147 | input = next_input;
148 | }
149 |
150 | Ok((input, left))
151 | }
152 | // A parser for basic factors: numbers, parentheses, 'x', etc.
153 | fn parse_factor(input: &str) -> IResult<&str, Expr> {
154 | alt((
155 | delimited(
156 | preceded(multispace0, char('(')),
157 | parse_expr,
158 | preceded(multispace0, char(')')),
159 | ),
160 | parse_number,
161 | parse_variable,
162 | parse_negate,
163 | parse_abs,
164 | parse_pi,
165 | parse_e,
166 | parse_trig_func,
167 | parse_log2_func,
168 | parse_log10_func,
169 | parse_ln_func,
170 | parse_sqrt_func
171 | ))(input)
172 | }
173 |
174 | // A parser for addition and subtraction
175 | fn parse_op(input: &str) -> IResult<&str, Op> {
176 | preceded(
177 | multispace0,
178 | alt((map(tag("+"), |_| Op::Add), map(tag("-"), |_| Op::Sub))),
179 | )(input)
180 | }
181 |
182 | // A parser for multiplication, division, and exponentiation
183 | fn parse_mul_div_op(input: &str) -> IResult<&str, Op> {
184 | preceded(
185 | multispace0,
186 | alt((
187 | map(tag("*"), |_| Op::Mul),
188 | map(tag("/"), |_| Op::Div),
189 | map(tag("^"), |_| Op::Pow),
190 | map(tag("%"), |_| Op::Mod),
191 | )),
192 | )(input)
193 | }
194 |
195 | // A parser for binary operations (addition, subtraction, etc.)
196 | pub fn parse_expr(input: &str) -> IResult<&str, Expr> {
197 | let (input, mut left) = parse_term(input)?;
198 | let mut input = input;
199 |
200 | while let Ok((next_input, op)) = parse_op(input) {
201 | let (next_input, right) = parse_term(next_input)?;
202 | left = Expr::BinaryOp(Box::new(left), op, Box::new(right));
203 | input = next_input;
204 | }
205 |
206 | Ok((input, left))
207 | }
208 |
209 | // Main evaluation function
210 | pub fn evaluate(expr: &Expr, x_value: f64) -> f64 {
211 | match expr {
212 | Expr::Number(value) => *value,
213 | Expr::Variable => x_value, // Substitute variable 'x' with its value
214 | Expr::Pi => std::f64::consts::PI,
215 | Expr::E => std::f64::consts::E,
216 | Expr::NegateOp(inner) => -evaluate(inner, x_value),
217 | Expr::AbsOp(inner) => evaluate(inner, x_value).abs(),
218 | Expr::BinaryOp(left, op, right) => {
219 | let left_value = evaluate(left, x_value);
220 | let right_value = evaluate(right, x_value);
221 | match op {
222 | Op::Add => left_value + right_value,
223 | Op::Sub => left_value - right_value,
224 | Op::Mul => left_value * right_value,
225 | Op::Div => left_value / right_value,
226 | Op::Pow => left_value.powf(right_value),
227 | Op::Mod => left_value % right_value,
228 | }
229 | }
230 | Expr::Log2Op(arg) => {
231 | let arg_value = evaluate(arg, x_value);
232 | arg_value.log2()
233 | }
234 | Expr::Log10Op(arg) => {
235 | let arg_value = evaluate(arg, x_value);
236 | arg_value.log10()
237 | }
238 | Expr::Ln(arg) => {
239 | let arg_value = evaluate(arg, x_value);
240 | arg_value.ln()
241 | }
242 | Expr::Sqrt(arg) => {
243 | let arg_value = evaluate(arg, x_value);
244 | arg_value.sqrt()
245 | }
246 | Expr::TrigOp(func, arg) => {
247 | let arg_value = evaluate(arg, x_value);
248 | match func {
249 | TrigFunc::Sin => arg_value.sin(),
250 | TrigFunc::aSin => arg_value.asin(),
251 | TrigFunc::Sinh => arg_value.sinh(),
252 |
253 | TrigFunc::Cos => arg_value.cos(),
254 | TrigFunc::aCos => arg_value.acos(),
255 | TrigFunc::Cosh => arg_value.cosh(),
256 |
257 | TrigFunc::Tan => arg_value.tan(),
258 | TrigFunc::aTan => arg_value.atan(),
259 | TrigFunc::Tanh => arg_value.tanh(),
260 | }
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/src/mem_commands.rs:
--------------------------------------------------------------------------------
1 | use serde_derive::Serialize;
2 | use std::time::Duration;
3 | use sysinfo::System;
4 |
5 | use crate::{format_utils, graph_utils};
6 |
7 | pub async fn system_mem_usage(watch: bool, json: bool, plot: bool) {
8 | let mut system = System::new_all();
9 |
10 | if watch {
11 | let mut points: Vec<(f32, f32)> = Vec::new();
12 | let mut i:f32 = 0.0;
13 | loop {
14 | system.refresh_memory();
15 | let used_memory = system.used_memory();
16 | let total_memory = system.total_memory();
17 | let free_memory = system.free_memory();
18 | let used_percentage = format_utils::percent(used_memory, total_memory);
19 |
20 | if points.len() >= 30 {
21 | // Remove the oldest point (optional, only if you want a rolling window)
22 | points.remove(0); // or points.drain(..1); for potentially more efficient operation
23 | }
24 | points.push((i, used_percentage as f32));
25 | i += 1.0;
26 |
27 |
28 | format_utils::clear_terminal();
29 | if json {
30 | let mem_usage = MemUsage {
31 | total_gb: format_utils::bytes_to_gb(total_memory),
32 | used_gb: format_utils::bytes_to_gb(used_memory),
33 | used_percent: used_percentage,
34 | free_gb: format_utils::bytes_to_gb(free_memory),
35 | free_percent: format_utils::percent(free_memory, total_memory),
36 | };
37 | format_utils::print_json(&mem_usage);
38 | } else {
39 | println!(
40 | "total: {}, used: {} ({}), free: {} ({})",
41 | format_utils::format_bytes(total_memory),
42 | format_utils::format_bytes(used_memory),
43 | format_utils::calculate_format_percent(used_memory, total_memory),
44 | format_utils::format_bytes(free_memory),
45 | format_utils::calculate_format_percent(free_memory, total_memory)
46 | );
47 | }
48 |
49 | if plot{
50 | // Plot the chart
51 | let title: &str = "used memory %";
52 | graph_utils::plot_chart(&points, String::from(title));
53 | }
54 |
55 | tokio::time::sleep(Duration::from_millis(500)).await;
56 | }
57 | } else {
58 | system.refresh_memory();
59 | let used_memory = system.used_memory();
60 | let total_memory = system.total_memory();
61 | let free_memory = system.free_memory();
62 |
63 | if json {
64 | let mem_usage = MemUsage {
65 | total_gb: format_utils::bytes_to_gb(total_memory),
66 | used_gb: format_utils::bytes_to_gb(used_memory),
67 | used_percent: format_utils::percent(used_memory, total_memory),
68 | free_gb: format_utils::bytes_to_gb(free_memory),
69 | free_percent: format_utils::percent(free_memory, total_memory),
70 | };
71 | format_utils::print_json(&mem_usage);
72 | } else {
73 | println!(
74 | "total: {}, used: {} ({}), free: {} ({})",
75 | format_utils::format_bytes(total_memory),
76 | format_utils::format_bytes(used_memory),
77 | format_utils::calculate_format_percent(used_memory, total_memory),
78 | format_utils::format_bytes(free_memory),
79 | format_utils::calculate_format_percent(free_memory, total_memory)
80 | );
81 | }
82 | }
83 | }
84 |
85 | #[derive(Serialize, Debug)]
86 | struct MemUsage {
87 | total_gb: f64,
88 | used_gb: f64,
89 | used_percent: f64,
90 | free_gb: f64,
91 | free_percent: f64,
92 | }
93 |
--------------------------------------------------------------------------------
/src/network_commands.rs:
--------------------------------------------------------------------------------
1 | use serde_derive::Serialize;
2 | use sysinfo::Networks;
3 |
4 | use crate::format_utils;
5 | use std::thread::sleep;
6 | use std::time::{Duration, Instant};
7 |
8 | // Helper function to format bytes to human-readable units (kB, MB, GB)
9 | fn format_bytes(bytes: u64) -> String {
10 | let kilobyte = 1024.0;
11 | let megabyte = kilobyte * 1024.0;
12 | let gigabyte = megabyte * 1024.0;
13 |
14 | if bytes as f64 >= gigabyte {
15 | format!("{:.2} GB/s", bytes as f64 / gigabyte)
16 | } else if bytes as f64 >= megabyte {
17 | format!("{:.2} MB/s", bytes as f64 / megabyte)
18 | } else if bytes as f64 >= kilobyte {
19 | format!("{:.2} kB/s", bytes as f64 / kilobyte)
20 | } else {
21 | format!("{} B/s", bytes)
22 | }
23 | }
24 |
25 | pub fn system_network_usage(watch: bool) {
26 |
27 | let mut networks = Networks::new_with_refreshed_list();
28 |
29 | let mut last_instant = Instant::now();
30 |
31 | loop {
32 | sleep(Duration::from_secs(1));
33 |
34 | networks.refresh();
35 | let current_instant = Instant::now();
36 | let elapsed_time = current_instant.duration_since(last_instant).as_secs_f64();
37 |
38 | format_utils::clear_terminal();
39 | for (interface_name, data) in networks.iter() {
40 | let received = data.received();
41 | let transmitted = data.transmitted();
42 |
43 | // Calculate speeds
44 | let receive_speed = received as f64 / elapsed_time; // Bytes per second
45 | let transmit_speed = transmitted as f64 / elapsed_time; // Bytes per second
46 |
47 | // Output the receive/transmit speeds in a human-readable format
48 | println!("{}: rx {}, tx {}", interface_name, format_bytes(receive_speed as u64), format_bytes(transmit_speed as u64));
49 | }
50 |
51 | last_instant = current_instant;
52 | if !watch{
53 | return;
54 | }
55 | }
56 | }
57 |
58 | #[derive(Serialize, Debug)]
59 | struct NetworkInfoOutput {
60 | name: String,
61 | transmitted: f64,
62 | received: f64,
63 | mac: String,
64 | }
65 |
66 | pub async fn system_network_info(json: bool) {
67 |
68 | let networks = Networks::new_with_refreshed_list();
69 |
70 | let mut outputs: Vec = Vec::new();
71 | for (interface_name, data) in &networks {
72 | outputs.push(NetworkInfoOutput{
73 | name: interface_name.clone(),
74 | transmitted: format_utils::bytes_to_gb(data.total_transmitted()),
75 | received: format_utils::bytes_to_gb(data.total_received()),
76 | mac: data.mac_address().to_string(),
77 | });
78 | }
79 |
80 | if json {
81 | format_utils::print_json(&outputs);
82 | }else{
83 | for o in outputs{
84 | println!("name: {}, transmitted: {}GB, received: {}GB, mac: {}", o.name, o.transmitted, o.received, o.mac);
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/src/port_commands.rs:
--------------------------------------------------------------------------------
1 | use std::net::Ipv4Addr;
2 | use std::str::FromStr;
3 | use std::time::Duration;
4 | use serde_derive::Serialize;
5 | use crate::ip_utils;
6 |
7 | use crate::format_utils;
8 |
9 | #[derive(Serialize, Debug)]
10 | struct OpenResponse {
11 | open: bool,
12 | }
13 |
14 | #[derive(Serialize, Debug)]
15 | struct MultiPortResponse {
16 | ports: Vec,
17 | }
18 |
19 | pub async fn port_scan(ip_str: Option, json: bool) {
20 |
21 | let ip_str = ip_utils::to_ip_or_local(ip_str).await;
22 |
23 | let ip = match Ipv4Addr::from_str(&ip_str) {
24 | Ok(s) => s,
25 | Err(e) => {
26 | eprintln!("Failed parsing Ip address {}", e);
27 | return;
28 | }
29 | };
30 |
31 | let timeout = Duration::from_millis(100); // Timeout for each port scan
32 |
33 | let ping_tasks: Vec<_> = (1..9999)
34 | .map(|i: u16| {
35 | tokio::spawn(async move {
36 | match ip_utils::can_connect(ip, i, timeout).await {
37 | Ok(is_open) => {
38 | if is_open {
39 | Some(i)
40 | } else {
41 | None
42 | }
43 | }
44 | Err(_) => None,
45 | }
46 | })
47 | })
48 | .collect();
49 |
50 | let mut ports: Vec = Vec::new();
51 | for task in ping_tasks {
52 | if let Ok(Some(result)) = task.await {
53 | ports.push(result);
54 | }
55 | }
56 |
57 | if json {
58 | format_utils::print_json(&MultiPortResponse { ports: ports });
59 | } else {
60 | let port_strings: Vec = ports.iter().map(|p| p.to_string()).collect();
61 | println!("{}", port_strings.join("\n\r"))
62 | }
63 | }
64 |
65 | pub async fn port_status(ip_str: Option, port: u16, json: bool) {
66 |
67 | let ip_str = ip_utils::to_ip_or_local(ip_str).await;
68 |
69 | let ip = match Ipv4Addr::from_str(&ip_str) {
70 | Ok(s) => s,
71 | Err(e) => {
72 | eprintln!("Failed to bind udp socket: {}", e);
73 | return;
74 | }
75 | };
76 |
77 | let timeout = Duration::from_millis(100);
78 |
79 | if json {
80 | match ip_utils::can_connect(ip, port, timeout).await {
81 | Ok(is_open) => format_utils::print_json(&OpenResponse { open: is_open }),
82 | Err(_) => format_utils::print_json(&OpenResponse { open: false }),
83 | }
84 | } else {
85 | match ip_utils::can_connect(ip, port, timeout).await {
86 | Ok(_) => println!("open"),
87 | Err(_) => println!("closed"),
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/process_commands.rs:
--------------------------------------------------------------------------------
1 | use regex::Regex;
2 | use std::thread::sleep;
3 | use std::time::{Duration, Instant};
4 | use sysinfo::{Pid, System};
5 |
6 | use crate::format_utils;
7 |
8 | pub fn system_process_info(
9 | name_filter: Option,
10 | sort_by: Option,
11 | limit: Option,
12 | watch: bool,
13 | ) {
14 | let mut sys = System::new_all();
15 |
16 | let name_regex = name_filter
17 | .as_ref()
18 | .map(|filter| Regex::new(filter).unwrap());
19 |
20 | loop {
21 | sys.refresh_all();
22 |
23 | // Collect process info
24 | let mut process_info: Vec<(Pid, &sysinfo::Process)> = sys
25 | .processes()
26 | .iter()
27 | .filter(|(_, process)| {
28 | // Filter processes by name if regex is provided
29 | if let Some(ref regex) = name_regex {
30 | regex.is_match(&process.name().to_str().unwrap_or_default())
31 | } else {
32 | true
33 | }
34 | })
35 | .map(|(pid, process)| (*pid, process))
36 | .collect();
37 |
38 | // Sort processes based on the specified criteria
39 | match sort_by.as_deref() {
40 | Some("cpu") => {
41 | process_info.sort_by(|a, b| b.1.cpu_usage().partial_cmp(&a.1.cpu_usage()).unwrap())
42 | }
43 | Some("mem") => {
44 | process_info.sort_by(|a, b| b.1.memory().partial_cmp(&a.1.memory()).unwrap())
45 | }
46 | Some("disk") => process_info.sort_by(|a, b| {
47 | let disk_a = a.1.disk_usage();
48 | let disk_b = b.1.disk_usage();
49 | (disk_b.read_bytes + disk_b.written_bytes)
50 | .partial_cmp(&(disk_a.read_bytes + disk_a.written_bytes))
51 | .unwrap()
52 | }),
53 | Some("name") | None => process_info.sort_by(|a, b| a.1.name().cmp(b.1.name())),
54 | _ => eprintln!("Invalid sort option provided. Defaulting to sort by name."),
55 | }
56 |
57 | let last_instant = Instant::now();
58 |
59 | format_utils::clear_terminal();
60 | println!(
61 | "{:<5}\t{:>6}\t{:>10}\t{:>12}\t{:>12}\tname",
62 | "[pid]", "[cpu %]", "[mem]", "[disk read]", "[disk write]"
63 | );
64 |
65 | for (pid, process) in process_info.iter().take(limit.unwrap_or(usize::MAX)) {
66 | let disk_usage = process.disk_usage();
67 |
68 | let elapsed_time = last_instant.elapsed().as_secs_f64();
69 | let read_speed = if elapsed_time > 0.0 {
70 | disk_usage.read_bytes as f64 / elapsed_time // Bytes per second
71 | } else {
72 | 0.0
73 | };
74 | let write_speed = if elapsed_time > 0.0 {
75 | disk_usage.written_bytes as f64 / elapsed_time // Bytes per second
76 | } else {
77 | 0.0
78 | };
79 |
80 | // Display process information
81 | println!(
82 | "[{:>5}]\t{:<6.2}%\t{:>10}\t{:>12}\t{:>12}\t{}\t",
83 | pid,
84 | process.cpu_usage(),
85 | format_memory(process.memory()),
86 | format_disk_speed(read_speed),
87 | format_disk_speed(write_speed),
88 | process.name().to_str().unwrap()
89 | );
90 | }
91 |
92 | // If not watching, break out of the loop after one display
93 | if !watch {
94 | break;
95 | }
96 |
97 | sleep(Duration::from_secs(1));
98 | }
99 | }
100 |
101 | fn format_memory(bytes: u64) -> String {
102 | let kilobyte = 1024.0;
103 | let megabyte = kilobyte * 1024.0;
104 | let gigabyte = megabyte * 1024.0;
105 |
106 | if bytes as f64 >= gigabyte {
107 | format!("{:.0} GB", bytes as f64 / gigabyte)
108 | } else if bytes as f64 >= megabyte {
109 | format!("{:.0} MB", bytes as f64 / megabyte)
110 | } else if bytes as f64 >= kilobyte {
111 | format!("{:.0} kB", bytes as f64 / kilobyte)
112 | } else {
113 | format!("{} B", bytes)
114 | }
115 | }
116 |
117 | fn format_disk_speed(bytes_per_sec: f64) -> String {
118 | let kilobyte = 1024.0;
119 | let megabyte = kilobyte * 1024.0;
120 | let gigabyte = megabyte * 1024.0;
121 |
122 | if bytes_per_sec >= gigabyte {
123 | format!("{:.0} GB/s", bytes_per_sec / gigabyte)
124 | } else if bytes_per_sec >= megabyte {
125 | format!("{:.0} MB/s", bytes_per_sec / megabyte)
126 | } else if bytes_per_sec >= kilobyte {
127 | format!("{:.0} kB/s", bytes_per_sec / kilobyte)
128 | } else {
129 | format!("{:.0} B/s", bytes_per_sec)
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/text_commands.rs:
--------------------------------------------------------------------------------
1 | use base64::prelude::*;
2 | use percent_encoding::{percent_decode, utf8_percent_encode, NON_ALPHANUMERIC};
3 | use regex::Regex;
4 | use uuid::Uuid;
5 | use std::{fs::File, io::{self, BufRead}, str};
6 | use base64::engine::general_purpose::URL_SAFE;
7 |
8 | pub fn base64_encode(input: String) {
9 | // Encode the input string as bytes into Base64 URL format
10 | let encoded = URL_SAFE.encode(input.as_bytes());
11 | // Print the Base64 encoded string
12 | println!("{}", encoded);
13 | }
14 |
15 | pub fn base64_decode(input: String) {
16 | // Ensure correct padding for Base64 URL encoding
17 | let mut padding = String::new();
18 | let missing_padding = 4 - (input.len() % 4);
19 | if missing_padding != 4 {
20 | padding.push_str(&"=".repeat(missing_padding));
21 | }
22 |
23 | // Create the Base64 URL string with proper padding
24 | let base64_encoded = format!("{}{}", input, padding);
25 |
26 | // Decode the Base64 string
27 | match URL_SAFE.decode(&base64_encoded) {
28 | Ok(decoded_bytes) => {
29 | // Convert bytes to string
30 | let decoded_string = str::from_utf8(&decoded_bytes)
31 | .map(|s| s.to_string())
32 | .map_err(|e| e.to_string());
33 |
34 | if let Ok(decoded_string) = decoded_string{
35 | println!("{}", decoded_string);
36 | }else if let Err(e) = decoded_string{
37 | eprint!("{}", e);
38 | }
39 | }
40 | Err(e) => eprintln!("Base64 decode error: {}", e),
41 | }
42 | }
43 |
44 | pub fn regex_search(filename: String, regex: String){
45 | let file = File::open(filename);
46 | if let Ok(file) = file{
47 | let reader = io::BufReader::new(file);
48 | let re: Regex = Regex::new(regex.as_str()).unwrap();
49 |
50 | for (index, line) in reader.lines().enumerate() {
51 | if let Ok(line) = line{
52 | if re.is_match(&line) {
53 | println!("{}: {}", index + 1, line);
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | pub fn print_lines(
61 | filename: String,
62 | start: Option,
63 | end: Option,
64 | head: Option,
65 | tail: Option
66 | ) {
67 | // Check for conflicting options
68 | if (head.is_some() || tail.is_some()) && (start.is_some() || end.is_some()) {
69 | eprintln!("Error: Cannot specify both head/tail and start/end ranges.");
70 | return;
71 | }
72 | if head.is_some() && tail.is_some() {
73 | eprintln!("Error: Cannot specify both head and tail.");
74 | return;
75 | }
76 |
77 | // Open the file
78 | let file = File::open(&filename).expect("Unable to open file");
79 | let reader = io::BufReader::new(file);
80 |
81 | // Handle head: print first N lines
82 | if let Some(n) = head {
83 | for (i, line) in reader.lines().enumerate() {
84 | if i >= n {
85 | break;
86 | }
87 | if let Ok(line) = line {
88 | println!("{}", line);
89 | }
90 | }
91 | return;
92 | }
93 |
94 | // If tail is specified, we need to scan the file and count lines first
95 | if let Some(n) = tail {
96 | let mut lines: Vec = Vec::new();
97 | for line in reader.lines() {
98 | if let Ok(line) = line {
99 | lines.push(line);
100 | }
101 | }
102 | let total_lines = lines.len();
103 | let start_tail_idx = total_lines.saturating_sub(n);
104 | for line in lines.iter().skip(start_tail_idx) {
105 | println!("{}", line);
106 | }
107 | return;
108 | }
109 |
110 | // Handle start/end range without loading the entire file into memory
111 | let start_idx = start.unwrap_or(0); // If start is None, default to the beginning
112 | let end_idx = end.unwrap_or(usize::MAX); // If end is None, default to the max usize
113 |
114 | for (index, line) in reader.lines().enumerate() {
115 | if index < start_idx {
116 | continue;
117 | }
118 | if index > end_idx {
119 | break;
120 | }
121 | if let Ok(line) = line {
122 | println!("{}", line);
123 | }
124 | }
125 | }
126 |
127 | pub fn guid(){
128 | println!("{}", Uuid::new_v4());
129 | }
130 |
131 | pub fn url_encode(input: &String) {
132 | println!("{}", utf8_percent_encode(input, NON_ALPHANUMERIC).to_string());
133 | }
134 |
135 | pub fn url_decode(input: &String) {
136 | println!("{}",percent_decode(input.as_bytes())
137 | .decode_utf8_lossy()
138 | .to_string());
139 | }
140 |
141 | pub fn replace(source: String, pattern: &str, replace: &str) {
142 | let re = Regex::new(pattern).expect("Invalid regex pattern");
143 | let result = re.replace_all(&source, replace).to_string();
144 |
145 | println!("{}", result);
146 | }
147 |
148 | pub fn count(source: String, pattern: &str) {
149 | let re = Regex::new(pattern).expect("Invalid regex pattern");
150 | let match_count = re.find_iter(&source).count();
151 |
152 | println!("{}", match_count);
153 | }
--------------------------------------------------------------------------------
/src/time_commands.rs:
--------------------------------------------------------------------------------
1 | use std::thread::sleep;
2 | use std::time::{Duration, Instant};
3 | use chrono::{DateTime, Local, NaiveDateTime, TimeZone, Utc};
4 | use cron_descriptor::cronparser::cron_expression_descriptor;
5 | use time::format_description::well_known::Rfc3339;
6 | use time::macros;
7 | use time::OffsetDateTime;
8 |
9 | use crate::format_utils;
10 |
11 | pub fn unix_timestamp(milli: bool, dt: Option) {
12 | let now = match dt {
13 | Some(ref datetime_str) => {
14 | // Try parsing with multiple formats
15 | match DateTime::parse_from_rfc3339(datetime_str)
16 | .or_else(|_| DateTime::parse_from_rfc2822(datetime_str))
17 | .or_else(|_| DateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S %z"))
18 | .or_else(|_| DateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S"))
19 | {
20 | Ok(parsed_dt) => parsed_dt,
21 | Err(_) => {
22 | println!("Could not parse the provided datetime string.");
23 | return;
24 | }
25 | }
26 | }
27 | None => Utc::now().into(),
28 | };
29 |
30 | if milli {
31 | let timestamp_milliseconds = now.timestamp_millis();
32 | println!("{}", timestamp_milliseconds);
33 | } else {
34 | let timestamp_seconds = now.timestamp();
35 | println!("{}", timestamp_seconds);
36 | }
37 | }
38 |
39 |
40 | pub fn date_time(local: bool, rfc: bool, unix: Option) {
41 |
42 | // If a Unix timestamp is provided, parse it
43 | let date_time: DateTime = if let Some(unix_time) = unix {
44 | DateTime::from_timestamp(unix_time as i64, 0).unwrap()
45 | } else {
46 | Utc::now() // Use the current time if no Unix timestamp is provided
47 | };
48 |
49 | if local {
50 | // Convert UTC time to local time
51 | if rfc {
52 | let formatted = date_time.with_timezone(&Local).to_rfc3339(); // Use chrono's RFC 3339 method
53 | println!("{}", formatted);
54 | } else {
55 | let formatted = date_time.with_timezone(&Local).format("%Y-%m-%d %H:%M:%S").to_string(); // Custom format
56 | println!("{}", formatted);
57 | }
58 | } else {
59 | if rfc {
60 | let formatted = date_time.to_rfc3339(); // Use chrono's RFC 3339 method
61 | println!("{}", formatted);
62 | } else {
63 | let formatted = date_time.format("%Y-%m-%d %H:%M:%S").to_string(); // Custom format
64 | println!("{}", formatted);
65 | }
66 | };
67 | }
68 |
69 |
70 | pub fn chron_tostring(cron_expr: String) {
71 | let description = cron_expression_descriptor::get_description_cron(&cron_expr);
72 | println!("{}", description);
73 | }
74 |
75 | pub fn countdown(input: String) {
76 | // Parse input into minutes and seconds
77 | let countdown_time = match parse_duration(&input) {
78 | Some(duration) => duration,
79 | None => {
80 | println!("Please provide a valid time in the format MM:SS or just seconds.");
81 | return;
82 | }
83 | };
84 |
85 | // Countdown loop
86 | let mut total_seconds = countdown_time.as_secs();
87 | while total_seconds > 0 {
88 | format_utils::clear_terminal(); // Assume you have this utility for clearing the terminal
89 | let minutes = total_seconds / 60;
90 | let seconds = total_seconds % 60;
91 |
92 | // Display the remaining time in a readable format
93 | if minutes > 1 {
94 | if seconds == 1 {
95 | println!("Time left: {} minutes 1 second", minutes);
96 | } else if seconds == 0 {
97 | println!("Time left: {} minutes", minutes);
98 | } else {
99 | println!("Time left: {} minutes {} seconds", minutes, seconds);
100 | }
101 | } else if minutes == 1 {
102 | if seconds == 1 {
103 | println!("Time left: 1 minute 1 second");
104 | } else if seconds == 0 {
105 | println!("Time left: 1 minute");
106 | } else {
107 | println!("Time left: 1 minute {} seconds", seconds);
108 | }
109 | } else {
110 | if seconds == 1 {
111 | println!("Time left: 1 second");
112 | } else if seconds == 0 {
113 | println!("Time left: 0 seconds");
114 | } else {
115 | println!("Time left: {} seconds", seconds);
116 | }
117 | }
118 |
119 | sleep(Duration::from_secs(1));
120 | total_seconds -= 1;
121 | }
122 |
123 | // Flashing "Time's up" message
124 | let mut flash = true;
125 | loop {
126 | format_utils::clear_terminal();
127 | if flash {
128 | println!("Time's up!");
129 | } else {
130 | println!(" ");
131 | }
132 | flash = !flash;
133 | sleep(Duration::from_millis(500));
134 | }
135 | }
136 |
137 | // Function to parse input string into a Duration
138 | fn parse_duration(input: &str) -> Option {
139 | // Try to parse in "MM:SS" format
140 | if let Some((minutes, seconds)) = input.split_once(':') {
141 | if let (Ok(min), Ok(sec)) = (minutes.parse::(), seconds.parse::()) {
142 | return Some(Duration::from_secs(min * 60 + sec));
143 | }
144 | }
145 |
146 | // If no ":" found, treat the input as just seconds
147 | if let Ok(seconds) = input.parse::() {
148 | return Some(Duration::from_secs(seconds));
149 | }
150 |
151 | // Invalid input
152 | None
153 | }
154 |
--------------------------------------------------------------------------------
/tests/csv_sql_search_tests.rs:
--------------------------------------------------------------------------------
1 | use aid::csv_utils;
2 | use std::io;
3 |
4 | pub fn load_and_query(sql: &str) -> Result<(Vec, Vec>), io::Error> {
5 | // Use the input SQL parameter instead of hardcoded SQL
6 | let parsed = match csv_utils::parse_sql(sql) {
7 | Ok(result) => result,
8 | Err(e) => {
9 | return Err(io::Error::new(
10 | io::ErrorKind::InvalidData,
11 | format!("Failed to parse SQL query: {}", e),
12 | ))
13 | }
14 | };
15 |
16 | let query = parsed.1;
17 |
18 | // Load the CSV file
19 | let (headers, records) = match csv_utils::load_csv(&query.table) {
20 | Ok(result) => result,
21 | Err(e) => {
22 | eprintln!();
23 | return Err(io::Error::new(
24 | io::ErrorKind::InvalidData,
25 | format!("Failed to load CSV file '{}': {}", query.table, e),
26 | ));
27 | }
28 | };
29 |
30 | // Apply the query
31 | Ok(csv_utils::apply_query(&headers, records, &query))
32 | }
33 |
34 | #[cfg(test)]
35 | mod tests {
36 | use super::*;
37 |
38 | #[test]
39 | fn select_all() {
40 | let result = load_and_query("SELECT * FROM ./vgsales.csv");
41 |
42 | if let Err(e) = &result {
43 | eprint!("{}", e);
44 | assert!(!result.is_err());
45 | }
46 |
47 | if let Ok((headers, data)) = result {
48 | assert_eq!(
49 | vec![
50 | "Rank",
51 | "Name",
52 | "Platform",
53 | "Year",
54 | "Genre",
55 | "Publisher",
56 | "NA_Sales",
57 | "EU_Sales",
58 | "JP_Sales",
59 | "Other_Sales",
60 | "Global_Sales"
61 | ],
62 | headers
63 | );
64 | assert_eq!(
65 | vec![
66 | "1",
67 | "Wii Sports",
68 | "Wii",
69 | "2006",
70 | "Sports",
71 | "Nintendo",
72 | "41.49",
73 | "29.02",
74 | "3.77",
75 | "8.46",
76 | "82.74"
77 | ],
78 | data[0]
79 | );
80 | assert_eq!(
81 | vec![
82 | "16600",
83 | "Spirits & Spells",
84 | "GBA",
85 | "2003",
86 | "Platform",
87 | "Wanadoo",
88 | "0.01",
89 | "0",
90 | "0",
91 | "0",
92 | "0.01"
93 | ],
94 | data[16597]
95 | );
96 | assert_eq!(16598, data.len());
97 | }
98 | }
99 |
100 | #[test]
101 | fn count_all() {
102 | let result = load_and_query("SELECT COUNT(*) FROM ./vgsales.csv");
103 |
104 | if let Err(e) = &result {
105 | eprint!("{}", e);
106 | assert!(!result.is_err());
107 | }
108 |
109 | if let Ok((headers, data)) = result {
110 | assert_eq!(vec!["COUNT(*)"], headers);
111 | assert_eq!(vec!["16598"], data[0]);
112 | assert_eq!(1, data.len());
113 | }
114 | }
115 |
116 | #[test]
117 | fn select_multi_column() {
118 | let result = load_and_query(
119 | "SELECT Year,Name FROM ./vgsales.csv WHERE Platform = 'Wii' AND Year = 2015",
120 | );
121 |
122 | if let Err(e) = &result {
123 | eprint!("{}", e);
124 | assert!(!result.is_err());
125 | }
126 |
127 | if let Ok((headers, data)) = result {
128 | assert_eq!(vec!["Year", "Name"], headers);
129 | assert_eq!(vec!["2015", "Just Dance 2016"], data[0]);
130 | assert_eq!(vec!["2015", "Monster High: New Ghoul in School"], data[3]);
131 | assert_eq!(4, data.len());
132 | }
133 | }
134 |
135 | #[test]
136 | fn select_distinct() {
137 | let result = load_and_query("SELECT DISTINCT Platform FROM vgsales.csv WHERE Publisher LIKE 'Nin%' ORDER BY Platform DESC");
138 |
139 | if let Err(e) = &result {
140 | eprint!("{}", e);
141 | assert!(!result.is_err());
142 | }
143 |
144 | if let Ok((headers, data)) = result {
145 | assert_eq!(vec!["Platform"], headers);
146 | assert_eq!(vec!["WiiU"], data[0]);
147 | assert_eq!(vec!["3DS"], data[9]);
148 | assert_eq!(10, data.len());
149 | }
150 | }
151 |
152 | #[test]
153 | fn select_between() {
154 | let result = load_and_query("SELECT DISTINCT Platform FROM vgsales.csv WHERE Year BETWEEN 2005 AND 2006 ORDER BY Platform ASC");
155 |
156 | if let Err(e) = &result {
157 | eprint!("{}", e);
158 | assert!(!result.is_err());
159 | }
160 |
161 | if let Ok((headers, data)) = result {
162 | assert_eq!(vec!["Platform"], headers);
163 | assert_eq!(vec!["DS"], data[0]);
164 | assert_eq!(vec!["XB"], data[9]);
165 | assert_eq!(10, data.len());
166 | }
167 | }
168 |
169 | #[test]
170 | fn select_filter_or() {
171 | let result = load_and_query("SELECT DISTINCT Publisher FROM vgsales.csv WHERE Name LIKE 'Call of Duty%' OR Name LIKE 'Battlefield%' ORDER BY Publisher ASC");
172 |
173 | if let Err(e) = &result {
174 | eprint!("{}", e);
175 | assert!(!result.is_err());
176 | }
177 |
178 | if let Ok((headers, data)) = result {
179 | assert_eq!(vec!["Publisher"], headers);
180 | assert_eq!(vec!["Activision"], data[0]);
181 | assert_eq!(vec!["Electronic Arts"], data[1]);
182 | assert_eq!(2, data.len());
183 | }
184 | }
185 |
186 | #[test]
187 | fn select_filter_and_or() {
188 | let result = load_and_query("SELECT Name FROM vgsales.csv WHERE Year = 1984 AND (Genre = 'Puzzle' OR Genre = 'Racing') ORDER BY Year DESC");
189 |
190 | if let Err(e) = &result {
191 | eprint!("{}", e);
192 | assert!(!result.is_err());
193 | }
194 |
195 | if let Ok((headers, data)) = result {
196 | assert_eq!(vec!["Name"], headers);
197 | assert_eq!(vec!["Excitebike"], data[0]);
198 | assert_eq!(vec!["Beamrider"], data[5]);
199 | assert_eq!(6, data.len());
200 | }
201 | }
202 | #[test]
203 | fn select_filter_not() {
204 | let result = load_and_query("SELECT DISTINCT Platform FROM vgsales.csv WHERE Name LIKE 'Call of Duty%' AND NOT (Platform = 'PC' OR Platform = 'DS') ORDER BY Platform DESC");
205 |
206 | if let Err(e) = &result {
207 | eprint!("{}", e);
208 | assert!(!result.is_err());
209 | }
210 |
211 | if let Ok((headers, data)) = result {
212 | assert_eq!(vec!["Platform"], headers);
213 | assert_eq!(vec!["XOne"], data[0]);
214 | assert_eq!(vec!["GC"], data[10]);
215 | assert_eq!(11, data.len());
216 | }
217 | }
218 |
219 | #[test]
220 | fn select_group_by() {
221 | let result = load_and_query(
222 | "SELECT Genre, COUNT(Name) FROM vgsales.csv GROUP BY Genre ORDER BY COUNT(Name) DESC",
223 | );
224 |
225 | if let Err(e) = &result {
226 | eprint!("{}", e);
227 | assert!(!result.is_err());
228 | }
229 |
230 | if let Ok((headers, data)) = result {
231 | assert_eq!(vec!["Genre", "COUNT(Name)"], headers);
232 | assert_eq!(vec!["Action", "3316"], data[0]);
233 | assert_eq!(vec!["Puzzle", "582"], data[11]);
234 | assert_eq!(12, data.len());
235 | }
236 | }
237 |
238 | #[test]
239 | fn select_aggregate() {
240 | let result = load_and_query("SELECT Genre,COUNT(Name),SUM(Global_Sales),MIN(Global_Sales),MAX(Global_Sales),AVG(Global_Sales) FROM vgsales.csv GROUP BY Genre ORDER BY SUM(Global_Sales) DESC");
241 |
242 | if let Err(e) = &result {
243 | eprint!("{}", e);
244 | assert!(!result.is_err());
245 | }
246 |
247 | if let Ok((headers, data)) = result {
248 | assert_eq!(
249 | vec![
250 | "Genre",
251 | "COUNT(Name)",
252 | "SUM(Global_Sales)",
253 | "MIN(Global_Sales)",
254 | "MAX(Global_Sales)",
255 | "AVG(Global_Sales)"
256 | ],
257 | headers
258 | );
259 | assert_eq!(
260 | vec![
261 | "Action",
262 | "3316",
263 | "1751.1799999999691",
264 | "0.01",
265 | "8.24",
266 | "0.5281001206272524"
267 | ],
268 | data[0]
269 | );
270 | assert_eq!(
271 | vec![
272 | "Strategy",
273 | "681",
274 | "175.1200000000004",
275 | "0.01",
276 | "5.45",
277 | "0.2571512481644646"
278 | ],
279 | data[11]
280 | );
281 | assert_eq!(12, data.len());
282 | }
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/tests/math_exp_tests.rs:
--------------------------------------------------------------------------------
1 | use aid::math_utils;
2 |
3 | #[cfg(test)]
4 | mod tests {
5 | use core::f64;
6 |
7 | use super::*;
8 |
9 | #[test]
10 | fn addition() {
11 | let expression = "1 + 2";
12 | let parse_result = math_utils::parse_expr(expression);
13 | if let Err(e) = &parse_result {
14 | eprint!("{}", e);
15 | assert!(!parse_result.is_err());
16 | }
17 |
18 | let (_, ast) = parse_result.unwrap();
19 | let eval = math_utils::evaluate(&ast, 0.0);
20 | assert_eq!(3.0, eval);
21 | }
22 |
23 | #[test]
24 | fn subtraction() {
25 | let expression = "1 - 2";
26 | let parse_result = math_utils::parse_expr(expression);
27 | if let Err(e) = &parse_result {
28 | eprint!("{}", e);
29 | assert!(!parse_result.is_err());
30 | }
31 |
32 | let (_, ast) = parse_result.unwrap();
33 | let eval = math_utils::evaluate(&ast, 0.0);
34 | assert_eq!(-1.0, eval);
35 | }
36 |
37 | #[test]
38 | fn multiplication() {
39 | let expression = "1 * 2";
40 | let parse_result = math_utils::parse_expr(expression);
41 | if let Err(e) = &parse_result {
42 | eprint!("{}", e);
43 | assert!(!parse_result.is_err());
44 | }
45 |
46 | let (_, ast) = parse_result.unwrap();
47 | let eval = math_utils::evaluate(&ast, 0.0);
48 | assert_eq!(2.0, eval);
49 | }
50 |
51 | #[test]
52 | fn division() {
53 | let expression = "1 / 2";
54 | let parse_result = math_utils::parse_expr(expression);
55 | if let Err(e) = &parse_result {
56 | eprint!("{}", e);
57 | assert!(!parse_result.is_err());
58 | }
59 |
60 | let (_, ast) = parse_result.unwrap();
61 | let eval = math_utils::evaluate(&ast, 0.0);
62 | assert_eq!(0.5, eval);
63 | }
64 |
65 | #[test]
66 | fn exponent() {
67 | let expression = "2 ^ 3";
68 | let parse_result = math_utils::parse_expr(expression);
69 | if let Err(e) = &parse_result {
70 | eprint!("{}", e);
71 | assert!(!parse_result.is_err());
72 | }
73 |
74 | let (_, ast) = parse_result.unwrap();
75 | let eval = math_utils::evaluate(&ast, 0.0);
76 | assert_eq!(8.0, eval);
77 | }
78 |
79 | #[test]
80 | fn sin() {
81 | let expression = "sin(pi)";
82 | let parse_result = math_utils::parse_expr(expression);
83 | if let Err(e) = &parse_result {
84 | eprint!("{}", e);
85 | assert!(!parse_result.is_err());
86 | }
87 |
88 | let (_, ast) = parse_result.unwrap();
89 | let eval = math_utils::evaluate(&ast, 0.0);
90 | let expected = 0.0;
91 | assert!((eval - expected).abs() < f64::EPSILON);
92 | }
93 |
94 | #[test]
95 | fn tan() {
96 | let expression = "tan(pi/4)";
97 | let parse_result = math_utils::parse_expr(expression);
98 | if let Err(e) = &parse_result {
99 | eprint!("{}", e);
100 | assert!(!parse_result.is_err());
101 | }
102 |
103 | let (_, ast) = parse_result.unwrap();
104 | let eval = math_utils::evaluate(&ast, 0.0);
105 | let expected = 1.0;
106 | assert!((eval - expected).abs() < f64::EPSILON);
107 | }
108 |
109 | #[test]
110 | fn cos() {
111 | let expression = "cos(pi)";
112 | let parse_result = math_utils::parse_expr(expression);
113 | if let Err(e) = &parse_result {
114 | eprint!("{}", e);
115 | assert!(!parse_result.is_err());
116 | }
117 |
118 | let (_, ast) = parse_result.unwrap();
119 | let eval = math_utils::evaluate(&ast, 0.0);
120 | let expected = -1.0;
121 | assert!((eval - expected).abs() < f64::EPSILON);
122 | }
123 |
124 | #[test]
125 | fn parentheses() {
126 | let expression = "2 * (4 - 2)";
127 | let parse_result = math_utils::parse_expr(expression);
128 | if let Err(e) = &parse_result {
129 | eprint!("{}", e);
130 | assert!(!parse_result.is_err());
131 | }
132 |
133 | let (_, ast) = parse_result.unwrap();
134 | let eval = math_utils::evaluate(&ast, 0.0);
135 | let expected = 4.0;
136 | assert!((eval - expected).abs() < f64::EPSILON);
137 | }
138 |
139 | #[test]
140 | fn negative() {
141 | let expression = "-2 * (4 - 2)";
142 | let parse_result = math_utils::parse_expr(expression);
143 | if let Err(e) = &parse_result {
144 | eprint!("{}", e);
145 | assert!(!parse_result.is_err());
146 | }
147 |
148 | let (_, ast) = parse_result.unwrap();
149 | let eval = math_utils::evaluate(&ast, 0.0);
150 | let expected = -4.0;
151 | assert!((eval - expected).abs() < f64::EPSILON);
152 | }
153 |
154 | #[test]
155 | fn modulo() {
156 | let expression = "7 % 3";
157 | let parse_result = math_utils::parse_expr(expression);
158 | if let Err(e) = &parse_result {
159 | eprint!("{}", e);
160 | assert!(!parse_result.is_err());
161 | }
162 |
163 | let (_, ast) = parse_result.unwrap();
164 | let eval = math_utils::evaluate(&ast, 0.0);
165 | let expected = 1.0;
166 | assert!((eval - expected).abs() < f64::EPSILON);
167 | }
168 |
169 | #[test]
170 | fn formula() {
171 | let expression = "e ^ (pi / 2)";
172 | let parse_result = math_utils::parse_expr(expression);
173 | if let Err(e) = &parse_result {
174 | eprint!("{}", e);
175 | assert!(!parse_result.is_err());
176 | }
177 |
178 | let (_, ast) = parse_result.unwrap();
179 | let eval = math_utils::evaluate(&ast, 0.0);
180 | let expected = 4.810477380965351;
181 | println!("{}", eval);
182 | assert!((eval - expected).abs() < f64::EPSILON);
183 | }
184 | }
185 |
--------------------------------------------------------------------------------