From 28b78e2cdc43fd8d0dbf35c3c79cf1b048f73f75 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 29 Jan 2022 22:26:23 +0530 Subject: init --- .gitignore | 3 + Cargo.lock | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 +++ flake.lock | 87 +++++++++++++++++++++++ flake.nix | 101 +++++++++++++++++++++++++++ src/main.rs | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 613 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3e728e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.envrc +.direnv +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..052dd11 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,229 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "git2" +version = "0.13.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" + +[[package]] +name = "libgit2-sys" +version = "0.12.26+1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "prompt" +version = "0.1.0" +dependencies = [ + "git2", + "tico", +] + +[[package]] +name = "tico" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d73fbfe0205d9c15fb2768ac13d5bba2c39909bf326f5b36818aade6739464" + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9c24663 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "prompt" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tico = "1.0.0" +git2 = "0.13.25" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1e575e3 --- /dev/null +++ b/flake.lock @@ -0,0 +1,87 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1642141613, + "narHash": "sha256-WFS7T5oOLD8anu/77iz7EPz7zQsJZtP5qVa5485k3dI=", + "owner": "nix-community", + "repo": "fenix", + "rev": "d75ffc9f1d3b60bb597e48ae01f486a6de27d30e", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1635165013, + "narHash": "sha256-o/BdVjNwcB6jOmzZjOH703BesSkkS5O7ej3xhyO8hAY=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "5b9e0ff9d3b551234b4f3eb3983744fa354b17f1", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1642069818, + "narHash": "sha256-666w6j8wl/bojfgpp0k58/UJ5rbrdYFbI2RFT2BXbSQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "46821ea01c8f54d2a20f5a503809abfc605269d7", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1642101527, + "narHash": "sha256-C4zcaWULWlrirpEt/pA85CCMBt+7SWZweMaMJ7EvrXw=", + "owner": "rust-analyzer", + "repo": "rust-analyzer", + "rev": "b4c31481a554d0132003228ba319bd9476fe85ae", + "type": "github" + }, + "original": { + "owner": "rust-analyzer", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3f416a9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,101 @@ +{ + inputs = { + + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + gitignore = { + url = "github:hercules-ci/gitignore.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + }; + + outputs = + { self + , nixpkgs + , fenix + , gitignore + }: + let + inherit (gitignore.lib) gitignoreSource; + + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + nixpkgsFor = forAllSystems (system: + import nixpkgs { + inherit system; + overlays = [ self.overlay ]; + }); + + chanspec = { + date = "2021-12-01"; + channel = "nightly"; + sha256 = "DhIP1w63/hMbWlgElJGBumEK/ExFWCdLaeBV5F8uWHc="; # set zeros after modifying channel or date + }; + rustChannel = p: (fenix.overlay p p).fenix.toolchainOf chanspec; + + in + { + + overlay = final: prev: { + + prompt = with final; + let + pname = "prompt"; + packageMeta = (lib.importTOML ./bin/Cargo.toml).package; + rustPlatform = makeRustPlatform { + inherit (rustChannel final) cargo rustc; + }; + in + rustPlatform.buildRustPackage { + inherit pname; + inherit (packageMeta) version; + + buildInputs = [ + final.openssl + final.pkgconfig + ]; + + src = gitignoreSource ./.; + cargoLock.lockFile = ./Cargo.lock; + }; + + }; + + packages = forAllSystems (system: { + inherit (nixpkgsFor."${system}") prompt; + }); + + defaultPackage = + forAllSystems (system: self.packages."${system}".prompt); + + devShell = forAllSystems (system: + let + pkgs = nixpkgsFor."${system}"; + toolchain = (rustChannel pkgs).withComponents [ + "rustc" + "cargo" + "rust-std" + "rustfmt" + "clippy" + "rust-src" + ]; + inherit (fenix.packages."${system}") rust-analyzer; + in + pkgs.mkShell { + nativeBuildInputs = [ + pkgs.bacon + rust-analyzer + toolchain + ]; + RUST_LOG = "info"; + RUST_BACKTRACE = 1; + }); + + }; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0b1ec1b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,183 @@ +use std::{env, fmt, path::Path}; + +use git2::{Oid, Repository, Status}; +use tico::tico; + +fn main() { + let args = env::args().collect::>(); + match args + .iter() + .map(String::as_str) + .collect::>() + .as_slice() + { + [_, "cwd", target] => print!("{}", cwd(target)), + [_, "vcs", target] => { + if let Some(status) = vcs(target) { + print!("{}", status) + } + } + _ => (), + } +} + +fn cwd(target: &str) -> String { + let home = env::var("HOME").unwrap(); + + let home_dir_ext = format!("{}{}", home, "/"); + if target == home.as_str() || target.starts_with(&home_dir_ext) { + let replaced = target.replacen(home.as_str(), "~", 1); + tico(&replaced) + } else { + tico(&target) + } +} + +struct VcsStatus { + branch: Branch, + dist: Dist, + status: StatusSummary, +} + +impl fmt::Display for VcsStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}{}#[fg=colour7]", + self.branch, self.dist, self.status + ) + } +} + +enum Branch { + Id(Oid), + Ref(String), + Unknown, +} + +impl fmt::Display for Branch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Branch::Id(id) => write!(f, "#[fg=colour3]{:.7} ", id), + Branch::Ref(s) => write!(f, "#[fg=colour8]{} ", s), + Branch::Unknown => write!(f, ""), + } + } +} + +enum Dist { + Ahead, + Behind, + Both, + Neither, +} + +impl fmt::Display for Dist { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "#[fg=colour8]{}", + match self { + Self::Ahead => "↑ ", + Self::Behind => "↓ ", + Self::Both => "↑↓ ", + Self::Neither => "", + } + ) + } +} + +enum StatusSummary { + WtModified, + IdxModified, + Conflict, + Clean, +} + +impl fmt::Display for StatusSummary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::WtModified => "#[fg=colour1]×", + Self::IdxModified => "#[fg=colour3]±", + Self::Conflict => "#[fg=colour5]!", + Self::Clean => "#[fg=colour2]·", + } + ) + } +} + +fn vcs(target: &str) -> Option { + let repo = match Path::new(target) + .ancestors() + .map(Repository::open) + .find_map(|r| r.ok()) + { + Some(r) => r, + None => return None, + }; + + let dist = match get_ahead_behind(&repo) { + Some((ahead, behind)) if ahead > 0 && behind > 0 => Dist::Both, + Some((ahead, _)) if ahead > 0 => Dist::Ahead, + Some((_, behind)) if behind > 0 => Dist::Behind, + _ => Dist::Neither, + }; + + let branch = match repo.head() { + Ok(reference) if reference.is_branch() => { + Branch::Ref(reference.shorthand().unwrap().to_string()) + } + Ok(reference) => Branch::Id(reference.peel_to_commit().unwrap().id()), + _ => Branch::Unknown, + }; + + let status = repo_status(&repo); + + Some(VcsStatus { + branch, + dist, + status, + }) +} + +fn repo_status(repo: &Repository) -> StatusSummary { + for file in repo.statuses(None).unwrap().iter() { + match file.status() { + // STATE: conflicted + Status::CONFLICTED => return StatusSummary::Conflict, + // STATE: unstaged (working tree modified) + Status::WT_NEW + | Status::WT_MODIFIED + | Status::WT_DELETED + | Status::WT_TYPECHANGE + | Status::WT_RENAMED => return StatusSummary::WtModified, + // STATE: staged (changes added to index) + Status::INDEX_NEW + | Status::INDEX_MODIFIED + | Status::INDEX_DELETED + | Status::INDEX_TYPECHANGE + | Status::INDEX_RENAMED => return StatusSummary::IdxModified, + // STATE: committed (changes have been saved in the repo) + _ => return StatusSummary::Clean, + } + } + StatusSummary::Clean +} + +fn get_ahead_behind(r: &Repository) -> Option<(usize, usize)> { + let head = (r.head().ok())?; + if !head.is_branch() { + return None; + } + + let head_name = head.shorthand()?; + let head_branch = r.find_branch(head_name, git2::BranchType::Local).ok()?; + let upstream = head_branch.upstream().ok()?; + let head_oid = head.target()?; + let upstream_oid = upstream.get().target()?; + + r.graph_ahead_behind(head_oid, upstream_oid).ok() +} -- cgit v1.2.3