feat: Created my personal website Shell
This commit is contained in:
+5
-1
@@ -18,4 +18,8 @@ Cargo.lock
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
#.idea/
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "clash"
|
||||
version = "0.1.0"
|
||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||
description = "An interactive Clarke Shell for browsing my personal website."
|
||||
keywords = ["command-line-interface"]
|
||||
documentation = "https://github.com/Dark-Alex-17/ClaSH"
|
||||
repository = "https://github.com/Dark-Alex-17/ClaSH"
|
||||
homepage = "https://github.com/Dark-Alex-17/ClaSH"
|
||||
readme = "README.md"
|
||||
edition = "2021"
|
||||
rust-version = "1.82.0"
|
||||
exclude = [".github", "CONTRIBUTING.md", "*.log", "tags"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.93"
|
||||
clap = { version = "4.5.21", features = ["derive", "cargo", "env"] }
|
||||
indoc = "2.0.5"
|
||||
rustyline = { version = "15.0.0", features = [
|
||||
"case_insensitive_history_search",
|
||||
"with-file-history",
|
||||
"with-fuzzy",
|
||||
] }
|
||||
termimad = "0.31.1"
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
FROM rust:latest AS builder
|
||||
WORKDIR /usr/src/clash
|
||||
COPY . .
|
||||
|
||||
RUN cargo build --release
|
||||
|
||||
FROM ubuntu:latest
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ttyd \
|
||||
bash \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /usr/src/clash/target/release/clash /bin/alexjclarke/clash
|
||||
RUN useradd aclarke
|
||||
|
||||
USER aclarke
|
||||
WORKDIR /home/aclarke
|
||||
ENV TERM=xterm
|
||||
CMD ["/bin/ttyd", "--max-clients", "5", "--writable", "--port", "8080", "/bin/alexjclarke/clash"]
|
||||
EXPOSE 8080
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
use std::io::{stdout, Write};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Subcommand;
|
||||
use projects::PROJECTS;
|
||||
use resume::RESUME;
|
||||
use termimad::crossterm::cursor::{Hide, Show};
|
||||
use termimad::crossterm::event::{Event, KeyCode, KeyEvent};
|
||||
use termimad::crossterm::style::Color::*;
|
||||
use termimad::crossterm::terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen};
|
||||
use termimad::crossterm::{event, queue, terminal};
|
||||
use termimad::{rgb, Alignment, Area, MadSkin, MadView};
|
||||
|
||||
mod projects;
|
||||
mod resume;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Subcommand, PartialEq, Eq)]
|
||||
pub enum Command {
|
||||
#[command(about = "Display my Resume", override_usage = "resume")]
|
||||
Resume,
|
||||
#[command(
|
||||
about = "See a list of projects that I'm working on that you can check out",
|
||||
override_usage = "projects"
|
||||
)]
|
||||
Projects,
|
||||
#[command(about = "Output my GitHub Profile", override_usage = "github")]
|
||||
Github,
|
||||
#[command(about = "Output my LinkedIn Profile", override_usage = "linkedin")]
|
||||
Linkedin,
|
||||
#[command(about = "Output my email address", override_usage = "email")]
|
||||
Email,
|
||||
#[command(
|
||||
about = "Print this message or the help of the given subcommand(s)",
|
||||
override_usage = "help"
|
||||
)]
|
||||
Help,
|
||||
#[command(about = "Exit this ClaSH session", override_usage = "exit")]
|
||||
Exit,
|
||||
}
|
||||
|
||||
pub static COMMANDS: LazyLock<[String; 8]> = LazyLock::new(|| {
|
||||
[
|
||||
"resume".into(),
|
||||
"projects".into(),
|
||||
"github".into(),
|
||||
"linkedin".into(),
|
||||
"email".into(),
|
||||
"help".into(),
|
||||
"exit".into(),
|
||||
"--help".into(),
|
||||
]
|
||||
});
|
||||
|
||||
pub(super) fn handle_command(command: Command) -> Result<()> {
|
||||
match command {
|
||||
Command::Resume => display_markdown(RESUME)?,
|
||||
Command::Projects => display_markdown(PROJECTS)?,
|
||||
Command::Github => println!("https://github.com/Dark-Alex-17"),
|
||||
Command::Linkedin => println!("https://linkedin.com/in/alex-clarke-a78b99132"),
|
||||
Command::Email => println!("alex.j.tusa@gmail.com"),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn view_area() -> Area {
|
||||
let mut area = Area::full_screen();
|
||||
area.pad_for_max_width(180);
|
||||
area
|
||||
}
|
||||
|
||||
fn make_skin() -> MadSkin {
|
||||
let mut skin = MadSkin::default();
|
||||
skin.table.align = Alignment::Center;
|
||||
skin.set_headers_fg(rgb(255, 187, 0));
|
||||
skin.bold.set_fg(Yellow);
|
||||
skin.italic.set_fgbg(Magenta, rgb(30, 30, 40));
|
||||
skin.paragraph.align = Alignment::Left;
|
||||
skin.scrollbar.thumb.set_fg(AnsiValue(178));
|
||||
skin.code_block.align = Alignment::Center;
|
||||
skin
|
||||
}
|
||||
|
||||
fn display_markdown(text: &'static str) -> Result<()> {
|
||||
let mut w = stdout();
|
||||
queue!(w, EnterAlternateScreen)?;
|
||||
terminal::enable_raw_mode()?;
|
||||
queue!(w, Hide)?;
|
||||
let mut view = MadView::from(text.to_owned(), view_area(), make_skin());
|
||||
loop {
|
||||
view.write_on(&mut w)?;
|
||||
w.flush()?;
|
||||
match event::read() {
|
||||
Ok(Event::Key(KeyEvent { code, .. })) => match code {
|
||||
KeyCode::Up | KeyCode::Char('k') => view.try_scroll_lines(-1),
|
||||
KeyCode::Down | KeyCode::Char('j') => view.try_scroll_lines(1),
|
||||
KeyCode::PageUp => view.try_scroll_pages(-1),
|
||||
KeyCode::PageDown => view.try_scroll_pages(1),
|
||||
_ => break,
|
||||
},
|
||||
Ok(Event::Resize(..)) => {
|
||||
queue!(w, Clear(ClearType::All))?;
|
||||
view.resize(&view_area());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
terminal::disable_raw_mode()?;
|
||||
queue!(w, Show)?; // we must restore the cursor
|
||||
queue!(w, LeaveAlternateScreen)?;
|
||||
w.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
pub(super) static PROJECTS: &str = r#"# Projects
|
||||
The easiest way to see what I've been up to lately is to check out my GitHub profile: *https://github.com/Dark-Alex-17*
|
||||
|
||||
* **Managarr**: A Rust-based TUI for managing many different Servarr instances (*https://github.com/Dark-Alex-17/managarr*).
|
||||
* **Alerting-API**: A simple API that utilizes the Quartz library to provide a dynamic, reactive alerting system (minus the notification component), built with Kotlin. (*https://github.com/Dark-Alex-17/alerting-api*)
|
||||
* **AWS Cloud Gaming**: A Bash TUI built with whiptail to provision an EC2 instance in AWS, configure it with Steam, set up a low-latency RDP session using AWS' own NICE DCV to play games using AWS EC2 instances. (*https://github.com/Dark-Alex-17/aws-cloud-gaming*)
|
||||
* **DynamoDB and DAX Benchmarker**: A simple benchmarking utility built in both Rust and Go to benchmark performance differences between a DynamoDB table that doesn't use DAX, and one that does. An Elastic stack is used to receive the benchmarking metrics in real time so that users can also visualize the benchmarking results in Kibana using the pre-built dashboard. (*https://github.com/Dark-Alex-17/dynamodb-dax-benchmarker*)
|
||||
* **cp_sat**: Rust FFI bindings for the Google OR-Tools CP-SAT Solver to allow using the OR-Tools CP-SAT solver in Rust. (*https://github.com/Dark-Alex-17/cp_sat*)
|
||||
* **Real Analysis - Theorems and Definitions**: A large LaTeX book that's designed to be a quick-reference for many theorems, definitions, lemmas, corrollaries, etc. that are useful when proving conjectures or when studying properties of real numbers. (*https://github.com/Dark-Alex-17/real-analysis-theorems-and-definitions*)
|
||||
* **Abstract Algebra - Theorems and Definitions**: Like the previous project, this one is the same idea, except it's a collection of theorems, definitions, etc. in Abstract Algebra for quick-reference. (*https://github.com/Dark-Alex-17/abstract-algebra-theorems-and-definitions*)
|
||||
* **Linear Algebra - Theorems and Definitions**: Like the previous two projects, this one is the same idea, except it's a collection of theorems, definitions, etc. in Linear Algebra for quick-reference. It's proven to be incredibly useful when designing models, or when optimizing database queries. (*https://github.com/Dark-Alex-17/linear-algebra-theorems-and-definitions*)
|
||||
"#;
|
||||
@@ -0,0 +1,68 @@
|
||||
pub(super) static RESUME: &str = r#"# Alex Clarke
|
||||
| ---------------- | ------------------------------------- |
|
||||
| Location | Denver, Colorado, United States |
|
||||
| Email | alex.j.tusa@gmail.com |
|
||||
| LinkedIn Profile | linkedin.com/in/alex-clarke-a78b99132 |
|
||||
| GitHub Profile | https://github.com/Dark-Alex-17 |
|
||||
| Personal Website | https://alexjclarke.com/ |
|
||||
| ---------------- | ------------------------------------- |
|
||||
|
||||
## Summary
|
||||
Senior Software Engineer with 8 years of professional work experience, looking to apply and learn new and proven technologies. Passionate about learning. Skilled in JVM languages (Java, Kotlin, Groovy), Rust and Golang, Spring Framework, Bash, TypeScript (among many others); Relational and Non-Relational databases; API design, designing and building out AWS Architectures; R&D from POC to enterprise-ready. Proponent of automating all the things, from local to prod. Selfhosted and homelab fanboy.
|
||||
|
||||
## Experience
|
||||
|
||||
### Senior Software Engineer
|
||||
#### IoTecha Corp
|
||||
September 2022 - Present
|
||||
* Developed an optimization algorithm for EV charging schedules that won the company a combined $65,000,000 in grants and funding, and was thus responsible to taking the Proof-of-concept to enterprise-ready from high level architectural design down to the low level code.
|
||||
* Wrote a whitepaper on the above algorithm that is presently in the process of being patented.
|
||||
* Managed a team of engineers to discuss, collaborate, design, and implement the above optimization algorithm and the necessary infrastructure.
|
||||
* Responsible for the research, development, documentation, and production implementation of new advanced optimization algorithms using a host of techniques, ranging from constraint programming and Boolean satisfiability, to vector-calculus based algorithms.
|
||||
* Responsible for researching new Enterprise applications, creating proof -of-concepts, and implementing production applications with thorough documentation, CI/CD, etc., in a host of languages that make the most sense for the use case, including Rust, Go, Python, and Java.
|
||||
* Responsible for researching, designing, and implementing new cloud architectures in AWS to meet global Enterprise customer's needs and specific requirements, including regulatory requirements like GDPR.
|
||||
* Responsible for the performance analysis of existing applications to discover bottlenecks. Once discovered, I document, recommend, and implement architectural and design improvements to remove said bottlenecks.
|
||||
|
||||
### Full Stack Software Engineer
|
||||
#### Bandwidth Inc.
|
||||
May 2018 - September 2022
|
||||
|
||||
* Design, iterate, implement, deploy global insights system, from concept to production, providing real-time analytics on 300,000,000 daily events, using AWS (Kinesis, CloudWatch, OpenSearch, EventBridge, Lambda, API Gateway, ALB, ECS)
|
||||
* Utilize machine learning (SageMaker), statistical process control, and different time-series models (ARIMA, SARIMAX) to monitor data quality
|
||||
* Automate CI/CD into AWS using Ansible, AWS CDK, and GitHub Actions
|
||||
* Mentor team members on technical (AWS, automation, optimizations) and theoretical topics (CAP Theorem, DataOps, time-series modeling), as well as engineering practices (Clean Code, OO, Design Patterns)
|
||||
* Foster company-wide community around learning, e.g. organized Lunch and Learns, inter-team tech round-tables
|
||||
* Implemented custom call routing logic for very large customers, resulting in 6,000,000 new phone numbers, dramatically boosting revenue
|
||||
* Implemented new call routing logic that enables IoT devices to make 911 phone calls with dynamic locations
|
||||
* Led team knowledge shares and curated team-wide learning
|
||||
* Expanded emergency call routing algorithms, thus enabling support for international emergency calls
|
||||
|
||||
### Theoretical Mathematics Researcher
|
||||
#### Metropolitan State University of Denver
|
||||
January 2020 - August 2020
|
||||
|
||||
Researched, proved, and published theorem in character theory and representation theory on the McKay conjecture under Dr. Mandi Schaeffer-Fry
|
||||
|
||||
### Software Engineer
|
||||
#### IKEA
|
||||
May 2016 - May 2018
|
||||
|
||||
Write software for both the IKEA Centennial store and the greater IKEA company, creating software that can be used both locally and for any specified task, as well as used globally for any IKEA store. In addition, I was required to create a website specifically for the IKEA Centennial store using the following languages to create the web pages from real time data: Excel VBA, Java, JavaScript, CSS, HTML, and JSON. I was also required to refactor large portions of code that were not my own in order to enhance maintainability. This also included creating unit tests for already existing programs and using test driven development to create new programs, thus ensuring the longevity of the programs that are being written as well as the efficiency of any regression and integration testing that may be performed. In regards to the programs I did create, I created programs to lookup specifics about articles and print them to an already existing paper form that would be fed into the printer, a program to manage and sort over 120,000 different entries and a multitude of different data types, a website for the Centennial Store, and an outgoing program that is still in the works.
|
||||
|
||||
## Education
|
||||
### Metropolitan State University of Denver
|
||||
* Bachelor of Science - BS, Double Major
|
||||
* Computer Science
|
||||
* Theoretical Mathematics
|
||||
|
||||
## Licenses and Certifications
|
||||
* AWS Certified Solutions Architect
|
||||
* Patent holder for the EV optimization algorithm mentioned above (In progress)
|
||||
|
||||
## Honors and Awards
|
||||
### Certificate of Achievement - Department of Mathematics
|
||||
May 2019
|
||||
|
||||
This certificate of achievement is awarded in recognition of exceptional accomplishments in the field of theoretical mathematics
|
||||
|
||||
"#;
|
||||
@@ -0,0 +1,76 @@
|
||||
use std::result::Result;
|
||||
|
||||
use rustyline::{
|
||||
completion::{Candidate, Completer},
|
||||
error::ReadlineError,
|
||||
highlight::Highlighter,
|
||||
hint::Hinter,
|
||||
validate::Validator,
|
||||
Context, Helper,
|
||||
};
|
||||
|
||||
use crate::cli::COMMANDS;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommandCandidate {
|
||||
display: String,
|
||||
replacement: String,
|
||||
}
|
||||
|
||||
impl Candidate for CommandCandidate {
|
||||
fn display(&self) -> &str {
|
||||
&self.display
|
||||
}
|
||||
|
||||
fn replacement(&self) -> &str {
|
||||
&self.replacement
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClashShellHelper {
|
||||
commands: Vec<String>,
|
||||
}
|
||||
|
||||
impl ClashShellHelper {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
commands: COMMANDS.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for ClashShellHelper {
|
||||
type Candidate = CommandCandidate;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
_: usize,
|
||||
_: &Context<'_>,
|
||||
) -> Result<(usize, Vec<CommandCandidate>), ReadlineError> {
|
||||
let matches: Vec<Self::Candidate> = self
|
||||
.commands
|
||||
.iter()
|
||||
.filter(|cmd| cmd.starts_with(line))
|
||||
.map(|cmd| CommandCandidate {
|
||||
display: cmd.clone(),
|
||||
replacement: cmd.clone(),
|
||||
})
|
||||
.collect();
|
||||
Ok((0, matches))
|
||||
}
|
||||
}
|
||||
|
||||
impl Highlighter for ClashShellHelper {}
|
||||
|
||||
impl Hinter for ClashShellHelper {
|
||||
type Hint = String;
|
||||
|
||||
fn hint(&self, _: &str, _: usize, _: &Context<'_>) -> Option<Self::Hint> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for ClashShellHelper {}
|
||||
|
||||
impl Helper for ClashShellHelper {}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
use anyhow::Result;
|
||||
use clap::{crate_authors, crate_description, crate_name, crate_version, CommandFactory, Parser};
|
||||
use cli::{handle_command, Command};
|
||||
use completer::ClashShellHelper;
|
||||
use indoc::formatdoc;
|
||||
use rustyline::{error::ReadlineError, Config, Editor};
|
||||
|
||||
mod cli;
|
||||
mod completer;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(
|
||||
name = crate_name!(),
|
||||
author = crate_authors!(),
|
||||
version = crate_version!(),
|
||||
about = crate_description!(),
|
||||
disable_version_flag = true,
|
||||
disable_help_subcommand = true,
|
||||
disable_help_flag = true,
|
||||
override_usage = "[COMMAND]",
|
||||
help_template = "\
|
||||
{before-help}{name} {version}
|
||||
{author-with-newline}
|
||||
{about-with-newline}
|
||||
{usage-heading} {usage}
|
||||
|
||||
{all-args}{after-help}
|
||||
"
|
||||
)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Option<Command>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let config = Config::builder().history_ignore_dups(true)?.build();
|
||||
let mut rl = Editor::with_config(config)?;
|
||||
rl.set_helper(Some(ClashShellHelper::new()));
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
formatdoc!(
|
||||
"Welcome to Alex Clarke's personal website!
|
||||
Here's some commands to get you started:
|
||||
|
||||
resume - View my Resume
|
||||
projects - See a list of projects that I'm working on that you can check out
|
||||
github - Output my GitHub profile
|
||||
linkedin - Output my LinkedIn
|
||||
email - Output my email
|
||||
help - See all the commands you can run"
|
||||
)
|
||||
);
|
||||
|
||||
loop {
|
||||
let readline = rl.readline("clash> ");
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
rl.add_history_entry(line.trim())?;
|
||||
|
||||
let args = Cli::try_parse_from(format!("clash {}", line).split_whitespace());
|
||||
match args {
|
||||
Ok(shell_args) => {
|
||||
if let Some(cmd) = shell_args.command {
|
||||
match cmd {
|
||||
Command::Help => Cli::command().print_long_help()?,
|
||||
Command::Exit => {
|
||||
println!("Goodbye!");
|
||||
break;
|
||||
}
|
||||
_ => handle_command(cmd)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {}
|
||||
Err(ReadlineError::Eof) => {}
|
||||
Err(err) => {
|
||||
eprintln!("Error: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = rl.save_history("history.txt");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user