mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-23 07:06:19 +02:00
Add utility to build exercise starting directory from Markdown.
This commit is contained in:
parent
b85c893390
commit
89c8eb0ef2
144
Cargo.lock
generated
144
Cargo.lock
generated
@ -2,6 +2,15 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-example"
|
||||
version = "0.1.0"
|
||||
@ -9,6 +18,23 @@ dependencies = [
|
||||
"buddy_system_allocator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -167,6 +193,19 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.0"
|
||||
@ -188,6 +227,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exerciser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"pulldown-cmark",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
@ -350,6 +399,15 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
@ -413,6 +471,15 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.25"
|
||||
@ -839,6 +906,16 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
@ -854,6 +931,23 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
@ -962,6 +1056,23 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.16"
|
||||
@ -1263,6 +1374,15 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
@ -1370,6 +1490,15 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
@ -1420,6 +1549,12 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
@ -1534,6 +1669,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"exerciser",
|
||||
"src/exercises",
|
||||
"src/bare-metal/useful-crates/allocator-example",
|
||||
"src/bare-metal/useful-crates/zerocopy-example",
|
||||
|
14
exerciser/Cargo.toml
Normal file
14
exerciser/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "exerciser"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
authors = ["Andrew Walbran <qwandor@google.com>"]
|
||||
description = "A tool for extracting starter code for exercises from Markdown files."
|
||||
repository = "https://github.com/google/comprehensive-rust"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
log = "0.4.17"
|
||||
pretty_env_logger = "0.4.0"
|
||||
pulldown-cmark = { version = "0.9.2", default-features = false }
|
151
exerciser/src/main.rs
Normal file
151
exerciser/src/main.rs
Normal file
@ -0,0 +1,151 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use anyhow::Context;
|
||||
use log::{info, trace};
|
||||
use pulldown_cmark::{CowStr, Event, Parser, Tag};
|
||||
use std::{
|
||||
env::args,
|
||||
fs::{create_dir, create_dir_all, read_to_string, File},
|
||||
io::Write,
|
||||
path::Path,
|
||||
process::exit,
|
||||
};
|
||||
|
||||
const INCLUDE_START: &str = "{{#include ";
|
||||
const INCLUDE_END: &str = "}}";
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
pretty_env_logger::init();
|
||||
|
||||
let args = args().collect::<Vec<_>>();
|
||||
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage:");
|
||||
eprintln!(
|
||||
" {} <src/exercises/exercise.md> <output directory>",
|
||||
args[0]
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let input_filename = Path::new(&args[1]);
|
||||
let output_directory = Path::new(&args[2]);
|
||||
|
||||
create_dir(output_directory).with_context(|| {
|
||||
format!("Failed to create output directory {:?}", output_directory)
|
||||
})?;
|
||||
|
||||
let input_directory = input_filename
|
||||
.parent()
|
||||
.with_context(|| "Input file has no parent directory.")?;
|
||||
let input_contents = read_to_string(input_filename)
|
||||
.with_context(|| format!("Failed to open {:?}", input_filename))?;
|
||||
let parser = Parser::new(&input_contents);
|
||||
|
||||
let mut next_filename: Option<String> = None;
|
||||
let mut current_file: Option<File> = None;
|
||||
for event in parser {
|
||||
trace!("{:?}", event);
|
||||
match event {
|
||||
Event::Code(x) => {
|
||||
info!("{}:", x);
|
||||
next_filename = Some(x.into_string());
|
||||
}
|
||||
Event::Start(Tag::CodeBlock(x)) => {
|
||||
info!("Start {:?}", x);
|
||||
if let Some(filename) = &next_filename {
|
||||
let full_filename = output_directory.join(filename);
|
||||
info!("Opening {:?}", full_filename);
|
||||
if let Some(directory) = full_filename.parent() {
|
||||
create_dir_all(directory)?;
|
||||
}
|
||||
current_file = Some(File::create(full_filename)?);
|
||||
next_filename = None;
|
||||
}
|
||||
}
|
||||
Event::Text(text) => {
|
||||
info!("Text: {:?}", text);
|
||||
if let Some(output_file) = &mut current_file {
|
||||
write_output(text, input_directory, output_file)?;
|
||||
}
|
||||
}
|
||||
Event::End(Tag::CodeBlock(x)) => {
|
||||
info!("End {:?}", x);
|
||||
current_file = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_output(
|
||||
text: CowStr,
|
||||
input_directory: &Path,
|
||||
output_file: &mut File,
|
||||
) -> anyhow::Result<()> {
|
||||
for line in text.lines() {
|
||||
info!("Line: {:?}", line);
|
||||
if let (Some(start), Some(end)) =
|
||||
(line.find(INCLUDE_START), line.find(INCLUDE_END))
|
||||
{
|
||||
let include = line[start + INCLUDE_START.len()..end].trim();
|
||||
info!("Include {:?}", include);
|
||||
if let Some(colon) = include.find(":") {
|
||||
write_include(
|
||||
&include[0..colon],
|
||||
Some(&include[colon + 1..]),
|
||||
input_directory,
|
||||
output_file,
|
||||
)?;
|
||||
} else {
|
||||
write_include(include, None, input_directory, output_file)?;
|
||||
}
|
||||
} else {
|
||||
output_file.write(line.as_bytes())?;
|
||||
output_file.write(b"\n")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_include(
|
||||
include_filename: &str,
|
||||
section: Option<&str>,
|
||||
input_directory: &Path,
|
||||
output_file: &mut File,
|
||||
) -> anyhow::Result<()> {
|
||||
let full_include_filename = input_directory.join(include_filename);
|
||||
let input_file = read_to_string(full_include_filename)?;
|
||||
if let Some(section) = section {
|
||||
let start_anchor = format!("ANCHOR: {}", section);
|
||||
let end_anchor = format!("ANCHOR_END: {}", section);
|
||||
for line in input_file
|
||||
.lines()
|
||||
.skip_while(|line| !line.contains(&start_anchor))
|
||||
.skip(1)
|
||||
.take_while(|line| !line.contains(&end_anchor))
|
||||
{
|
||||
output_file.write(line.as_bytes())?;
|
||||
output_file.write(b"\n")?;
|
||||
}
|
||||
} else {
|
||||
output_file.write(input_file.as_bytes())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user