Compare commits
5 commits
493193872f
...
502749c838
Author | SHA1 | Date | |
---|---|---|---|
502749c838 | |||
7a20b7f86b | |||
d91f655177 | |||
4fb22e2194 | |||
e06044fc97 |
6 changed files with 664 additions and 642 deletions
948
Cargo.lock
generated
948
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
14
Cargo.toml
14
Cargo.toml
|
@ -2,13 +2,13 @@
|
|||
name = "panres"
|
||||
version = "0.1.0"
|
||||
authors = ["Edward Shen <code@eddie.sh>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tera = "1.0.0-beta.13"
|
||||
clap = "2"
|
||||
tera = "1"
|
||||
clap = { version = "3", features = ["derive", "cargo"] }
|
||||
serde = "1"
|
||||
serde_json = "1.0"
|
||||
json5 = "0.2.8"
|
||||
notify = "5.0.0-pre.3"
|
||||
crossbeam = "0.7"
|
||||
serde_json = "1"
|
||||
json5 = "0.4"
|
||||
notify = "5.0.0-pre.15"
|
||||
crossbeam = "0.8"
|
||||
|
|
105
config.json5
105
config.json5
|
@ -2,10 +2,10 @@
|
|||
"author": {
|
||||
"name": "Edward Shen",
|
||||
"addresses": {
|
||||
"home": "8080 Highland Farms Drive, East Amherst, NY 14051",
|
||||
"home": "550 Moreland Way Apt 4410, Santa Clara, CA 95054",
|
||||
"school": "480 Parker Street, Boston, MA 02115"
|
||||
},
|
||||
"email": "code@eddie.sh",
|
||||
"email": "hi@eddie.sh",
|
||||
"phone": "(716) 491-3343",
|
||||
"website": "eddie.sh",
|
||||
"github": "edward-shen",
|
||||
|
@ -19,22 +19,17 @@
|
|||
"school": "Northeastern University",
|
||||
"college": "Khoury College of Computer Sciences",
|
||||
"location": "Boston, MA",
|
||||
"start": "Sept. 2017",
|
||||
"end": "Present",
|
||||
"end_date": "Present (May 2021)",
|
||||
"start": "Sep. 2017",
|
||||
"end_date": "May 2021",
|
||||
"list": [
|
||||
{
|
||||
"name": "degree",
|
||||
"desc": "Candidate for a Bachelor of Science in Computer Science"
|
||||
"desc": "Bachelor of Science in Computer Science"
|
||||
},
|
||||
{
|
||||
"name": "honors",
|
||||
"desc": "3.84 / 4.00, Dean's List"
|
||||
"desc": "3.85 / 4.00; summa cum laude; Dean's List"
|
||||
},
|
||||
{
|
||||
"name": "courses",
|
||||
"desc": "Data Collection, Integration, and Analysis; Machine Learning/Data Mining 1; Algorithms and Data; Object-Oriented Design; Networks and Distributed Systems"
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": [
|
||||
|
@ -42,63 +37,82 @@
|
|||
"name": "languages",
|
||||
"desc": [
|
||||
{
|
||||
"level": "proficient",
|
||||
"langs": "Rust, Python, TypeScript, HTML5, CSS"
|
||||
"level": "primary",
|
||||
"langs": "Rust",
|
||||
},
|
||||
{
|
||||
"level": "familiar",
|
||||
"langs": "JavaScript, Java, LaTeX, C++, Bash"
|
||||
"level": "secondary",
|
||||
"langs": "Python, Bash",
|
||||
},
|
||||
{
|
||||
"level": "explored",
|
||||
"langs": "C, Ruby, Racket, Scala"
|
||||
"level": "auxiliary",
|
||||
"langs": "C, C++, TypeScript",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "other",
|
||||
"desc": "Git, (Arch) Linux, VS Code, Raspberry Pi"
|
||||
"name": "recent technology",
|
||||
"desc": "cxx.rs, libc, fbthrift, cgroups v2",
|
||||
},
|
||||
{
|
||||
"name": "certs",
|
||||
"desc": "CompTIA A+, Dell Certified Technician, Apple Certified Mac Technician"
|
||||
"name": "miscellaneous",
|
||||
"desc": "Regex, LaTeX"
|
||||
},
|
||||
{
|
||||
"name": "interests",
|
||||
"desc": "Rhythm games; Self-hosting; Security; Scalability; Free software",
|
||||
}
|
||||
],
|
||||
"experiences": [
|
||||
// {
|
||||
// "title": "Software Development Engineer Intern",
|
||||
// "company": "Apple",
|
||||
// "location": "Menlo Park, CA",
|
||||
// "start": "Aug. 2020",
|
||||
// "end": "Dec. 2020",
|
||||
// "notes": [
|
||||
// "sekureetee"
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
"title": "Production Engineer",
|
||||
"company": "Meta",
|
||||
"location": "Menlo Park, CA",
|
||||
"start": "Sep. 2021",
|
||||
"end": "Present",
|
||||
"notes": [
|
||||
"Joined the PE Executable Access Control team to identify, track, and jail untrusted binaries running on internal hosts.",
|
||||
"Tech lead for Alcatraz, an internal binary that presents common frontend for process-level jailing backends.",
|
||||
"Advocate for Rust adoption, volunteering for Rust code reviews and presenting Rust talks to teams.",
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "SEAR Intern",
|
||||
"company": "Apple",
|
||||
"location": "Cupertino, CA",
|
||||
"remote": true,
|
||||
"start": "Aug. 2020",
|
||||
"end": "Dec. 2020",
|
||||
"notes": [
|
||||
"Joined the Trusted Execution team, focused on code execution across the Apple ecosystem.",
|
||||
"Planned, driven, and completed proof-of-concept for iOS 16's Lockdown Mode.",
|
||||
"Developed kernel and bootloader security policies focused on attack surface reduction.",
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Production Engineer Intern",
|
||||
"company": "Facebook",
|
||||
"location": "Menlo Park, CA",
|
||||
"remote": true,
|
||||
"start": "May 2020",
|
||||
"end": "Aug. 2020",
|
||||
"notes": [
|
||||
"Rewritten Python config generation in multithreaded asynchronous Rust, reducing overall runtime by 80\\% on large inputs and improving runtime of profiled areas by a factor of 36.6.",
|
||||
"Designed core Rust libraries for foundational services for ubiquitous use across all Rust applications.",
|
||||
"Detected and resolved soundness issues and misconfigurations in existing python code to preempt Severity 1 site events."
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Software Engineer Intern",
|
||||
"company": "Datto",
|
||||
"location": "Boston, MA",
|
||||
"start": "Aug. 2019",
|
||||
"end": "Dec. 2019",
|
||||
"notes": [
|
||||
"Refactored frontend codebase to utilize Typescript, Webpack, and Babel while reducing average JS footprint by 98\\% percent, from 10.4MB to 250KB.",
|
||||
"Assisted and completed developer environment virtualization project to reduce onboarding time for new developers by 5 days.",
|
||||
"Integrated into Scrum team that focused on re-implementation of license management systems in a legacy Ruby codebase to Scala and PHP."
|
||||
"Highlighted as a top intern project for the Production Engineering organization."
|
||||
]
|
||||
},
|
||||
// {
|
||||
// "title": "Software Engineer Intern",
|
||||
// "company": "Datto",
|
||||
// "location": "Boston, MA",
|
||||
// "start": "Aug. 2019",
|
||||
// "end": "Dec. 2019",
|
||||
// "notes": [
|
||||
// "Refactored frontend codebase to utilize Typescript, Webpack, and Babel while reducing average JS footprint by 98\\% percent, from 10.4MB to 250KB.",
|
||||
// "Integrated into Scrum team that focused on re-implementation of license management systems in a legacy Ruby codebase to Scala and PHP."
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
"title": "Production Engineer Intern",
|
||||
"company": "Facebook",
|
||||
|
@ -107,8 +121,7 @@
|
|||
"end": "Aug. 2019",
|
||||
"notes": [
|
||||
"Implemented a disaster mitigation for decentralized service discovery when upstream data source is unavailable or in a degraded state.",
|
||||
"Designed an out-of-band flow to allow resolution of core infrastructure services dependent on service discovery in disaster scenarios to minimize downtime for all Facebook products.",
|
||||
"Modernized and restructured core tooling to improve reliability and testing."
|
||||
"Designed an out-of-band flow to resolve core infrastructure services dependent on service discovery in disaster scenarios to minimize downtime for all Facebook products.",
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
64
src/main.rs
64
src/main.rs
|
@ -1,13 +1,14 @@
|
|||
#![forbid(unsafe_code)]
|
||||
|
||||
use clap::{crate_authors, crate_version, App, AppSettings, Arg};
|
||||
use crossbeam::crossbeam_channel::unbounded;
|
||||
use clap::{crate_authors, crate_version, Parser};
|
||||
use crossbeam::channel::unbounded;
|
||||
use json5::from_str;
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use serde_json::Value;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::{create_dir_all, read_dir, read_to_string, write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use tera::{Context, Error as TeraError, Tera};
|
||||
|
||||
|
@ -28,60 +29,51 @@ fn env_or_default(env_name: &str, default: &str) -> String {
|
|||
env::var(env_name).unwrap_or_else(|_| String::from(default))
|
||||
}
|
||||
|
||||
/// Universal resume formatter
|
||||
#[derive(Parser)]
|
||||
#[clap(version = crate_version!(), author = crate_authors!())]
|
||||
struct Args {
|
||||
/// Watch for changes
|
||||
#[clap(short)]
|
||||
watch: bool,
|
||||
/// Specifies which output format you want
|
||||
#[clap(short, long, multiple_occurrences = true)]
|
||||
output: Vec<String>,
|
||||
}
|
||||
|
||||
fn run() -> Result<(), Box<dyn Error>> {
|
||||
let config: Value = from_str(&read_to_string("config.json5")?)?;
|
||||
let template_dir = env_or_default("PANRES_TEMPLATE_DIR", DEFAULT_TEMPLATE_DIR);
|
||||
let output_dir = env_or_default("PANRES_OUTPUT_DIR", DEFAULT_OUTPUT_DIR);
|
||||
|
||||
let matches = get_args();
|
||||
let args = Args::parse();
|
||||
|
||||
let tera = Tera::new(&format!("{}/**/*", template_dir))?;
|
||||
let tera = Tera::new(&format!("{template_dir}/**/*"))?;
|
||||
let mut context = Context::new();
|
||||
context.insert("config", &config);
|
||||
let outputs: Vec<String> = matches
|
||||
.values_of("output-format")
|
||||
.unwrap_or_default()
|
||||
.map(String::from)
|
||||
.collect();
|
||||
let template_dir = PathBuf::from(template_dir);
|
||||
|
||||
output(&tera, &context, &output_dir, &outputs, &template_dir)?;
|
||||
output(&tera, &context, &output_dir, &args.output, &template_dir)?;
|
||||
|
||||
if matches.is_present("watch") {
|
||||
watch_mode(tera, context, output_dir, outputs, template_dir)?;
|
||||
if args.watch {
|
||||
watch_mode(tera, context, output_dir, args.output, &template_dir)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the args passed by the user.
|
||||
fn get_args() -> clap::ArgMatches<'static> {
|
||||
App::new("panres")
|
||||
.version(crate_version!())
|
||||
.author(crate_authors!())
|
||||
.about("Universal resume formatter")
|
||||
.arg(Arg::with_name("watch").short("w").help("Watch for changes"))
|
||||
.arg(
|
||||
Arg::with_name("output-format")
|
||||
.help("Specifies which output format you want")
|
||||
.required(true)
|
||||
.multiple(true),
|
||||
)
|
||||
.settings(&[AppSettings::ArgRequiredElseHelp])
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
/// Usually never returns, unless there was an error initializing the watcher.
|
||||
/// Handles watching for file changes, and reloads tera if there's a change.
|
||||
fn watch_mode<'a>(
|
||||
fn watch_mode(
|
||||
mut engine: Tera,
|
||||
context: Context,
|
||||
dir: String,
|
||||
outputs: Vec<String>,
|
||||
template_dir: String,
|
||||
template_dir: &Path,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let (tx, rx) = unbounded();
|
||||
let mut watcher: RecommendedWatcher = Watcher::new_immediate(move |res| tx.send(res).unwrap())?;
|
||||
watcher.watch(&template_dir, RecursiveMode::Recursive)?;
|
||||
let mut watcher: RecommendedWatcher = Watcher::new(move |res| tx.send(res).unwrap())?;
|
||||
watcher.watch(template_dir, RecursiveMode::Recursive)?;
|
||||
|
||||
for res in rx {
|
||||
match res {
|
||||
|
@ -89,7 +81,7 @@ fn watch_mode<'a>(
|
|||
Ok(event) => {
|
||||
println!("got event {:?}", event);
|
||||
engine.full_reload().expect("Failed to perform full reload");
|
||||
output(&engine, &context, &dir, &outputs, &template_dir)
|
||||
output(&engine, &context, &dir, &outputs, template_dir)
|
||||
.expect("Failed to call output");
|
||||
}
|
||||
}
|
||||
|
@ -100,12 +92,12 @@ fn watch_mode<'a>(
|
|||
|
||||
/// Parses the output values and generates a file for each format specified or
|
||||
/// found, if told to generate all outputs.
|
||||
fn output<'a>(
|
||||
fn output(
|
||||
engine: &Tera,
|
||||
context: &Context,
|
||||
dir: &str,
|
||||
outputs: &[String],
|
||||
template_dir: &'a str,
|
||||
template_dir: &Path,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if outputs.contains(&String::from("all")) {
|
||||
for output in read_dir(template_dir)? {
|
||||
|
|
171
templates/latex
171
templates/latex
|
@ -9,6 +9,7 @@
|
|||
\usepackage{multirow}
|
||||
\usepackage{colortbl} % Colored table vlines
|
||||
\usepackage{graphicx} % Rotate text
|
||||
\usepackage[hidelinks]{hyperref} % Clickable PDF URLs
|
||||
|
||||
% macros which are illegal in .sty (primarily just font awesome stuff)
|
||||
|
||||
|
@ -22,93 +23,111 @@
|
|||
|
||||
\setmainfont{Corbert}
|
||||
% Set up table styles
|
||||
\arrayrulecolor{red}
|
||||
\arrayrulecolor{AccentColor}
|
||||
\setlength\arrayrulewidth{1.2pt}
|
||||
\begin{tabularx}{\textwidth}{X c l}
|
||||
\multirow[b]{3}{*}{\name}
|
||||
& \accent{\glyph{"F0AC}} & {{ config.author.website }} \\
|
||||
& \accent{\glyph{"F003}} & {{ config.author.email }} \\
|
||||
& \accent{\glyph{"F09B}} & {{ config.author.github }}
|
||||
& \accent{\glyph{"F0AC}} & \href{
|
||||
https://{{ config.author.website }}
|
||||
}{
|
||||
{{ config.author.website }}
|
||||
}\\
|
||||
& \accent{\glyph{"F003}} & \href{
|
||||
mailto:{{ config.author.email }}
|
||||
}{
|
||||
{{ config.author.email }}
|
||||
} \\
|
||||
& \accent{\glyph{"F09B}} & \href {
|
||||
https://github.com/{{ config.author.github }}
|
||||
}{
|
||||
{{ config.author.github }}
|
||||
}
|
||||
\end{tabularx}
|
||||
|
||||
% Contact info
|
||||
|
||||
\begin{tabularx}{\textwidth}{ c | r X r }
|
||||
\sectiontitle{Education} \\[-8pt]
|
||||
{% raw -%}
|
||||
& \widel{ {\large\title{%
|
||||
{% endraw -%}
|
||||
{{ config.education.school }}}}\hint{ {{ config.education.location }}}} & {{ config.education.start }}---{{ config.education.end_date }} \\
|
||||
& \widel{\footnotesize {{ config.education.college }}} \\
|
||||
{% for row in config.education.list -%}
|
||||
{% raw -%}
|
||||
& \hint{%
|
||||
{% endraw -%}
|
||||
{{ row.name | title }}:} &
|
||||
{% raw -%}
|
||||
\multicolumn{2}{X}{%
|
||||
{% endraw -%}
|
||||
{{ row.desc }}} \\
|
||||
{% endfor -%}
|
||||
\begin{tabularx}{\textwidth}{c X c}
|
||||
{
|
||||
|
||||
\spacer
|
||||
\begin{tabularx}{300pt}{c | r r r }
|
||||
\sectiontitle{Experiences\;\;}
|
||||
{% for experience in config.experiences -%} \\[-8pt]
|
||||
{% raw -%}
|
||||
& \widel{\large\title{%
|
||||
{% endraw -%}
|
||||
{{ experience.company }}}\hint{ {{experience.title }} }} & {{ experience.start }}---{{ experience.end }} \\
|
||||
& \widel{\footnotesize {{ experience.location }}
|
||||
{% if experience.remote -%}
|
||||
\color[rgb]{.8,.8,.8}{(Remote)}
|
||||
{% endif -%}
|
||||
} \\[-8pt]
|
||||
\\
|
||||
|
||||
\sectiontitle{Skills} \\[-8pt]
|
||||
{% for row in config.skills -%}
|
||||
& \hint{ {{ row.name | title }}:} &
|
||||
{% raw -%}
|
||||
\multicolumn{2}{X}{%
|
||||
{% endraw -%}
|
||||
{% if row.name == "languages" -%}
|
||||
\multirow{3}{*}{
|
||||
\begin{tabularx}{\textwidth}{ r l }
|
||||
{% for group in row.desc -%}
|
||||
{% raw -%}
|
||||
\hint{%
|
||||
{% endraw -%}
|
||||
{{ group.level | title }}:} & {{ group.langs }} \\
|
||||
{% endfor -%}
|
||||
\end{tabularx}
|
||||
} \newline \newline % 100% hack pls fix
|
||||
{% else -%}
|
||||
{{ row.desc }}
|
||||
{% endif -%}
|
||||
}\\
|
||||
{% endfor -%}
|
||||
{% for note in experience.notes -%}
|
||||
& \bullet
|
||||
{% raw -%}
|
||||
& \widex{%
|
||||
{% endraw -%}
|
||||
{{note}}
|
||||
} \\[-8pt]
|
||||
\\
|
||||
{% endfor -%}
|
||||
\\[-8pt]
|
||||
{% endfor -%}
|
||||
\end{tabularx}
|
||||
}
|
||||
& &
|
||||
{
|
||||
\begin{tabularx}{150pt}{ c | X }
|
||||
\sectiontitle{Degree\;\;} \\[-8pt]
|
||||
{% raw -%}
|
||||
& {\large\title{%
|
||||
{% endraw -%}
|
||||
{{ config.education.school }}
|
||||
}} \\
|
||||
& \footnotesize{ {{ config.education.location }}} \\
|
||||
& \hint{Date}\\
|
||||
& {{ config.education.start }}---{{ config.education.end_date }} \\
|
||||
{% for row in config.education.list -%}
|
||||
{% raw -%}
|
||||
& \hint{%
|
||||
{% endraw -%}
|
||||
{{ row.name | title }}} \\
|
||||
& {{ row.desc }} \\
|
||||
{% endfor -%}
|
||||
\\[-8pt]
|
||||
\\
|
||||
|
||||
\begin{tabularx}{\textwidth}{c | r X r }
|
||||
\sectiontitle{Experiences}
|
||||
{% for experience in config.experiences -%} \\[-8pt]
|
||||
{% raw -%}
|
||||
& \widel{\large\title{%
|
||||
{% endraw -%}
|
||||
{{ experience.company }}}\hint{ {{experience.title }} }} & {{ experience.start }}---{{ experience.end }} \\
|
||||
& \widel{\footnotesize {{ experience.location }}} \\
|
||||
{% for note in experience.notes -%}
|
||||
& \bullet
|
||||
{% raw -%}
|
||||
& \widex{%
|
||||
{% endraw -%}
|
||||
{{note}}} \\
|
||||
{% endfor -%}
|
||||
{% endfor -%}
|
||||
\end{tabularx}
|
||||
\spacer
|
||||
\spacer
|
||||
\spacer
|
||||
\spacer
|
||||
|
||||
\begin{tabularx}{\textwidth}{c | r X r }
|
||||
\sectiontitle{Projects}
|
||||
{% for project in config.projects -%} \\[-8pt]
|
||||
{% raw -%}
|
||||
& \widel{\large\title{%
|
||||
{% endraw -%}
|
||||
{{ project.name }}}\hint{ {{ project.lang }} }} \\
|
||||
{% for note in project.notes -%}
|
||||
& \bullet
|
||||
{% raw -%}
|
||||
& \widex{%
|
||||
{% endraw -%}
|
||||
{{note}}} \\
|
||||
{% endfor -%}
|
||||
{% endfor -%}
|
||||
\sectiontitle{Skills and Interests\;\;} \\[-8pt]
|
||||
{% for row in config.skills -%}
|
||||
|
||||
{% raw -%}
|
||||
& \title{%
|
||||
{% endraw -%}
|
||||
{{ row.name | title }}} \\
|
||||
|
||||
{% if row.name == "languages" -%}
|
||||
{% for group in row.desc -%}
|
||||
{% raw -%}
|
||||
& \hint{%
|
||||
{% endraw -%}
|
||||
{{ group.level | title }}} \\
|
||||
& {{ group.langs }} \\
|
||||
{% endfor -%}
|
||||
{% else -%}
|
||||
& \begin{flushleft}
|
||||
{{ row.desc }} \\
|
||||
\end{flushleft}
|
||||
{% endif -%}
|
||||
\\[-8pt]
|
||||
\\
|
||||
{% endfor -%}
|
||||
\end{tabularx}
|
||||
}
|
||||
\end{tabularx}
|
||||
\end{document}
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
]
|
||||
\newfontfamily{\fa}{FontAwesome}
|
||||
|
||||
\definecolor{AccentColor}{HTML}{238de4}
|
||||
|
||||
{% raw %}
|
||||
% Changes the color of any text inside to the accent color
|
||||
\renewcommand{\accent}[1] {{\color{red}{#1}}}
|
||||
\renewcommand{\accent}[1] {{\color{AccentColor}{#1}}}
|
||||
{% endraw %}
|
||||
|
||||
{% raw %}
|
||||
|
|
Loading…
Reference in a new issue