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,256 @@
|
|
|
1
|
+
use std::fs;
|
|
2
|
+
use std::io::Write;
|
|
3
|
+
use std::path::{Path, PathBuf};
|
|
4
|
+
use std::process::ExitCode;
|
|
5
|
+
|
|
6
|
+
use anyhow::{anyhow, Context, Result};
|
|
7
|
+
use clap::Parser;
|
|
8
|
+
use flate2::write::GzEncoder;
|
|
9
|
+
use flate2::Compression;
|
|
10
|
+
use rustdoc_json::Builder;
|
|
11
|
+
use rustdoc_types::Crate;
|
|
12
|
+
use time::OffsetDateTime;
|
|
13
|
+
|
|
14
|
+
use sourcey_rustdoc::diagnostics::{self, codes};
|
|
15
|
+
use sourcey_rustdoc::extract::{extract_crate, ExtractOptions};
|
|
16
|
+
use sourcey_rustdoc::spec::{Diagnostic, RustdocSpec, SOURCEY_RUSTDOC_VERSION, SPEC_VERSION};
|
|
17
|
+
|
|
18
|
+
const EXPECTED_FORMAT_VERSION: u32 = rustdoc_types::FORMAT_VERSION;
|
|
19
|
+
|
|
20
|
+
#[derive(Parser, Debug)]
|
|
21
|
+
#[command(version, about = "Convert nightly rustdoc JSON into a sourcey RustdocSpec snapshot.", long_about = None)]
|
|
22
|
+
struct Args {
|
|
23
|
+
/// Path to a Cargo manifest (Cargo.toml) or a workspace directory.
|
|
24
|
+
#[arg(long)]
|
|
25
|
+
manifest: PathBuf,
|
|
26
|
+
|
|
27
|
+
/// Crate names to include. Repeat to include multiple. If omitted, the
|
|
28
|
+
/// manifest's own package is used.
|
|
29
|
+
#[arg(long = "crate", value_name = "NAME")]
|
|
30
|
+
crates: Vec<String>,
|
|
31
|
+
|
|
32
|
+
/// Enable a feature. Repeat for multiple.
|
|
33
|
+
#[arg(long = "features", value_name = "NAME")]
|
|
34
|
+
features: Vec<String>,
|
|
35
|
+
|
|
36
|
+
/// Enable all features.
|
|
37
|
+
#[arg(long)]
|
|
38
|
+
all_features: bool,
|
|
39
|
+
|
|
40
|
+
/// Disable default features.
|
|
41
|
+
#[arg(long)]
|
|
42
|
+
no_default_features: bool,
|
|
43
|
+
|
|
44
|
+
/// Include `pub(crate)` and private items.
|
|
45
|
+
#[arg(long)]
|
|
46
|
+
include_private: bool,
|
|
47
|
+
|
|
48
|
+
/// Include items marked `#[doc(hidden)]`.
|
|
49
|
+
#[arg(long)]
|
|
50
|
+
include_hidden: bool,
|
|
51
|
+
|
|
52
|
+
/// Target triple to build docs for.
|
|
53
|
+
#[arg(long)]
|
|
54
|
+
target: Option<String>,
|
|
55
|
+
|
|
56
|
+
/// rustup toolchain name (default: nightly).
|
|
57
|
+
#[arg(long, default_value = "nightly")]
|
|
58
|
+
toolchain: String,
|
|
59
|
+
|
|
60
|
+
/// rustc cap-lints level (default: warn).
|
|
61
|
+
#[arg(long, default_value = "warn")]
|
|
62
|
+
cap_lints: String,
|
|
63
|
+
|
|
64
|
+
/// Output path; "-" for stdout.
|
|
65
|
+
#[arg(long, default_value = "-")]
|
|
66
|
+
output: String,
|
|
67
|
+
|
|
68
|
+
/// Fail (exit code 2) on any blocking diagnostic.
|
|
69
|
+
#[arg(long)]
|
|
70
|
+
strict: bool,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn main() -> ExitCode {
|
|
74
|
+
let args = Args::parse();
|
|
75
|
+
match run(args) {
|
|
76
|
+
Ok(exit) => exit,
|
|
77
|
+
Err(err) => {
|
|
78
|
+
eprintln!("sourcey-rustdoc: {err:#}");
|
|
79
|
+
ExitCode::from(2)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn run(args: Args) -> Result<ExitCode> {
|
|
85
|
+
let crate_names = resolve_crate_names(&args)?;
|
|
86
|
+
let mut crates = Vec::new();
|
|
87
|
+
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
|
88
|
+
|
|
89
|
+
for crate_name in &crate_names {
|
|
90
|
+
let json_path = build_rustdoc_json(&args, crate_name)
|
|
91
|
+
.with_context(|| format!("building rustdoc JSON for {crate_name}"))?;
|
|
92
|
+
let krate = load_rustdoc_json(&json_path)
|
|
93
|
+
.with_context(|| format!("loading rustdoc JSON for {crate_name}"))?;
|
|
94
|
+
if krate.format_version != EXPECTED_FORMAT_VERSION {
|
|
95
|
+
let msg = format!(
|
|
96
|
+
"rustdoc JSON format v{} produced by toolchain `{}`; this helper was built against v{}. Install `rustup toolchain install nightly-YYYY-MM-DD` to match.",
|
|
97
|
+
krate.format_version, args.toolchain, EXPECTED_FORMAT_VERSION
|
|
98
|
+
);
|
|
99
|
+
diagnostics.push(diagnostics::error(codes::FORMAT_VERSION_MISMATCH, msg));
|
|
100
|
+
if args.strict {
|
|
101
|
+
return Ok(ExitCode::from(2));
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let opts = ExtractOptions {
|
|
106
|
+
include_private: args.include_private,
|
|
107
|
+
include_hidden: args.include_hidden,
|
|
108
|
+
crate_name_hint: crate_name.clone(),
|
|
109
|
+
};
|
|
110
|
+
let result = extract_crate(&krate, &opts);
|
|
111
|
+
diagnostics.extend(result.diagnostics);
|
|
112
|
+
crates.push(result.crate_spec);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let has_error_diagnostic = diagnostics
|
|
116
|
+
.iter()
|
|
117
|
+
.any(|d| matches!(d.severity, sourcey_rustdoc::spec::DiagnosticSeverity::Error));
|
|
118
|
+
let snapshot = RustdocSpec {
|
|
119
|
+
version: SPEC_VERSION,
|
|
120
|
+
sourcey_rustdoc_version: SOURCEY_RUSTDOC_VERSION.to_string(),
|
|
121
|
+
rustdoc_format_version: EXPECTED_FORMAT_VERSION,
|
|
122
|
+
rust_toolchain: args.toolchain.clone(),
|
|
123
|
+
generated_at: now_rfc3339(),
|
|
124
|
+
crates,
|
|
125
|
+
diagnostics,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Compact JSON: large snapshots (multi-crate workspaces) benefit from
|
|
129
|
+
// dropping pretty-print whitespace. Gzip is layered on top when the
|
|
130
|
+
// output path ends in `.gz`.
|
|
131
|
+
let serialized =
|
|
132
|
+
serde_json::to_vec(&snapshot).context("serializing RustdocSpec to JSON")?;
|
|
133
|
+
write_output(&args.output, &serialized)?;
|
|
134
|
+
|
|
135
|
+
if args.strict && has_error_diagnostic {
|
|
136
|
+
return Ok(ExitCode::from(2));
|
|
137
|
+
}
|
|
138
|
+
Ok(ExitCode::SUCCESS)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
fn resolve_crate_names(args: &Args) -> Result<Vec<String>> {
|
|
142
|
+
if !args.crates.is_empty() {
|
|
143
|
+
return Ok(args.crates.clone());
|
|
144
|
+
}
|
|
145
|
+
// Read the package name from the manifest as a fallback.
|
|
146
|
+
let manifest = manifest_toml_path(&args.manifest)?;
|
|
147
|
+
let text = fs::read_to_string(&manifest)
|
|
148
|
+
.with_context(|| format!("reading {}", manifest.display()))?;
|
|
149
|
+
let parsed: toml::Value = toml::from_str(&text).context("parsing Cargo.toml")?;
|
|
150
|
+
if let Some(name) = parsed
|
|
151
|
+
.get("package")
|
|
152
|
+
.and_then(|p| p.get("name"))
|
|
153
|
+
.and_then(|n| n.as_str())
|
|
154
|
+
{
|
|
155
|
+
return Ok(vec![name.to_string()]);
|
|
156
|
+
}
|
|
157
|
+
Err(anyhow!(
|
|
158
|
+
"no --crate provided and no [package].name in {}",
|
|
159
|
+
manifest.display()
|
|
160
|
+
))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
fn manifest_toml_path(input: &PathBuf) -> Result<PathBuf> {
|
|
164
|
+
if input.is_file() {
|
|
165
|
+
return Ok(input.clone());
|
|
166
|
+
}
|
|
167
|
+
let joined = input.join("Cargo.toml");
|
|
168
|
+
if joined.is_file() {
|
|
169
|
+
return Ok(joined);
|
|
170
|
+
}
|
|
171
|
+
Err(anyhow!("no Cargo.toml at {}", input.display()))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fn build_rustdoc_json(args: &Args, crate_name: &str) -> Result<PathBuf> {
|
|
175
|
+
let manifest = manifest_toml_path(&args.manifest)?;
|
|
176
|
+
let mut builder = Builder::default()
|
|
177
|
+
.toolchain(&args.toolchain)
|
|
178
|
+
.manifest_path(&manifest)
|
|
179
|
+
.package(crate_name)
|
|
180
|
+
.cap_lints(Some(&args.cap_lints))
|
|
181
|
+
.document_private_items(args.include_private)
|
|
182
|
+
.quiet(true);
|
|
183
|
+
if args.all_features {
|
|
184
|
+
builder = builder.all_features(true);
|
|
185
|
+
}
|
|
186
|
+
if args.no_default_features {
|
|
187
|
+
builder = builder.no_default_features(true);
|
|
188
|
+
}
|
|
189
|
+
if !args.features.is_empty() {
|
|
190
|
+
builder = builder.features(args.features.iter().map(String::as_str));
|
|
191
|
+
}
|
|
192
|
+
if let Some(target) = &args.target {
|
|
193
|
+
builder = builder.target(target.clone());
|
|
194
|
+
}
|
|
195
|
+
let path = builder
|
|
196
|
+
.build()
|
|
197
|
+
.map_err(|err| anyhow!("rustdoc-json builder failed: {err}"))?;
|
|
198
|
+
Ok(path)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fn load_rustdoc_json(path: &PathBuf) -> Result<Crate> {
|
|
202
|
+
let bytes =
|
|
203
|
+
fs::read(path).with_context(|| format!("reading rustdoc JSON at {}", path.display()))?;
|
|
204
|
+
let krate: Crate = serde_json::from_slice(&bytes).context("parsing rustdoc JSON")?;
|
|
205
|
+
Ok(krate)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fn write_output(target: &str, contents: &[u8]) -> Result<()> {
|
|
209
|
+
if target == "-" {
|
|
210
|
+
let stdout = std::io::stdout();
|
|
211
|
+
let mut handle = stdout.lock();
|
|
212
|
+
handle.write_all(contents)?;
|
|
213
|
+
handle.write_all(b"\n")?;
|
|
214
|
+
return Ok(());
|
|
215
|
+
}
|
|
216
|
+
let path = PathBuf::from(target);
|
|
217
|
+
if let Some(parent) = path.parent() {
|
|
218
|
+
if !parent.as_os_str().is_empty() {
|
|
219
|
+
fs::create_dir_all(parent).with_context(|| format!("creating {}", parent.display()))?;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if is_gz_path(&path) {
|
|
223
|
+
let file = fs::File::create(&path)
|
|
224
|
+
.with_context(|| format!("creating {}", path.display()))?;
|
|
225
|
+
let mut encoder = GzEncoder::new(file, Compression::default());
|
|
226
|
+
encoder
|
|
227
|
+
.write_all(contents)
|
|
228
|
+
.with_context(|| format!("writing gzip stream to {}", path.display()))?;
|
|
229
|
+
encoder.finish().with_context(|| format!("finalising {}", path.display()))?;
|
|
230
|
+
} else {
|
|
231
|
+
fs::write(&path, contents).with_context(|| format!("writing {}", path.display()))?;
|
|
232
|
+
}
|
|
233
|
+
Ok(())
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
fn is_gz_path(path: &Path) -> bool {
|
|
237
|
+
path.extension()
|
|
238
|
+
.and_then(|e| e.to_str())
|
|
239
|
+
.is_some_and(|ext| ext.eq_ignore_ascii_case("gz"))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn now_rfc3339() -> String {
|
|
243
|
+
OffsetDateTime::now_utc()
|
|
244
|
+
.format(&time::format_description::well_known::Rfc3339)
|
|
245
|
+
.unwrap_or_else(|_| "0".to_string())
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[cfg(test)]
|
|
249
|
+
mod tests {
|
|
250
|
+
#[test]
|
|
251
|
+
fn format_version_constant_present() {
|
|
252
|
+
// Sentinel: confirms the helper's expected format version matches
|
|
253
|
+
// the pinned rustdoc-types dependency at compile time.
|
|
254
|
+
assert_eq!(super::EXPECTED_FORMAT_VERSION, rustdoc_types::FORMAT_VERSION);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
use rustdoc_types::{
|
|
2
|
+
DynTrait, FunctionSignature, GenericArg, GenericArgs, GenericBound, Generics as RdGenerics,
|
|
3
|
+
Path as RdPath, PreciseCapturingArg, Type, WherePredicate,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
use crate::links::LinkContext;
|
|
7
|
+
use crate::spec::{
|
|
8
|
+
GenericParam, GenericParamKind, Generics, SigInput, SigToken, Signature, TypePath,
|
|
9
|
+
};
|
|
10
|
+
use rustdoc_types::Type as RdType;
|
|
11
|
+
|
|
12
|
+
pub fn lower_signature(
|
|
13
|
+
sig: &FunctionSignature,
|
|
14
|
+
name: &str,
|
|
15
|
+
is_const: bool,
|
|
16
|
+
is_async: bool,
|
|
17
|
+
is_unsafe: bool,
|
|
18
|
+
links: &LinkContext<'_>,
|
|
19
|
+
) -> Signature {
|
|
20
|
+
let inputs: Vec<SigInput> = sig
|
|
21
|
+
.inputs
|
|
22
|
+
.iter()
|
|
23
|
+
.map(|(name, ty)| SigInput {
|
|
24
|
+
name: name.clone(),
|
|
25
|
+
type_display: render_type(ty),
|
|
26
|
+
})
|
|
27
|
+
.collect();
|
|
28
|
+
|
|
29
|
+
let output_display = sig.output.as_ref().map(render_type);
|
|
30
|
+
|
|
31
|
+
let mut tokens: Vec<SigToken> = Vec::new();
|
|
32
|
+
let kw = |text: &str| SigToken::Keyword { text: text.to_string() };
|
|
33
|
+
let punct = |text: &str| SigToken::Punct { text: text.to_string() };
|
|
34
|
+
if is_const {
|
|
35
|
+
tokens.push(kw("const"));
|
|
36
|
+
tokens.push(SigToken::Whitespace);
|
|
37
|
+
}
|
|
38
|
+
if is_async {
|
|
39
|
+
tokens.push(kw("async"));
|
|
40
|
+
tokens.push(SigToken::Whitespace);
|
|
41
|
+
}
|
|
42
|
+
if is_unsafe {
|
|
43
|
+
tokens.push(kw("unsafe"));
|
|
44
|
+
tokens.push(SigToken::Whitespace);
|
|
45
|
+
}
|
|
46
|
+
tokens.push(kw("fn"));
|
|
47
|
+
tokens.push(SigToken::Whitespace);
|
|
48
|
+
tokens.push(SigToken::Type {
|
|
49
|
+
text: name.to_string(),
|
|
50
|
+
target: None,
|
|
51
|
+
});
|
|
52
|
+
tokens.push(punct("("));
|
|
53
|
+
for (idx, ((input_name, input_ty), input)) in sig.inputs.iter().zip(inputs.iter()).enumerate() {
|
|
54
|
+
let _ = input_name;
|
|
55
|
+
if idx > 0 {
|
|
56
|
+
tokens.push(punct(","));
|
|
57
|
+
tokens.push(SigToken::Whitespace);
|
|
58
|
+
}
|
|
59
|
+
tokens.push(SigToken::Punct { text: input.name.clone() });
|
|
60
|
+
tokens.push(punct(":"));
|
|
61
|
+
tokens.push(SigToken::Whitespace);
|
|
62
|
+
push_type_tokens(&mut tokens, &resolve_type_path(input_ty, links));
|
|
63
|
+
}
|
|
64
|
+
if sig.is_c_variadic {
|
|
65
|
+
if !inputs.is_empty() {
|
|
66
|
+
tokens.push(punct(","));
|
|
67
|
+
tokens.push(SigToken::Whitespace);
|
|
68
|
+
}
|
|
69
|
+
tokens.push(punct("..."));
|
|
70
|
+
}
|
|
71
|
+
tokens.push(punct(")"));
|
|
72
|
+
|
|
73
|
+
if let Some(output_ty) = &sig.output {
|
|
74
|
+
tokens.push(SigToken::Whitespace);
|
|
75
|
+
tokens.push(punct("->"));
|
|
76
|
+
tokens.push(SigToken::Whitespace);
|
|
77
|
+
push_type_tokens(&mut tokens, &resolve_type_path(output_ty, links));
|
|
78
|
+
}
|
|
79
|
+
let _ = output_display;
|
|
80
|
+
|
|
81
|
+
let mut display = String::new();
|
|
82
|
+
for token in &tokens {
|
|
83
|
+
match token {
|
|
84
|
+
SigToken::Keyword { text }
|
|
85
|
+
| SigToken::Punct { text }
|
|
86
|
+
| SigToken::Generic { text } => display.push_str(text),
|
|
87
|
+
SigToken::Lifetime { text } => {
|
|
88
|
+
display.push('\'');
|
|
89
|
+
display.push_str(text);
|
|
90
|
+
}
|
|
91
|
+
SigToken::Type { text, .. } => display.push_str(text),
|
|
92
|
+
SigToken::Whitespace => display.push(' '),
|
|
93
|
+
SigToken::Newline => display.push('\n'),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Signature {
|
|
98
|
+
display,
|
|
99
|
+
tokens,
|
|
100
|
+
inputs,
|
|
101
|
+
output_display,
|
|
102
|
+
is_c_variadic: sig.is_c_variadic,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn push_type_tokens(out: &mut Vec<SigToken>, resolved: &ResolvedTypeDisplay) {
|
|
107
|
+
out.push(SigToken::Type {
|
|
108
|
+
text: resolved.text.clone(),
|
|
109
|
+
target: resolved.target.clone(),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
struct ResolvedTypeDisplay {
|
|
114
|
+
text: String,
|
|
115
|
+
target: Option<TypePath>,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
fn resolve_type_path(ty: &RdType, links: &LinkContext<'_>) -> ResolvedTypeDisplay {
|
|
119
|
+
let text = render_type(ty);
|
|
120
|
+
let target = type_path_target(ty, links);
|
|
121
|
+
ResolvedTypeDisplay { text, target }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
fn type_path_target(ty: &RdType, links: &LinkContext<'_>) -> Option<TypePath> {
|
|
125
|
+
match ty {
|
|
126
|
+
RdType::ResolvedPath(p) => {
|
|
127
|
+
let resolved = links.resolve_id(&p.id);
|
|
128
|
+
let display = p.path.clone();
|
|
129
|
+
match resolved {
|
|
130
|
+
Some(r) => Some(TypePath {
|
|
131
|
+
crate_id: r.crate_id,
|
|
132
|
+
path: r.path,
|
|
133
|
+
display,
|
|
134
|
+
external: r.external,
|
|
135
|
+
html_root_url: r.html_root_url,
|
|
136
|
+
}),
|
|
137
|
+
None => Some(TypePath {
|
|
138
|
+
crate_id: 0,
|
|
139
|
+
path: vec![p.path.clone()],
|
|
140
|
+
display,
|
|
141
|
+
external: false,
|
|
142
|
+
html_root_url: None,
|
|
143
|
+
}),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
RdType::BorrowedRef { type_, .. } | RdType::RawPointer { type_, .. } => {
|
|
147
|
+
type_path_target(type_, links)
|
|
148
|
+
}
|
|
149
|
+
_ => None,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
pub fn lower_generics(generics: &RdGenerics) -> Generics {
|
|
154
|
+
let params = generics
|
|
155
|
+
.params
|
|
156
|
+
.iter()
|
|
157
|
+
.map(|p| GenericParam {
|
|
158
|
+
name: p.name.clone(),
|
|
159
|
+
kind: match p.kind {
|
|
160
|
+
rustdoc_types::GenericParamDefKind::Lifetime { .. } => GenericParamKind::Lifetime,
|
|
161
|
+
rustdoc_types::GenericParamDefKind::Type { .. } => GenericParamKind::Type,
|
|
162
|
+
rustdoc_types::GenericParamDefKind::Const { .. } => GenericParamKind::Const,
|
|
163
|
+
},
|
|
164
|
+
default_display: generic_param_default(&p.kind),
|
|
165
|
+
bounds: generic_param_bounds(&p.kind),
|
|
166
|
+
})
|
|
167
|
+
.collect();
|
|
168
|
+
let where_predicates = generics
|
|
169
|
+
.where_predicates
|
|
170
|
+
.iter()
|
|
171
|
+
.map(render_where_predicate)
|
|
172
|
+
.collect();
|
|
173
|
+
Generics {
|
|
174
|
+
params,
|
|
175
|
+
where_predicates,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fn generic_param_default(kind: &rustdoc_types::GenericParamDefKind) -> Option<String> {
|
|
180
|
+
match kind {
|
|
181
|
+
rustdoc_types::GenericParamDefKind::Type { default, .. } => default.as_ref().map(render_type),
|
|
182
|
+
rustdoc_types::GenericParamDefKind::Const { default, .. } => default.clone(),
|
|
183
|
+
rustdoc_types::GenericParamDefKind::Lifetime { .. } => None,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fn generic_param_bounds(kind: &rustdoc_types::GenericParamDefKind) -> Vec<String> {
|
|
188
|
+
match kind {
|
|
189
|
+
rustdoc_types::GenericParamDefKind::Type { bounds, .. } => {
|
|
190
|
+
bounds.iter().map(render_bound).collect()
|
|
191
|
+
}
|
|
192
|
+
rustdoc_types::GenericParamDefKind::Lifetime { outlives, .. } => outlives.clone(),
|
|
193
|
+
rustdoc_types::GenericParamDefKind::Const { .. } => Vec::new(),
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn render_where_predicate(pred: &WherePredicate) -> String {
|
|
198
|
+
match pred {
|
|
199
|
+
WherePredicate::BoundPredicate {
|
|
200
|
+
type_, bounds, generic_params, ..
|
|
201
|
+
} => {
|
|
202
|
+
let mut s = String::new();
|
|
203
|
+
if !generic_params.is_empty() {
|
|
204
|
+
s.push_str("for<");
|
|
205
|
+
let parts: Vec<String> = generic_params.iter().map(|p| p.name.clone()).collect();
|
|
206
|
+
s.push_str(&parts.join(", "));
|
|
207
|
+
s.push_str("> ");
|
|
208
|
+
}
|
|
209
|
+
s.push_str(&render_type(type_));
|
|
210
|
+
if !bounds.is_empty() {
|
|
211
|
+
s.push_str(": ");
|
|
212
|
+
let parts: Vec<String> = bounds.iter().map(render_bound).collect();
|
|
213
|
+
s.push_str(&parts.join(" + "));
|
|
214
|
+
}
|
|
215
|
+
s
|
|
216
|
+
}
|
|
217
|
+
WherePredicate::LifetimePredicate { lifetime, outlives } => {
|
|
218
|
+
let mut s = lifetime.clone();
|
|
219
|
+
if !outlives.is_empty() {
|
|
220
|
+
s.push_str(": ");
|
|
221
|
+
s.push_str(&outlives.join(" + "));
|
|
222
|
+
}
|
|
223
|
+
s
|
|
224
|
+
}
|
|
225
|
+
WherePredicate::EqPredicate { lhs, rhs } => {
|
|
226
|
+
format!("{} = {}", render_type(lhs), render_term(rhs))
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
pub fn render_bound(bound: &GenericBound) -> String {
|
|
232
|
+
match bound {
|
|
233
|
+
GenericBound::TraitBound {
|
|
234
|
+
trait_,
|
|
235
|
+
generic_params,
|
|
236
|
+
modifier,
|
|
237
|
+
} => {
|
|
238
|
+
let mut s = String::new();
|
|
239
|
+
if !generic_params.is_empty() {
|
|
240
|
+
s.push_str("for<");
|
|
241
|
+
let parts: Vec<String> = generic_params.iter().map(|p| p.name.clone()).collect();
|
|
242
|
+
s.push_str(&parts.join(", "));
|
|
243
|
+
s.push_str("> ");
|
|
244
|
+
}
|
|
245
|
+
match modifier {
|
|
246
|
+
rustdoc_types::TraitBoundModifier::None => {}
|
|
247
|
+
rustdoc_types::TraitBoundModifier::Maybe => s.push('?'),
|
|
248
|
+
rustdoc_types::TraitBoundModifier::MaybeConst => s.push_str("~const "),
|
|
249
|
+
}
|
|
250
|
+
s.push_str(&render_path(trait_));
|
|
251
|
+
s
|
|
252
|
+
}
|
|
253
|
+
GenericBound::Outlives(lifetime) => lifetime.clone(),
|
|
254
|
+
GenericBound::Use(captures) => {
|
|
255
|
+
let parts: Vec<String> = captures
|
|
256
|
+
.iter()
|
|
257
|
+
.map(|c| match c {
|
|
258
|
+
PreciseCapturingArg::Lifetime(s) | PreciseCapturingArg::Param(s) => s.clone(),
|
|
259
|
+
})
|
|
260
|
+
.collect();
|
|
261
|
+
format!("use<{}>", parts.join(", "))
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fn render_term(term: &rustdoc_types::Term) -> String {
|
|
267
|
+
match term {
|
|
268
|
+
rustdoc_types::Term::Type(t) => render_type(t),
|
|
269
|
+
rustdoc_types::Term::Constant(c) => c.expr.clone(),
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
pub fn render_type(ty: &Type) -> String {
|
|
274
|
+
match ty {
|
|
275
|
+
Type::ResolvedPath(p) => render_path(p),
|
|
276
|
+
Type::DynTrait(d) => render_dyn(d),
|
|
277
|
+
Type::Generic(s) => s.clone(),
|
|
278
|
+
Type::Primitive(s) => s.clone(),
|
|
279
|
+
Type::FunctionPointer(f) => format!(
|
|
280
|
+
"fn({}){}",
|
|
281
|
+
f.sig
|
|
282
|
+
.inputs
|
|
283
|
+
.iter()
|
|
284
|
+
.map(|(_, t)| render_type(t))
|
|
285
|
+
.collect::<Vec<_>>()
|
|
286
|
+
.join(", "),
|
|
287
|
+
f.sig
|
|
288
|
+
.output
|
|
289
|
+
.as_ref()
|
|
290
|
+
.map(|t| format!(" -> {}", render_type(t)))
|
|
291
|
+
.unwrap_or_default()
|
|
292
|
+
),
|
|
293
|
+
Type::Tuple(parts) => {
|
|
294
|
+
let inner: Vec<String> = parts.iter().map(render_type).collect();
|
|
295
|
+
format!("({})", inner.join(", "))
|
|
296
|
+
}
|
|
297
|
+
Type::Slice(inner) => format!("[{}]", render_type(inner)),
|
|
298
|
+
Type::Array { type_, len } => format!("[{}; {}]", render_type(type_), len),
|
|
299
|
+
Type::Pat { type_, .. } => render_type(type_),
|
|
300
|
+
Type::ImplTrait(bounds) => {
|
|
301
|
+
let parts: Vec<String> = bounds.iter().map(render_bound).collect();
|
|
302
|
+
format!("impl {}", parts.join(" + "))
|
|
303
|
+
}
|
|
304
|
+
Type::Infer => "_".to_string(),
|
|
305
|
+
Type::RawPointer { is_mutable, type_ } => {
|
|
306
|
+
let m = if *is_mutable { "*mut " } else { "*const " };
|
|
307
|
+
format!("{}{}", m, render_type(type_))
|
|
308
|
+
}
|
|
309
|
+
Type::BorrowedRef {
|
|
310
|
+
lifetime,
|
|
311
|
+
is_mutable,
|
|
312
|
+
type_,
|
|
313
|
+
} => {
|
|
314
|
+
let lt = lifetime
|
|
315
|
+
.as_ref()
|
|
316
|
+
.map(|l| format!("{} ", l))
|
|
317
|
+
.unwrap_or_default();
|
|
318
|
+
let m = if *is_mutable { "mut " } else { "" };
|
|
319
|
+
format!("&{}{}{}", lt, m, render_type(type_))
|
|
320
|
+
}
|
|
321
|
+
Type::QualifiedPath {
|
|
322
|
+
name,
|
|
323
|
+
args,
|
|
324
|
+
self_type,
|
|
325
|
+
trait_,
|
|
326
|
+
..
|
|
327
|
+
} => {
|
|
328
|
+
let self_render = render_type(self_type);
|
|
329
|
+
let trait_render = trait_
|
|
330
|
+
.as_ref()
|
|
331
|
+
.map(|p| format!(" as {}", render_path(p)))
|
|
332
|
+
.unwrap_or_default();
|
|
333
|
+
let args_render = match args.as_ref() {
|
|
334
|
+
Some(a) => render_generic_args(a),
|
|
335
|
+
None => String::new(),
|
|
336
|
+
};
|
|
337
|
+
format!("<{}{}>::{}{}", self_render, trait_render, name, args_render)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
fn render_path(path: &RdPath) -> String {
|
|
343
|
+
let mut s = path.path.clone();
|
|
344
|
+
if let Some(args) = &path.args {
|
|
345
|
+
s.push_str(&render_generic_args(args));
|
|
346
|
+
}
|
|
347
|
+
s
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
fn render_generic_args(args: &GenericArgs) -> String {
|
|
351
|
+
match args {
|
|
352
|
+
GenericArgs::AngleBracketed { args, constraints } => {
|
|
353
|
+
let mut parts: Vec<String> = args
|
|
354
|
+
.iter()
|
|
355
|
+
.map(|a| match a {
|
|
356
|
+
GenericArg::Lifetime(s) => s.clone(),
|
|
357
|
+
GenericArg::Type(t) => render_type(t),
|
|
358
|
+
GenericArg::Const(c) => c.expr.clone(),
|
|
359
|
+
GenericArg::Infer => "_".to_string(),
|
|
360
|
+
})
|
|
361
|
+
.collect();
|
|
362
|
+
for c in constraints {
|
|
363
|
+
let term = match &c.binding {
|
|
364
|
+
rustdoc_types::AssocItemConstraintKind::Equality(t) => render_term(t),
|
|
365
|
+
rustdoc_types::AssocItemConstraintKind::Constraint(bounds) => {
|
|
366
|
+
let bs: Vec<String> = bounds.iter().map(render_bound).collect();
|
|
367
|
+
bs.join(" + ")
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
let sep = matches!(
|
|
371
|
+
c.binding,
|
|
372
|
+
rustdoc_types::AssocItemConstraintKind::Equality(_)
|
|
373
|
+
)
|
|
374
|
+
.then(|| "=".to_string())
|
|
375
|
+
.unwrap_or_else(|| ":".to_string());
|
|
376
|
+
parts.push(format!("{}{}{}", c.name, sep, term));
|
|
377
|
+
}
|
|
378
|
+
if parts.is_empty() {
|
|
379
|
+
String::new()
|
|
380
|
+
} else {
|
|
381
|
+
format!("<{}>", parts.join(", "))
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
GenericArgs::Parenthesized { inputs, output } => {
|
|
385
|
+
let in_render: Vec<String> = inputs.iter().map(render_type).collect();
|
|
386
|
+
let out_render = output
|
|
387
|
+
.as_ref()
|
|
388
|
+
.map(|t| format!(" -> {}", render_type(t)))
|
|
389
|
+
.unwrap_or_default();
|
|
390
|
+
format!("({}){}", in_render.join(", "), out_render)
|
|
391
|
+
}
|
|
392
|
+
GenericArgs::ReturnTypeNotation => "(..)".to_string(),
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
fn render_dyn(d: &DynTrait) -> String {
|
|
397
|
+
let parts: Vec<String> = d
|
|
398
|
+
.traits
|
|
399
|
+
.iter()
|
|
400
|
+
.map(|t| {
|
|
401
|
+
let mut s = render_path(&t.trait_);
|
|
402
|
+
if !t.generic_params.is_empty() {
|
|
403
|
+
let lts: Vec<String> = t.generic_params.iter().map(|p| p.name.clone()).collect();
|
|
404
|
+
s.push_str(&format!(" + for<{}>", lts.join(", ")));
|
|
405
|
+
}
|
|
406
|
+
s
|
|
407
|
+
})
|
|
408
|
+
.collect();
|
|
409
|
+
let lt = d
|
|
410
|
+
.lifetime
|
|
411
|
+
.as_ref()
|
|
412
|
+
.map(|l| format!(" + {}", l))
|
|
413
|
+
.unwrap_or_default();
|
|
414
|
+
format!("dyn {}{}", parts.join(" + "), lt)
|
|
415
|
+
}
|