Generate environment variables doc from code (#8493)

## Summary

Resolves #8417

I've just begun learning procedural macros, so this PR is more of a
proof of concept. It's still a work in progress, and I welcome any
assistance or feedback.
This commit is contained in:
Jo 2024-11-03 22:31:38 +08:00 committed by GitHub
parent 545a55f58f
commit 3dfedf1fef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 491 additions and 135 deletions

View file

@ -2,7 +2,10 @@
use anyhow::Result;
use crate::{generate_cli_reference, generate_json_schema, generate_options_reference};
use crate::{
generate_cli_reference, generate_env_vars_reference, generate_json_schema,
generate_options_reference,
};
#[derive(clap::Args)]
pub(crate) struct Args {
@ -27,5 +30,6 @@ pub(crate) fn main(args: &Args) -> Result<()> {
generate_json_schema::main(&generate_json_schema::Args { mode: args.mode })?;
generate_options_reference::main(&generate_options_reference::Args { mode: args.mode })?;
generate_cli_reference::main(&generate_cli_reference::Args { mode: args.mode })?;
generate_env_vars_reference::main(&generate_env_vars_reference::Args { mode: args.mode })?;
Ok(())
}

View file

@ -31,7 +31,6 @@ const SHOW_HIDDEN_COMMANDS: &[&str] = &["generate-shell-completion"];
#[derive(clap::Args)]
pub(crate) struct Args {
/// Write the generated output to stdout (rather than to `settings.md`).
#[arg(long, default_value_t, value_enum)]
pub(crate) mode: Mode,
}

View file

@ -0,0 +1,98 @@
//! Generate the environment variables reference from `uv_static::EnvVars`.
use anyhow::bail;
use pretty_assertions::StrComparison;
use std::path::PathBuf;
use uv_static::EnvVars;
use crate::generate_all::Mode;
use crate::ROOT_DIR;
#[derive(clap::Args)]
pub(crate) struct Args {
#[arg(long, default_value_t, value_enum)]
pub(crate) mode: Mode,
}
pub(crate) fn main(args: &Args) -> anyhow::Result<()> {
let reference_string = generate();
let filename = "environment.md";
let reference_path = PathBuf::from(ROOT_DIR)
.join("docs")
.join("configuration")
.join(filename);
match args.mode {
Mode::DryRun => {
anstream::println!("{reference_string}");
}
Mode::Check => match fs_err::read_to_string(reference_path) {
Ok(current) => {
if current == reference_string {
anstream::println!("Up-to-date: {filename}");
} else {
let comparison = StrComparison::new(&current, &reference_string);
bail!("{filename} changed, please run `cargo dev generate-env-vars-reference`:\n{comparison}");
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
bail!("{filename} not found, please run `cargo dev generate-env-vars-reference`");
}
Err(err) => {
bail!(
"{filename} changed, please run `cargo dev generate-env-vars-reference`:\n{err}"
);
}
},
Mode::Write => match fs_err::read_to_string(&reference_path) {
Ok(current) => {
if current == reference_string {
anstream::println!("Up-to-date: {filename}");
} else {
anstream::println!("Updating: {filename}");
fs_err::write(reference_path, reference_string.as_bytes())?;
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
anstream::println!("Updating: {filename}");
fs_err::write(reference_path, reference_string.as_bytes())?;
}
Err(err) => {
bail!("{filename} changed, please run `cargo dev generate-env-vars-reference`:\n{err}");
}
},
}
Ok(())
}
fn generate() -> String {
let mut output = String::new();
output.push_str("# Environment variables\n\n");
output.push_str("uv respects the following environment variables:\n\n");
for (var, doc) in EnvVars::metadata() {
// Remove empty lines and ddd two spaces to the beginning from the second line.
let doc = doc
.lines()
.enumerate()
.filter(|(_, line)| !line.trim().is_empty())
.map(|(i, line)| {
if i == 0 {
line.to_string()
} else {
format!(" {line}")
}
})
.collect::<Vec<_>>()
.join("\n");
output.push_str(&format!("- `{var}`: {doc}\n"));
}
output
}
#[cfg(test)]
mod tests;

View file

@ -0,0 +1,19 @@
use std::env;
use anyhow::Result;
use uv_static::EnvVars;
use crate::generate_all::Mode;
use super::{main, Args};
#[test]
fn test_generate_env_vars_reference() -> Result<()> {
let mode = if env::var(EnvVars::UV_UPDATE_SCHEMA).as_deref() == Ok("1") {
Mode::Write
} else {
Mode::Check
};
main(&Args { mode })
}

View file

@ -27,7 +27,6 @@ struct CombinedOptions {
#[derive(clap::Args)]
pub(crate) struct Args {
/// Write the generated output to stdout (rather than to `uv.schema.json`).
#[arg(long, default_value_t, value_enum)]
pub(crate) mode: Mode,
}

View file

@ -34,7 +34,6 @@ struct CombinedOptions {
#[derive(clap::Args)]
pub(crate) struct Args {
/// Write the generated output to stdout (rather than to `settings.md`).
#[arg(long, default_value_t, value_enum)]
pub(crate) mode: Mode,
}

View file

@ -20,6 +20,7 @@ use crate::clear_compile::ClearCompileArgs;
use crate::compile::CompileArgs;
use crate::generate_all::Args as GenerateAllArgs;
use crate::generate_cli_reference::Args as GenerateCliReferenceArgs;
use crate::generate_env_vars_reference::Args as GenerateEnvVarsReferenceArgs;
use crate::generate_json_schema::Args as GenerateJsonSchemaArgs;
use crate::generate_options_reference::Args as GenerateOptionsReferenceArgs;
#[cfg(feature = "render")]
@ -31,6 +32,7 @@ mod clear_compile;
mod compile;
mod generate_all;
mod generate_cli_reference;
mod generate_env_vars_reference;
mod generate_json_schema;
mod generate_options_reference;
mod render_benchmarks;
@ -54,6 +56,8 @@ enum Cli {
GenerateOptionsReference(GenerateOptionsReferenceArgs),
/// Generate the CLI reference for the documentation.
GenerateCliReference(GenerateCliReferenceArgs),
/// Generate the environment variables reference for the documentation.
GenerateEnvVarsReference(GenerateEnvVarsReferenceArgs),
#[cfg(feature = "render")]
/// Render the benchmarks.
RenderBenchmarks(RenderBenchmarksArgs),
@ -70,6 +74,7 @@ async fn run() -> Result<()> {
Cli::GenerateJSONSchema(args) => generate_json_schema::main(&args)?,
Cli::GenerateOptionsReference(args) => generate_options_reference::main(&args)?,
Cli::GenerateCliReference(args) => generate_cli_reference::main(&args)?,
Cli::GenerateEnvVarsReference(args) => generate_env_vars_reference::main(&args)?,
#[cfg(feature = "render")]
Cli::RenderBenchmarks(args) => render_benchmarks::render_benchmarks(&args)?,
}