sourcey 3.6.0 → 3.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/adapters/index.d.ts +3 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +35 -3
- package/dist/adapters/types.d.ts +8 -2
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/cli.js +28 -34
- package/dist/client/search.js +181 -119
- package/dist/client/tabs.js +23 -0
- package/dist/components/layout/Head.d.ts.map +1 -1
- package/dist/components/layout/Head.js +7 -6
- package/dist/components/openapi/Introduction.d.ts.map +1 -1
- package/dist/components/openapi/Introduction.js +16 -1
- package/dist/components/openapi/Security.d.ts.map +1 -1
- package/dist/components/openapi/Security.js +18 -2
- package/dist/config.d.ts +70 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -7
- package/dist/core/api-rendering.d.ts +47 -0
- package/dist/core/api-rendering.d.ts.map +1 -0
- package/dist/core/api-rendering.js +76 -0
- package/dist/core/doxygen-loader.d.ts +1 -1
- package/dist/core/doxygen-loader.d.ts.map +1 -1
- package/dist/core/doxygen-loader.js +37 -19
- package/dist/core/godoc-loader.d.ts.map +1 -1
- package/dist/core/godoc-loader.js +17 -6
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +8 -5
- package/dist/core/mcp-normalizer.d.ts.map +1 -1
- package/dist/core/mcp-normalizer.js +48 -34
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +75 -4
- package/dist/core/rustdoc-introspector.d.ts +29 -0
- package/dist/core/rustdoc-introspector.d.ts.map +1 -0
- package/dist/core/rustdoc-introspector.js +236 -0
- package/dist/core/rustdoc-loader.d.ts +25 -0
- package/dist/core/rustdoc-loader.d.ts.map +1 -0
- package/dist/core/rustdoc-loader.js +472 -0
- package/dist/core/rustdoc-render.d.ts +82 -0
- package/dist/core/rustdoc-render.d.ts.map +1 -0
- package/dist/core/rustdoc-render.js +808 -0
- package/dist/core/rustdoc-types.d.ts +314 -0
- package/dist/core/rustdoc-types.d.ts.map +1 -0
- package/dist/core/rustdoc-types.js +18 -0
- package/dist/core/search-indexer.js +1 -1
- package/dist/core/sourcey-rustdoc/Cargo.lock +705 -0
- package/dist/core/sourcey-rustdoc/Cargo.toml +38 -0
- package/dist/core/sourcey-rustdoc/README.md +59 -0
- package/dist/core/sourcey-rustdoc/src/diagnostics.rs +42 -0
- package/dist/core/sourcey-rustdoc/src/doctest.rs +268 -0
- package/dist/core/sourcey-rustdoc/src/extract.rs +492 -0
- package/dist/core/sourcey-rustdoc/src/lib.rs +58 -0
- package/dist/core/sourcey-rustdoc/src/links.rs +126 -0
- package/dist/core/sourcey-rustdoc/src/main.rs +256 -0
- package/dist/core/sourcey-rustdoc/src/signature.rs +415 -0
- package/dist/core/sourcey-rustdoc/src/spec.rs +390 -0
- package/dist/core/sourcey-rustdoc/tests/format_version.rs +45 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +50 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -13
- package/dist/init.js +2 -2
- package/dist/renderer/changelog-feed.d.ts.map +1 -1
- package/dist/renderer/changelog-feed.js +27 -19
- package/dist/renderer/context.d.ts +1 -0
- package/dist/renderer/context.d.ts.map +1 -1
- package/dist/renderer/html-builder.d.ts.map +1 -1
- package/dist/renderer/html-builder.js +4 -3
- package/dist/renderer/llms.js +57 -2
- package/dist/site-assembly.d.ts +5 -0
- package/dist/site-assembly.d.ts.map +1 -1
- package/dist/site-assembly.js +57 -2
- package/dist/themes/default/sourcey.css +300 -0
- package/dist/utils/code-samples.d.ts +1 -0
- package/dist/utils/code-samples.d.ts.map +1 -1
- package/dist/utils/code-samples.js +30 -22
- package/dist/utils/html.d.ts +10 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +152 -0
- package/dist/utils/http.d.ts +0 -2
- package/dist/utils/http.d.ts.map +1 -1
- package/dist/utils/http.js +19 -34
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +102 -37
- package/package.json +10 -5
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "sourcey-rustdoc"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Convert nightly rustdoc JSON into a stable RustdocSpec snapshot. Powers sourcey's rustdoc() source adapter; works standalone for any tool that wants a stable Rust API surface representation."
|
|
6
|
+
license = "AGPL-3.0-only"
|
|
7
|
+
repository = "https://github.com/sourcey/sourcey"
|
|
8
|
+
homepage = "https://sourcey.com"
|
|
9
|
+
documentation = "https://sourcey.com/docs/adapters/rustdoc"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
keywords = ["rustdoc", "documentation", "json", "rust-api"]
|
|
12
|
+
categories = ["development-tools", "command-line-utilities"]
|
|
13
|
+
rust-version = "1.85"
|
|
14
|
+
|
|
15
|
+
[lib]
|
|
16
|
+
name = "sourcey_rustdoc"
|
|
17
|
+
path = "src/lib.rs"
|
|
18
|
+
|
|
19
|
+
[[bin]]
|
|
20
|
+
name = "sourcey-rustdoc"
|
|
21
|
+
path = "src/main.rs"
|
|
22
|
+
|
|
23
|
+
[dependencies]
|
|
24
|
+
rustdoc-types = "=0.57"
|
|
25
|
+
rustdoc-json = "0.9"
|
|
26
|
+
serde = { version = "1", features = ["derive"] }
|
|
27
|
+
serde_json = "1"
|
|
28
|
+
anyhow = "1"
|
|
29
|
+
thiserror = "1"
|
|
30
|
+
clap = { version = "4", features = ["derive"] }
|
|
31
|
+
pulldown-cmark = "0.10"
|
|
32
|
+
time = { version = "0.3", features = ["formatting"] }
|
|
33
|
+
toml = "0.8"
|
|
34
|
+
flate2 = "1"
|
|
35
|
+
|
|
36
|
+
[profile.release]
|
|
37
|
+
lto = "thin"
|
|
38
|
+
codegen-units = 1
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# sourcey-rustdoc
|
|
2
|
+
|
|
3
|
+
[](https://crates.io/crates/sourcey-rustdoc)
|
|
4
|
+
[](https://docs.rs/sourcey-rustdoc)
|
|
5
|
+
|
|
6
|
+
Convert nightly rustdoc JSON into a stable `RustdocSpec` snapshot. Powers
|
|
7
|
+
the [sourcey](https://sourcey.com) `rustdoc()` source adapter; published
|
|
8
|
+
standalone so any tool that wants a stable representation of a Rust crate's
|
|
9
|
+
API surface can consume the same schema.
|
|
10
|
+
|
|
11
|
+
Most users install sourcey via npm (`npx sourcey init`) and never touch
|
|
12
|
+
this crate directly. It is published for:
|
|
13
|
+
|
|
14
|
+
- alternative renderers that want a stable Rust-side schema
|
|
15
|
+
- semver checkers and public-API analyzers
|
|
16
|
+
- CI tools that drive snapshot generation from Rust
|
|
17
|
+
|
|
18
|
+
If you just want sourcey for your project, see
|
|
19
|
+
[sourcey.com/docs/adapters/rustdoc](https://sourcey.com/docs/adapters/rustdoc).
|
|
20
|
+
|
|
21
|
+
## CLI
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
sourcey-rustdoc --manifest <path> [options] [--output <file>|-]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Key flags:
|
|
28
|
+
|
|
29
|
+
- `--manifest <path>` Path to the Cargo manifest (file or directory).
|
|
30
|
+
- `--crate <name>` Crate name to document; repeat for several. Defaults to
|
|
31
|
+
the manifest's own package.
|
|
32
|
+
- `--features <name>` Enable a feature; repeat for several.
|
|
33
|
+
- `--all-features` Enable all features.
|
|
34
|
+
- `--no-default-features` Disable default features.
|
|
35
|
+
- `--include-private` Include `pub(crate)` and private items.
|
|
36
|
+
- `--include-hidden` Include `#[doc(hidden)]` items.
|
|
37
|
+
- `--target <triple>` Build docs for a specific target triple.
|
|
38
|
+
- `--toolchain <name>` rustup toolchain to invoke; default `nightly`.
|
|
39
|
+
- `--cap-lints <level>` Pass through to rustdoc; default `warn`.
|
|
40
|
+
- `--output <path>|-` Where to write the snapshot. `-` writes to stdout.
|
|
41
|
+
- `--strict` Exit non-zero on any error-level diagnostic.
|
|
42
|
+
|
|
43
|
+
## Snapshot format
|
|
44
|
+
|
|
45
|
+
See `src/spec.rs`. The schema is versioned by the top-level `version` field
|
|
46
|
+
(currently `1`) and pinned to a specific rustdoc JSON `format_version` via
|
|
47
|
+
the `rustdoc-types` dependency. The `format_version` carried in the
|
|
48
|
+
snapshot lets consumers verify they read a compatible artifact.
|
|
49
|
+
|
|
50
|
+
## First-run compile cost
|
|
51
|
+
|
|
52
|
+
The first invocation runs `cargo build --release` for this crate and all of
|
|
53
|
+
its dependencies (`rustdoc-types`, `rustdoc-json`, `serde`, `clap`,
|
|
54
|
+
`pulldown-cmark`, ...). On a warm cargo cache this is fast; on a cold
|
|
55
|
+
machine expect 30–60s. Subsequent invocations are near-instant.
|
|
56
|
+
|
|
57
|
+
Snapshot mode in the TS adapter sidesteps this entirely by reading a
|
|
58
|
+
committed `rustdoc.json`, so projects that build their docs site in CI
|
|
59
|
+
without a Rust toolchain are unaffected.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
use crate::spec::{Diagnostic, DiagnosticSeverity};
|
|
2
|
+
|
|
3
|
+
pub mod codes {
|
|
4
|
+
pub const FORMAT_VERSION_MISMATCH: &str = "RUSTDOC_FORMAT_VERSION_MISMATCH";
|
|
5
|
+
pub const INTRA_DOC_LINK_UNRESOLVED: &str = "RUSTDOC_INTRA_DOC_LINK_UNRESOLVED";
|
|
6
|
+
pub const MISSING_HTML_ROOT_URL: &str = "RUSTDOC_MISSING_HTML_ROOT_URL";
|
|
7
|
+
pub const CARGO_FAILED: &str = "RUSTDOC_CARGO_FAILED";
|
|
8
|
+
pub const INVALID_SNAPSHOT_SCHEMA: &str = "RUSTDOC_INVALID_SNAPSHOT_SCHEMA";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
pub fn error(code: &str, message: impl Into<String>) -> Diagnostic {
|
|
12
|
+
Diagnostic {
|
|
13
|
+
severity: DiagnosticSeverity::Error,
|
|
14
|
+
code: code.to_string(),
|
|
15
|
+
message: message.into(),
|
|
16
|
+
crate_name: None,
|
|
17
|
+
file: None,
|
|
18
|
+
line: None,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn warning(code: &str, message: impl Into<String>) -> Diagnostic {
|
|
23
|
+
Diagnostic {
|
|
24
|
+
severity: DiagnosticSeverity::Warning,
|
|
25
|
+
code: code.to_string(),
|
|
26
|
+
message: message.into(),
|
|
27
|
+
crate_name: None,
|
|
28
|
+
file: None,
|
|
29
|
+
line: None,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn info(code: &str, message: impl Into<String>) -> Diagnostic {
|
|
34
|
+
Diagnostic {
|
|
35
|
+
severity: DiagnosticSeverity::Info,
|
|
36
|
+
code: code.to_string(),
|
|
37
|
+
message: message.into(),
|
|
38
|
+
crate_name: None,
|
|
39
|
+
file: None,
|
|
40
|
+
line: None,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
use pulldown_cmark::{CodeBlockKind, Event, Parser, Tag, TagEnd};
|
|
2
|
+
|
|
3
|
+
use crate::spec::Doctest;
|
|
4
|
+
|
|
5
|
+
/// Tokens that explicitly mark a fence as non-rust. Anything else is treated
|
|
6
|
+
/// as a rust doctest with the tokens collected as fence attributes,
|
|
7
|
+
/// matching rustdoc's own behavior.
|
|
8
|
+
const NON_RUST_LANGS: &[&str] = &[
|
|
9
|
+
"text",
|
|
10
|
+
"ignore-but-not-rust",
|
|
11
|
+
"json",
|
|
12
|
+
"toml",
|
|
13
|
+
"yaml",
|
|
14
|
+
"html",
|
|
15
|
+
"bash",
|
|
16
|
+
"sh",
|
|
17
|
+
"shell",
|
|
18
|
+
"console",
|
|
19
|
+
"diff",
|
|
20
|
+
"ini",
|
|
21
|
+
"markdown",
|
|
22
|
+
"md",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/// Tokens that are recognised by rustdoc as fence attributes (kept as
|
|
26
|
+
/// attributes, not consumed as a language identifier).
|
|
27
|
+
const KNOWN_FENCE_ATTRS: &[&str] = &[
|
|
28
|
+
"ignore",
|
|
29
|
+
"no_run",
|
|
30
|
+
"should_panic",
|
|
31
|
+
"compile_fail",
|
|
32
|
+
"allow_fail",
|
|
33
|
+
"standalone_crate",
|
|
34
|
+
"edition2015",
|
|
35
|
+
"edition2018",
|
|
36
|
+
"edition2021",
|
|
37
|
+
"edition2024",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/// Parse the docstring markdown and return every code fence as a Doctest.
|
|
41
|
+
/// Doctests in rustdoc are any ` ``` ` fence whose language token is empty, `rust`,
|
|
42
|
+
/// or starts with `rust,`. We also surface non-rust fences so the renderer can
|
|
43
|
+
/// label them; the caller filters on `lang`/`fence_attributes`.
|
|
44
|
+
pub fn extract_doctests(docs_markdown: &str) -> Vec<Doctest> {
|
|
45
|
+
let mut out = Vec::new();
|
|
46
|
+
let parser = Parser::new(docs_markdown);
|
|
47
|
+
let mut active: Option<ActiveFence> = None;
|
|
48
|
+
let mut ordinal: u32 = 0;
|
|
49
|
+
for ev in parser {
|
|
50
|
+
match ev {
|
|
51
|
+
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(info))) => {
|
|
52
|
+
let info = info.into_string();
|
|
53
|
+
active = Some(ActiveFence::new(&info));
|
|
54
|
+
}
|
|
55
|
+
Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => {
|
|
56
|
+
active = Some(ActiveFence::default_rust());
|
|
57
|
+
}
|
|
58
|
+
Event::Text(text) => {
|
|
59
|
+
if let Some(fence) = active.as_mut() {
|
|
60
|
+
fence.body.push_str(&text);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
Event::End(TagEnd::CodeBlock) => {
|
|
64
|
+
if let Some(fence) = active.take() {
|
|
65
|
+
if let Some(doctest) = fence.into_doctest(ordinal) {
|
|
66
|
+
out.push(doctest);
|
|
67
|
+
ordinal += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
_ => {}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
out
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
struct ActiveFence {
|
|
78
|
+
lang: String,
|
|
79
|
+
fence_attributes: Vec<String>,
|
|
80
|
+
body: String,
|
|
81
|
+
is_rust: bool,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
impl ActiveFence {
|
|
85
|
+
fn new(info: &str) -> Self {
|
|
86
|
+
let parts: Vec<String> = info
|
|
87
|
+
.split(',')
|
|
88
|
+
.map(str::trim)
|
|
89
|
+
.filter(|s| !s.is_empty())
|
|
90
|
+
.map(|s| s.to_string())
|
|
91
|
+
.collect();
|
|
92
|
+
if parts.is_empty() {
|
|
93
|
+
return Self {
|
|
94
|
+
lang: "rust".to_string(),
|
|
95
|
+
fence_attributes: Vec::new(),
|
|
96
|
+
body: String::new(),
|
|
97
|
+
is_rust: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
let first = &parts[0];
|
|
101
|
+
let first_lc = first.to_lowercase();
|
|
102
|
+
// Explicit non-rust language: not a doctest.
|
|
103
|
+
if NON_RUST_LANGS.contains(&first_lc.as_str()) {
|
|
104
|
+
return Self {
|
|
105
|
+
lang: first.clone(),
|
|
106
|
+
fence_attributes: parts[1..].to_vec(),
|
|
107
|
+
body: String::new(),
|
|
108
|
+
is_rust: false,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Explicit `rust` lang: drop it, keep the rest as attributes.
|
|
112
|
+
if first_lc == "rust" {
|
|
113
|
+
return Self {
|
|
114
|
+
lang: "rust".to_string(),
|
|
115
|
+
fence_attributes: parts[1..].to_vec(),
|
|
116
|
+
body: String::new(),
|
|
117
|
+
is_rust: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// First token is a known rust attribute (e.g. `no_run`): all tokens
|
|
121
|
+
// are attributes; implicit rust lang.
|
|
122
|
+
if KNOWN_FENCE_ATTRS.contains(&first_lc.as_str()) {
|
|
123
|
+
return Self {
|
|
124
|
+
lang: "rust".to_string(),
|
|
125
|
+
fence_attributes: parts.clone(),
|
|
126
|
+
body: String::new(),
|
|
127
|
+
is_rust: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Unknown first token (e.g. `mermaid`): treat as a foreign language,
|
|
131
|
+
// not a doctest.
|
|
132
|
+
Self {
|
|
133
|
+
lang: first.clone(),
|
|
134
|
+
fence_attributes: parts[1..].to_vec(),
|
|
135
|
+
body: String::new(),
|
|
136
|
+
is_rust: false,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fn default_rust() -> Self {
|
|
141
|
+
Self {
|
|
142
|
+
lang: "rust".to_string(),
|
|
143
|
+
fence_attributes: Vec::new(),
|
|
144
|
+
body: String::new(),
|
|
145
|
+
is_rust: true,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fn into_doctest(self, ordinal: u32) -> Option<Doctest> {
|
|
150
|
+
if !self.is_rust {
|
|
151
|
+
return None;
|
|
152
|
+
}
|
|
153
|
+
let (display_code, executable_code) = split_hidden_lines(&self.body);
|
|
154
|
+
let implicit_main_wrap = !contains_top_level_main(&executable_code);
|
|
155
|
+
Some(Doctest {
|
|
156
|
+
lang: self.lang,
|
|
157
|
+
fence_attributes: self.fence_attributes,
|
|
158
|
+
display_code,
|
|
159
|
+
executable_code,
|
|
160
|
+
implicit_main_wrap,
|
|
161
|
+
source: None,
|
|
162
|
+
ordinal,
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fn split_hidden_lines(body: &str) -> (String, String) {
|
|
168
|
+
let mut display = String::new();
|
|
169
|
+
let mut executable = String::new();
|
|
170
|
+
for line in body.lines() {
|
|
171
|
+
let trimmed = line.trim_start();
|
|
172
|
+
if let Some(rest) = trimmed.strip_prefix("# ") {
|
|
173
|
+
let indent_len = line.len() - trimmed.len();
|
|
174
|
+
executable.push_str(&line[..indent_len]);
|
|
175
|
+
executable.push_str(rest);
|
|
176
|
+
executable.push('\n');
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if trimmed == "#" {
|
|
180
|
+
executable.push('\n');
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if let Some(rest) = trimmed.strip_prefix("##") {
|
|
184
|
+
let indent_len = line.len() - trimmed.len();
|
|
185
|
+
display.push_str(&line[..indent_len]);
|
|
186
|
+
display.push('#');
|
|
187
|
+
display.push_str(rest);
|
|
188
|
+
display.push('\n');
|
|
189
|
+
executable.push_str(&line[..indent_len]);
|
|
190
|
+
executable.push('#');
|
|
191
|
+
executable.push_str(rest);
|
|
192
|
+
executable.push('\n');
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
display.push_str(line);
|
|
196
|
+
display.push('\n');
|
|
197
|
+
executable.push_str(line);
|
|
198
|
+
executable.push('\n');
|
|
199
|
+
}
|
|
200
|
+
(display, executable)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fn contains_top_level_main(code: &str) -> bool {
|
|
204
|
+
let mut depth: i32 = 0;
|
|
205
|
+
for line in code.lines() {
|
|
206
|
+
let trimmed = line.trim_start();
|
|
207
|
+
if depth == 0 && (trimmed.starts_with("fn main(") || trimmed.starts_with("fn main ")) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
for ch in line.chars() {
|
|
211
|
+
match ch {
|
|
212
|
+
'{' => depth += 1,
|
|
213
|
+
'}' => depth -= 1,
|
|
214
|
+
_ => {}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
false
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
#[cfg(test)]
|
|
222
|
+
mod tests {
|
|
223
|
+
use super::*;
|
|
224
|
+
|
|
225
|
+
#[test]
|
|
226
|
+
fn extracts_rust_fences() {
|
|
227
|
+
let docs = "Example\n\n```\nlet x = 1;\n```\n";
|
|
228
|
+
let doctests = extract_doctests(docs);
|
|
229
|
+
assert_eq!(doctests.len(), 1);
|
|
230
|
+
assert!(doctests[0].display_code.contains("let x = 1;"));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#[test]
|
|
234
|
+
fn skips_text_fences() {
|
|
235
|
+
let docs = "```text\nnot rust\n```\n";
|
|
236
|
+
assert!(extract_doctests(docs).is_empty());
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#[test]
|
|
240
|
+
fn captures_fence_attributes() {
|
|
241
|
+
let docs = "```rust,no_run,should_panic\nlet x = 1;\n```\n";
|
|
242
|
+
let doctests = extract_doctests(docs);
|
|
243
|
+
assert_eq!(doctests[0].fence_attributes, vec!["no_run", "should_panic"]);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#[test]
|
|
247
|
+
fn strips_hidden_lines_from_display() {
|
|
248
|
+
let docs = "```\n# use foo::Bar;\nlet x = Bar::new();\n```\n";
|
|
249
|
+
let doctests = extract_doctests(docs);
|
|
250
|
+
assert!(!doctests[0].display_code.contains("# use foo::Bar"));
|
|
251
|
+
assert!(doctests[0].display_code.contains("let x = Bar::new();"));
|
|
252
|
+
assert!(doctests[0].executable_code.contains("use foo::Bar;"));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#[test]
|
|
256
|
+
fn detects_explicit_main() {
|
|
257
|
+
let docs = "```\nfn main() {\n let _ = 1;\n}\n```\n";
|
|
258
|
+
let doctests = extract_doctests(docs);
|
|
259
|
+
assert!(!doctests[0].implicit_main_wrap);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#[test]
|
|
263
|
+
fn detects_implicit_main() {
|
|
264
|
+
let docs = "```\nlet x = 1;\nassert_eq!(x, 1);\n```\n";
|
|
265
|
+
let doctests = extract_doctests(docs);
|
|
266
|
+
assert!(doctests[0].implicit_main_wrap);
|
|
267
|
+
}
|
|
268
|
+
}
|