Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setup xtask infra for cheatsheets #159

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[alias]
xtask = "run --manifest-path xtask/Cargo.toml --"
228 changes: 228 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[workspace]
members = [
"xtask",
]
exclude = ["example-code/"]

resolver = "2"

8 changes: 8 additions & 0 deletions xtask/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2021"

[dependencies]
color-eyre = "0.6.3"
eyre = "0.6.12"
79 changes: 79 additions & 0 deletions xtask/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Cheatsheets

## Why

Teaching Rust can be hard, and it's harder when trainees we teach come from different programming language backgrounds.

A person in Python might have more questions about memory safety in general, since the GC allows them not to worry about that, but a person from C++ would be confused by the keyword `move` in a closure.

To alleviate this, we've created some cheatsheets of the form

```
# Applied Rust

## Methods and Traits
...
## Rust I/O Traits
...
## Generics
...
```

per programming language that match each slide we have in our normal syllabus with an entry on the second header (`## Methods and Traits`, for example) level.

## How

As `training-material` grows and changes, maintenance could be a nightmare. We basically don't want to be in the business of remembering that a certain slide got reordered or moved from one section to another and thus needs changing in all the cheatsheets as well. Therefore, this tool seeks to alleviate that with the following workflow:

* call `cargo xtask make-cheatsheet python` at the root folder
* scrape Markdown headers in `SUMMARY.md` and segment topics by `Rust Fundamentals`, `Applied Rust` and `Advanced Rust`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who does this - the user or the tool? Because step 1 in this list is something the user has to do.

Copy link
Contributor Author

@miguelraz miguelraz Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for now it's the user's responsibility to run the check after modifying any of the cheatsheets - I wouldn't mind doing a follow up PR that adds it to the build scripts. I'll add that as a follow up task for myself on Clickup as this PR is chunky enough.

* write out to `src/python-cheatsheet.md` if it doesn't exist
* if it does exist, check that it in sync: all headers in `python-cheatsheet.md` are in the appropriate sections, in order, and none are missing.

Specifically, `make-cheatsheet` and `test-cheatsheet` are defined in `xtask/src/tasks.rs` with utility functions to take our `SUMMARY.md`

```
# Rust Fundamentals
* [Overview](./src/overview.md)
* [Installation](.src/installation.md)
* [Basic Types](./src/basic-types.md)
...
# Applied Rust
* [Methods and Traits](./src/methods-and-traits.md)
* [Rust I/O Traits](./src/rust-io-traits.md)
* [Generics](./src/generics.md)
```

and convert it into a `Vec<SlidesSection>`:

```rust
vec![SlideSection {header: "Rust Fundamentals",
slide_titles: vec!["Overview", "Installation", "Basic Types"]},
SlideSection {header: "Applied Rust",
slide_titles: vec!["Methods and Traits", "Rust I/O Traits", "Generics"]}]
```

From there we can

* create the cheatsheet for Python and have it written out to `training-slides/src/python-cheatsheet.md` by just iterating over `Vec<SlideSection>` and prefixing with the appropriate header level before printing
* test that the cheathseet is in sync by scraping for all the lines that start with `#` in `python-cheatsheet.md` and check that they match, in order, those we scraped from `SUMMARY.md`.

Note: some languages will warrant some special entries - any headers after the last `SlideSection` header will be ignored,
so that we can add additional relevant information without having to conform to the slides.

Concretely, this is allowed:

```md

# Applied Rust
## Methods and Traits
## Rust I/O Traits
## Generics
# FAQ
## How to do...
# Syntax Clashes
## Operator overloading
...
```

but the code will signal if `# FAQ` or `# Syntax Clashes` appear before `# Applied Rust`.
45 changes: 45 additions & 0 deletions xtask/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![deny(warnings)]

mod tasks;

use std::env;

static HELP_TEXT: &'static str = "cargo xtask

USAGE:
cargo xtask [COMMAND]

COMMANDS:
make-cheatsheet [LANG] make LANG cheatsheet by scraping slides names in `SUMMARY.md`
test-cheatsheet [LANG] test LANG's cheatsheet (all `SUMMARY.md` items are in sheet and viceversa)

LANG:
Valid values are `python, go, cpp, swift, java, julia, c`
";

// Code adapted from the xtask workflow in rust-exercises
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;

// first arg is the name of the executable; skip it
let unprocessed_args = env::args().skip(1).collect::<Vec<_>>();
let args: Vec<&str> = unprocessed_args.iter().map(|arg| &arg[..]).collect::<Vec<&str>>();

let langs = ["python", "go", "cpp", "swift", "java", "julia", "c"];

if !langs.contains(&args[1]) {
let panic_text = format!("{} {}\n{}\n", args[1], "is not a valid language name. \n\nExpected one of:", langs.join("\n"));
panic!("{panic_text}");
}

match &args[..] {
["make-cheatsheet", lang] => tasks::make_cheatsheet(lang),
["test-cheatsheet", lang] => tasks::test_cheatsheet(lang),
_ => {
eprintln!("{HELP_TEXT}");

Ok(())
}
}
}

Loading