commit 77a23918340223e133b32e48b73bc1b593772029
parent 99b8905b3281ad9cc4fec347dd771c0ea11f6937
Author: Dasho <git@dasho.dev>
Date: Thu, 20 Mar 2025 04:48:55 +0000
[repo] ➕ Add unishell for git interactions
Added the unishell via src/main.rs
Diffstat:
| A | .gitignore | | | 1 | + |
| A | Cargo.lock | | | 296 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | Cargo.toml | | | 7 | +++++++ |
| A | src/main.rs | | | 262 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 566 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
@@ -0,0 +1,296 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "bitflags"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
+dependencies = [
+ "encode_unicode",
+ "libc",
+ "once_cell",
+ "unicode-width",
+ "windows-sys",
+]
+
+[[package]]
+name = "dialoguer"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
+dependencies = [
+ "console",
+ "shell-words",
+ "tempfile",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+
+[[package]]
+name = "errno"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "getrandom"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
+
+[[package]]
+name = "once_cell"
+version = "1.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
+name = "rustix"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "shell-words"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+
+[[package]]
+name = "syn"
+version = "2.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
+dependencies = [
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
+[[package]]
+name = "unishell"
+version = "0.0.1"
+dependencies = [
+ "dialoguer",
+]
+
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
diff --git a/Cargo.toml b/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "unishell"
+version = "0.0.1"
+edition = "2024"
+
+[dependencies]
+dialoguer = "0.11.0"
diff --git a/src/main.rs b/src/main.rs
@@ -0,0 +1,262 @@
+use dialoguer::{theme::ColorfulTheme, Select, Input};
+use std::os::windows::process::ExitStatusExt;
+use std::process::Command;
+use std::process::Output;
+
+/// Main entry point for UniShell - Git Operations Terminal
+fn main() {
+ loop {
+ println!("Welcome to UniShell - Git Operations Terminal");
+
+ let options = vec![
+ "Status",
+ "Add Files",
+ "Commit Changes",
+ "Push Changes",
+ "Pull Changes",
+ "Switch Branch",
+ "Initialize Repository",
+ "Clone Repository",
+ "Pull Specific Folder",
+ "Include Submodules",
+ "Remove Folder",
+ "Restore Folder",
+ "Exit",
+ ];
+
+ let selection = Select::with_theme(&ColorfulTheme::default())
+ .with_prompt("Choose a Git operation")
+ .items(&options)
+ .default(0)
+ .interact()
+ .unwrap();
+
+ match selection {
+ 0 => git_status(),
+ 1 => git_add(),
+ 2 => git_commit(),
+ 3 => git_push(),
+ 4 => git_pull(),
+ 5 => git_switch_branch(),
+ 6 => git_init(),
+ 7 => git_clone(),
+ 8 => git_pull_folder(),
+ 9 => git_include_submodules(),
+ 10 => git_remove_folder(),
+ 11 => git_restore_folder(),
+ 12 => {
+ println!("Exiting UniShell. Goodbye!");
+ break;
+ }
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Runs `git status` and prints the output
+fn git_status() {
+ println!("Running `git status`...");
+ let output = Command::new("git")
+ .arg("status")
+ .output()
+ .expect("Failed to execute git status");
+ println!("{}", String::from_utf8_lossy(&output.stdout));
+}
+
+/// Adds files to the staging area
+fn git_add() {
+ let files: String = Input::new()
+ .with_prompt("Enter files to add (use '.' to add all)")
+ .interact_text()
+ .unwrap();
+
+ println!("Running `git add {}`...", files);
+ Command::new("git")
+ .arg("add")
+ .arg(files)
+ .status()
+ .expect("Failed to execute git add");
+}
+
+/// Commits changes with a message
+fn git_commit() {
+ let message: String = Input::new()
+ .with_prompt("Enter commit message")
+ .interact_text()
+ .unwrap();
+
+ println!("Running `git commit -m \"{}\"`...", message);
+ Command::new("git")
+ .arg("commit")
+ .arg("-m")
+ .arg(message)
+ .status()
+ .expect("Failed to execute git commit");
+}
+
+/// Pushes changes to the remote repository
+fn git_push() {
+ println!("Running `git push`...");
+ Command::new("git")
+ .arg("push")
+ .status()
+ .expect("Failed to execute git push");
+}
+
+/// Pulls changes from the remote repository
+fn git_pull() {
+ println!("Running `git pull`...");
+ Command::new("git")
+ .arg("pull")
+ .status()
+ .expect("Failed to execute git pull");
+}
+
+/// Switches to a specified branch
+fn git_switch_branch() {
+ let branch: String = Input::new()
+ .with_prompt("Enter branch name to switch to")
+ .interact_text()
+ .unwrap();
+
+ println!("Running `git checkout {}`...", branch);
+ Command::new("git")
+ .arg("checkout")
+ .arg(branch)
+ .status()
+ .expect("Failed to execute git checkout");
+}
+
+/// Initializes a new Git repository
+fn git_init() {
+ println!("Running `git init`...");
+ Command::new("git")
+ .arg("init")
+ .status()
+ .expect("Failed to execute git init");
+}
+
+/// Clones a repository from a given URL
+fn git_clone() {
+ let repo_url: String = Input::new()
+ .with_prompt("Enter repository URL to clone")
+ .interact_text()
+ .unwrap();
+
+ println!("Running `git clone {}`...", repo_url);
+ Command::new("git")
+ .arg("clone")
+ .arg(repo_url)
+ .status()
+ .expect("Failed to execute git clone");
+}
+
+/// Pulls a specific folder using sparse-checkout
+fn git_pull_folder() {
+ let folder: String = Input::new()
+ .with_prompt("Enter folder path to pull")
+ .interact_text()
+ .unwrap();
+
+ println!("Running `git sparse-checkout set {}` and pulling...", folder);
+ Command::new("git")
+ .arg("sparse-checkout")
+ .arg("set")
+ .arg(&folder)
+ .status()
+ .expect("Failed to set sparse-checkout folder");
+
+ Command::new("git")
+ .arg("pull")
+ .status()
+ .expect("Failed to pull specific folder");
+}
+
+/// Includes submodules in the repository
+fn git_include_submodules() {
+ println!("Running `git submodule update --init --recursive`...");
+ Command::new("git")
+ .arg("submodule")
+ .arg("update")
+ .arg("--init")
+ .arg("--recursive")
+ .status()
+ .expect("Failed to include submodules");
+}
+
+/// Removes a folder from the repository
+fn git_remove_folder() {
+ let folder: String = Input::new()
+ .with_prompt("Enter folder path to remove")
+ .interact_text()
+ .unwrap();
+
+ println!("Removing folder `{}`...", folder);
+ Command::new("rm")
+ .arg("-rf")
+ .arg(&folder)
+ .status()
+ .expect("Failed to remove folder");
+}
+
+/// Restores a folder to its previous state
+fn git_restore_folder() {
+ let folder: String = Input::new()
+ .with_prompt("Enter folder path to restore")
+ .interact_text()
+ .unwrap();
+
+ println!("Restoring folder `{}`...", folder);
+ Command::new("git")
+ .arg("checkout")
+ .arg("--")
+ .arg(&folder)
+ .status()
+ .expect("Failed to restore folder");
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn mock_command_output(stdout: &str) -> Output {
+ Output {
+ status: std::process::ExitStatus::from_raw(0),
+ stdout: stdout.as_bytes().to_vec(),
+ stderr: Vec::new(),
+ }
+ }
+
+ #[test]
+ fn test_git_status() {
+ let output = mock_command_output("On branch main\nYour branch is up to date.");
+ assert_eq!(
+ String::from_utf8_lossy(&output.stdout),
+ "On branch main\nYour branch is up to date."
+ );
+ }
+
+ #[test]
+ fn test_git_add() {
+ // Mocking user input and command execution
+ let files = "test_file.txt";
+ assert_eq!(files, "test_file.txt");
+ }
+
+ #[test]
+ fn test_git_commit() {
+ // Mocking user input and command execution
+ let message = "Initial commit";
+ assert_eq!(message, "Initial commit");
+ }
+
+ #[test]
+ fn test_git_push() {
+ // Mocking command execution
+ let output = mock_command_output("Everything up-to-date");
+ assert_eq!(
+ String::from_utf8_lossy(&output.stdout),
+ "Everything up-to-date"
+ );
+ }
+}