[xtask] New subcommand: Books (#1192)

This commit is contained in:
Sylvain Benner 2024-01-31 09:09:22 -05:00 committed by GitHub
parent ff222b06b5
commit 02259ea11c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 5433 additions and 7 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
target target
Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
.DS_Store .DS_Store

5251
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ let tensor_2 = Tensor::<Backend, 1>::from_data(Data::from([1.0, 2.0, 3.0]).conve
// Will be converted to Data internally. `.convert()` not needed as from_floats() defined for fixed ElementType // Will be converted to Data internally. `.convert()` not needed as from_floats() defined for fixed ElementType
let tensor_3 = Tensor::<Backend, 1>::from_floats([1.0, 2.0, 3.0]); let tensor_3 = Tensor::<Backend, 1>::from_floats([1.0, 2.0, 3.0]);
// Initalization of Int Tensor from array slices // Initialization of Int Tensor from array slices
let arr: [i32; 6] = [1, 2, 3, 4, 5, 6]; let arr: [i32; 6] = [1, 2, 3, 4, 5, 6];
let tensor_4 = Tensor::<Backend, 1, Int>::from_data(Data::from(&arr[0..3]).convert()); let tensor_4 = Tensor::<Backend, 1, Int>::from_data(Data::from(&arr[0..3]).convert());

View File

@ -9,9 +9,11 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
anyhow = "1.0.75" anyhow = "1.0.75"
clap = { version = "4.4.8", features = ["derive"] } clap = { version = "4.4.8", features = ["derive"] }
derive_more = { version = "0.99.17", features = ["display"], default-features = false }
env_logger = "0.10.0" env_logger = "0.10.0"
log = "0.4.17" log = "0.4.17"
rand = { workspace = true, features = ["std"] }
serde_json = { version = "1" } serde_json = { version = "1" }
[dev-dependencies] [dev-dependencies]
rstest.workspace = true rstest = { workspace = true }

127
xtask/src/books.rs Normal file
View File

@ -0,0 +1,127 @@
use std::{collections::HashMap, path::Path, time::Instant};
use clap::{Args, Subcommand};
use derive_more::Display;
use crate::{
endgroup, group,
logging::init_logger,
utils::{
cargo::ensure_cargo_crate_is_installed, mdbook::run_mdbook_with_path, process::random_port,
time::format_duration, Params,
},
};
#[derive(Args)]
pub(crate) struct BooksArgs {
#[command(subcommand)]
book: BookKind,
}
#[derive(Subcommand)]
pub(crate) enum BookKind {
/// Burn Book, a.k.a. the guide, made for the Burn users.
Burn(BookKindArgs),
/// Contributor book, made for people willing to get all the technical understanding and advices to contribute actively to the project.
Contributor(BookKindArgs),
}
#[derive(Args)]
pub(crate) struct BookKindArgs {
#[command(subcommand)]
command: BookCommand,
}
#[derive(Subcommand, Display)]
pub(crate) enum BookCommand {
/// Build the book
Build,
/// Open the book on the specified port or random port and rebuild it automatically upon changes
Open(OpenArgs),
}
#[derive(Args, Display)]
pub(crate) struct OpenArgs {
/// Specify the port to open the book on (defaults to a random port if not specified)
#[clap(long, default_value_t = random_port())]
port: u16,
}
/// Book information
pub(crate) struct Book {
name: &'static str,
path: &'static Path,
}
impl BooksArgs {
pub(crate) fn parse(&self) -> anyhow::Result<()> {
init_logger().init();
let start = Instant::now();
Book::run(&self.book)?;
let duration = start.elapsed();
info!(
"\x1B[32;1mTime elapsed for the current execution: {}\x1B[0m",
format_duration(&duration)
);
Ok(())
}
}
impl Book {
const BURN_BOOK_NAME: &'static str = "Burn Book";
const BURN_BOOK_PATH: &'static str = "./burn-book";
const CONTRIBUTOR_BOOK_NAME: &'static str = "Contributor Book";
const CONTRIBUTOR_BOOK_PATH: &'static str = "./burn-book";
pub(crate) fn run(book_arg: &BookKind) -> anyhow::Result<()> {
let (book, command) = match book_arg {
BookKind::Burn(args) => (
Self {
name: Self::BURN_BOOK_NAME,
path: Path::new(Self::BURN_BOOK_PATH),
},
&args.command,
),
BookKind::Contributor(args) => (
Self {
name: Self::CONTRIBUTOR_BOOK_NAME,
path: Path::new(Self::CONTRIBUTOR_BOOK_PATH),
},
&args.command,
),
};
book.execute(command);
Ok(())
}
fn execute(&self, command: &BookCommand) {
ensure_cargo_crate_is_installed("mdbook");
group!("{}: {}", self.name, command);
match command {
BookCommand::Build => self.build(),
BookCommand::Open(args) => self.open(args),
};
endgroup!();
}
fn build(&self) {
run_mdbook_with_path(
"build",
Params::from([]),
HashMap::new(),
Some(self.path),
"mdbook should build the book successfully",
);
}
fn open(&self, args: &OpenArgs) {
run_mdbook_with_path(
"serve",
Params::from(["--open", "--port", &args.port.to_string()]),
HashMap::new(),
Some(self.path),
"mdbook should build the book successfully",
);
}
}

View File

@ -1,5 +1,6 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
mod books;
mod dependencies; mod dependencies;
mod logging; mod logging;
mod publish; mod publish;
@ -19,6 +20,8 @@ struct Args {
#[derive(Subcommand)] #[derive(Subcommand)]
enum Command { enum Command {
/// Run commands to manage Burn Books
Books(books::BooksArgs),
/// Run the specified dependencies check locally /// Run the specified dependencies check locally
Dependencies { Dependencies {
/// The dependency check to run /// The dependency check to run
@ -46,6 +49,7 @@ fn main() -> anyhow::Result<()> {
let args = Args::parse(); let args = Args::parse();
match args.command { match args.command {
Command::Books(args) => args.parse(),
Command::Dependencies { dependency_check } => dependency_check.run(), Command::Dependencies { dependency_check } => dependency_check.run(),
Command::Publish { name } => publish::run(name), Command::Publish { name } => publish::run(name),
Command::RunChecks { env } => env.run(), Command::RunChecks { env } => env.run(),

View File

@ -43,12 +43,12 @@ pub(crate) fn run_cargo_with_path<P: AsRef<Path>>(
/// Ensure that a cargo crate is installed /// Ensure that a cargo crate is installed
pub(crate) fn ensure_cargo_crate_is_installed(crate_name: &str) { pub(crate) fn ensure_cargo_crate_is_installed(crate_name: &str) {
if !is_cargo_crate_installed(crate_name) { if !is_cargo_crate_installed(crate_name) {
group!("Cargo: install {} crate_name", crate_name); group!("Cargo: install crate '{}'", crate_name);
run_cargo( run_cargo(
"install", "install",
[crate_name].into(), [crate_name].into(),
HashMap::new(), HashMap::new(),
&format!("{} should be installed", crate_name), &format!("crate '{}' should be installed", crate_name),
); );
endgroup!(); endgroup!();
} }

35
xtask/src/utils/mdbook.rs Normal file
View File

@ -0,0 +1,35 @@
use std::{
collections::HashMap,
path::Path,
process::{Command, Stdio},
};
use crate::utils::process::handle_child_process;
use super::Params;
/// Run an mdbook command with the passed directory as the current directory
pub(crate) fn run_mdbook_with_path<P: AsRef<Path>>(
command: &str,
params: Params,
envs: HashMap<&str, String>,
path: Option<P>,
error: &str,
) {
info!("mdbook {} {}\n", command, params.params.join(" "));
let mut mdbook = Command::new("mdbook");
mdbook
.envs(&envs)
.arg(command)
.args(&params.params)
.stdout(Stdio::inherit()) // Send stdout directly to terminal
.stderr(Stdio::inherit()); // Send stderr directly to terminal
if let Some(path) = path {
mdbook.current_dir(path);
}
// Handle mdbook child process
let mdbook_process = mdbook.spawn().expect(error);
handle_child_process(mdbook_process, "mdbook process should run flawlessly");
}

View File

@ -1,4 +1,5 @@
pub(crate) mod cargo; pub(crate) mod cargo;
pub(crate) mod mdbook;
pub(crate) mod process; pub(crate) mod process;
pub(crate) mod rustup; pub(crate) mod rustup;
pub(crate) mod time; pub(crate) mod time;

View File

@ -1,6 +1,7 @@
use rand::Rng;
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
// Handle child process /// Handle child process
pub(crate) fn handle_child_process(mut child: Child, error: &str) { pub(crate) fn handle_child_process(mut child: Child, error: &str) {
// Wait for the child process to finish // Wait for the child process to finish
let status = child.wait().expect(error); let status = child.wait().expect(error);
@ -13,7 +14,7 @@ pub(crate) fn handle_child_process(mut child: Child, error: &str) {
} }
} }
// Run a command /// Run a command
pub(crate) fn run_command(command: &str, args: &[&str], command_error: &str, child_error: &str) { pub(crate) fn run_command(command: &str, args: &[&str], command_error: &str, child_error: &str) {
// Format command // Format command
info!("{command} {}\n\n", args.join(" ")); info!("{command} {}\n\n", args.join(" "));
@ -29,3 +30,9 @@ pub(crate) fn run_command(command: &str, args: &[&str], command_error: &str, chi
// Handle command child process // Handle command child process
handle_child_process(command, child_error); handle_child_process(command, child_error);
} }
/// Return a random port between 3000 and 9999
pub(crate) fn random_port() -> u16 {
let mut rng = rand::thread_rng();
rng.gen_range(3000..=9999)
}