Skip to content

Commit 2630a46

Browse files
Add a test assembler to website-test
1 parent fd96a9a commit 2630a46

File tree

7 files changed

+374
-215
lines changed

7 files changed

+374
-215
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/website-test/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ version = "0.1.0"
44
edition = "2021"
55
build = "build.rs"
66
publish = false
7-
rust-version = "1.62"
7+
rust-version = "1.81"
88

99
[dependencies]
1010
yew-agent = { path = "../../packages/yew-agent/" }
1111

1212
[dev-dependencies]
1313
derive_more = { version = "2.0", features = ["from"] }
1414
gloo = "0.11"
15+
gloo-net = "0.6"
1516
js-sys = "0.3"
17+
serde = { version = "1.0", features = ["derive"] }
1618
wasm-bindgen = "0.2"
1719
wasm-bindgen-futures = "0.4"
1820
weblog = "0.3.0"

tools/website-test/build.rs

Lines changed: 185 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,145 @@
11
use std::collections::HashMap;
2-
use std::fmt::{self, Write};
2+
use std::error::Error;
3+
use std::fmt::Write;
4+
use std::fs::File;
5+
use std::io::{self, BufRead, BufReader, ErrorKind, Read, Seek, SeekFrom};
36
use std::path::{Path, PathBuf};
7+
use std::process::ExitCode;
48
use std::{env, fs};
59

610
use glob::glob;
711

12+
type Result<T = ()> = core::result::Result<T, Box<dyn Error + 'static>>;
13+
14+
macro_rules! e {
15+
($($fmt:tt),* $(,)?) => {
16+
return Err(format!($($fmt),*).into())
17+
};
18+
}
19+
20+
macro_rules! assert {
21+
($condition:expr, $($fmt:tt),* $(,)?) => {
22+
if !$condition { e!($($fmt),*) }
23+
};
24+
}
25+
826
#[derive(Debug, Default)]
927
struct Level {
1028
nested: HashMap<String, Level>,
1129
files: Vec<PathBuf>,
1230
}
1331

