-
Notifications
You must be signed in to change notification settings - Fork 13k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
910 additions
and
314 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "htmldocck" | ||
version = "0.1.0" | ||
description = "A test framework for rustdoc's HTML backend" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
getopts = "0.2" | ||
regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace | ||
shlex = "1.3.0" | ||
unicode-width = "0.1.4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use std::{ | ||
collections::{hash_map::Entry, HashMap}, | ||
path::Path, | ||
}; | ||
|
||
use crate::error::DiagCtxt; | ||
|
||
pub(crate) struct Cache<'a> { | ||
root: &'a Path, | ||
// FIXME: `&'a str`s | ||
files: HashMap<String, String>, | ||
// FIXME: `&'a str`, comment what this is for -- `-` | ||
last_path: Option<String>, | ||
} | ||
|
||
impl<'a> Cache<'a> { | ||
pub(crate) fn new(root: &'a Path) -> Self { | ||
Self { root, files: HashMap::new(), last_path: None } | ||
} | ||
|
||
// FIXME: check file vs. dir (`@has <PATH>` vs. `@has-dir <PATH>`) | ||
/// Check if the path points to an existing entity. | ||
pub(crate) fn has(&mut self, path: String, dcx: &mut DiagCtxt) -> Result<bool, ()> { | ||
// FIXME: should we use `try_exists` over `exists` instead? matters the most for `@!has <PATH>`. | ||
let path = self.resolve(path, dcx)?; | ||
|
||
Ok(self.files.contains_key(&path) || Path::new(&path).exists()) | ||
} | ||
|
||
/// Load the contents of the given path. | ||
pub(crate) fn load(&mut self, path: String, dcx: &mut DiagCtxt) -> Result<&str, ()> { | ||
let path = self.resolve(path, dcx)?; | ||
|
||
Ok(match self.files.entry(path) { | ||
Entry::Occupied(entry) => entry.into_mut(), | ||
Entry::Vacant(entry) => { | ||
// FIXME: better message, location | ||
let data = | ||
std::fs::read_to_string(self.root.join(entry.key())).map_err(|error| { | ||
dcx.emit(&format!("failed to read file: {error}"), None, None) | ||
})?; | ||
entry.insert(data) | ||
} | ||
}) | ||
} | ||
|
||
// FIXME: &str -> &str if possible | ||
fn resolve(&mut self, path: String, dcx: &mut DiagCtxt) -> Result<String, ()> { | ||
if path == "-" { | ||
// FIXME: no cloning | ||
return self | ||
.last_path | ||
.clone() | ||
// FIXME better diag, location | ||
.ok_or_else(|| { | ||
dcx.emit( | ||
"attempt to use `-` ('previous path') in the very first command", | ||
None, | ||
None, | ||
) | ||
}); | ||
} | ||
|
||
// While we could normalize the `path` at this point by | ||
// using `std::path::absolute`, it's likely not worth it. | ||
self.last_path = Some(path.clone()); | ||
Ok(path) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// FIXME: document that the "channel" is indeed a channel *URL*! | ||
|
||
use std::{borrow::Cow, sync::OnceLock}; | ||
|
||
use crate::error::DiagCtxt; | ||
|
||
const PLACEHOLDER: &str = "{{channel}}"; | ||
const ENV_VAR_KEY: &str = "DOC_RUST_LANG_ORG_CHANNEL"; | ||
|
||
pub(crate) fn instantiate<'a>(input: &'a str, dcx: &mut DiagCtxt) -> Result<Cow<'a, str>, ()> { | ||
let Some(channel) = channel(dcx)? else { return Ok(input.into()) }; | ||
Ok(input.replace(PLACEHOLDER, channel).into()) | ||
} | ||
|
||
#[allow(dead_code)] // FIXME | ||
pub(crate) fn anonymize<'a>(input: &'a str, dcx: &'_ mut DiagCtxt) -> Result<Cow<'a, str>, ()> { | ||
let Some(channel) = channel(dcx)? else { return Ok(input.into()) }; | ||
Ok(input.replace(channel, PLACEHOLDER).into()) | ||
} | ||
|
||
fn channel(dcx: &mut DiagCtxt) -> Result<Option<&'static str>, ()> { | ||
static CHANNEL_URL: OnceLock<Option<String>> = OnceLock::new(); | ||
|
||
// FIXME: Use `get_or_try_init` here (instead of `get`→`set`→`get`) if/once stabilized (on beta). | ||
|
||
if let Some(channel_url) = CHANNEL_URL.get() { | ||
return Ok(channel_url.as_deref()); | ||
} | ||
|
||
let channel_url = match std::env::var(ENV_VAR_KEY) { | ||
Ok(url) => Some(url), | ||
// FIXME: should we make the channel mandatory instead? | ||
Err(std::env::VarError::NotPresent) => None, | ||
Err(std::env::VarError::NotUnicode(var)) => { | ||
// FIXME: better diag | ||
// FIXME: Use `OsStr::display` (instead of `to_string_lossy`) if/once stabilized (on beta). | ||
dcx.emit( | ||
&format!("env var `{ENV_VAR_KEY}` is not valid UTF-8: `{}`", var.to_string_lossy()), | ||
None, | ||
None, | ||
); | ||
return Err(()); | ||
} | ||
}; | ||
|
||
// unwrap: The static item is locally scoped and no other thread tries to initialize it. | ||
CHANNEL_URL.set(channel_url).unwrap(); | ||
// unwrap: Initialized above. | ||
Ok(CHANNEL_URL.get().unwrap().as_deref()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
use crate::cache::Cache; | ||
use crate::error::{DiagCtxt, Source}; | ||
use crate::{channel, Command, CommandKind}; | ||
|
||
impl Command<'_> { | ||
pub(crate) fn check(self, cache: &mut Cache<'_>, dcx: &mut DiagCtxt) -> Result<(), ()> { | ||
let result = self.kind.check(cache, self.source.clone(), dcx)?; | ||
|
||
if result == self.negated { | ||
// FIXME: better diag | ||
dcx.emit("check failed", self.source, None); | ||
return Err(()); | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl CommandKind { | ||
// FIXME: implement all checks! | ||
fn check( | ||
self, | ||
cache: &mut Cache<'_>, | ||
_source: Source<'_>, // FIXME: unused | ||
dcx: &mut DiagCtxt, | ||
) -> Result<bool, ()> { | ||
Ok(match self { | ||
Self::HasFile { path } => cache.has(path, dcx)?, // FIXME: check if it's actually a file | ||
Self::HasDir { path } => cache.has(path, dcx)?, // FIXME: check if it's actually a directory | ||
Self::Has { path, xpath, text } => { | ||
let _data = cache.load(path, dcx)?; | ||
_ = xpath; | ||
_ = text; | ||
true // FIXME | ||
} | ||
Self::HasRaw { path, text } => { | ||
let data = cache.load(path, dcx)?; | ||
|
||
if text.is_empty() { | ||
// fast path | ||
return Ok(true); | ||
} | ||
|
||
let text = channel::instantiate(&text, dcx)?; | ||
let text = text.replace(|c: char| c.is_ascii_whitespace(), " "); | ||
let data = data.replace(|c: char| c.is_ascii_whitespace(), " "); | ||
|
||
data.contains(&text) | ||
} | ||
Self::Matches { path, xpath, pattern } => { | ||
let _data = cache.load(path, dcx)?; | ||
_ = xpath; | ||
_ = pattern; | ||
|
||
true // FIXME | ||
} | ||
Self::MatchesRaw { path, pattern } => pattern.is_match(cache.load(path, dcx)?), | ||
Self::Count { path, xpath, text, count } => { | ||
let _data = cache.load(path, dcx)?; | ||
_ = xpath; | ||
_ = text; | ||
_ = count; | ||
true // FIXME | ||
} | ||
Self::Files { path, files } => { | ||
let _data = cache.load(path, dcx)?; | ||
_ = files; | ||
true // FIXME | ||
} | ||
Self::Snapshot { name, path, xpath } => { | ||
let _data = cache.load(path, dcx)?; | ||
_ = name; | ||
_ = path; | ||
_ = xpath; | ||
true // FIXME | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use std::path::PathBuf; | ||
|
||
use crate::error::DiagCtxt; | ||
|
||
pub(crate) struct Config { | ||
/// The path to the directory that contains the generated HTML documentation. | ||
pub(crate) doc_dir: PathBuf, | ||
/// The path to the test file the docs were generated for and which may contain check commands. | ||
pub(crate) template: String, | ||
/// Whether to automatically update snapshot files. | ||
#[allow(dead_code)] // FIXME | ||
pub(crate) bless: bool, | ||
} | ||
|
||
impl Config { | ||
pub(crate) fn parse(args: &[String], dcx: &mut DiagCtxt) -> Result<Self, ()> { | ||
const DOC_DIR_OPT: &str = "doc-dir"; | ||
const TEMPLATE_OPT: &str = "template"; | ||
const BLESS_FLAG: &str = "bless"; | ||
|
||
let mut opts = getopts::Options::new(); | ||
opts.reqopt("", DOC_DIR_OPT, "Path to the documentation directory", "<PATH>") | ||
.reqopt("", TEMPLATE_OPT, "Path to the template file", "<PATH>") | ||
.optflag("", BLESS_FLAG, "Whether to automatically update snapshot files"); | ||
|
||
// We may not assume the presence of the first argument. On some platforms, | ||
// it's possible to pass an empty array of arguments to `execve`. | ||
let program = args.get(0).map(|arg| arg.as_str()).unwrap_or("htmldocck"); | ||
let args = args.get(1..).unwrap_or_default(); | ||
|
||
match opts.parse(args) { | ||
Ok(matches) => Ok(Self { | ||
doc_dir: matches.opt_str(DOC_DIR_OPT).unwrap().into(), | ||
template: matches.opt_str(TEMPLATE_OPT).unwrap(), | ||
bless: matches.opt_present(BLESS_FLAG), | ||
}), | ||
Err(err) => { | ||
let mut err = err.to_string(); | ||
err.push_str("\n\n"); | ||
err.push_str(&opts.short_usage(program)); | ||
err.push_str(&opts.usage("")); | ||
dcx.emit(&err, None, None); | ||
Err(()) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.