diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index b58b603..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
diff --git a/.idea/PrettyPrompt.iml b/.idea/PrettyPrompt.iml
deleted file mode 100644
index cf84ae4..0000000
--- a/.idea/PrettyPrompt.iml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 3792141..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 932f2f8..39cac17 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,92 +1,41 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
-name = "colored"
-version = "2.1.0"
+name = "ansi_term"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
- "lazy_static",
- "windows-sys",
+ "winapi",
]
-[[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-
[[package]]
name = "prettyprompt"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
- "colored",
+ "ansi_term",
]
[[package]]
-name = "windows-sys"
-version = "0.48.0"
+name = "winapi"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
- "windows-targets",
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
]
[[package]]
-name = "windows-targets"
-version = "0.48.5"
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.5"
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
index 5c2432d..56acdc0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "prettyprompt"
-version = "0.1.0"
+version = "0.2.0"
edition = "2021"
[dependencies]
-colored = "2.1.0"
+ansi_term = "0.12"
diff --git a/README.md b/README.md
index f974528..4f5c7d0 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,129 @@
-# PrettyPrompt
+# PrettyPrompt
-A pretty shell prompt, written in rust
+A pretty shell prompt, written in rust.
+
+## Current Features
+
+- **User indicator** - Symbol with different colors for root user and normal users
+- **Error indicator** - Symbol with different colors to indicate if the last comment was successful
+- **Git repo indicator**
+ - Indicates if the current directory is a repo or a regular directory
+ - Branches indicated by different colors
+- **SSH indicator** - Symbol to indicate if the current shell is in an SSH session
+- **Current directory**
+ - Abbreviated if the path is too long
+ - Replaces the user's home directory with a `~` symbol
+ - Show the repo's name if currently in a git repo
## Screenshot
![screenshot](https://git.candifloss.cc/candifloss/PrettyPrompt/raw/branch/main/screenshot/BashPromptExampleScreenshoot.png "Screenshot")
-Ignore the zsh prompt in the screenshot.
+
+## Planned Features
+
+- **Right-hand side prompt**: Challenging to implement on non-zsh shells.
+- **Configuration file**
+ - Choose only the components you need
+ - Change appearance
+ - Symbols and text
+ - Colors
+ - Order and position
+ - Custom components
+ - Static: Shell icon, Host name, etc.
+ - Dynamic: Time & date, system stats, or any custom commands
+
+## Current Limitations
+
+- **Hard-Coded Configuration**: User customization is not available yet.
+- **Exit Code Requirement**: Must pass the last command’s exit code as a command-line argument.
+
+## Tested on
+
+Ubuntu 24.04
+ - `bash` 5.2
+ - `zsh` 5.9
+ - `ion` 1.0.0-alpha
+
+## Installation
+
+**Step 1. Build binary from source**
+This requires [rust](https://www.rust-lang.org/tools/install) installed on your system.
+
+```bash
+git clone https://git.candifloss.cc/candifloss/PrettyPrompt.git
+cd PrettyPrompt/
+cargo build --release
+# Binary location: `target/release/prettyprompt`
+```
+
+**Step 2. Add to `$PATH`**
+ - Option 1. Move the binary to a directory in your `$PATH`. Eg:
+```bash
+sudo mv target/release/prettyprompt /usr/bin/
+```
+ - Option 2. Add the directory containing the binary to `$PATH`
+ System-wide: `/etc/profile`
+ User-specific: `~/.profile`
+ Shell-specific: `bashrc`, `zshrc`, etc.
+```bash
+export PATH="$PATH:/path/where/the/binary/is/"
+```
+
+## Usage
+
+Configuration varies by shell, and the file location varies by distro. Refer your shell's docs or community resources for details. Note that the exit code of the last command(usually `$?` variable) must be passed as a command-line argument.
+
+### `bash`
+
+ - The `PS1` variable sets a fixed prompt string.
+ - This `PROMPT_COMMAND` variable sets a dynamic prompt.
+
+System-wide: `/etc/bash.bashrc`
+User-specific: `~/.bashrc`:
+```bash
+PS1="" # Set it to an empty string
+PROMPT_COMMAND='prettyprompt $?' # Single quotes, not double quotes
+```
+
+### `ion`
+
+The `PROMPT` function is currently the only way to customize the prompt according to the `ion` shell docs.
+User-specific config: `~/.config/ion/initrc`:
+```ion
+fn PROMPT
+ prettyprompt $?
+end
+```
+
+### `zsh`
+Export the `PS1` variable with the output of `prettyprompt $?` as its value.
+User-specific: `~/.zshrc`
+System-wide: `/etc/zsh/zshrc`
+```sh
+export PS1='$(prettyprompt $?)' # Notice the single quotes
+```
+
+### Other shells
+
+For other shells, refer their docs to set a dynamic prompt. Ensure the last command's exit code (`$?` or equivalent) is passed to `prettyprompt`.
+
+## Changes since the last version
+ - **Updated Output String Type:** Ansi strings improved compatibility with other shells.
+ - **Revamped Indicator Symbols:** Enhanced the visual aspect of the prompt.
+ - **Removed Shell Icon:** Determining the shell is practically not possible.
+ - **Conditional Component Inclusion:** A first step towards user-configuration expected in future versions.
+ - **Code Improvements:** readability and performance
+ - **Refactoring:** Modular structure for better readability and maintenance.
+ - **Modularization:** Separate modules for cleaner organization.
+ - **Error Handling:** Improved logic to exclude error messages from the prompt.
+ - **Enhanced Documentation:** Comments for better comprehension.
+
+## Acknowledgement
+
+The current default (and only) theme draws inspiration from [s1ck94](https://github.com/zimfw/s1ck94) theme of [zimfw](https://zimfw.sh/).
+
+## Why this project?
+
+- **Efficiency**: Avoids repeated invocation of multiple binaries like `tr`, `grep`, `echo`, `git`, `sed`, etc., which would otherwise be used dozens of times in shell scripts just to generate a colored string.
+- **Portability**: Eliminates the need to write separate scripts in different shell languages for various shells.
+- **Learning Rust**: Serves as a fun and practical project to learn and apply Rust programming skills.
\ No newline at end of file
diff --git a/src/indicators/error.rs b/src/indicators/error.rs
new file mode 100644
index 0000000..5ce1992
--- /dev/null
+++ b/src/indicators/error.rs
@@ -0,0 +1,16 @@
+use ansi_term::ANSIGenericString;
+use ansi_term::Colour::RGB;
+
+pub const ERR_SYMBOL: &str = "\u{276F}"; // Error indicator symbol: "❯"
+pub const ERR_COL: ansi_term::Colour = RGB(255, 53, 94); // Error
+pub const NORMIE_COL: ansi_term::Colour = RGB(0, 255, 180); // Success
+
+// Error indicator
+pub fn indicator(args: &[String]) -> ANSIGenericString<'static, str> {
+ let exit_code = args.get(1).map_or("0", String::as_str); // Default to "0" (success, no error) if arg missing
+ if exit_code == "0" {
+ NORMIE_COL.paint(ERR_SYMBOL) // Success
+ } else {
+ ERR_COL.paint(ERR_SYMBOL) // Error
+ }
+}
diff --git a/src/indicators/git.rs b/src/indicators/git.rs
new file mode 100644
index 0000000..e459fe9
--- /dev/null
+++ b/src/indicators/git.rs
@@ -0,0 +1,48 @@
+use ansi_term::ANSIGenericString;
+use ansi_term::Colour::RGB;
+use std::path::Path;
+use std::process::Command;
+
+pub const GIT_SYMBOL: &str = "\u{276F}"; // Git indicator symbol: "❯"
+pub const MAIN_COL: ansi_term::Colour = RGB(178, 98, 44);
+pub const DEV_COL: ansi_term::Colour = RGB(54, 159, 150);
+pub const DEFAULT_BRANCH_COL: ansi_term::Colour = RGB(255, 255, 255);
+pub const NORMIE_COL: ansi_term::Colour = RGB(82, 82, 82);
+
+/// Returns the repo's root and branch, if present
+pub fn info() -> Option<(String, String)> {
+ let output = Command::new("git") // Program/Command to execute
+ .args(["rev-parse", "--show-toplevel", "--abbrev-ref", "HEAD"]) // Arguments
+ .output()
+ .ok()?;
+
+ if output.status.success() {
+ let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
+ let parts: Vec<&str> = output_str.split('\n').collect();
+ if parts.len() == 2 {
+ return Some((parts[0].to_string(), parts[1].to_string())); // If the `git`` command returns a repo and branch
+ }
+ }
+ None // If the current directory is not in a git repo
+}
+
+/// The name of the repo
+pub fn repo_name(path: &str) -> String {
+ Path::new(path)
+ .file_name() // Extracts the last component of the path.
+ .and_then(|name| name.to_str()) // Converts the `OsStr` to `&str`.
+ .unwrap_or("") // Default value(empty string) if None(no valid name)
+ .to_string() // Converts &str to String
+}
+
+/// Git branch indicator
+pub fn indicator(branch: Option) -> ANSIGenericString<'static, str> {
+ match branch {
+ Some(b) => match b.as_str() {
+ "main" => MAIN_COL.paint(GIT_SYMBOL),
+ "dev" => DEV_COL.paint(GIT_SYMBOL),
+ _ => DEFAULT_BRANCH_COL.paint(GIT_SYMBOL),
+ },
+ None => NORMIE_COL.paint(GIT_SYMBOL),
+ }
+}
diff --git a/src/indicators/pwd.rs b/src/indicators/pwd.rs
new file mode 100644
index 0000000..21fb3a3
--- /dev/null
+++ b/src/indicators/pwd.rs
@@ -0,0 +1,97 @@
+use crate::indicators::git::{repo_name, MAIN_COL};
+use ansi_term::ANSIGenericString;
+use ansi_term::Colour::RGB;
+use std::env::current_dir;
+
+pub const UNKNOWN_PATH: &str = "\u{2248}"; // "≈"
+pub const PATH_COL: ansi_term::Colour = RGB(82, 82, 82);
+pub const REPO_COL: ansi_term::Colour = RGB(55, 120, 130);
+
+/// Find the current user's home directory.
+fn home_dir() -> String {
+ std::env::var("HOME").map_or_else(|_| String::new(), |path| path.to_string())
+}
+
+/// Returns the full path of the current directory as a string.
+fn full_path() -> String {
+ current_dir().map_or_else(
+ |_| UNKNOWN_PATH.to_string(),
+ |path| path.display().to_string(),
+ )
+}
+
+/// Remove the path of the repo's root from the path, to replace it with the repo's name.
+fn remove_repo(pwd_path: &str, repo_path: &str) -> String {
+ pwd_path.replacen(repo_path, "", 1)
+}
+
+/// Replace the 'home directory' part of the path with a the '~' symbol
+fn replace_home(path: &str) -> String {
+ path.replacen(&home_dir(), "~", 1)
+}
+
+fn short(path: &str, slash_limit: u8) -> String {
+ let slashes = path.matches('/').count(); // Get the number of slashes
+ if slashes <= slash_limit.into() {
+ return path.to_string(); // Short path, return without changes
+ }
+
+ // Else: Long path, shorten it
+ let parts: Vec<&str> = path.split('/').collect(); // Split the path into parts
+ let first = if path.starts_with('~') { "~" } else { "" }; // Determine the first part correctly
+ let last = parts[parts.len() - 1]; // The last part
+
+ // Abbreviate middle parts (take the first character of each)
+ let abbreviated_middle = parts[1..parts.len() - 1] // Skip the first and last part
+ .iter()
+ .filter(|&&part| !part.is_empty()) // Avoid empty parts (like "//")
+ .map(|&part| part.chars().next().unwrap().to_string()) // Take the first letter
+ .collect::>() // Collect the parts as a Vec
+ .join("/"); // Join them with a "/" separator
+
+ format!("{first}/{abbreviated_middle}/{last}") // Final
+}
+
+pub fn pwd(
+ abbrev_home: bool,
+ shorten_path: bool,
+ replace_repo: bool,
+ git_repo: Option,
+) -> ANSIGenericString<'static, str> {
+ let mut slash_limit: u8 = 3; // Max number of slashes
+ let mut path = full_path(); // Get the full path of he current directory
+
+ // Replace a git repo root path with the repo's name
+ if replace_repo && git_repo.is_some() {
+ if let Some(repo_path) = git_repo {
+ path = remove_repo(&path, &repo_path);
+ let repo_name = repo_name(&repo_path);
+ if shorten_path {
+ slash_limit = 2; // Max number of slashes
+ path = short(&path, slash_limit);
+ }
+ return if path.is_empty() {
+ REPO_COL.paint(repo_name) // In the root dir of the repo
+ } else {
+ // In a subdir inside the repo
+ format!(
+ "{}{}{}",
+ REPO_COL.paint(repo_name), // Repo name
+ MAIN_COL.paint(" \u{F02A2} "), // Seperator
+ PATH_COL.italic().paint(path) // Sub-directory
+ )
+ .into()
+ };
+ }
+ }
+
+ // Replace the home directory path with the 'home symbol': ~
+ if abbrev_home {
+ path = replace_home(&path);
+ }
+ if shorten_path {
+ path = short(&path, slash_limit);
+ }
+
+ PATH_COL.italic().paint(path)
+}
diff --git a/src/indicators/ssh.rs b/src/indicators/ssh.rs
new file mode 100644
index 0000000..500b5eb
--- /dev/null
+++ b/src/indicators/ssh.rs
@@ -0,0 +1,23 @@
+use ansi_term::ANSIGenericString;
+use ansi_term::Colour::RGB;
+use std::env::var; // For environment variables
+
+pub const SSH_SYMBOL: &str = "\u{276F}"; // SSH indicator symbol: "❯"
+pub const SSH_COL: ansi_term::Colour = RGB(255, 149, 0); // In SSH session
+pub const NORMIE_COL: ansi_term::Colour = RGB(82, 82, 82); // Non-SSH session
+const SSH_ENV_VARS: [&str; 2] = ["SSH_TTY", "SSH_CONNECTION"]; // Environment variables normally present in SSH sessions
+
+/// Checks if current session is an SSH session
+fn is_ssh_session() -> bool {
+ SSH_ENV_VARS.iter().any(|&var_name| var(var_name).is_ok()) // any() iterates through the iter and stops at first `true`. Equal to `||`(`or` condition)
+}
+
+/// SSH shell indicator
+pub fn indicator() -> ANSIGenericString<'static, str> {
+ let is_ssh: bool = is_ssh_session();
+ if is_ssh {
+ SSH_COL.paint(SSH_SYMBOL)
+ } else {
+ NORMIE_COL.paint(SSH_SYMBOL)
+ }
+}
diff --git a/src/indicators/user.rs b/src/indicators/user.rs
new file mode 100644
index 0000000..451303f
--- /dev/null
+++ b/src/indicators/user.rs
@@ -0,0 +1,23 @@
+use ansi_term::ANSIGenericString;
+use ansi_term::Colour::RGB;
+use std::env::var; // For environment variables
+
+pub const USER_SYMBOL: &str = "\u{276F}"; // User indicator symbol: "❯"
+pub const ROOT_COL: ansi_term::Colour = RGB(255, 53, 94); // If the user is root
+pub const NORMIE_COL: ansi_term::Colour = RGB(0, 255, 180); // Regular user
+const ROOT_USER: &str = "root"; // Root username constant
+
+/// Username of current user
+pub fn username() -> String {
+ var("USER").unwrap_or_else(|_| "UnknownUser".to_string())
+}
+
+/// Root user indicator
+pub fn indicator() -> ANSIGenericString<'static, str> {
+ let user = username();
+ if user == ROOT_USER {
+ ROOT_COL.paint(USER_SYMBOL)
+ } else {
+ NORMIE_COL.paint(USER_SYMBOL)
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 7bd6c8c..3d6ed5c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,161 +1,69 @@
-use colored::{ColoredString, Colorize};
-use std::env::{args, current_dir, var_os};
-use std::process::Command;
-
-fn get_shell_char(shell: &str) -> String {
- let shell_char = match shell {
- "bash" | "/bin/bash" | "/usr/bin/bash" | "-bash" => " ",
- "zsh" | "/bin/zsh" | "/usr/bin/zsh" | "-zsh" => " ",
- "fish" => " ",
- "nushell" => " ",
- "ion" => " ",
- "oursh" => " ",
- _ => " ",
- };
- shell_char.to_string()
+mod indicators {
+ pub mod error;
+ pub mod git;
+ pub mod pwd;
+ pub mod ssh;
+ pub mod user;
}
-fn get_git_branch() -> String {
- let git_status_cmd = Command::new("git")
- .arg("status")
- .output()
- .expect("git_status_cmd_fail");
- let git_status_output = String::from_utf8_lossy(&git_status_cmd.stdout);
- let git_err = String::from_utf8_lossy(&git_status_cmd.stderr);
+use crate::indicators::{error, git, pwd, ssh, user};
+use ansi_term::{ANSIGenericString, ANSIGenericStrings};
- if git_err == "" {
- git_status_output.split('\n').collect::>()[0]
- .split(' ')
- .collect::>()[2]
- .to_string()
+// Add a component to the prompt if the condition is true.
+fn add_component(
+ components: &mut Vec>, // Vector to hold components of the prompt.
+ condition: bool, // Condition to add the component
+ component_fn: impl FnOnce() -> ANSIGenericString<'static, str>, // Function to create the component(takes no arguments, returns `ANSIGenericString`) is passed here.
+) {
+ if condition {
+ components.push(component_fn()); // Push the generated component to the vector.
+ }
+}
+
+fn main() {
+ let cmd_args: Vec = std::env::args().collect(); // Command-line args
+
+ // Vector to hold the different parts of the prompt.
+ let mut components: Vec> = Vec::new(); // The components will be concatenated into a single string in the end.
+
+ // Hard-coded configuration. This will be replaced by a configuration file in a future version
+ let indicate_user: bool = true;
+ let indicate_ssh: bool = true;
+ let indicate_err: bool = true;
+ let indicate_git_branch: bool = true;
+ let show_pwd: bool = true;
+ let abbrev_home: bool = true;
+ let shorten_path: bool = true;
+ let replace_repo: bool = true;
+
+ // Conditionally fetch Git-related info if required, or set to None
+ let git_info: Option<(String, String)> = if indicate_git_branch || replace_repo {
+ git::info()
} else {
- String::new()
- }
-}
-
-fn get_git_root() -> String {
- let git_repo_root_cmd = Command::new("git")
- .arg("rev-parse")
- .arg("--show-toplevel")
- .output()
- .expect("git_repo_root_cmd_fail");
- let mut git_repo_path = String::from_utf8_lossy(&git_repo_root_cmd.stdout).to_string();
- let git_repo_err = String::from_utf8_lossy(&git_repo_root_cmd.stderr);
-
- if git_repo_err == "" {
- let len = git_repo_path.trim_end_matches(&['\r', '\n'][..]).len();
- git_repo_path.truncate(len);
- } else {
- git_repo_path = String::new();
- }
- git_repo_path
-}
-
-fn get_git_repo_name(git_repo_root: &str) -> String {
- let repo_path_split: Vec<&str> = git_repo_root.split('/').collect();
- let last_index = repo_path_split.len() - 1;
- let git_repo_name = repo_path_split[last_index];
-
- git_repo_name.to_string()
-}
-
-fn get_git_char(git_branch: &str) -> ColoredString {
- match git_branch {
- "" => "".clear(),
- "main" => " ".truecolor(178, 98, 44),
- "master" => " ".truecolor(196, 132, 29),
- _ => " ".truecolor(82, 82, 82),
- }
-}
-
-fn abrev_path(path: &str) -> String {
- let mut short_dir = path.to_string();
-
- let slashes = path.matches('/').count();
-
- if slashes > 3 {
- let parts: Vec<&str> = path.split('/').collect();
- let len = parts.len() - 1;
- let mut ch1: String;
-
- for part in &parts[0..len] {
- if part.to_string() != "" {
- // to avoid the 1st "/"
- ch1 = part.chars().next().expect(part).to_string(); // 1st char of each part
- short_dir = short_dir.replace(part, &ch1);
- }
- }
- }
- short_dir
-}
-
-fn main() -> std::io::Result<()> {
- let angle = "❯";
-
- //Root user indicator
- let user = var_os("USER")
- .expect("UnknownUser")
- .to_str()
- .expect("UnknownUser")
- .to_string();
-
- let mut err: String = String::new();
-
- let args: Vec = args().collect();
- let shell: String;
- if args.len() > 1 {
- shell = args[1].clone(); // Shell symbol
- if args.len() > 2 {
- err.clone_from(&args[2]); // Error status
- }
- } else {
- shell = "none".to_string();
- }
-
- let root_indicator = match user.as_str() {
- "root" => angle.truecolor(255, 53, 94),
- _ => angle.truecolor(0, 255, 180),
+ None
};
- let err_indicator = match err.as_str() {
- "0" => angle.truecolor(0, 255, 180),
- _ => angle.truecolor(255, 53, 94),
- };
+ // Conditionally add the parts of the prompt
+ add_component(&mut components, indicate_ssh, ssh::indicator);
+ add_component(&mut components, indicate_git_branch, || {
+ git::indicator(git_info.as_ref().map(|info| info.1.clone()))
+ });
+ add_component(&mut components, indicate_user, user::indicator);
+ add_component(&mut components, indicate_err, || {
+ error::indicator(&cmd_args)
+ });
- //SSH shell indicator
- let ssh_char: ColoredString = match var_os("SSH_TTY") {
- Some(_val) => " ".truecolor(0, 150, 180),
- None => "".clear(),
- };
+ // Insert `pwd` at the beginning of the prompt
+ if show_pwd {
+ let repo_path = git_info.map(|info| info.0);
+ components.insert(
+ 0,
+ pwd::pwd(abbrev_home, shorten_path, replace_repo, repo_path),
+ );
+ }
- //Git status
- let git_branch = get_git_branch();
- let git_repo_root = get_git_root();
- let git_repo_name = get_git_repo_name(&git_repo_root.clone()).truecolor(122, 68, 24);
- let git_char = get_git_char(&git_branch);
-
- //pwd
- let homedir = var_os("HOME")
- .expect("UnknownDir")
- .to_str()
- .expect("UnknownDir")
- .to_string();
- let pwd = current_dir()?;
- let mut cur_dir = pwd.display().to_string();
- cur_dir = cur_dir.replace(&git_repo_root, ""); // Remove git repo root
- cur_dir = cur_dir.replace(&homedir, "~"); // Abreviate homedir with "~"
- cur_dir = abrev_path(&cur_dir);
-
- print!(
- "{}{}{}{}{}{}{} ",
- ssh_char,
- get_shell_char(&shell).truecolor(75, 75, 75),
- git_repo_name,
- git_char,
- cur_dir.italic().truecolor(82, 82, 82),
- root_indicator,
- err_indicator,
- );
-
- Ok(())
+ // Finally, combine the parts into a single prompts string
+ let prompt: ANSIGenericStrings<'_, str> = ANSIGenericStrings(&components[..]);
+ // `print!()` prevents an extra newline, unlike `println!()`
+ print!("{prompt} "); // A trailing space for aesthetic formatting.
}