14-
fn main() {
15-
let home = env::var("CARGO_MANIFEST_DIR").unwrap();
16-
let pattern = format!("{home}/../../website/docs/**/*.md*");
17-
let base = format!("{home}/../../website");
18-
let base = Path::new(&base).canonicalize().unwrap();
19-
let dir_pattern = format!("{home}/../../website/docs/**");
20-
for dir in glob(&dir_pattern).unwrap() {
21-
println!("cargo:rerun-if-changed={}", dir.unwrap().display());
32+
fn should_combine_code_blocks(path: &Path) -> io::Result<bool> {
33+
const FLAG: &[u8] = b"<!-- COMBINE CODE BLOCKS -->";
34+
35+
let mut file = File::open(path)?;
36+
match file.seek(SeekFrom::End(-32)) {
37+
Ok(_) => (),
38+
Err(e) if e.kind() == ErrorKind::InvalidInput => return Ok(false),
39+
Err(e) => return Err(e),
2240
}
41+
let mut buf = [0u8; 32];
42+
file.read_exact(&mut buf)?;
43+
Ok(buf.trim_ascii_end().ends_with(FLAG))
44+
}
2345

24-
let mut level = Level::default();
46+
fn apply_diff(src: &mut String, preamble: &str, added: &str, removed: &str)
47+
-> Result
48+
{
49+
assert!(
50+
!preamble.is_empty() || !removed.is_empty(),
51+
"Failure on applying a diff: \n\
52+
No preamble or text to remove provided, unable to find location to insert:\n{added}\n\
53+
In the following text:\n{src}",
54+
);
2555

26-
for entry in glob(&pattern).unwrap() {
27-
let path = entry.unwrap();
28-
let path = Path::new(&path).canonicalize().unwrap();
29-
println!("cargo:rerun-if-changed={}", path.display());
30-
let rel = path.strip_prefix(&base).unwrap();
56+
let mut matches = src.match_indices(if preamble.is_empty() {
57+
removed
58+
} else {
59+
&preamble
60+
});
61+
let Some((preamble_start, _)) = matches.next() else {
62+
e!("Failure on applying a diff: \n\
63+
couldn't find the following text:\n{preamble}\n\nIn the following text:\n{src}")
64+
};
65+
66+
assert!(
67+
matches.next().is_none(),
68+
"Failure on applying a diff: \n\
69+
Ambiguous preamble:\n{preamble}\n\
70+
In the following text:\n{src}\n\
71+
While trying to remove the following text:\n{removed}\n\
72+
And add the following:\n{added}\n"
73+
);
3174

32-
let mut parts = vec![];
75+
let preamble_end = preamble_start + preamble.len();
76+
assert!(
77+
src.get(preamble_end .. preamble_end + removed.len()) == Some(removed),
78+
"Failure on applying a diff: \n\
79+
Text to remove not found:\n{removed}\n\nIn the following text:\n{src}",
80+
);
3381

34-
for part in rel {
35-
parts.push(part.to_str().unwrap());
82+
src.replace_range(preamble_end .. preamble_end + removed.len(), added);
83+
Ok(())
84+
}
85+
86+
fn combined_code_blocks(path: &Path) -> Result<String> {
87+
let file = BufReader::new(File::open(path)?);
88+
let mut res = String::new();
89+
90+
let mut err = Ok(());
91+
let mut lines = file.lines().filter_map(|i| {
92+
i.map_err(|e| err = Err(e)).ok()
93+
}).enumerate();
94+
while let Some((i, line)) = lines.next() {
95+
if !line.starts_with("```rust") {
96+
continue;
3697
}
3798

38-
level.insert(path.clone(), &parts[..]);
99+
let mut preamble = String::new();
100+
let mut added = String::new();
101+
let mut removed = String::new();
102+
let mut diff_applied = false;
103+
for (i, line) in &mut lines {
104+
if line.starts_with("```") {
105+
if !added.is_empty() || !removed.is_empty() {
106+
apply_diff(&mut res, &preamble, &added, &removed)
107+
.inspect_err(|_| eprintln!("Line {i}"))?;
108+
} else if !diff_applied { // if no diff markers were found, just add the contents
109+
eprintln!("Shrimply added {preamble:?}, line {i}");
110+
res += &preamble;
111+
}
112+
break;
113+
} else if let Some(line) = line.strip_prefix('+') {
114+
if line.starts_with(char::is_whitespace) {
115+
added += " ";
116+
}
117+
added += line;
118+
added += "\n";
119+
} else if let Some(line) = line.strip_prefix('-') {
120+
if line.starts_with(char::is_whitespace) {
121+
removed += " ";
122+
}
123+
removed += line;
124+
removed += "\n";
125+
} else if line.trim_ascii() == "// ..." { // disregard the preamble
126+
preamble.clear();
127+
} else {
128+
if !added.is_empty() || !removed.is_empty() {
129+
diff_applied = true;
130+
apply_diff(&mut res, &preamble, &added, &removed)
131+
.inspect_err(|_| eprintln!("Line {i}"))?;
132+
preamble += &added;
133+
added.clear();
134+
removed.clear();
135+
}
136+
preamble += &line;
137+
preamble += "\n";
138+
}
139+
}
39140
}
40141

41-
let out = format!("{}/website_tests.rs", env::var("OUT_DIR").unwrap());
42-
43-
fs::write(out, level.to_contents()).unwrap();
142+
Ok(res)
44143
}
45144

46145
impl Level {
@@ -53,14 +152,14 @@ impl Level {
53152
}
54153
}
55154

56-
fn to_contents(&self) -> String {
155+
fn to_contents(&self) -> Result<String> {
57156
let mut dst = String::new();
58157

59-
self.write_inner(&mut dst, 0).unwrap();
60-
dst
158+
self.write_inner(&mut dst, 0)?;
159+
Ok(dst)
61160
}
62161

63-
fn write_into(&self, dst: &mut String, name: &str, level: usize) -> fmt::Result {
162+
fn write_into(&self, dst: &mut String, name: &str, level: usize) -> Result {
64163
self.write_space(dst, level);
65164
let name = name.replace(['-', '.'], "_");
66165
writeln!(dst, "pub mod {name} {{")?;
@@ -73,24 +172,33 @@ impl Level {
73172
Ok(())
74173
}
75174

76-
fn write_inner(&self, dst: &mut String, level: usize) -> fmt::Result {
175+
fn write_inner(&self, dst: &mut String, level: usize) -> Result {
77176
for (name, nested) in &self.nested {
78177
nested.write_into(dst, name, level)?;
79178
}
80179

81-
self.write_space(dst, level);
82-
83180
for file in &self.files {
84-
let stem = Path::new(file)
181+
let stem = file
85182
.file_stem()
86-
.unwrap()
183+
.ok_or_else(|| format!("no filename in path {file:?}"))?
87184
.to_str()
88-
.unwrap()
185+
.ok_or_else(|| format!("non-UTF8 path: {file:?}"))?
89186
.replace('-', "_");
90187

91-
self.write_space(dst, level);
92-
93-
writeln!(dst, "#[doc = include_str!(r\"{}\")]", file.display())?;
188+
if should_combine_code_blocks(file)? {
189+
let res = combined_code_blocks(file)?;
190+
self.write_space(dst, level);
191+
writeln!(dst, "/// ```rust, no_run")?;
192+
for line in res.lines() {
193+
self.write_space(dst, level);
194+
writeln!(dst, "/// {line}")?;
195+
}
196+
self.write_space(dst, level);
197+
writeln!(dst, "/// ```")?;
198+
} else {
199+
self.write_space(dst, level);
200+
writeln!(dst, "#[doc = include_str!(r\"{}\")]", file.display())?;
201+
}
94202
self.write_space(dst, level);
95203
writeln!(dst, "pub fn {stem}_md() {{}}")?;
96204
}
@@ -104,3 +212,45 @@ impl Level {
104212
}
105213
}
106214
}
215+
216+
fn inner_main() -> Result {
217+
let home = env::var("CARGO_MANIFEST_DIR")?;
218+
let pattern = format!("{home}/../../website/docs/**/*.md*");
219+
let base = format!("{home}/../../website");
220+
let base = Path::new(&base).canonicalize()?;
221+
let dir_pattern = format!("{home}/../../website/docs/**");
222+
for dir in glob(&dir_pattern)? {
223+
println!("cargo:rerun-if-changed={}", dir?.display());
224+
}
225+
226+
let mut level = Level::default();
227+
228+
for entry in glob(&pattern)? {
229+
let path = entry?.canonicalize()?;
230+
println!("cargo:rerun-if-changed={}", path.display());
231+
let rel = path.strip_prefix(&base)?;
232+
233+
let mut parts = vec![];
234+
235+
for part in rel {
236+
parts.push(part.to_str().ok_or_else(|| format!("Non-UTF8 path: {rel:?}"))?);
237+
}
238+
239+
level.insert(path.clone(), &parts[..]);
240+
}
241+
242+
let out = format!("{}/website_tests.rs", env::var("OUT_DIR")?);
243+
244+
fs::write(out, level.to_contents()?)?;
245+
Ok(())
246+
}
247+
248+
fn main() -> ExitCode {
249+
match inner_main() {
250+
Ok(_) => ExitCode::SUCCESS,
251+
Err(e) => {
252+
eprintln!("{e}");
253+
ExitCode::FAILURE
254+
}
255+
}
256+
}

tools/website-test/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
#![allow(clippy::needless_doctest_main)]
2-
pub mod tutorial;
3-
41
include!(concat!(env!("OUT_DIR"), "/website_tests.rs"));

tools/website-test/src/tutorial.rs

Lines changed: 0 additions & 7 deletions
This file was deleted.

website/docs/concepts/html/introduction.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ html! { <div attribute={value} /> };
164164

165165
Properties are specified with `~` before the element name:
166166

167-
```rust
167+
```rust , ignore
168168
use yew::prelude::*;
169169

170170
html! { <my-element ~property="abc" /> };

0 commit comments

Comments
 (0)