From 5e67241faf86d37b6ace04e0004667785f984c08 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sat, 9 Mar 2024 15:43:34 +0000 Subject: [PATCH 01/27] add docgen cmd to crux cli and run cargo doc --- Cargo.lock | 199 +++++++++++++++++++++++++++++++++ crux_cli/Cargo.toml | 5 +- crux_cli/src/args.rs | 26 +++-- crux_cli/src/codegen.rs | 25 +++++ crux_cli/src/command_runner.rs | 68 +++++++++++ crux_cli/src/main.rs | 19 +++- 6 files changed, 328 insertions(+), 14 deletions(-) create mode 100644 crux_cli/src/codegen.rs create mode 100644 crux_cli/src/command_runner.rs diff --git a/Cargo.lock b/Cargo.lock index f505c138f..e1f2c7dec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -148,6 +163,21 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + [[package]] name = "base64" version = "0.13.1" @@ -197,6 +227,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + [[package]] name = "cc" version = "1.1.31" @@ -343,9 +379,12 @@ dependencies = [ "clap", "console", "ignore", + "libc", "ramhorns", "serde", "similar", + "tokio", + "tokio-fd", "toml", ] @@ -772,6 +811,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "glob" version = "0.3.1" @@ -823,6 +868,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "http-types-red-badger-temporary-fork" version = "2.12.0" @@ -1004,6 +1055,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -1049,6 +1110,27 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1058,6 +1140,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.20.2" @@ -1070,6 +1161,29 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1362,6 +1476,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + [[package]] name = "regex-automata" version = "0.4.8" @@ -1379,6 +1502,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustix" version = "0.38.37" @@ -1407,6 +1536,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.213" @@ -1511,6 +1646,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "similar" version = "2.6.0" @@ -1532,12 +1676,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "smawk" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1636,6 +1796,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-fd" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cedf0b897610a4baff98bf6116c060c5cfe7574d4339c50e9d23fe09377641d" +dependencies = [ + "libc", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "toml" version = "0.8.19" diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index d38e1ee41..7a1cf5740 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true repository.workspace = true license.workspace = true keywords.workspace = true -rust-version.workspace = true +rust-version = "1.70" [[bin]] name = "crux" @@ -19,6 +19,9 @@ clap = { version = "4.3.24", features = ["derive"] } console = "0.15.8" ignore = "0.4.23" ramhorns = "1.0.1" +libc = "0.2.155" serde = { workspace = true, features = ["derive"] } similar = { version = "2.6.0", features = ["inline"] } +tokio = { version = "1.37.0", features = ["full"] } +tokio-fd = "0.3.0" toml = "0.8.19" diff --git a/crux_cli/src/args.rs b/crux_cli/src/args.rs index 47c43f18e..f94e283ad 100644 --- a/crux_cli/src/args.rs +++ b/crux_cli/src/args.rs @@ -19,6 +19,21 @@ pub(crate) struct Cli { #[arg(long, short, action = ArgAction::Count)] pub verbose: u8, +} + +#[derive(Subcommand)] +pub(crate) enum Commands { + #[command(visible_alias = "doc")] + Doctor(DoctorArgs), + + #[command(visible_alias = "gen")] + Codegen(CodegenArgs), +} + +#[derive(Args)] +pub(crate) struct DoctorArgs { + #[arg(long, short)] + pub(crate) fix: Option, #[arg(long, short, default_value = "false")] pub include_source_code: bool, @@ -31,16 +46,11 @@ pub(crate) struct Cli { pub path: Option, } -#[derive(Subcommand)] -pub(crate) enum Commands { - #[command(visible_alias = "doc")] - Doctor(DoctorArgs), -} - #[derive(Args)] -pub(crate) struct DoctorArgs { +pub(crate) struct CodegenArgs { + /// path to the directory containing the Cargo.toml manifest #[arg(long, short)] - pub(crate) fix: Option, + pub path: PathBuf, } #[cfg(test)] diff --git a/crux_cli/src/codegen.rs b/crux_cli/src/codegen.rs new file mode 100644 index 000000000..efc89a60d --- /dev/null +++ b/crux_cli/src/codegen.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use std::io::{stdout, IsTerminal}; +use tokio::process::Command; + +use crate::{args::CodegenArgs, command_runner}; + +pub async fn codegen(args: &CodegenArgs) -> Result<()> { + let mut cmd = Command::new("cargo"); + cmd.env("RUSTC_BOOTSTRAP", "1") + .env( + "RUSTDOCFLAGS", + "-Z unstable-options --output-format=json --cap-lints=allow", + ) + .arg("doc") + .arg("--manifest-path") + .arg(&args.path) + .arg("--lib"); + if stdout().is_terminal() { + cmd.arg("--color=always"); + } + + command_runner::run(&mut cmd).await?; + + Ok(()) +} diff --git a/crux_cli/src/command_runner.rs b/crux_cli/src/command_runner.rs new file mode 100644 index 000000000..761b47b6d --- /dev/null +++ b/crux_cli/src/command_runner.rs @@ -0,0 +1,68 @@ +// inspired by @fasterthanlime's brilliant post https://fasterthanli.me/articles/a-terminal-case-of-linux +// and Jakub Kądziołka's great follow up https://compilercrim.es/amos-nerdsniped-me/ + +use anyhow::{bail, Result}; +use std::convert::TryFrom; +use tokio::{io::AsyncReadExt, process::Command}; +use tokio_fd::AsyncFd; + +pub async fn run(cmd: &mut Command) -> Result<()> { + let (primary_fd, secondary_fd) = open_terminal(); + + unsafe { + cmd.pre_exec(move || { + if libc::login_tty(secondary_fd) != 0 { + panic!("couldn't set the controlling terminal or something"); + } + Ok(()) + }) + }; + let mut child = cmd.spawn()?; + + let mut buf = vec![0u8; 1024]; + let mut primary = AsyncFd::try_from(primary_fd)?; + + loop { + tokio::select! { + n = primary.read(&mut buf) => { + let n = n?; + let slice = &buf[..n]; + + let s = std::str::from_utf8(slice)?; + print!("{}", s); + }, + + status = child.wait() => { + match status { + Ok(s) => { + if s.success() { + break; + } + bail!("command failed with {}", s) + } + Err(e) => bail!(e), + } + }, + } + } + + Ok(()) +} + +fn open_terminal() -> (i32, i32) { + let mut primary_fd: i32 = -1; + let mut secondary_fd: i32 = -1; + unsafe { + let ret = libc::openpty( + &mut primary_fd, + &mut secondary_fd, + std::ptr::null_mut(), + std::ptr::null_mut(), + std::ptr::null_mut(), + ); + if ret != 0 { + panic!("Failed to openpty!"); + } + }; + (primary_fd, secondary_fd) +} diff --git a/crux_cli/src/main.rs b/crux_cli/src/main.rs index 0155bc751..2443c6ab0 100644 --- a/crux_cli/src/main.rs +++ b/crux_cli/src/main.rs @@ -5,21 +5,30 @@ use clap::Parser; use args::Cli; mod args; +mod codegen; +mod command_runner; mod config; mod diff; mod doctor; mod template; mod workspace; -fn main() -> Result<()> { +#[tokio::main] +async fn main() -> Result<()> { let cli = Cli::parse(); match &cli.command { - Some(Commands::Doctor(DoctorArgs { .. })) => doctor::doctor( - &cli.template_dir, - cli.path.as_deref(), + Some(Commands::Doctor(DoctorArgs { + fix: _, + include_source_code, + template_dir, + path, + })) => doctor::doctor( + template_dir, + path.as_deref(), cli.verbose, - cli.include_source_code, + *include_source_code, ), + Some(Commands::Codegen(args)) => codegen::codegen(args).await, None => Ok(()), } } From e2b178288e54a8ca2452577b54d01f497e76c21f Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sun, 10 Mar 2024 09:18:17 +0000 Subject: [PATCH 02/27] use guppy to slurp workspace structure --- Cargo.lock | 179 ++++++++++++++++++++++++++++++++++++++++ crux_cli/Cargo.toml | 5 +- crux_cli/src/args.rs | 4 +- crux_cli/src/codegen.rs | 53 ++++++++++-- crux_cli/src/graph.rs | 8 ++ crux_cli/src/main.rs | 1 + 6 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 crux_cli/src/graph.rs diff --git a/Cargo.lock b/Cargo.lock index e1f2c7dec..b341849d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,19 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom 0.2.15", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -233,6 +246,38 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cc" version = "1.1.31" @@ -242,6 +287,16 @@ dependencies = [ "shlex", ] +[[package]] +name = "cfg-expr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -378,10 +433,13 @@ dependencies = [ "anyhow", "clap", "console", + "guppy", "ignore", "libc", "ramhorns", + "rustdoc-types", "serde", + "serde_json", "similar", "tokio", "tokio-fd", @@ -520,6 +578,12 @@ dependencies = [ "syn 2.0.82", ] +[[package]] +name = "debug-ignore" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" + [[package]] name = "derive_builder" version = "0.20.2" @@ -571,6 +635,12 @@ dependencies = [ "serde", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -654,6 +724,12 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fnv" version = "1.0.7" @@ -847,6 +923,39 @@ dependencies = [ "walkdir", ] +[[package]] +name = "guppy" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bff2f6a9d515cf6453282af93363f93bdf570792a6f4f619756e46696d773fa" +dependencies = [ + "ahash", + "camino", + "cargo_metadata", + "cfg-if", + "debug-ignore", + "fixedbitset", + "guppy-workspace-hack", + "indexmap", + "itertools", + "nested", + "once_cell", + "pathdiff", + "petgraph", + "semver", + "serde", + "serde_json", + "smallvec", + "static_assertions", + "target-spec", +] + +[[package]] +name = "guppy-workspace-hack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" + [[package]] name = "hashbrown" version = "0.15.0" @@ -1016,6 +1125,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1131,6 +1249,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nested" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b420f638f07fe83056b55ea190bb815f609ec5a35e7017884a10f78839c9e" + [[package]] name = "num-traits" version = "0.2.19" @@ -1184,12 +1308,31 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +dependencies = [ + "camino", +] + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.10.1" @@ -1508,6 +1651,15 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustdoc-types" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0904b9147011800e63763fb9e49bbeaf76c1b6ab8982824c659dce5433712559" +dependencies = [ + "serde", +] + [[package]] name = "rustix" version = "0.38.37" @@ -1542,6 +1694,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + [[package]] name = "serde" version = "1.0.213" @@ -1732,6 +1893,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "target-spec" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c5743abbf7bc7d5296ae61368b50cd218ac09432281cb5d48b97308d5c27909" +dependencies = [ + "cfg-expr", + "guppy-workspace-hack", + "target-lexicon", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.13.0" diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index 7a1cf5740..2b346720e 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -17,11 +17,14 @@ path = "src/main.rs" anyhow.workspace = true clap = { version = "4.3.24", features = ["derive"] } console = "0.15.8" +guppy = "0.17.5" ignore = "0.4.23" -ramhorns = "1.0.1" libc = "0.2.155" +ramhorns = "1.0.0" +rustdoc-types = "0.24.0" serde = { workspace = true, features = ["derive"] } similar = { version = "2.6.0", features = ["inline"] } +serde_json = "1.0.114" tokio = { version = "1.37.0", features = ["full"] } tokio-fd = "0.3.0" toml = "0.8.19" diff --git a/crux_cli/src/args.rs b/crux_cli/src/args.rs index f94e283ad..70aabd562 100644 --- a/crux_cli/src/args.rs +++ b/crux_cli/src/args.rs @@ -48,9 +48,9 @@ pub(crate) struct DoctorArgs { #[derive(Args)] pub(crate) struct CodegenArgs { - /// path to the directory containing the Cargo.toml manifest + /// name of the library containing your Crux App #[arg(long, short)] - pub path: PathBuf, + pub lib: String, } #[cfg(test)] diff --git a/crux_cli/src/codegen.rs b/crux_cli/src/codegen.rs index efc89a60d..3b9316e13 100644 --- a/crux_cli/src/codegen.rs +++ b/crux_cli/src/codegen.rs @@ -1,10 +1,20 @@ -use anyhow::Result; -use std::io::{stdout, IsTerminal}; -use tokio::process::Command; +use anyhow::{bail, Result}; +use rustdoc_types::{Crate, Impl, ItemEnum, Path}; +use std::{ + fs::File, + io::{stdout, IsTerminal}, +}; +use tokio::{process::Command, task::spawn_blocking}; -use crate::{args::CodegenArgs, command_runner}; +use crate::{args::CodegenArgs, command_runner, graph}; pub async fn codegen(args: &CodegenArgs) -> Result<()> { + let graph = graph::compute_package_graph()?; + + let Ok(lib) = graph.workspace().member_by_path(&args.lib) else { + bail!("Could not find workspace package with path {}", args.lib) + }; + let mut cmd = Command::new("cargo"); cmd.env("RUSTC_BOOTSTRAP", "1") .env( @@ -12,8 +22,9 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { "-Z unstable-options --output-format=json --cap-lints=allow", ) .arg("doc") + .arg("--no-deps") .arg("--manifest-path") - .arg(&args.path) + .arg(lib.manifest_path()) .arg("--lib"); if stdout().is_terminal() { cmd.arg("--color=always"); @@ -21,5 +32,37 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { command_runner::run(&mut cmd).await?; + let target_directory = graph.workspace().target_directory().as_std_path(); + let json_path = target_directory + .join("doc") + .join(format!("{}.json", lib.name().replace("-", "_"))); + + let rustdoc: Crate = spawn_blocking(move || { + let file = File::open(&json_path)?; + let crate_: Crate = serde_json::from_reader(file)?; + Ok::(crate_) + }) + .await??; + + if let Some(name) = rustdoc.index.iter().find_map(|(_k, v)| { + if let ItemEnum::Impl(Impl { + trait_: Some(rustdoc_types::Path { + name: trait_name, .. + }), + for_: rustdoc_types::Type::ResolvedPath(Path { name, .. }), + .. + }) = &v.inner + { + (trait_name == &"App".to_string()).then(|| name) + } else { + None + } + }) { + println!( + "The struct that implements crux_core::App is called {:?}", + name + ); + } + Ok(()) } diff --git a/crux_cli/src/graph.rs b/crux_cli/src/graph.rs new file mode 100644 index 000000000..90cc84ec0 --- /dev/null +++ b/crux_cli/src/graph.rs @@ -0,0 +1,8 @@ +use anyhow::{anyhow, Result}; +use guppy::{graph::PackageGraph, MetadataCommand}; + +pub(crate) fn compute_package_graph() -> Result { + let mut cmd = MetadataCommand::new(); + let package_graph = PackageGraph::from_command(&mut cmd); + package_graph.map_err(|e| anyhow!(e)) +} diff --git a/crux_cli/src/main.rs b/crux_cli/src/main.rs index 2443c6ab0..453962559 100644 --- a/crux_cli/src/main.rs +++ b/crux_cli/src/main.rs @@ -10,6 +10,7 @@ mod command_runner; mod config; mod diff; mod doctor; +mod graph; mod template; mod workspace; From d2fa4593a60c003a9db0f7c51bef3564932c53f6 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sat, 16 Mar 2024 12:01:14 +0000 Subject: [PATCH 03/27] rebase on master conflicts From 9ca8539be8e56ffa4107a244c8a036883ad2ca5f Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sat, 16 Mar 2024 12:03:25 +0000 Subject: [PATCH 04/27] clippy --- crux_cli/src/codegen.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/crux_cli/src/codegen.rs b/crux_cli/src/codegen.rs index 3b9316e13..6d20fd5b7 100644 --- a/crux_cli/src/codegen.rs +++ b/crux_cli/src/codegen.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use rustdoc_types::{Crate, Impl, ItemEnum, Path}; +use rustdoc_types::{Crate, Impl, ItemEnum, Path, Type}; use std::{ fs::File, io::{stdout, IsTerminal}, @@ -35,33 +35,49 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { let target_directory = graph.workspace().target_directory().as_std_path(); let json_path = target_directory .join("doc") - .join(format!("{}.json", lib.name().replace("-", "_"))); + .join(format!("{}.json", lib.name().replace('-', "_"))); - let rustdoc: Crate = spawn_blocking(move || { - let file = File::open(&json_path)?; + let crate_: Crate = spawn_blocking(move || { + let file = File::open(json_path)?; let crate_: Crate = serde_json::from_reader(file)?; Ok::(crate_) }) .await??; - if let Some(name) = rustdoc.index.iter().find_map(|(_k, v)| { + if let Some((id, items)) = crate_.index.iter().find_map(|(_k, v)| { if let ItemEnum::Impl(Impl { trait_: Some(rustdoc_types::Path { name: trait_name, .. }), - for_: rustdoc_types::Type::ResolvedPath(Path { name, .. }), + for_: rustdoc_types::Type::ResolvedPath(Path { id, .. }), + items, .. }) = &v.inner { - (trait_name == &"App".to_string()).then(|| name) + (trait_name.as_str() == "App").then_some((id, items)) } else { None } }) { println!( - "The struct that implements crux_core::App is called {:?}", - name + "The struct that implements crux_core::App is {}", + crate_.paths[id].path.join("::") ); + + for item in items { + let assoc = &crate_.index[item]; + for name in &["Event", "ViewModel", "Capabilities"] { + if assoc.name == Some(name.to_string()) { + if let ItemEnum::AssocType { + default: Some(Type::ResolvedPath(path)), + .. + } = &assoc.inner + { + println!("{name} type is {}", crate_.paths[&path.id].path.join("::")) + } + } + } + } } Ok(()) From b48bf18e444997d8d285a64b9474f50db4b6a7a2 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Wed, 29 May 2024 14:08:54 +0100 Subject: [PATCH 05/27] start discovery --- crux_cli/src/codegen.rs | 193 ++++++++++++++++++++++++----- crux_cli/src/graph.rs | 6 +- examples/counter/shared/src/app.rs | 6 +- examples/notes/shared/src/app.rs | 5 +- 4 files changed, 171 insertions(+), 39 deletions(-) diff --git a/crux_cli/src/codegen.rs b/crux_cli/src/codegen.rs index 6d20fd5b7..d1dfb274b 100644 --- a/crux_cli/src/codegen.rs +++ b/crux_cli/src/codegen.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use rustdoc_types::{Crate, Impl, ItemEnum, Path, Type}; +use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind}; use std::{ fs::File, io::{stdout, IsTerminal}, @@ -37,48 +37,181 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { .join("doc") .join(format!("{}.json", lib.name().replace('-', "_"))); - let crate_: Crate = spawn_blocking(move || { + let crate_: Crate = spawn_blocking(move || -> Result { let file = File::open(json_path)?; - let crate_: Crate = serde_json::from_reader(file)?; - Ok::(crate_) + let crate_ = serde_json::from_reader(file)?; + Ok(crate_) }) .await??; - if let Some((id, items)) = crate_.index.iter().find_map(|(_k, v)| { - if let ItemEnum::Impl(Impl { - trait_: Some(rustdoc_types::Path { - name: trait_name, .. - }), - for_: rustdoc_types::Type::ResolvedPath(Path { id, .. }), - items, - .. - }) = &v.inner - { - (trait_name.as_str() == "App").then_some((id, items)) - } else { - None + for (id, associated_items) in find_impls(&crate_, "Effect", &["Ffi"]) { + println!( + "\nThe struct that implements crux_core::Effect is {}", + crate_.paths[id].path.join("::") + ); + + for (name, id) in associated_items { + visit(0, name, id, &crate_)?; } - }) { + } + println!(); + for (id, associated_items) in find_impls(&crate_, "App", &["Event", "ViewModel"]) { println!( - "The struct that implements crux_core::App is {}", + "\nThe struct that implements crux_core::App is {}", crate_.paths[id].path.join("::") ); - for item in items { - let assoc = &crate_.index[item]; - for name in &["Event", "ViewModel", "Capabilities"] { - if assoc.name == Some(name.to_string()) { - if let ItemEnum::AssocType { - default: Some(Type::ResolvedPath(path)), - .. - } = &assoc.inner - { - println!("{name} type is {}", crate_.paths[&path.id].path.join("::")) + for (name, id) in associated_items { + visit(0, name, id, &crate_)?; + } + } + + Ok(()) +} + +fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { + let item = crate_.index.get(id); + + print!( + "\n{level} {id:18} {} {name:20} ", + " ".repeat(level * 4), + id = format!("{:?}", id) + ); + + if let Some(summary) = crate_.paths.get(id) { + let path_str = summary.path.join("::"); + print!("{path_str}"); + } + + if let Some(item) = item { + match &item.inner { + ItemEnum::Struct(ref struct_) => match &struct_.kind { + StructKind::Unit => { + print!("unit struct"); + } + StructKind::Tuple(fields) => { + print!("tuple struct: {fields:?}"); + } + StructKind::Plain { + fields, + fields_stripped, + } => { + if *fields_stripped { + anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); + } + for id in fields { + let item = &crate_.index[id]; + if let Some(name) = &item.name { + visit(level + 1, name, id, crate_)?; + } + } + } + }, + ItemEnum::Enum(ref enum_) => { + for id in &enum_.variants { + let item = &crate_.index[id]; + if let Some(name) = &item.name { + visit(level + 1, name, id, crate_)?; } } } + ItemEnum::StructField(Type::ResolvedPath(path)) => { + visit(level, name, &path.id, crate_)?; + } + ItemEnum::StructField(Type::Primitive(name)) => { + print!("{name}"); + } + ItemEnum::Module(_) => (), + ItemEnum::ExternCrate { .. } => (), + ItemEnum::Import(_) => (), + ItemEnum::Union(_) => (), + ItemEnum::Variant(v) => match &v.kind { + VariantKind::Plain => {} + VariantKind::Tuple(fields) => { + for id in fields { + let Some(id) = id else { continue }; + let item = &crate_.index[id]; + if let Some(name) = &item.name { + visit(level + 1, name, id, crate_)?; + } + } + } + VariantKind::Struct { + fields, + fields_stripped, + } => { + if *fields_stripped { + anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); + } + for id in fields { + let item = &crate_.index[id]; + if let Some(name) = &item.name { + visit(level + 1, name, id, crate_)?; + } + } + } + }, + ItemEnum::Function(_) => (), + ItemEnum::Trait(_) => (), + ItemEnum::TraitAlias(_) => (), + ItemEnum::Impl(_) => (), + ItemEnum::TypeAlias(_) => (), + ItemEnum::OpaqueTy(_) => (), + ItemEnum::Constant(_) => (), + ItemEnum::Static(_) => (), + ItemEnum::ForeignType => (), + ItemEnum::Macro(_) => (), + ItemEnum::ProcMacro(_) => (), + ItemEnum::Primitive(_) => (), + ItemEnum::AssocConst { .. } => (), + ItemEnum::AssocType { .. } => (), + _ => (), } } - Ok(()) } + +fn find_impls<'a>( + crate_: &'a Crate, + trait_name: &'a str, + filter: &'a [&'a str], +) -> impl Iterator)> { + crate_.index.iter().filter_map(move |(_k, v)| { + if let ItemEnum::Impl(Impl { + trait_: Some(Path { name, .. }), + for_: Type::ResolvedPath(Path { id, .. }), + items, + .. + }) = &v.inner + { + if name.as_str() == trait_name { + let assoc_types = items + .iter() + .filter_map(|id| { + let item = &crate_.index[id]; + item.name.as_deref().and_then(|name| { + if filter.contains(&name) { + if let ItemEnum::AssocType { + default: Some(Type::ResolvedPath(Path { id, .. })), + .. + } = &item.inner + { + Some((name, id)) + } else { + None + } + } else { + None + } + }) + }) + .collect(); + Some((id, assoc_types)) + } else { + None + } + } else { + None + } + }) +} diff --git a/crux_cli/src/graph.rs b/crux_cli/src/graph.rs index 90cc84ec0..4cd1efa08 100644 --- a/crux_cli/src/graph.rs +++ b/crux_cli/src/graph.rs @@ -1,8 +1,8 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use guppy::{graph::PackageGraph, MetadataCommand}; pub(crate) fn compute_package_graph() -> Result { let mut cmd = MetadataCommand::new(); - let package_graph = PackageGraph::from_command(&mut cmd); - package_graph.map_err(|e| anyhow!(e)) + let package_graph = PackageGraph::from_command(&mut cmd)?; + Ok(package_graph) } diff --git a/examples/counter/shared/src/app.rs b/examples/counter/shared/src/app.rs index c4352fb4f..9c25de57d 100644 --- a/examples/counter/shared/src/app.rs +++ b/examples/counter/shared/src/app.rs @@ -45,9 +45,9 @@ pub enum Event { #[cfg_attr(feature = "typegen", derive(crux_core::macros::Export))] #[derive(crux_core::macros::Effect)] pub struct Capabilities { - pub render: Render, - pub http: Http, - pub sse: ServerSentEvents, + render: Render, + http: Http, + sse: ServerSentEvents, } #[derive(Default)] diff --git a/examples/notes/shared/src/app.rs b/examples/notes/shared/src/app.rs index 252c09210..3aa8311cb 100644 --- a/examples/notes/shared/src/app.rs +++ b/examples/notes/shared/src/app.rs @@ -88,11 +88,10 @@ pub struct Model { edit_timer: EditTimer, } -// Same as Model for now, but may change #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct ViewModel { - text: String, - cursor: TextCursor, + pub text: String, + pub cursor: TextCursor, } impl From<&Model> for ViewModel { From 438f2b302bfa41ef7e519a616636228374b924e8 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 31 May 2024 09:18:56 +0100 Subject: [PATCH 06/27] first stab at generics --- crux_cli/src/codegen.rs | 80 ++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/crux_cli/src/codegen.rs b/crux_cli/src/codegen.rs index d1dfb274b..24c8db24e 100644 --- a/crux_cli/src/codegen.rs +++ b/crux_cli/src/codegen.rs @@ -1,5 +1,7 @@ use anyhow::{bail, Result}; -use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind}; +use rustdoc_types::{ + Crate, GenericArg, GenericArgs, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind, +}; use std::{ fs::File, io::{stdout, IsTerminal}, @@ -51,7 +53,7 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { ); for (name, id) in associated_items { - visit(0, name, id, &crate_)?; + visit_item(0, name, id, &crate_)?; } } println!(); @@ -62,16 +64,14 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { ); for (name, id) in associated_items { - visit(0, name, id, &crate_)?; + visit_item(0, name, id, &crate_)?; } } Ok(()) } -fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { - let item = crate_.index.get(id); - +fn visit_item(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { print!( "\n{level} {id:18} {} {name:20} ", " ".repeat(level * 4), @@ -83,7 +83,7 @@ fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { print!("{path_str}"); } - if let Some(item) = item { + if let Some(item) = crate_.index.get(id) { match &item.inner { ItemEnum::Struct(ref struct_) => match &struct_.kind { StructKind::Unit => { @@ -102,7 +102,7 @@ fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { for id in fields { let item = &crate_.index[id]; if let Some(name) = &item.name { - visit(level + 1, name, id, crate_)?; + visit_item(level + 1, name, id, crate_)?; } } } @@ -111,15 +111,12 @@ fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { for id in &enum_.variants { let item = &crate_.index[id]; if let Some(name) = &item.name { - visit(level + 1, name, id, crate_)?; + visit_item(level + 1, name, id, crate_)?; } } } - ItemEnum::StructField(Type::ResolvedPath(path)) => { - visit(level, name, &path.id, crate_)?; - } - ItemEnum::StructField(Type::Primitive(name)) => { - print!("{name}"); + ItemEnum::StructField(ty) => { + visit_type(level, "", ty, crate_)?; } ItemEnum::Module(_) => (), ItemEnum::ExternCrate { .. } => (), @@ -132,7 +129,7 @@ fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { let Some(id) = id else { continue }; let item = &crate_.index[id]; if let Some(name) = &item.name { - visit(level + 1, name, id, crate_)?; + visit_item(level + 1, name, id, crate_)?; } } } @@ -146,7 +143,7 @@ fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { for id in fields { let item = &crate_.index[id]; if let Some(name) = &item.name { - visit(level + 1, name, id, crate_)?; + visit_item(level + 1, name, id, crate_)?; } } } @@ -165,12 +162,61 @@ fn visit(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { ItemEnum::Primitive(_) => (), ItemEnum::AssocConst { .. } => (), ItemEnum::AssocType { .. } => (), - _ => (), } } Ok(()) } +fn visit_type(level: usize, name: &str, ty: &Type, crate_: &Crate) -> Result<()> { + match ty { + Type::ResolvedPath(path) => { + visit_item(level + 1, name, &path.id, crate_)?; + if let Some(args) = &path.args { + match args.as_ref() { + GenericArgs::AngleBracketed { args, bindings: _ } => { + for (i, arg) in args.iter().enumerate() { + match arg { + GenericArg::Lifetime(_) => todo!(), + GenericArg::Type(ty) => { + print!(" "); + visit_type(level, &i.to_string(), ty, crate_)?; + } + GenericArg::Const(_) => todo!(), + GenericArg::Infer => todo!(), + } + } + } + GenericArgs::Parenthesized { .. } => (), + } + } + } + Type::DynTrait(_) => (), + Type::Generic(s) => print!("{s}"), + Type::Primitive(name) => { + print!("{name}"); + } + Type::FunctionPointer(_) => (), + Type::Tuple(types) => { + for (i, ty) in types.iter().enumerate() { + visit_type(level, &i.to_string(), ty, crate_)?; + } + } + Type::Slice(_) => (), + Type::Array { type_: _, len: _ } => (), + Type::ImplTrait(_) => (), + Type::Infer => (), + Type::RawPointer { .. } => (), + Type::BorrowedRef { .. } => (), + Type::QualifiedPath { + name: _, + args: _, + self_type: _, + trait_: _, + } => (), + } + Ok(()) +} + fn find_impls<'a>( crate_: &'a Crate, trait_name: &'a str, From d35b03508ff26a37c3315d8a370a382badea1ff9 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 31 May 2024 11:45:05 +0100 Subject: [PATCH 07/27] begin introducing output types WIP --- crux_cli/src/codegen.rs | 263 ----------------------- crux_cli/src/{ => codegen}/graph.rs | 0 crux_cli/src/codegen/mod.rs | 55 +++++ crux_cli/src/codegen/parser.rs | 308 +++++++++++++++++++++++++++ crux_cli/src/codegen/rust_types.rs | 311 ++++++++++++++++++++++++++++ crux_cli/src/main.rs | 1 - 6 files changed, 674 insertions(+), 264 deletions(-) delete mode 100644 crux_cli/src/codegen.rs rename crux_cli/src/{ => codegen}/graph.rs (100%) create mode 100644 crux_cli/src/codegen/mod.rs create mode 100644 crux_cli/src/codegen/parser.rs create mode 100644 crux_cli/src/codegen/rust_types.rs diff --git a/crux_cli/src/codegen.rs b/crux_cli/src/codegen.rs deleted file mode 100644 index 24c8db24e..000000000 --- a/crux_cli/src/codegen.rs +++ /dev/null @@ -1,263 +0,0 @@ -use anyhow::{bail, Result}; -use rustdoc_types::{ - Crate, GenericArg, GenericArgs, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind, -}; -use std::{ - fs::File, - io::{stdout, IsTerminal}, -}; -use tokio::{process::Command, task::spawn_blocking}; - -use crate::{args::CodegenArgs, command_runner, graph}; - -pub async fn codegen(args: &CodegenArgs) -> Result<()> { - let graph = graph::compute_package_graph()?; - - let Ok(lib) = graph.workspace().member_by_path(&args.lib) else { - bail!("Could not find workspace package with path {}", args.lib) - }; - - let mut cmd = Command::new("cargo"); - cmd.env("RUSTC_BOOTSTRAP", "1") - .env( - "RUSTDOCFLAGS", - "-Z unstable-options --output-format=json --cap-lints=allow", - ) - .arg("doc") - .arg("--no-deps") - .arg("--manifest-path") - .arg(lib.manifest_path()) - .arg("--lib"); - if stdout().is_terminal() { - cmd.arg("--color=always"); - } - - command_runner::run(&mut cmd).await?; - - let target_directory = graph.workspace().target_directory().as_std_path(); - let json_path = target_directory - .join("doc") - .join(format!("{}.json", lib.name().replace('-', "_"))); - - let crate_: Crate = spawn_blocking(move || -> Result { - let file = File::open(json_path)?; - let crate_ = serde_json::from_reader(file)?; - Ok(crate_) - }) - .await??; - - for (id, associated_items) in find_impls(&crate_, "Effect", &["Ffi"]) { - println!( - "\nThe struct that implements crux_core::Effect is {}", - crate_.paths[id].path.join("::") - ); - - for (name, id) in associated_items { - visit_item(0, name, id, &crate_)?; - } - } - println!(); - for (id, associated_items) in find_impls(&crate_, "App", &["Event", "ViewModel"]) { - println!( - "\nThe struct that implements crux_core::App is {}", - crate_.paths[id].path.join("::") - ); - - for (name, id) in associated_items { - visit_item(0, name, id, &crate_)?; - } - } - - Ok(()) -} - -fn visit_item(level: usize, name: &str, id: &Id, crate_: &Crate) -> Result<()> { - print!( - "\n{level} {id:18} {} {name:20} ", - " ".repeat(level * 4), - id = format!("{:?}", id) - ); - - if let Some(summary) = crate_.paths.get(id) { - let path_str = summary.path.join("::"); - print!("{path_str}"); - } - - if let Some(item) = crate_.index.get(id) { - match &item.inner { - ItemEnum::Struct(ref struct_) => match &struct_.kind { - StructKind::Unit => { - print!("unit struct"); - } - StructKind::Tuple(fields) => { - print!("tuple struct: {fields:?}"); - } - StructKind::Plain { - fields, - fields_stripped, - } => { - if *fields_stripped { - anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); - } - for id in fields { - let item = &crate_.index[id]; - if let Some(name) = &item.name { - visit_item(level + 1, name, id, crate_)?; - } - } - } - }, - ItemEnum::Enum(ref enum_) => { - for id in &enum_.variants { - let item = &crate_.index[id]; - if let Some(name) = &item.name { - visit_item(level + 1, name, id, crate_)?; - } - } - } - ItemEnum::StructField(ty) => { - visit_type(level, "", ty, crate_)?; - } - ItemEnum::Module(_) => (), - ItemEnum::ExternCrate { .. } => (), - ItemEnum::Import(_) => (), - ItemEnum::Union(_) => (), - ItemEnum::Variant(v) => match &v.kind { - VariantKind::Plain => {} - VariantKind::Tuple(fields) => { - for id in fields { - let Some(id) = id else { continue }; - let item = &crate_.index[id]; - if let Some(name) = &item.name { - visit_item(level + 1, name, id, crate_)?; - } - } - } - VariantKind::Struct { - fields, - fields_stripped, - } => { - if *fields_stripped { - anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); - } - for id in fields { - let item = &crate_.index[id]; - if let Some(name) = &item.name { - visit_item(level + 1, name, id, crate_)?; - } - } - } - }, - ItemEnum::Function(_) => (), - ItemEnum::Trait(_) => (), - ItemEnum::TraitAlias(_) => (), - ItemEnum::Impl(_) => (), - ItemEnum::TypeAlias(_) => (), - ItemEnum::OpaqueTy(_) => (), - ItemEnum::Constant(_) => (), - ItemEnum::Static(_) => (), - ItemEnum::ForeignType => (), - ItemEnum::Macro(_) => (), - ItemEnum::ProcMacro(_) => (), - ItemEnum::Primitive(_) => (), - ItemEnum::AssocConst { .. } => (), - ItemEnum::AssocType { .. } => (), - } - } - Ok(()) -} - -fn visit_type(level: usize, name: &str, ty: &Type, crate_: &Crate) -> Result<()> { - match ty { - Type::ResolvedPath(path) => { - visit_item(level + 1, name, &path.id, crate_)?; - if let Some(args) = &path.args { - match args.as_ref() { - GenericArgs::AngleBracketed { args, bindings: _ } => { - for (i, arg) in args.iter().enumerate() { - match arg { - GenericArg::Lifetime(_) => todo!(), - GenericArg::Type(ty) => { - print!(" "); - visit_type(level, &i.to_string(), ty, crate_)?; - } - GenericArg::Const(_) => todo!(), - GenericArg::Infer => todo!(), - } - } - } - GenericArgs::Parenthesized { .. } => (), - } - } - } - Type::DynTrait(_) => (), - Type::Generic(s) => print!("{s}"), - Type::Primitive(name) => { - print!("{name}"); - } - Type::FunctionPointer(_) => (), - Type::Tuple(types) => { - for (i, ty) in types.iter().enumerate() { - visit_type(level, &i.to_string(), ty, crate_)?; - } - } - Type::Slice(_) => (), - Type::Array { type_: _, len: _ } => (), - Type::ImplTrait(_) => (), - Type::Infer => (), - Type::RawPointer { .. } => (), - Type::BorrowedRef { .. } => (), - Type::QualifiedPath { - name: _, - args: _, - self_type: _, - trait_: _, - } => (), - } - Ok(()) -} - -fn find_impls<'a>( - crate_: &'a Crate, - trait_name: &'a str, - filter: &'a [&'a str], -) -> impl Iterator)> { - crate_.index.iter().filter_map(move |(_k, v)| { - if let ItemEnum::Impl(Impl { - trait_: Some(Path { name, .. }), - for_: Type::ResolvedPath(Path { id, .. }), - items, - .. - }) = &v.inner - { - if name.as_str() == trait_name { - let assoc_types = items - .iter() - .filter_map(|id| { - let item = &crate_.index[id]; - item.name.as_deref().and_then(|name| { - if filter.contains(&name) { - if let ItemEnum::AssocType { - default: Some(Type::ResolvedPath(Path { id, .. })), - .. - } = &item.inner - { - Some((name, id)) - } else { - None - } - } else { - None - } - }) - }) - .collect(); - Some((id, assoc_types)) - } else { - None - } - } else { - None - } - }) -} diff --git a/crux_cli/src/graph.rs b/crux_cli/src/codegen/graph.rs similarity index 100% rename from crux_cli/src/graph.rs rename to crux_cli/src/codegen/graph.rs diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs new file mode 100644 index 000000000..eea604c7f --- /dev/null +++ b/crux_cli/src/codegen/mod.rs @@ -0,0 +1,55 @@ +mod graph; +mod parser; +mod rust_types; + +use anyhow::{bail, Result}; +use rustdoc_types::Crate; +use std::{ + fs::File, + io::{stdout, IsTerminal}, +}; +use tokio::{process::Command, task::spawn_blocking}; + +use crate::{args::CodegenArgs, command_runner}; + +pub async fn codegen(args: &CodegenArgs) -> Result<()> { + let graph = graph::compute_package_graph()?; + + let Ok(lib) = graph.workspace().member_by_path(&args.lib) else { + bail!("Could not find workspace package with path {}", args.lib) + }; + + let mut cmd = Command::new("cargo"); + cmd.env("RUSTC_BOOTSTRAP", "1") + .env( + "RUSTDOCFLAGS", + "-Z unstable-options --output-format=json --cap-lints=allow", + ) + .arg("doc") + .arg("--no-deps") + .arg("--manifest-path") + .arg(lib.manifest_path()) + .arg("--lib"); + if stdout().is_terminal() { + cmd.arg("--color=always"); + } + + command_runner::run(&mut cmd).await?; + + let target_directory = graph.workspace().target_directory().as_std_path(); + let json_path = target_directory + .join("doc") + .join(format!("{}.json", lib.name().replace('-', "_"))); + + let crate_: Crate = spawn_blocking(move || -> Result { + let file = File::open(json_path)?; + let crate_ = serde_json::from_reader(file)?; + Ok(crate_) + }) + .await??; + + let data = parser::parse(crate_)?; + println!("\n\ndata: {data:?}"); + + Ok(()) +} diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs new file mode 100644 index 000000000..e6dd7a54d --- /dev/null +++ b/crux_cli/src/codegen/parser.rs @@ -0,0 +1,308 @@ +use std::collections::HashMap; + +use anyhow::Result; +use rustdoc_types::{ + Crate, GenericArg, GenericArgs, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind, +}; + +use crate::codegen::rust_types::{RustField, SpecialRustType}; + +use super::rust_types::{RustEnum, RustStruct, RustType, RustTypeAlias}; + +/// The results of parsing Rust source input. +#[derive(Default, Debug)] +pub struct ParsedData { + /// Structs defined in the source + pub structs: HashMap, + /// Enums defined in the source + pub enums: HashMap, + /// Type aliases defined in the source + pub aliases: HashMap, +} + +impl ParsedData { + pub fn new() -> Self { + Default::default() + } +} + +/// An import visitor that collects all use or +/// qualified referenced items. +#[derive(Default)] +pub struct Visitor { + parsed_data: ParsedData, +} + +impl Visitor { + pub fn new() -> Self { + Self { + parsed_data: ParsedData::new(), + } + } + + fn visit_item( + &mut self, + level: usize, + name: &str, + parent: &Id, + id: &Id, + crate_: &Crate, + ) -> Result<()> { + print!( + "\n{level} {id:18} {} {name:20} ", + " ".repeat(level * 4), + id = format!("{:?}", id) + ); + + if let Some(summary) = crate_.paths.get(id) { + let path_str = summary.path.join("::"); + print!("{path_str}"); + } + + if let Some(item) = crate_.index.get(id) { + match &item.inner { + ItemEnum::Struct(ref struct_) => match &struct_.kind { + StructKind::Unit => { + print!("unit struct"); + } + StructKind::Tuple(fields) => { + print!("tuple struct: {fields:?}"); + } + StructKind::Plain { + fields, + fields_stripped, + } => { + if *fields_stripped { + anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); + } + let parent = id.clone(); + let _entry = self + .parsed_data + .structs + .entry(parent.clone()) + .or_insert_with(|| RustStruct::new(parent.clone().into())); + for field in fields { + let item = &crate_.index[field]; + if let Some(name) = &item.name { + self.visit_item(level + 1, name, &parent, field, crate_)?; + } + } + } + }, + ItemEnum::Enum(ref enum_) => { + let parent = id; + for id in &enum_.variants { + let item = &crate_.index[id]; + if let Some(name) = &item.name { + if !&item.attrs.contains(&"#[serde(skip)]".to_string()) { + self.visit_item(level + 1, name, parent, id, crate_)?; + } + } + } + } + ItemEnum::StructField(ty) => { + if let Some(ty) = self.visit_type(level, "", id, ty, crate_)? { + self.parsed_data + .structs + .get_mut(parent) + .unwrap() + .fields + .push(RustField { + id: id.clone().into(), + ty, + comments: vec![], + has_default: false, + }); + } + } + ItemEnum::Module(_) => (), + ItemEnum::ExternCrate { .. } => (), + ItemEnum::Import(_) => (), + ItemEnum::Union(_) => (), + ItemEnum::Variant(v) => match &v.kind { + VariantKind::Plain => {} + VariantKind::Tuple(fields) => { + let parent = id; + for id in fields { + let Some(id) = id else { continue }; + let item = &crate_.index[id]; + if let Some(name) = &item.name { + self.visit_item(level + 1, name, parent, id, crate_)?; + } + } + } + VariantKind::Struct { + fields, + fields_stripped, + } => { + if *fields_stripped { + anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); + } + let parent = id; + for id in fields { + let item = &crate_.index[id]; + if let Some(name) = &item.name { + self.visit_item(level + 1, name, parent, id, crate_)?; + } + } + } + }, + ItemEnum::Function(_) => (), + ItemEnum::Trait(_) => (), + ItemEnum::TraitAlias(_) => (), + ItemEnum::Impl(_) => (), + ItemEnum::TypeAlias(_) => (), + ItemEnum::OpaqueTy(_) => (), + ItemEnum::Constant(_) => (), + ItemEnum::Static(_) => (), + ItemEnum::ForeignType => (), + ItemEnum::Macro(_) => (), + ItemEnum::ProcMacro(_) => (), + ItemEnum::Primitive(_) => (), + ItemEnum::AssocConst { .. } => (), + ItemEnum::AssocType { .. } => (), + } + } + Ok(()) + } + + fn visit_type( + &mut self, + level: usize, + name: &str, + parent: &Id, + ty: &Type, + crate_: &Crate, + ) -> Result> { + let out_type: Option = match ty { + Type::ResolvedPath(path) => { + self.visit_item(level + 1, name, parent, &path.id, crate_)?; + if let Some(args) = &path.args { + match args.as_ref() { + GenericArgs::AngleBracketed { args, bindings: _ } => { + for (i, arg) in args.iter().enumerate() { + match arg { + GenericArg::Lifetime(_) => todo!(), + GenericArg::Type(ty) => { + print!(" "); + self.visit_type(level, &i.to_string(), parent, ty, crate_)?; + } + GenericArg::Const(_) => todo!(), + GenericArg::Infer => todo!(), + } + } + None + } + GenericArgs::Parenthesized { .. } => None, + } + } else { + None + } + } + Type::DynTrait(_) => None, + Type::Generic(s) => { + print!("{s}"); + None + } + Type::Primitive(name) => { + print!("{name}"); + let out_type = SpecialRustType::try_from(name.as_str()) + .map_err(|e| anyhow::anyhow!(e.to_owned()))?; + Some(RustType::Special(out_type)) + } + Type::FunctionPointer(_) => None, + Type::Tuple(types) => { + for (i, ty) in types.iter().enumerate() { + self.visit_type(level, &i.to_string(), parent, ty, crate_)?; + } + None + } + Type::Slice(_) => None, + Type::Array { type_: _, len: _ } => None, + Type::ImplTrait(_) => None, + Type::Infer => None, + Type::RawPointer { .. } => None, + Type::BorrowedRef { .. } => None, + Type::QualifiedPath { + name: _, + args: _, + self_type: _, + trait_: _, + } => None, + }; + Ok(out_type) + } +} + +pub fn parse(crate_: Crate) -> Result { + let mut visitor = Visitor::new(); + for (id, associated_items) in find_impls(&crate_, "Effect", &["Ffi"]) { + println!( + "\nThe struct that implements crux_core::Effect is {}", + crate_.paths[id].path.join("::") + ); + + let parent = id; + for (name, id) in associated_items { + visitor.visit_item(0, name, parent, id, &crate_)?; + } + } + println!(); + for (id, associated_items) in find_impls(&crate_, "App", &["Event", "ViewModel"]) { + println!( + "\nThe struct that implements crux_core::App is {}", + crate_.paths[id].path.join("::") + ); + + let parent = id; + for (name, id) in associated_items { + visitor.visit_item(0, name, parent, id, &crate_)?; + } + } + Ok(visitor.parsed_data) +} + +fn find_impls<'a>( + crate_: &'a Crate, + trait_name: &'a str, + filter: &'a [&'a str], +) -> impl Iterator)> { + crate_.index.iter().filter_map(move |(_k, v)| { + if let ItemEnum::Impl(Impl { + trait_: Some(Path { name, .. }), + for_: Type::ResolvedPath(Path { id, .. }), + items, + .. + }) = &v.inner + { + if name.as_str() == trait_name { + let assoc_types = items + .iter() + .filter_map(|id| { + let item = &crate_.index[id]; + item.name.as_deref().and_then(|name| { + if filter.contains(&name) { + if let ItemEnum::AssocType { + default: Some(Type::ResolvedPath(Path { id, .. })), + .. + } = &item.inner + { + Some((name, id)) + } else { + None + } + } else { + None + } + }) + }) + .collect(); + Some((id, assoc_types)) + } else { + None + } + } else { + None + } + }) +} diff --git a/crux_cli/src/codegen/rust_types.rs b/crux_cli/src/codegen/rust_types.rs new file mode 100644 index 000000000..e6fe7655f --- /dev/null +++ b/crux_cli/src/codegen/rust_types.rs @@ -0,0 +1,311 @@ +/// Identifier used in Rust structs, enums, and fields. It includes the `original` name and the `renamed` value after the transformation based on `serde` attributes. +#[derive(Debug, Clone, PartialEq)] +pub struct Id { + // the identifier from the rustdoc json + pub id: rustdoc_types::Id, + /// The original identifier name + pub original: String, + /// The renamed identifier, based on serde attributes. + /// If there is no re-naming going on, this will be identical to + /// `original`. + pub renamed: String, +} + +impl Id { + pub fn new(id: rustdoc_types::Id) -> Self { + Self { + id, + original: String::new(), + renamed: String::new(), + } + } +} + +impl From for Id { + fn from(id: rustdoc_types::Id) -> Self { + Self::new(id) + } +} + +// Rust struct. +#[derive(Debug, Clone, PartialEq)] +pub struct RustStruct { + /// The identifier for the struct. + pub id: Id, + /// The generic parameters that come after the struct name. + pub generic_types: Vec, + /// The fields of the struct. + pub fields: Vec, + /// Comments that were in the struct source. + /// We copy comments over to the typeshared files, + /// so we need to collect them here. + pub comments: Vec, +} + +impl RustStruct { + pub fn new(id: Id) -> Self { + Self { + id, + generic_types: Vec::new(), + fields: Vec::new(), + comments: Vec::new(), + } + } +} + +/// Rust type alias. +/// ``` +/// pub struct MasterPassword(String); +/// ``` +#[derive(Debug, Clone, PartialEq)] +pub struct RustTypeAlias { + /// The identifier for the alias. + pub id: Id, + /// The generic parameters that come after the type alias name. + pub generic_types: Vec, + /// The type identifier that this type alias is aliasing + pub r#type: RustType, + /// Comments that were in the type alias source. + pub comments: Vec, +} + +/// Rust field definition. +#[derive(Debug, Clone, PartialEq)] +pub struct RustField { + /// Identifier for the field. + pub id: Id, + /// Type of the field. + pub ty: RustType, + /// Comments that were in the original source. + pub comments: Vec, + /// This will be true if the field has a `serde(default)` decorator. + /// Even if the field's type is not optional, we need to make it optional + /// for the languages we generate code for. + pub has_default: bool, +} + +/// A Rust type. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RustType { + /// A type with generic parameters. Consists of a type ID + parameters that come + /// after in angled brackets. Examples include: + /// - `SomeStruct` + /// - `SomeEnum` + /// - `SomeTypeAlias<(), &str>` + /// However, there are some generic types that are considered to be _special_. These + /// include `Vec` `HashMap`, and `Option`, which are part of `SpecialRustType` instead + /// of `RustType::Generic`. + Generic { + #[allow(missing_docs)] + id: String, + #[allow(missing_docs)] + parameters: Vec, + }, + /// A type that requires a special transformation to its respective language. This includes + /// many core types, like string types, basic container types, numbers, and other primitives. + Special(SpecialRustType), + /// A type with no generic parameters that is not considered a **special** type. This includes + /// all user-generated types and some types from the standard library or third-party crates. + Simple { + #[allow(missing_docs)] + id: String, + }, +} + +/// A special rust type that needs a manual type conversion +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SpecialRustType { + /// Represents `Vec` from the standard library + Vec(Box), + /// Represents `[T; N]` from the standard library + Array(Box, usize), + /// Represents `&[T]` from the standard library + Slice(Box), + /// Represents `HashMap` from the standard library + HashMap(Box, Box), + /// Represents `Option` from the standard library + Option(Box), + /// Represents `()` + Unit, + /// Represents `String` from the standard library + String, + /// Represents `char` + Char, + /// Represents `i8` + I8, + /// Represents `i16` + I16, + /// Represents `i32` + I32, + /// Represents `i64` + I64, + /// Represents `u8` + U8, + /// Represents `u16` + U16, + /// Represents `u32` + U32, + /// Represents `u64` + U64, + /// Represents `isize` + ISize, + /// Represents `usize` + USize, + /// Represents `bool` + Bool, + /// Represents `f32` + F32, + /// Represents `f64` + F64, + /// Represents `I54` from `typeshare::I54` + I54, + /// Represents `U53` from `typeshare::U53` + U53, +} + +impl<'a> TryFrom<&'a str> for SpecialRustType { + type Error = &'a str; + + fn try_from(value: &'a str) -> Result { + match value { + "String" => Ok(SpecialRustType::String), + "char" => Ok(SpecialRustType::Char), + "i8" => Ok(SpecialRustType::I8), + "i16" => Ok(SpecialRustType::I16), + "i32" => Ok(SpecialRustType::I32), + "i64" => Ok(SpecialRustType::I64), + "u8" => Ok(SpecialRustType::U8), + "u16" => Ok(SpecialRustType::U16), + "u32" => Ok(SpecialRustType::U32), + "u64" => Ok(SpecialRustType::U64), + "isize" => Ok(SpecialRustType::ISize), + "usize" => Ok(SpecialRustType::USize), + "bool" => Ok(SpecialRustType::Bool), + "f32" => Ok(SpecialRustType::F32), + "f64" => Ok(SpecialRustType::F64), + "I54" => Ok(SpecialRustType::I54), + "U53" => Ok(SpecialRustType::U53), + _ => Err(value), + } + } +} + +/// Parsed information about a Rust enum definition +#[derive(Debug, Clone, PartialEq)] +pub enum RustEnum { + /// A unit enum + /// + /// An example of such an enum: + /// + /// ``` + /// enum UnitEnum { + /// Variant, + /// AnotherVariant, + /// Yay, + /// } + /// ``` + Unit(RustEnumShared), + /// An algebraic enum + /// + /// An example of such an enum: + /// + /// ``` + /// struct AssociatedData { /* ... */ } + /// + /// enum AlgebraicEnum { + /// UnitVariant, + /// TupleVariant(AssociatedData), + /// AnonymousStruct { + /// field: String, + /// another_field: bool, + /// }, + /// } + /// ``` + Algebraic { + /// The parsed value of the `#[serde(tag = "...")]` attribute + tag_key: String, + /// The parsed value of the `#[serde(content = "...")]` attribute + content_key: String, + /// Shared context for this enum. + shared: RustEnumShared, + }, +} + +impl RustEnum { + /// Get a reference to the inner shared content + pub fn shared(&self) -> &RustEnumShared { + match self { + Self::Unit(shared) | Self::Algebraic { shared, .. } => shared, + } + } +} + +/// Enum information shared among different enum types +#[derive(Debug, Clone, PartialEq)] +pub struct RustEnumShared { + /// The enum's ident + pub id: Id, + /// Generic parameters for the enum, e.g. `SomeEnum` would produce `vec!["T"]` + pub generic_types: Vec, + /// Comments on the enum definition itself + pub comments: Vec, + /// The enum's variants + pub variants: Vec, + /// True if this enum references itself in any field of any variant + /// Swift needs the special keyword `indirect` for this case + pub is_recursive: bool, +} + +/// Parsed information about a Rust enum variant +#[derive(Debug, Clone, PartialEq)] +pub enum RustEnumVariant { + /// A unit variant + Unit(RustEnumVariantShared), + /// A tuple variant + Tuple { + /// The type of the single tuple field + ty: RustType, + /// Shared context for this enum. + shared: RustEnumVariantShared, + }, + /// An anonymous struct variant + AnonymousStruct { + /// The fields of the anonymous struct + fields: Vec, + /// Shared context for this enum. + shared: RustEnumVariantShared, + }, +} + +impl RustEnumVariant { + /// Get a reference to the inner shared content + pub fn shared(&self) -> &RustEnumVariantShared { + match self { + Self::Unit(shared) + | Self::Tuple { shared, .. } + | Self::AnonymousStruct { shared, .. } => shared, + } + } +} + +/// Variant information shared among different variant types +#[derive(Debug, Clone, PartialEq)] +pub struct RustEnumVariantShared { + /// The variant's ident + pub id: Id, + /// Comments applied to the variant + pub comments: Vec, +} + +/// An enum that encapsulates units of code generation for Typeshare. +/// Analogous to `syn::Item`, even though our variants are more limited. +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub enum RustItem { + /// A `struct` definition + Struct(RustStruct), + /// An `enum` definition + Enum(RustEnum), + /// A `type` definition or newtype struct. + Alias(RustTypeAlias), +} diff --git a/crux_cli/src/main.rs b/crux_cli/src/main.rs index 453962559..2443c6ab0 100644 --- a/crux_cli/src/main.rs +++ b/crux_cli/src/main.rs @@ -10,7 +10,6 @@ mod command_runner; mod config; mod diff; mod doctor; -mod graph; mod template; mod workspace; From 14320488d9e8baa13e8d75e38322102862174b75 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Mon, 3 Jun 2024 13:44:18 +0100 Subject: [PATCH 08/27] borrow code from cargo-public-api crate WIP --- crux_cli/src/codegen/crate_wrapper.rs | 36 + .../src/codegen/intermediate_public_item.rs | 54 + crux_cli/src/codegen/item_processor.rs | 475 ++++++ crux_cli/src/codegen/mod.rs | 47 +- crux_cli/src/codegen/nameable_item.rs | 92 ++ crux_cli/src/codegen/parser.rs | 286 +--- crux_cli/src/codegen/path_component.rs | 23 + crux_cli/src/codegen/public_item.rs | 96 ++ crux_cli/src/codegen/render.rs | 1349 +++++++++++++++++ crux_cli/src/codegen/tokens.rs | 110 ++ 10 files changed, 2342 insertions(+), 226 deletions(-) create mode 100644 crux_cli/src/codegen/crate_wrapper.rs create mode 100644 crux_cli/src/codegen/intermediate_public_item.rs create mode 100644 crux_cli/src/codegen/item_processor.rs create mode 100644 crux_cli/src/codegen/nameable_item.rs create mode 100644 crux_cli/src/codegen/path_component.rs create mode 100644 crux_cli/src/codegen/public_item.rs create mode 100644 crux_cli/src/codegen/render.rs create mode 100644 crux_cli/src/codegen/tokens.rs diff --git a/crux_cli/src/codegen/crate_wrapper.rs b/crux_cli/src/codegen/crate_wrapper.rs new file mode 100644 index 000000000..7d698573c --- /dev/null +++ b/crux_cli/src/codegen/crate_wrapper.rs @@ -0,0 +1,36 @@ +use rustdoc_types::{Crate, Id, Item}; + +/// The [`Crate`] type represents the deserialized form of the rustdoc JSON +/// input. This wrapper adds some helpers and state on top. +pub struct CrateWrapper<'c> { + crate_: &'c Crate, + + /// Normally, an item referenced by [`Id`] is present in the rustdoc JSON. + /// If [`Self::crate_.index`] is missing an [`Id`], then we add it here, to + /// aid with debugging. It will typically be missing because of bugs (or + /// borderline bug such as re-exports of foreign items like discussed in + /// ) + /// We do not report it to users by default, because they can't do anything + /// about it. Missing IDs will be printed with `--verbose` however. + missing_ids: Vec<&'c Id>, +} + +impl<'c> CrateWrapper<'c> { + pub fn new(crate_: &'c Crate) -> Self { + Self { + crate_, + missing_ids: vec![], + } + } + + pub fn get_item(&mut self, id: &'c Id) -> Option<&'c Item> { + self.crate_.index.get(id).or_else(|| { + self.missing_ids.push(id); + None + }) + } + + pub fn missing_item_ids(&self) -> Vec { + self.missing_ids.iter().map(|m| m.0.clone()).collect() + } +} diff --git a/crux_cli/src/codegen/intermediate_public_item.rs b/crux_cli/src/codegen/intermediate_public_item.rs new file mode 100644 index 000000000..2e6dfd3f0 --- /dev/null +++ b/crux_cli/src/codegen/intermediate_public_item.rs @@ -0,0 +1,54 @@ +use rustdoc_types::Item; + +use super::nameable_item::NameableItem; +use super::path_component::PathComponent; +use super::public_item::PublicItemPath; +use super::render::RenderingContext; +use super::tokens::Token; + +/// This struct represents one public item of a crate, but in intermediate form. +/// Conceptually it wraps a single [`Item`] even though the path to the item +/// consists of many [`Item`]s. Later, one [`Self`] will be converted to exactly +/// one [`crate::PublicItem`]. +#[derive(Clone, Debug)] +pub struct IntermediatePublicItem<'c> { + path: Vec>, +} + +impl<'c> IntermediatePublicItem<'c> { + pub fn new(path: Vec>) -> Self { + Self { path } + } + + #[must_use] + pub fn item(&self) -> &'c Item { + self.path() + .last() + .expect("path must not be empty") + .item + .item + } + + #[must_use] + pub fn path(&self) -> &[PathComponent<'c>] { + &self.path + } + + /// See [`crate::item_processor::sorting_prefix()`] docs for an explanation why we have this. + #[must_use] + pub fn sortable_path(&self, context: &RenderingContext) -> PublicItemPath { + self.path() + .iter() + .map(|p| NameableItem::sortable_name(&p.item, context)) + .collect() + } + + #[must_use] + pub fn path_contains_renamed_item(&self) -> bool { + self.path().iter().any(|m| m.item.overridden_name.is_some()) + } + + pub fn render_token_stream(&self, context: &RenderingContext) -> Vec { + context.token_stream(self) + } +} diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/item_processor.rs new file mode 100644 index 000000000..d9e39c35e --- /dev/null +++ b/crux_cli/src/codegen/item_processor.rs @@ -0,0 +1,475 @@ +use super::nameable_item::NameableItem; +use super::CrateWrapper; +use super::{ + intermediate_public_item::IntermediatePublicItem, path_component::PathComponent, + public_item::PublicItem, render::RenderingContext, +}; +use rustdoc_types::{ + Crate, Id, Impl, Import, Item, ItemEnum, Module, Struct, StructKind, Type, Variant, VariantKind, +}; +use std::convert::identity; +use std::{ + collections::{HashMap, VecDeque}, + vec, +}; + +/// Items in rustdoc JSON reference each other by Id. The [`ItemProcessor`] +/// essentially takes one Id at a time and figure out what to do with it. Once +/// complete, the item is ready to be listed as part of the public API, and +/// optionally can also be used as part of a path to another (child) item. +/// +/// This struct contains a (processed) path to an item that is about to be +/// processed further, and the Id of that item. +#[derive(Debug)] +struct UnprocessedItem<'c> { + /// The path to the item to process. + parent_path: Vec>, + + /// The Id of the item to process. + id: &'c Id, +} + +/// Processes items to find more items and to figure out the path to each item. +/// Some non-obvious cases to take into consideration are: +/// 1. A single item is imported several times. +/// 2. An item is (publicly) imported from another crate +/// +/// Note that this implementation iterates over everything, so if the rustdoc +/// JSON is generated with `--document-private-items`, then private items will +/// also be included in the output. Use with `--document-private-items` is not +/// supported. +pub struct ItemProcessor<'c> { + /// The original and unmodified rustdoc JSON, in deserialized form. + crate_: CrateWrapper<'c>, + + /// A queue of unprocessed items to process. + work_queue: VecDeque>, + + /// The output. A list of processed items. Note that the order is + /// intentionally "logical", so that e.g. struct fields items follows from + /// struct items. + pub output: Vec>, +} + +impl<'c> ItemProcessor<'c> { + pub(crate) fn new(crate_: &'c Crate) -> Self { + ItemProcessor { + crate_: CrateWrapper::new(crate_), + work_queue: VecDeque::new(), + output: vec![], + } + } + + /// Adds an item to the front of the work queue. We want to add items to the + /// front of the work queue since we process items from the top, and since + /// we want to "fully" process an item before we move on to the next one. So + /// when we encounter a struct, we also encounter its struct fields, and we + /// want to insert the struct fields BEFORE everything else, so that these + /// items remain grouped together. And the same applies for many kinds of + /// groupings (enums, impls, etc). + pub fn add_to_work_queue(&mut self, parent_path: Vec>, id: &'c Id) { + self.work_queue + .push_front(UnprocessedItem { parent_path, id }); + } + + /// Processes the entire work queue. Adds more items based on items it + /// processes. When this returns, all items and their children and impls + /// have been recursively processed. + pub fn run(&mut self) { + while let Some(unprocessed_item) = self.work_queue.pop_front() { + if let Some(item) = self.crate_.get_item(unprocessed_item.id) { + self.process_any_item(item, unprocessed_item); + } + } + } + + /// Process any item. In particular, does the right thing if the item is an + /// impl or an import. + fn process_any_item(&mut self, item: &'c Item, unprocessed_item: UnprocessedItem<'c>) { + match &item.inner { + ItemEnum::Import(import) => { + if import.glob { + self.process_import_glob_item(import, unprocessed_item, item); + } else { + self.process_import_item(item, import, unprocessed_item); + } + } + ItemEnum::Impl(impl_) => { + self.process_impl_item(unprocessed_item, item, impl_); + } + _ => { + self.process_item_unless_recursive(unprocessed_item, item, None); + } + } + } + + /// We need to handle `pub use foo::*` specially. In case of such wildcard + /// imports, `glob` will be `true` and `id` will be the module we should + /// import all items from, but we should NOT add the module itself. Before + /// we inline this wildcard import, make sure that the module is not + /// indirectly trying to import itself. If we allow that, we'll get a stack + /// overflow. + fn process_import_glob_item( + &mut self, + import: &'c Import, + unprocessed_item: UnprocessedItem<'c>, + item: &'c Item, + ) { + if let Some(Item { + inner: ItemEnum::Module(Module { items, .. }), + .. + }) = import + .id + .as_ref() + .and_then(|id| self.get_item_if_not_in_path(&unprocessed_item.parent_path, id)) + { + for item_id in items { + self.add_to_work_queue(unprocessed_item.parent_path.clone(), item_id); + } + } else { + self.process_item( + unprocessed_item, + item, + Some(format!("<<{}::*>>", import.source)), + ); + } + } + + /// Since public imports are part of the public API, we inline them, i.e. + /// replace the item corresponding to an import with the item that is + /// imported. If we didn't do this, publicly imported items would show up as + /// just e.g. `pub use some::function`, which is not sufficient for the use + /// cases of this tool. We want to show the actual API, and thus also show + /// type information! There is one exception; for re-exports of primitive + /// types, there is no item Id to inline with, so they remain as e.g. `pub + /// use my_i32` in the output. + fn process_import_item( + &mut self, + item: &'c Item, + import: &'c Import, + unprocessed_item: UnprocessedItem<'c>, + ) { + let mut actual_item = item; + + if let Some(imported_item) = import + .id + .as_ref() + .and_then(|id| self.get_item_if_not_in_path(&unprocessed_item.parent_path, id)) + { + actual_item = imported_item; + } + + self.process_item(unprocessed_item, actual_item, Some(import.name.clone())); + } + + /// Processes impls. Impls are special because we support filtering out e.g. + /// blanket implementations to reduce output noise. + fn process_impl_item( + &mut self, + unprocessed_item: UnprocessedItem<'c>, + item: &'c Item, + impl_: &'c Impl, + ) { + if !ImplKind::from(item, impl_).is_active() { + return; + } + + self.process_item_for_type(unprocessed_item, item, None, Some(&impl_.for_)); + } + + /// Make sure the item we are about to process is not already part of the + /// item path. If it is, we have encountered recursion. Stop processing in + /// that case. + fn process_item_unless_recursive( + &mut self, + unprocessed_item: UnprocessedItem<'c>, + item: &'c Item, + overridden_name: Option, + ) { + if unprocessed_item + .parent_path + .iter() + .any(|m| m.item.item.id == item.id) + { + let recursion_breaker = unprocessed_item.finish( + item, + Some(format!("<<{}>>", item.name.as_deref().unwrap_or(""))), + None, + ); + self.output.push(recursion_breaker); + } else { + self.process_item(unprocessed_item, item, overridden_name); + } + } + + /// Process an item. Setup jobs for its children and impls and and then put + /// it in the output. + fn process_item( + &mut self, + unprocessed_item: UnprocessedItem<'c>, + item: &'c Item, + overridden_name: Option, + ) { + self.process_item_for_type(unprocessed_item, item, overridden_name, None); + } + + /// Process an item. Setup jobs for its children and impls and and then put + /// it in the output. + fn process_item_for_type( + &mut self, + unprocessed_item: UnprocessedItem<'c>, + item: &'c Item, + overridden_name: Option, + type_: Option<&'c Type>, + ) { + let finished_item = unprocessed_item.finish(item, overridden_name, type_); + + let children = children_for_item(item); + let impls = impls_for_item(item).into_iter().flatten(); + + if item.id == Id("0:428:2145".to_string()) { + println!("Processing: {:?}", item.id); + } + for id in children { + let parent_path = finished_item.path().into(); + self.add_to_work_queue(parent_path, id); + } + + // As usual, impls are special. We want impl items to appear grouped + // with the trait or type it involves. But when _rendering_ we want to + // use the type that we implement for, so that e.g. generic arguments + // can be shown. So hide the "sorting path" of the impl. We'll instead + // render the path to the type the impl is for. + for id in impls { + let mut path = finished_item.path().to_vec(); + for a in &mut path { + a.hide = true; + } + self.add_to_work_queue(path, id); + } + + self.output.push(finished_item); + } + + /// Get the rustdoc JSON item with `id`, but only if it is not already part + /// of the path. This can happen in the case of recursive re-exports, in + /// which case we need to break the recursion. + fn get_item_if_not_in_path( + &mut self, + parent_path: &[PathComponent<'c>], + id: &'c Id, + ) -> Option<&'c Item> { + if parent_path.iter().any(|m| m.item.item.id == *id) { + // The item is already in the path! Break import recursion... + return None; + } + + self.crate_.get_item(id) + } + + // Returns a HashMap where a rustdoc JSON Id is mapped to what public items + // that have this ID. The reason this is a one-to-many mapping is because of + // re-exports. If an API re-exports a public item in a different place, the + // same item will be reachable by different paths, and thus the Vec will + // contain many [`IntermediatePublicItem`]s for that ID. + // + // You might think this is rare, but it is actually a common thing in + // real-world code. + pub fn id_to_items(&self) -> HashMap<&Id, Vec<&IntermediatePublicItem>> { + let mut id_to_items: HashMap<&Id, Vec<&IntermediatePublicItem>> = HashMap::new(); + for finished_item in &self.output { + id_to_items + .entry(&finished_item.item().id) + .or_default() + .push(finished_item); + } + id_to_items + } +} + +impl<'c> UnprocessedItem<'c> { + /// Turns an [`UnprocessedItem`] into a finished [`IntermediatePublicItem`]. + fn finish( + self, + item: &'c Item, + overridden_name: Option, + type_: Option<&'c Type>, + ) -> IntermediatePublicItem<'c> { + // Transfer path ownership to output item + let mut path = self.parent_path; + + // Complete the path with the last item + path.push(PathComponent { + item: NameableItem { + item, + overridden_name, + sorting_prefix: sorting_prefix(item), + }, + type_, + hide: false, + }); + + // Done + IntermediatePublicItem::new(path) + } +} + +/// In order for items in the output to be nicely grouped, we add a prefix to +/// each item in the path to an item. That way, sorting on the name (with this +/// prefix) will group items. But we don't want this prefix to be be visible to +/// users of course, so we do this "behind the scenes". +pub(crate) fn sorting_prefix(item: &Item) -> u8 { + match &item.inner { + ItemEnum::ExternCrate { .. } => 1, + ItemEnum::Import(_) => 2, + + ItemEnum::Primitive(_) => 3, + + ItemEnum::Module(_) => 4, + + ItemEnum::Macro(_) => 5, + ItemEnum::ProcMacro(_) => 6, + + ItemEnum::Enum(_) => 7, + ItemEnum::Union(_) => 8, + ItemEnum::Struct(_) => 9, + ItemEnum::StructField(_) => 10, + ItemEnum::Variant(_) => 11, + + ItemEnum::Constant(_) => 12, + + ItemEnum::Static(_) => 13, + + ItemEnum::Trait(_) => 14, + + ItemEnum::AssocType { .. } => 15, + ItemEnum::AssocConst { .. } => 16, + + ItemEnum::Function(_) => 17, + + ItemEnum::TypeAlias(_) => 19, + + ItemEnum::Impl(impl_) => match ImplKind::from(item, impl_) { + // To not cause a diff when changing a manual impl to an + // auto-derived impl (or vice versa), we put them in the same group. + ImplKind::Inherent => 20, + ImplKind::Trait | ImplKind::AutoDerived => 21, + ImplKind::AutoTrait => 23, + ImplKind::Blanket => 24, + }, + + ItemEnum::ForeignType => 25, + + ItemEnum::OpaqueTy(_) => 26, + + ItemEnum::TraitAlias(_) => 27, + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum ImplKind { + /// E.g. `impl Foo` or `impl<'a> Foo<'a>` + Inherent, + + /// E.g. `impl Bar for Foo {}` + Trait, + + /// Auto-generated by `#[derive(...)]` + AutoDerived, + + /// Auto-trait impl such as `impl Sync for Foo` + AutoTrait, + + /// Blanket impls such as `impl Any for T` + Blanket, +} + +impl ImplKind { + fn from(impl_item: &Item, impl_: &Impl) -> Self { + let has_blanket_impl = impl_.blanket_impl.is_some(); + let is_automatically_derived = impl_item + .attrs + .iter() + .any(|a| a == "#[automatically_derived]"); + + // See https://github.com/rust-lang/rust/blob/54f20bbb8a7aeab93da17c0019c1aaa10329245a/src/librustdoc/json/conversions.rs#L589-L590 + match (impl_.synthetic, has_blanket_impl) { + (true, false) => ImplKind::AutoTrait, + (false, true) => ImplKind::Blanket, + _ if is_automatically_derived => ImplKind::AutoDerived, + _ if impl_.trait_.is_none() => ImplKind::Inherent, + _ => ImplKind::Trait, + } + } +} + +impl ImplKind { + fn is_active(&self) -> bool { + match self { + ImplKind::Blanket | ImplKind::AutoTrait | ImplKind::AutoDerived => false, + ImplKind::Inherent | ImplKind::Trait => true, + } + } +} + +/// Some items contain other items, which is relevant for analysis. Keep track +/// of such relationships. +fn children_for_item(item: &Item) -> Vec<&Id> { + match &item.inner { + ItemEnum::Module(m) => m.items.iter().collect(), + ItemEnum::Union(u) => u.fields.iter().collect(), + ItemEnum::Struct(Struct { + kind: StructKind::Plain { fields, .. }, + .. + }) => fields.iter().collect(), + ItemEnum::Variant(Variant { + kind: VariantKind::Struct { fields, .. }, + .. + }) => fields.iter().collect(), + ItemEnum::Variant(Variant { + kind: VariantKind::Plain, + .. + }) => vec![], + ItemEnum::Variant(Variant { + kind: VariantKind::Tuple(fields), + .. + }) => fields.iter().flatten().collect(), + ItemEnum::Struct(Struct { + kind: StructKind::Unit, + .. + }) => vec![], + ItemEnum::Struct(Struct { + kind: StructKind::Tuple(fields), + .. + }) => fields.iter().flatten().collect(), + ItemEnum::Enum(e) => e.variants.iter().collect(), + ItemEnum::Trait(t) => t.items.iter().collect(), + ItemEnum::Impl(i) => i.items.iter().collect(), + ItemEnum::ExternCrate { .. } => vec![], + ItemEnum::Import(_) => vec![], + ItemEnum::StructField(_) => vec![], + ItemEnum::Function(_) => vec![], + ItemEnum::TraitAlias(_) => vec![], + ItemEnum::TypeAlias(_) => vec![], + ItemEnum::OpaqueTy(_) => vec![], + ItemEnum::Constant(_) => vec![], + ItemEnum::Static(_) => vec![], + ItemEnum::ForeignType => vec![], + ItemEnum::Macro(_) => vec![], + ItemEnum::ProcMacro(_) => vec![], + ItemEnum::Primitive(_) => vec![], + ItemEnum::AssocConst { .. } => vec![], + ItemEnum::AssocType { .. } => vec![], + } +} + +pub fn impls_for_item(item: &Item) -> Option<&[Id]> { + match &item.inner { + ItemEnum::Union(u) => Some(&u.impls), + ItemEnum::Struct(s) => Some(&s.impls), + ItemEnum::Enum(e) => Some(&e.impls), + ItemEnum::Primitive(p) => Some(&p.impls), + ItemEnum::Trait(t) => Some(&t.implementations), + _ => None, + } +} diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs index eea604c7f..6f81af850 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -1,9 +1,52 @@ +mod crate_wrapper; mod graph; +mod intermediate_public_item; +mod item_processor; +mod nameable_item; mod parser; +mod path_component; +mod public_item; +use rustdoc_types::{Crate, Id, Item}; + +/// The [`Crate`] type represents the deserialized form of the rustdoc JSON +/// input. This wrapper adds some helpers and state on top. +pub struct CrateWrapper<'c> { + crate_: &'c Crate, + + /// Normally, an item referenced by [`Id`] is present in the rustdoc JSON. + /// If [`Self::crate_.index`] is missing an [`Id`], then we add it here, to + /// aid with debugging. It will typically be missing because of bugs (or + /// borderline bug such as re-exports of foreign items like discussed in + /// ) + /// We do not report it to users by default, because they can't do anything + /// about it. Missing IDs will be printed with `--verbose` however. + missing_ids: Vec<&'c Id>, +} + +impl<'c> CrateWrapper<'c> { + pub fn new(crate_: &'c Crate) -> Self { + Self { + crate_, + missing_ids: vec![], + } + } + + pub fn get_item(&mut self, id: &'c Id) -> Option<&'c Item> { + self.crate_.index.get(id).or_else(|| { + self.missing_ids.push(id); + None + }) + } + + pub fn missing_item_ids(&self) -> Vec { + self.missing_ids.iter().map(|m| m.0.clone()).collect() + } +} +mod render; mod rust_types; +mod tokens; use anyhow::{bail, Result}; -use rustdoc_types::Crate; use std::{ fs::File, io::{stdout, IsTerminal}, @@ -48,7 +91,7 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { }) .await??; - let data = parser::parse(crate_)?; + let data = parser::parse(&crate_)?; println!("\n\ndata: {data:?}"); Ok(()) diff --git a/crux_cli/src/codegen/nameable_item.rs b/crux_cli/src/codegen/nameable_item.rs new file mode 100644 index 000000000..d67fda469 --- /dev/null +++ b/crux_cli/src/codegen/nameable_item.rs @@ -0,0 +1,92 @@ +use rustdoc_types::{Item, ItemEnum}; + +use super::render::RenderingContext; + +/// Wraps an [`Item`] and allows us to override its name. +#[derive(Clone, Debug)] +pub struct NameableItem<'c> { + /// The item we are effectively wrapping. + pub item: &'c Item, + + /// If `Some`, this overrides [Item::name], which happens in the case of + /// renamed imports (`pub use other::Item as Foo;`). + /// + /// We can't calculate this on-demand, because we can't know the final name + /// until we have checked if we need to break import recursion. + pub overridden_name: Option, + + /// See [`crate::item_processor::sorting_prefix()`] docs for an explanation why we have this. + pub sorting_prefix: u8, +} + +impl<'c> NameableItem<'c> { + /// The regular name of the item. Shown to users. + pub fn name(&self) -> Option<&str> { + self.overridden_name + .as_deref() + .or(self.item.name.as_deref()) + } + + /// The name that, when sorted on, will group items nicely. Is never shown + /// to a user. + pub fn sortable_name(&self, context: &RenderingContext) -> String { + // Note that in order for the prefix to sort properly lexicographically, + // we need to pad it with leading zeroes. + let mut sortable_name = format!("{:0>3}-", self.sorting_prefix); + + if let Some(name) = self.name() { + sortable_name.push_str(name); + } else if let ItemEnum::Impl(impl_) = &self.item.inner { + // In order for items of impls to be grouped together with its impl, + // add the "name" of the impl to the sorting prefix. Ignore `!` when + // sorting however, because that just messes the expected order up. + sortable_name.push_str(&super::tokens::tokens_to_string(&context.render_impl( + impl_, + &[], + true, /* disregard_negativity */ + ))); + + // If this is an inherent impl, additionally add the concatenated + // names of all associated items to the "name" of the impl. This makes + // multiple inherent impls group together, even if they have the + // same "name". + // + // For example, consider this code: + // + // pub struct MultipleInherentImpls; + // + // impl MultipleInherentImpls { + // pub fn impl_one() {} + // } + // + // impl MultipleInherentImpls { + // pub fn impl_two() {} + // } + // + // In this case, we want to group the two impls together. So + // the name of the first impl should be + // + // impl MultipleInherentImpls-impl_one + // + // and the second one + // + // impl MultipleInherentImpls-impl_two + // + if impl_.trait_.is_none() { + let mut assoc_item_names: Vec<&str> = impl_ + .items + .iter() + .filter_map(|id| context.crate_.index.get(id)) + .filter_map(|item| item.name.as_ref()) + .map(String::as_str) + .collect(); + assoc_item_names.sort_unstable(); + + sortable_name.push('-'); + sortable_name.push_str(&assoc_item_names.join("-")); + } + } + + sortable_name + } +} diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index e6dd7a54d..932301f37 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -5,7 +5,12 @@ use rustdoc_types::{ Crate, GenericArg, GenericArgs, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind, }; -use crate::codegen::rust_types::{RustField, SpecialRustType}; +use crate::codegen::{ + item_processor::ItemProcessor, + public_item::PublicItem, + render::RenderingContext, + rust_types::{RustField, SpecialRustType}, +}; use super::rust_types::{RustEnum, RustStruct, RustType, RustTypeAlias}; @@ -26,247 +31,80 @@ impl ParsedData { } } -/// An import visitor that collects all use or -/// qualified referenced items. -#[derive(Default)] -pub struct Visitor { - parsed_data: ParsedData, -} - -impl Visitor { - pub fn new() -> Self { - Self { - parsed_data: ParsedData::new(), - } - } - - fn visit_item( - &mut self, - level: usize, - name: &str, - parent: &Id, - id: &Id, - crate_: &Crate, - ) -> Result<()> { - print!( - "\n{level} {id:18} {} {name:20} ", - " ".repeat(level * 4), - id = format!("{:?}", id) - ); - - if let Some(summary) = crate_.paths.get(id) { - let path_str = summary.path.join("::"); - print!("{path_str}"); - } - - if let Some(item) = crate_.index.get(id) { - match &item.inner { - ItemEnum::Struct(ref struct_) => match &struct_.kind { - StructKind::Unit => { - print!("unit struct"); - } - StructKind::Tuple(fields) => { - print!("tuple struct: {fields:?}"); - } - StructKind::Plain { - fields, - fields_stripped, - } => { - if *fields_stripped { - anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); - } - let parent = id.clone(); - let _entry = self - .parsed_data - .structs - .entry(parent.clone()) - .or_insert_with(|| RustStruct::new(parent.clone().into())); - for field in fields { - let item = &crate_.index[field]; - if let Some(name) = &item.name { - self.visit_item(level + 1, name, &parent, field, crate_)?; - } - } - } - }, - ItemEnum::Enum(ref enum_) => { - let parent = id; - for id in &enum_.variants { - let item = &crate_.index[id]; - if let Some(name) = &item.name { - if !&item.attrs.contains(&"#[serde(skip)]".to_string()) { - self.visit_item(level + 1, name, parent, id, crate_)?; - } - } - } - } - ItemEnum::StructField(ty) => { - if let Some(ty) = self.visit_type(level, "", id, ty, crate_)? { - self.parsed_data - .structs - .get_mut(parent) - .unwrap() - .fields - .push(RustField { - id: id.clone().into(), - ty, - comments: vec![], - has_default: false, - }); - } - } - ItemEnum::Module(_) => (), - ItemEnum::ExternCrate { .. } => (), - ItemEnum::Import(_) => (), - ItemEnum::Union(_) => (), - ItemEnum::Variant(v) => match &v.kind { - VariantKind::Plain => {} - VariantKind::Tuple(fields) => { - let parent = id; - for id in fields { - let Some(id) = id else { continue }; - let item = &crate_.index[id]; - if let Some(name) = &item.name { - self.visit_item(level + 1, name, parent, id, crate_)?; - } - } - } - VariantKind::Struct { - fields, - fields_stripped, - } => { - if *fields_stripped { - anyhow::bail!("The {name} struct has private fields. You may need to make them public to use them in your code."); - } - let parent = id; - for id in fields { - let item = &crate_.index[id]; - if let Some(name) = &item.name { - self.visit_item(level + 1, name, parent, id, crate_)?; - } - } - } - }, - ItemEnum::Function(_) => (), - ItemEnum::Trait(_) => (), - ItemEnum::TraitAlias(_) => (), - ItemEnum::Impl(_) => (), - ItemEnum::TypeAlias(_) => (), - ItemEnum::OpaqueTy(_) => (), - ItemEnum::Constant(_) => (), - ItemEnum::Static(_) => (), - ItemEnum::ForeignType => (), - ItemEnum::Macro(_) => (), - ItemEnum::ProcMacro(_) => (), - ItemEnum::Primitive(_) => (), - ItemEnum::AssocConst { .. } => (), - ItemEnum::AssocType { .. } => (), - } - } - Ok(()) - } - - fn visit_type( - &mut self, - level: usize, - name: &str, - parent: &Id, - ty: &Type, - crate_: &Crate, - ) -> Result> { - let out_type: Option = match ty { - Type::ResolvedPath(path) => { - self.visit_item(level + 1, name, parent, &path.id, crate_)?; - if let Some(args) = &path.args { - match args.as_ref() { - GenericArgs::AngleBracketed { args, bindings: _ } => { - for (i, arg) in args.iter().enumerate() { - match arg { - GenericArg::Lifetime(_) => todo!(), - GenericArg::Type(ty) => { - print!(" "); - self.visit_type(level, &i.to_string(), parent, ty, crate_)?; - } - GenericArg::Const(_) => todo!(), - GenericArg::Infer => todo!(), - } - } - None - } - GenericArgs::Parenthesized { .. } => None, - } - } else { - None - } - } - Type::DynTrait(_) => None, - Type::Generic(s) => { - print!("{s}"); - None - } - Type::Primitive(name) => { - print!("{name}"); - let out_type = SpecialRustType::try_from(name.as_str()) - .map_err(|e| anyhow::anyhow!(e.to_owned()))?; - Some(RustType::Special(out_type)) - } - Type::FunctionPointer(_) => None, - Type::Tuple(types) => { - for (i, ty) in types.iter().enumerate() { - self.visit_type(level, &i.to_string(), parent, ty, crate_)?; - } - None - } - Type::Slice(_) => None, - Type::Array { type_: _, len: _ } => None, - Type::ImplTrait(_) => None, - Type::Infer => None, - Type::RawPointer { .. } => None, - Type::BorrowedRef { .. } => None, - Type::QualifiedPath { - name: _, - args: _, - self_type: _, - trait_: _, - } => None, - }; - Ok(out_type) - } -} - -pub fn parse(crate_: Crate) -> Result { - let mut visitor = Visitor::new(); - for (id, associated_items) in find_impls(&crate_, "Effect", &["Ffi"]) { +pub fn parse(crate_: &Crate) -> Result { + let mut item_processor = ItemProcessor::new(crate_); + for (id, associated_items) in find_roots(crate_, "Effect", &["Ffi"]) { println!( "\nThe struct that implements crux_core::Effect is {}", crate_.paths[id].path.join("::") ); - let parent = id; - for (name, id) in associated_items { - visitor.visit_item(0, name, parent, id, &crate_)?; + for id in associated_items { + item_processor.add_to_work_queue(vec![], id); } } - println!(); - for (id, associated_items) in find_impls(&crate_, "App", &["Event", "ViewModel"]) { + + for (id, associated_items) in find_roots(crate_, "App", &["Event", "ViewModel"]) { println!( "\nThe struct that implements crux_core::App is {}", crate_.paths[id].path.join("::") ); - let parent = id; - for (name, id) in associated_items { - visitor.visit_item(0, name, parent, id, &crate_)?; + for id in associated_items { + item_processor.add_to_work_queue(vec![], id); } } - Ok(visitor.parsed_data) + + item_processor.run(); + + let context = RenderingContext { + crate_, + id_to_items: item_processor.id_to_items(), + }; + let items = item_processor + .output + .iter() + .filter_map(|item| { + if match &item.item().inner { + ItemEnum::Union(_) => true, + ItemEnum::Struct(_) => true, + ItemEnum::StructField(_) => true, + ItemEnum::Enum(_) => true, + ItemEnum::Variant(_) => true, + ItemEnum::Primitive(_) => true, + ItemEnum::TypeAlias(_) => true, + ItemEnum::ForeignType => true, + + ItemEnum::Module(_) => false, + ItemEnum::ExternCrate { .. } => false, + ItemEnum::Import(_) => false, + ItemEnum::Function(_) => false, + ItemEnum::Trait(_) => false, + ItemEnum::TraitAlias(_) => false, + ItemEnum::Impl(_) => false, + ItemEnum::OpaqueTy(_) => false, + ItemEnum::Constant(_) => true, + ItemEnum::Static(_) => false, + ItemEnum::Macro(_) => false, + ItemEnum::ProcMacro(_) => false, + ItemEnum::AssocConst { .. } => false, + ItemEnum::AssocType { .. } => false, + } { + Some(PublicItem::from_intermediate_public_item(&context, item)) + } else { + None + } + }) + .collect::>(); + println!("{items:#?}"); + Ok(ParsedData::new()) } -fn find_impls<'a>( +fn find_roots<'a>( crate_: &'a Crate, trait_name: &'a str, filter: &'a [&'a str], -) -> impl Iterator)> { +) -> impl Iterator)> { crate_.index.iter().filter_map(move |(_k, v)| { if let ItemEnum::Impl(Impl { trait_: Some(Path { name, .. }), @@ -287,7 +125,7 @@ fn find_impls<'a>( .. } = &item.inner { - Some((name, id)) + Some(id) } else { None } diff --git a/crux_cli/src/codegen/path_component.rs b/crux_cli/src/codegen/path_component.rs new file mode 100644 index 000000000..ab508f093 --- /dev/null +++ b/crux_cli/src/codegen/path_component.rs @@ -0,0 +1,23 @@ +use rustdoc_types::Type; + +use super::nameable_item::NameableItem; + +/// A public item in a public API can only be referenced via a path. For example +/// `mod_a::mod_b::StructC`. A `PathComponent` represents one component of such +/// a path. A path component can either be a Rust item, or a Rust type. Normally +/// it is an item. The typical case when it is a type is when there are generic +/// arguments involved. For example, `Option` is a type. The +/// corresponding item is `Option` (no specific generic args has been +/// specified for the generic parameter T). +#[derive(Clone, Debug)] +pub struct PathComponent<'c> { + /// The item for this path component. + pub item: NameableItem<'c>, + + /// The type, if applicable. If we have a type, we'll want to use that + /// instead of `item`, since the type might have specific generic args. + pub type_: Option<&'c Type>, + + /// If `true`, do not render this path component to users. + pub hide: bool, +} diff --git a/crux_cli/src/codegen/public_item.rs b/crux_cli/src/codegen/public_item.rs new file mode 100644 index 000000000..74ba630c2 --- /dev/null +++ b/crux_cli/src/codegen/public_item.rs @@ -0,0 +1,96 @@ +use std::cmp::Ordering; +use std::fmt::Display; +use std::hash::Hash; + +use super::intermediate_public_item::IntermediatePublicItem; +use super::render::RenderingContext; +use super::tokens::tokens_to_string; +use super::tokens::Token; + +/// Each public item (except `impl`s) have a path that is displayed like +/// `first::second::third`. Internally we represent that with a `vec!["first", +/// "second", "third"]`. This is a type alias for that internal representation +/// to make the code easier to read. +pub(crate) type PublicItemPath = Vec; + +/// Represent a public item of an analyzed crate, i.e. an item that forms part +/// of the public API of a crate. Implements [`Display`] so it can be printed. It +/// also implements [`Ord`], but how items are ordered are not stable yet, and +/// will change in later versions. +#[derive(Clone)] +pub struct PublicItem { + /// Read [`crate::item_processor::sorting_prefix()`] docs for more info + pub(crate) sortable_path: PublicItemPath, + + /// The rendered item as a stream of [`Token`]s + pub(crate) tokens: Vec, +} + +impl PublicItem { + pub(crate) fn from_intermediate_public_item( + context: &RenderingContext, + public_item: &IntermediatePublicItem<'_>, + ) -> PublicItem { + PublicItem { + sortable_path: public_item.sortable_path(context), + tokens: public_item.render_token_stream(context), + } + } + + /// The rendered item as a stream of [`Token`]s + pub fn tokens(&self) -> impl Iterator { + self.tokens.iter() + } + + /// Special version of [`cmp`](Ord::cmp) that is used to sort public items in a way that + /// makes them grouped logically. For example, struct fields will be put + /// right after the struct they are part of. + #[must_use] + pub fn grouping_cmp(&self, other: &Self) -> std::cmp::Ordering { + // This will make e.g. struct and struct fields be grouped together. + if let Some(ordering) = different_or_none(&self.sortable_path, &other.sortable_path) { + return ordering; + } + + // Fall back to lexical sorting if the above is not sufficient + self.to_string().cmp(&other.to_string()) + } +} + +impl PartialEq for PublicItem { + fn eq(&self, other: &Self) -> bool { + self.tokens == other.tokens + } +} + +impl Eq for PublicItem {} + +impl Hash for PublicItem { + fn hash(&self, state: &mut H) { + self.tokens.hash(state); + } +} + +/// We want pretty-printing (`"{:#?}"`) of [`crate::diff::PublicApiDiff`] to print +/// each public item as `Display`, so implement `Debug` with `Display`. +impl std::fmt::Debug for PublicItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +/// One of the basic uses cases is printing a sorted `Vec` of `PublicItem`s. So +/// we implement `Display` for it. +impl Display for PublicItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", tokens_to_string(&self.tokens)) + } +} + +/// Returns `None` if two items are equal. Otherwise their ordering is returned. +fn different_or_none(a: &T, b: &T) -> Option { + match a.cmp(b) { + Ordering::Equal => None, + c => Some(c), + } +} diff --git a/crux_cli/src/codegen/render.rs b/crux_cli/src/codegen/render.rs new file mode 100644 index 000000000..0dcab81ae --- /dev/null +++ b/crux_cli/src/codegen/render.rs @@ -0,0 +1,1349 @@ +#![allow(clippy::unused_self)] +use std::ops::Deref; +use std::{cmp::Ordering, collections::HashMap, vec}; + +use super::intermediate_public_item::IntermediatePublicItem; +use super::nameable_item::NameableItem; +use super::path_component::PathComponent; +use super::tokens::Token; + +use rustdoc_types::{ + Abi, Constant, Crate, FnDecl, FunctionPointer, GenericArg, GenericArgs, GenericBound, + GenericParamDef, GenericParamDefKind, Generics, Header, Id, Impl, Item, ItemEnum, MacroKind, + Path, PolyTrait, StructKind, Term, Trait, Type, TypeBinding, TypeBindingKind, VariantKind, + WherePredicate, +}; + +/// A simple macro to write `Token::Whitespace` in less characters. +macro_rules! ws { + () => { + Token::Whitespace + }; +} + +/// When we render an item, it might contain references to other parts of the +/// public API. For such cases, the rendering code can use the fields in this +/// struct. +pub struct RenderingContext<'c> { + /// The original and unmodified rustdoc JSON, in deserialized form. + pub crate_: &'c Crate, + + /// Given a rustdoc JSON ID, keeps track of what public items that have this Id. + pub id_to_items: HashMap<&'c Id, Vec<&'c IntermediatePublicItem<'c>>>, +} + +impl<'c> RenderingContext<'c> { + pub fn token_stream(&self, public_item: &IntermediatePublicItem<'c>) -> Vec { + let item = public_item.item(); + let item_path = public_item.path(); + + let mut tokens = vec![]; + + for attr in &item.attrs { + if attr_relevant_for_public_apis(attr) { + tokens.push(Token::Annotation(attr.clone())); + tokens.push(ws!()); + } + } + + let inner_tokens = match &item.inner { + ItemEnum::Module(_) => self.render_simple(&["mod"], item_path), + ItemEnum::ExternCrate { .. } => self.render_simple(&["extern", "crate"], item_path), + ItemEnum::Import(_) => self.render_simple(&["use"], item_path), + ItemEnum::Union(_) => self.render_simple(&["union"], item_path), + ItemEnum::Struct(s) => { + let mut output = self.render_simple(&["struct"], item_path); + output.extend(self.render_generics(&s.generics)); + if let StructKind::Tuple(fields) = &s.kind { + output.extend( + self.render_option_tuple(&self.resolve_tuple_fields(fields), Some(&pub_())), + ); + } + output + } + ItemEnum::StructField(inner) => { + let mut output = self.render_simple(&[], item_path); + output.extend(colon()); + output.extend(self.render_type(inner)); + output + } + ItemEnum::Enum(e) => { + let mut output = self.render_simple(&["enum"], item_path); + output.extend(self.render_generics(&e.generics)); + output + } + ItemEnum::Variant(inner) => { + let mut output = self.render_simple(&[], item_path); + match &inner.kind { + VariantKind::Struct { .. } => {} // Each struct field is printed individually + VariantKind::Plain => { + if let Some(discriminant) = &inner.discriminant { + output.extend(equals()); + output.push(Token::identifier(&discriminant.value)); + } + } + VariantKind::Tuple(fields) => { + output.extend( + self.render_option_tuple(&self.resolve_tuple_fields(fields), None), + ); + } + } + output + } + ItemEnum::Function(inner) => self.render_function( + self.render_path(item_path), + &inner.decl, + &inner.generics, + &inner.header, + ), + ItemEnum::Trait(trait_) => self.render_trait(trait_, item_path), + ItemEnum::TraitAlias(_) => self.render_simple(&["trait", "alias"], item_path), + ItemEnum::Impl(impl_) => { + self.render_impl(impl_, item_path, false /* disregard_negativity */) + } + ItemEnum::TypeAlias(inner) => { + let mut output = self.render_simple(&["type"], item_path); + output.extend(self.render_generics(&inner.generics)); + output.extend(equals()); + output.extend(self.render_type(&inner.type_)); + output + } + ItemEnum::AssocType { + generics, + bounds, + default, + } => { + let mut output = self.render_simple(&["type"], item_path); + output.extend(self.render_generics(generics)); + output.extend(self.render_generic_bounds_with_colon(bounds)); + if let Some(ty) = default { + output.extend(equals()); + output.extend(self.render_type(ty)); + } + output + } + ItemEnum::OpaqueTy(_) => self.render_simple(&["opaque", "type"], item_path), + ItemEnum::Constant(con) => { + let mut output = self.render_simple(&["const"], item_path); + output.extend(colon()); + output.extend(self.render_constant(con)); + output + } + ItemEnum::AssocConst { type_, .. } => { + let mut output = self.render_simple(&["const"], item_path); + output.extend(colon()); + output.extend(self.render_type(type_)); + output + } + ItemEnum::Static(inner) => { + let tags = if inner.mutable { + vec!["mut", "static"] + } else { + vec!["static"] + }; + let mut output = self.render_simple(&tags, item_path); + output.extend(colon()); + output.extend(self.render_type(&inner.type_)); + output + } + ItemEnum::ForeignType => self.render_simple(&["type"], item_path), + ItemEnum::Macro(_definition) => { + // TODO: _definition contains the whole definition, it would be really neat to get out all possible ways to invoke it + let mut output = self.render_simple(&["macro"], item_path); + output.push(Token::symbol("!")); + output + } + ItemEnum::ProcMacro(inner) => { + let mut output = self.render_simple(&["proc", "macro"], item_path); + output.pop(); // Remove name of macro to possibly wrap it in `#[]` + let name = Token::identifier(item.name.as_deref().unwrap_or("")); + match inner.kind { + MacroKind::Bang => output.extend(vec![name, Token::symbol("!()")]), + MacroKind::Attr => { + output.extend(vec![Token::symbol("#["), name, Token::symbol("]")]); + } + MacroKind::Derive => { + output.extend(vec![Token::symbol("#[derive("), name, Token::symbol(")]")]); + } + } + output + } + ItemEnum::Primitive(primitive) => { + // This is hard to write tests for since only Rust `core` is + // allowed to define primitives. So you have to test this code + // using the pre-built rustdoc JSON for core: + // + // rustup component add rust-docs-json --toolchain nightly + // cargo run -- --rustdoc-json ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/share/doc/rust/json/core.json + let mut output = pub_(); + output.extend([ + Token::kind("type"), + ws!(), + Token::primitive(&primitive.name), + ]); + output + } + }; + + tokens.extend(inner_tokens); + + tokens + } + + /// Tuple fields are referenced by ID in JSON, but we need to look up the + /// actual types that the IDs correspond to, in order to render the fields. + /// This helper does that for a slice of fields. + fn resolve_tuple_fields(&self, fields: &[Option]) -> Vec> { + let mut resolved_fields: Vec> = vec![]; + + for id in fields { + resolved_fields.push( + if let Some(Item { + inner: ItemEnum::StructField(type_), + .. + }) = id.as_ref().and_then(|id| self.crate_.index.get(id)) + { + Some(type_) + } else { + None + }, + ); + } + + resolved_fields + } + + fn render_simple(&self, tags: &[&str], path: &[PathComponent]) -> Vec { + let mut output = pub_(); + output.extend( + tags.iter() + .flat_map(|t| [Token::kind(*t), ws!()]) + .collect::>(), + ); + output.extend(self.render_path(path)); + output + } + + fn render_path(&self, path: &[PathComponent]) -> Vec { + let mut output = vec![]; + for component in path { + if component.hide { + continue; + } + + let (tokens, push_a_separator) = component.type_.map_or_else( + || self.render_nameable_item(&component.item), + |ty| self.render_type_and_separator(ty), + ); + + output.extend(tokens); + + if push_a_separator { + output.push(Token::symbol("::")); + } + } + if !path.is_empty() { + output.pop(); // Remove last "::" so "a::b::c::" becomes "a::b::c" + } + output + } + + fn render_nameable_item(&self, item: &NameableItem) -> (Vec, bool) { + let mut push_a_separator = false; + let mut output = vec![]; + let token_fn = if matches!(item.item.inner, ItemEnum::Function(_)) { + Token::function + } else if matches!( + item.item.inner, + ItemEnum::Trait(_) + | ItemEnum::Struct(_) + | ItemEnum::Union(_) + | ItemEnum::Enum(_) + | ItemEnum::TypeAlias(_) + ) { + Token::type_ + } else { + Token::identifier + }; + + if let Some(name) = item.name() { + // If we are not debugging, some items (read: impls) do not have + // a name, so only push a name if it exists + output.push(token_fn(name.to_string())); + push_a_separator = true; + } + (output, push_a_separator) + } + + fn render_sequence( + &self, + start: Vec, + end: Vec, + between: Vec, + sequence: &[T], + render: impl Fn(&T) -> Vec, + ) -> Vec { + self.render_sequence_impl(start, end, between, false, sequence, render) + } + + fn render_sequence_if_not_empty( + &self, + start: Vec, + end: Vec, + between: Vec, + sequence: &[T], + render: impl Fn(&T) -> Vec, + ) -> Vec { + self.render_sequence_impl(start, end, between, true, sequence, render) + } + + fn render_sequence_impl( + &self, + start: Vec, + end: Vec, + between: Vec, + return_nothing_if_empty: bool, + sequence: &[T], + render: impl Fn(&T) -> Vec, + ) -> Vec { + if return_nothing_if_empty && sequence.is_empty() { + return vec![]; + } + let mut output = start; + for (index, seq) in sequence.iter().enumerate() { + output.extend(render(seq)); + if index < sequence.len() - 1 { + output.extend(between.clone()); + } + } + output.extend(end); + output + } + + fn render_type(&self, ty: &Type) -> Vec { + self.render_option_type(&Some(ty)) + } + + fn render_type_and_separator(&self, ty: &Type) -> (Vec, bool) { + (self.render_type(ty), true) + } + + fn render_option_type(&self, ty: &Option<&Type>) -> Vec { + let Some(ty) = ty else { + return vec![Token::symbol("_")]; + }; // The `_` in `EnumWithStrippedTupleVariants::DoubleFirstHidden(_, bool)` + match ty { + Type::ResolvedPath(path) => self.render_resolved_path(path), + Type::DynTrait(dyn_trait) => self.render_dyn_trait(dyn_trait), + Type::Generic(name) => vec![Token::generic(name)], + Type::Primitive(name) => vec![Token::primitive(name)], + Type::FunctionPointer(ptr) => self.render_function_pointer(ptr), + Type::Tuple(types) => self.render_tuple(types), + Type::Slice(ty) => self.render_slice(ty), + Type::Array { type_, len } => self.render_array(type_, len), + Type::ImplTrait(bounds) => self.render_impl_trait(bounds), + Type::Infer => vec![Token::symbol("_")], + Type::RawPointer { mutable, type_ } => self.render_raw_pointer(*mutable, type_), + Type::BorrowedRef { + lifetime, + mutable, + type_, + } => self.render_borrowed_ref(lifetime.as_deref(), *mutable, type_), + Type::QualifiedPath { + name, + args: _, + self_type, + trait_, + } => self.render_qualified_path(self_type, trait_.as_ref(), name), + } + } + + fn render_trait(&self, trait_: &Trait, path: &[PathComponent]) -> Vec { + let mut output = pub_(); + if trait_.is_unsafe { + output.extend(vec![Token::qualifier("unsafe"), ws!()]); + }; + output.extend([Token::kind("trait"), ws!()]); + output.extend(self.render_path(path)); + output.extend(self.render_generics(&trait_.generics)); + output.extend(self.render_generic_bounds_with_colon(&trait_.bounds)); + output + } + + fn render_dyn_trait(&self, dyn_trait: &rustdoc_types::DynTrait) -> Vec { + let mut output = vec![]; + + let more_than_one = dyn_trait.traits.len() > 1 || dyn_trait.lifetime.is_some(); + if more_than_one { + output.push(Token::symbol("(")); + } + + output.extend(self.render_sequence_if_not_empty( + vec![Token::keyword("dyn"), ws!()], + vec![], + plus(), + &dyn_trait.traits, + |p| self.render_poly_trait(p), + )); + + if let Some(lt) = &dyn_trait.lifetime { + output.extend(plus()); + output.extend(vec![Token::lifetime(lt)]); + } + + if more_than_one { + output.push(Token::symbol(")")); + } + + output + } + + fn render_function( + &self, + name: Vec, + decl: &FnDecl, + generics: &Generics, + header: &Header, + ) -> Vec { + let mut output = pub_(); + if header.unsafe_ { + output.extend(vec![Token::qualifier("unsafe"), ws!()]); + }; + if header.const_ { + output.extend(vec![Token::qualifier("const"), ws!()]); + }; + if header.async_ { + output.extend(vec![Token::qualifier("async"), ws!()]); + }; + if header.abi != Abi::Rust { + output.push(match &header.abi { + Abi::C { .. } => Token::qualifier("c"), + Abi::Cdecl { .. } => Token::qualifier("cdecl"), + Abi::Stdcall { .. } => Token::qualifier("stdcall"), + Abi::Fastcall { .. } => Token::qualifier("fastcall"), + Abi::Aapcs { .. } => Token::qualifier("aapcs"), + Abi::Win64 { .. } => Token::qualifier("win64"), + Abi::SysV64 { .. } => Token::qualifier("sysV64"), + Abi::System { .. } => Token::qualifier("system"), + Abi::Other(text) => Token::qualifier(text), + Abi::Rust => unreachable!(), + }); + output.push(ws!()); + } + + output.extend(vec![Token::kind("fn"), ws!()]); + output.extend(name); + + // Generic parameters + output.extend(self.render_generic_param_defs(&generics.params)); + + // Regular parameters and return type + output.extend(self.render_fn_decl(decl)); + + // Where predicates + output.extend(self.render_where_predicates(&generics.where_predicates)); + + output + } + + fn render_fn_decl(&self, decl: &FnDecl) -> Vec { + let mut output = vec![]; + // Main arguments + output.extend(self.render_sequence( + vec![Token::symbol("(")], + vec![Token::symbol(")")], + comma(), + &decl.inputs, + |(name, ty)| { + self.simplified_self(name, ty).unwrap_or_else(|| { + let mut output = vec![]; + if name != "_" { + output.extend(vec![Token::identifier(name), Token::symbol(":"), ws!()]); + } + output.extend(self.render_type(ty)); + output + }) + }, + )); + // Return type + if let Some(ty) = &decl.output { + output.extend(arrow()); + output.extend(self.render_type(ty)); + } + output + } + + fn simplified_self(&self, name: &str, ty: &Type) -> Option> { + if name == "self" { + match ty { + Type::Generic(name) if name == "Self" => Some(vec![Token::self_("self")]), + Type::BorrowedRef { + lifetime, + mutable, + type_, + } => match type_.as_ref() { + Type::Generic(name) if name == "Self" => { + let mut output = vec![Token::symbol("&")]; + if let Some(lt) = lifetime { + output.extend(vec![Token::lifetime(lt), ws!()]); + } + if *mutable { + output.extend(vec![Token::keyword("mut"), ws!()]); + } + output.push(Token::self_("self")); + Some(output) + } + _ => None, + }, + _ => None, + } + } else { + None + } + } + + fn render_resolved_path(&self, path: &Path) -> Vec { + let mut output = vec![]; + if let Some(item) = self.best_item_for_id(&path.id) { + output.extend(self.render_path(item.path())); + } else if let Some(item) = self.crate_.paths.get(&path.id) { + output.extend(self.render_path_components(item.path.iter().map(Deref::deref))); + } else if !path.name.is_empty() { + // If we get here it means there was no item for this Path in the + // rustdoc JSON. Examples of when this happens: + // + // * The resolved path is for a public item inside a private mod + // (and thus effectively the item is not public) + // + // In these cases we simply use the `name` verbatim, which typically + // is equal to how it appears in the source text. It might not be + // ideal and end up identical to the corresponding rustdoc HTML, but + // it is good enough given the edge-case nature of this code path. + output.extend(self.render_path_name(&path.name)); + } + if let Some(args) = &path.args { + output.extend(self.render_generic_args(args)); + } + output + } + + fn render_path_name(&self, name: &str) -> Vec { + self.render_path_components(name.split("::")) + } + + fn render_path_components( + &self, + path_iter: impl Iterator>, + ) -> Vec { + let mut output = vec![]; + let path: Vec<_> = path_iter.collect(); + let len = path.len(); + for (index, part) in path.into_iter().enumerate() { + if index == len - 1 { + output.push(Token::type_(part.as_ref())); + } else { + output.push(Token::identifier(part.as_ref())); + } + output.push(Token::symbol("::")); + } + if len > 0 { + output.pop(); + } + output + } + + fn render_function_pointer(&self, ptr: &FunctionPointer) -> Vec { + let mut output = self.render_higher_rank_trait_bounds(&ptr.generic_params); + output.push(Token::kind("fn")); + output.extend(self.render_fn_decl(&ptr.decl)); + output + } + + fn render_tuple(&self, types: &[Type]) -> Vec { + let option_tuple: Vec> = types.iter().map(Some).collect(); + self.render_option_tuple(&option_tuple, None) + } + + /// `prefix` is to handle the difference between tuple structs and enum variant + /// tuple structs. The former marks public fields as `pub ` whereas all fields + /// of enum tuple structs are always implicitly `pub`. + fn render_option_tuple(&self, types: &[Option<&Type>], prefix: Option<&[Token]>) -> Vec { + self.render_sequence( + vec![Token::symbol("(")], + vec![Token::symbol(")")], + comma(), + types, + |type_| { + let mut output: Vec = vec![]; + if let (Some(prefix), Some(_)) = (prefix, type_) { + output.extend(prefix.to_owned()); + } + output.extend(self.render_option_type(type_)); + output + }, + ) + } + + fn render_slice(&self, ty: &Type) -> Vec { + let mut output = vec![Token::symbol("[")]; + output.extend(self.render_type(ty)); + output.push(Token::symbol("]")); + output + } + + fn render_array(&self, type_: &Type, len: &str) -> Vec { + let mut output = vec![Token::symbol("[")]; + output.extend(self.render_type(type_)); + output.extend(vec![ + Token::symbol(";"), + ws!(), + Token::primitive(len), + Token::symbol("]"), + ]); + output + } + + pub(crate) fn render_impl( + &self, + impl_: &Impl, + path: &[PathComponent], + disregard_negativity: bool, + ) -> Vec { + let mut output = vec![]; + + if impl_.is_unsafe { + output.extend(vec![Token::keyword("unsafe"), ws!()]); + } + + output.push(Token::keyword("impl")); + + output.extend(self.render_generic_param_defs(&impl_.generics.params)); + + output.push(ws!()); + + if let Some(trait_) = &impl_.trait_ { + if !disregard_negativity && impl_.negative { + output.push(Token::symbol("!")); + } + output.extend(self.render_resolved_path(trait_)); + output.extend(vec![ws!(), Token::keyword("for"), ws!()]); + output.extend(self.render_type(&impl_.for_)); + } else { + output.extend(self.render_type(&impl_.for_)); + } + + output.extend(self.render_where_predicates(&impl_.generics.where_predicates)); + + output + } + + fn render_impl_trait(&self, bounds: &[GenericBound]) -> Vec { + let mut output = vec![Token::keyword("impl")]; + output.push(ws!()); + output.extend(self.render_generic_bounds(bounds)); + output + } + + fn render_raw_pointer(&self, mutable: bool, type_: &Type) -> Vec { + let mut output = vec![Token::symbol("*")]; + output.push(Token::keyword(if mutable { "mut" } else { "const" })); + output.push(ws!()); + output.extend(self.render_type(type_)); + output + } + + fn render_borrowed_ref( + &self, + lifetime: Option<&str>, + mutable: bool, + type_: &Type, + ) -> Vec { + let mut output = vec![Token::symbol("&")]; + if let Some(lt) = lifetime { + output.extend(vec![Token::lifetime(lt), ws!()]); + } + if mutable { + output.extend(vec![Token::keyword("mut"), ws!()]); + } + output.extend(self.render_type(type_)); + output + } + + fn render_qualified_path(&self, type_: &Type, trait_: Option<&Path>, name: &str) -> Vec { + let mut output = vec![]; + match (type_, trait_) { + (Type::Generic(name), Some(trait_)) if name == "Self" && trait_.name.is_empty() => { + output.push(Token::keyword("Self")); + } + (_, trait_) => { + if trait_.is_some() { + output.push(Token::symbol("<")); + } + output.extend(self.render_type(type_)); + if let Some(trait_) = trait_ { + output.extend(vec![ws!(), Token::keyword("as"), ws!()]); + output.extend(self.render_resolved_path(trait_)); + output.push(Token::symbol(">")); + } + } + } + output.push(Token::symbol("::")); + output.push(Token::identifier(name)); + output + } + + fn render_generic_args(&self, args: &GenericArgs) -> Vec { + match args { + GenericArgs::AngleBracketed { args, bindings } => { + self.render_angle_bracketed(args, bindings) + } + GenericArgs::Parenthesized { inputs, output } => { + self.render_parenthesized(inputs, output) + } + } + } + + fn render_parenthesized(&self, inputs: &[Type], return_ty: &Option) -> Vec { + let mut output = self.render_sequence( + vec![Token::symbol("(")], + vec![Token::symbol(")")], + comma(), + inputs, + |type_| self.render_type(type_), + ); + if let Some(return_ty) = return_ty { + output.extend(arrow()); + output.extend(self.render_type(return_ty)); + } + output + } + + fn render_angle_bracketed(&self, args: &[GenericArg], bindings: &[TypeBinding]) -> Vec { + enum Arg<'c> { + GenericArg(&'c GenericArg), + TypeBinding(&'c TypeBinding), + } + self.render_sequence_if_not_empty( + vec![Token::symbol("<")], + vec![Token::symbol(">")], + comma(), + &args + .iter() + .map(Arg::GenericArg) + .chain(bindings.iter().map(Arg::TypeBinding)) + .collect::>(), + |arg| match arg { + Arg::GenericArg(arg) => self.render_generic_arg(arg), + Arg::TypeBinding(binding) => self.render_type_binding(binding), + }, + ) + } + + fn render_term(&self, term: &Term) -> Vec { + match term { + Term::Type(ty) => self.render_type(ty), + Term::Constant(c) => self.render_constant(c), + } + } + + fn render_poly_trait(&self, poly_trait: &PolyTrait) -> Vec { + let mut output = self.render_higher_rank_trait_bounds(&poly_trait.generic_params); + output.extend(self.render_resolved_path(&poly_trait.trait_)); + output + } + + fn render_generic_arg(&self, arg: &GenericArg) -> Vec { + match arg { + GenericArg::Lifetime(name) => vec![Token::lifetime(name)], + GenericArg::Type(ty) => self.render_type(ty), + GenericArg::Const(c) => self.render_constant(c), + GenericArg::Infer => vec![Token::symbol("_")], + } + } + + fn render_type_binding(&self, binding: &TypeBinding) -> Vec { + let mut output = vec![Token::identifier(&binding.name)]; + output.extend(self.render_generic_args(&binding.args)); + match &binding.binding { + TypeBindingKind::Equality(term) => { + output.extend(equals()); + output.extend(self.render_term(term)); + } + TypeBindingKind::Constraint(bounds) => { + output.extend(self.render_generic_bounds(bounds)); + } + } + output + } + + fn render_constant(&self, constant: &Constant) -> Vec { + let mut output = vec![]; + if constant.is_literal { + output.extend(self.render_type(&constant.type_)); + if let Some(value) = &constant.value { + output.extend(equals()); + if constant.is_literal { + output.push(Token::primitive(value)); + } else { + output.push(Token::identifier(value)); + } + } + } else { + output.push(Token::identifier(&constant.expr)); + } + output + } + + fn render_generics(&self, generics: &Generics) -> Vec { + let mut output = vec![]; + output.extend(self.render_generic_param_defs(&generics.params)); + output.extend(self.render_where_predicates(&generics.where_predicates)); + output + } + + fn render_generic_param_defs(&self, params: &[GenericParamDef]) -> Vec { + let params_without_synthetics: Vec<_> = params + .iter() + .filter(|p| { + if let GenericParamDefKind::Type { synthetic, .. } = p.kind { + !synthetic + } else { + true + } + }) + .collect(); + + self.render_sequence_if_not_empty( + vec![Token::symbol("<")], + vec![Token::symbol(">")], + comma(), + ¶ms_without_synthetics, + |param| self.render_generic_param_def(param), + ) + } + + fn render_generic_param_def(&self, generic_param_def: &GenericParamDef) -> Vec { + let mut output = vec![]; + match &generic_param_def.kind { + GenericParamDefKind::Lifetime { outlives } => { + output.push(Token::lifetime(&generic_param_def.name)); + if !outlives.is_empty() { + output.extend(colon()); + output.extend(self.render_sequence(vec![], vec![], plus(), outlives, |s| { + vec![Token::lifetime(s)] + })); + } + } + GenericParamDefKind::Type { bounds, .. } => { + output.push(Token::generic(&generic_param_def.name)); + output.extend(self.render_generic_bounds_with_colon(bounds)); + } + GenericParamDefKind::Const { type_, .. } => { + output.push(Token::qualifier("const")); + output.push(ws!()); + output.push(Token::identifier(&generic_param_def.name)); + output.extend(colon()); + output.extend(self.render_type(type_)); + } + } + output + } + + fn render_where_predicates(&self, where_predicates: &[WherePredicate]) -> Vec { + let mut output = vec![]; + if !where_predicates.is_empty() { + output.push(ws!()); + output.push(Token::Keyword("where".to_owned())); + output.push(ws!()); + output.extend( + self.render_sequence(vec![], vec![], comma(), where_predicates, |p| { + self.render_where_predicate(p) + }), + ); + } + output + } + + fn render_where_predicate(&self, where_predicate: &WherePredicate) -> Vec { + let mut output = vec![]; + match where_predicate { + WherePredicate::BoundPredicate { + type_, + bounds, + generic_params, + } => { + output.extend(self.render_higher_rank_trait_bounds(generic_params)); + output.extend(self.render_type(type_)); + output.extend(self.render_generic_bounds_with_colon(bounds)); + } + WherePredicate::RegionPredicate { + lifetime, + bounds: _, + } => output.push(Token::Lifetime(lifetime.clone())), + WherePredicate::EqPredicate { lhs, rhs } => { + output.extend(self.render_type(lhs)); + output.extend(equals()); + output.extend(self.render_term(rhs)); + } + } + output + } + + fn render_generic_bounds_with_colon(&self, bounds: &[GenericBound]) -> Vec { + let mut output = vec![]; + if !bounds.is_empty() { + output.extend(colon()); + output.extend(self.render_generic_bounds(bounds)); + } + output + } + + fn render_generic_bounds(&self, bounds: &[GenericBound]) -> Vec { + self.render_sequence_if_not_empty(vec![], vec![], plus(), bounds, |bound| match bound { + GenericBound::TraitBound { + trait_, + generic_params, + .. + } => { + let mut output = vec![]; + output.extend(self.render_higher_rank_trait_bounds(generic_params)); + output.extend(self.render_resolved_path(trait_)); + output + } + GenericBound::Outlives(id) => vec![Token::lifetime(id)], + }) + } + + fn render_higher_rank_trait_bounds(&self, generic_params: &[GenericParamDef]) -> Vec { + let mut output = vec![]; + if !generic_params.is_empty() { + output.push(Token::keyword("for")); + output.extend(self.render_generic_param_defs(generic_params)); + output.push(ws!()); + } + output + } + + fn best_item_for_id(&self, id: &'c Id) -> Option<&'c IntermediatePublicItem<'c>> { + match self.id_to_items.get(&id) { + None => None, + Some(items) => { + items + .iter() + .max_by(|a, b| { + // If there is any item in the path that has been + // renamed/re-exported, i.e. that is not the original + // path, prefer that less than an item with a path where + // all items are original. + let mut ordering = match ( + a.path_contains_renamed_item(), + b.path_contains_renamed_item(), + ) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => Ordering::Equal, + }; + + // If we still can't make up our mind, go with the shortest path + if ordering == Ordering::Equal { + ordering = b.path().len().cmp(&a.path().len()); + } + + ordering + }) + .copied() + } + } + } +} + +/// Our list of allowed attributes comes from +/// +fn attr_relevant_for_public_apis>(attr: S) -> bool { + let prefixes = [ + "#[export_name", + "#[link_section", + "#[no_mangle", + "#[non_exhaustive", + "#[repr", + ]; + + for prefix in prefixes { + if attr.as_ref().starts_with(prefix) { + return true; + } + } + + false +} + +fn pub_() -> Vec { + vec![Token::qualifier("pub"), ws!()] +} + +fn plus() -> Vec { + vec![ws!(), Token::symbol("+"), ws!()] +} + +fn colon() -> Vec { + vec![Token::symbol(":"), ws!()] +} + +fn comma() -> Vec { + vec![Token::symbol(","), ws!()] +} + +fn equals() -> Vec { + vec![ws!(), Token::symbol("="), ws!()] +} + +fn arrow() -> Vec { + vec![ws!(), Token::symbol("->"), ws!()] +} + +#[cfg(test)] +mod test { + macro_rules! s { + ($value:literal) => { + $value.to_string() + }; + } + + use crate::codegen::tokens; + + use super::*; + + #[test] + fn test_type_infer() { + assert_render( + |context| context.render_type(&Type::Infer), + vec![Token::symbol("_")], + "_", + ); + } + + #[test] + fn test_type_generic() { + assert_render( + |context| context.render_type(&Type::Generic(s!("name"))), + vec![Token::generic("name")], + "name", + ); + } + + #[test] + fn test_type_primitive() { + assert_render( + |context| context.render_type(&Type::Primitive(s!("name"))), + vec![Token::primitive("name")], + "name", + ); + } + + #[test] + fn test_type_resolved_simple() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("name"), + args: None, + id: Id(s!("id")), + })) + }, + vec![Token::type_("name")], + "name", + ); + } + + #[test] + fn test_type_resolved_long_name() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("name::with::parts"), + args: None, + id: Id(s!("id")), + })) + }, + vec![ + Token::identifier("name"), + Token::symbol("::"), + Token::identifier("with"), + Token::symbol("::"), + Token::type_("parts"), + ], + "name::with::parts", + ); + } + + #[test] + fn test_type_resolved_crate_name() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("$crate::name"), + args: None, + id: Id(s!("id")), + })) + }, + vec![ + Token::identifier("$crate"), + Token::symbol("::"), + Token::type_("name"), + ], + "$crate::name", + ); + } + + #[test] + fn test_type_resolved_name_crate() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("name::$crate"), + args: None, + id: Id(s!("id")), + })) + }, + vec![ + Token::identifier("name"), + Token::symbol("::"), + Token::type_("$crate"), + ], + "name::$crate", + ); + } + + #[test] + fn test_type_tuple_empty() { + assert_render( + |context| context.render_type(&Type::Tuple(vec![])), + vec![Token::symbol("("), Token::symbol(")")], + "()", + ); + } + + #[test] + fn test_type_tuple() { + assert_render( + |context| { + context.render_type(&Type::Tuple(vec![Type::Infer, Type::Generic(s!("gen"))])) + }, + vec![ + Token::symbol("("), + Token::symbol("_"), + Token::symbol(","), + ws!(), + Token::generic("gen"), + Token::symbol(")"), + ], + "(_, gen)", + ); + } + + #[test] + fn test_type_slice() { + assert_render( + |context| context.render_type(&Type::Slice(Box::new(Type::Infer))), + vec![Token::symbol("["), Token::symbol("_"), Token::symbol("]")], + "[_]", + ); + } + + #[test] + fn test_type_array() { + assert_render( + |context| { + context.render_type(&Type::Array { + type_: Box::new(Type::Infer), + len: s!("20"), + }) + }, + vec![ + Token::symbol("["), + Token::symbol("_"), + Token::symbol(";"), + ws!(), + Token::primitive("20"), + Token::symbol("]"), + ], + "[_; 20]", + ); + } + + #[test] + fn test_type_pointer() { + assert_render( + |context| { + context.render_type(&Type::RawPointer { + mutable: false, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("*"), + Token::keyword("const"), + ws!(), + Token::symbol("_"), + ], + "*const _", + ); + } + + #[test] + fn test_type_pointer_mut() { + assert_render( + |context| { + context.render_type(&Type::RawPointer { + mutable: true, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("*"), + Token::keyword("mut"), + ws!(), + Token::symbol("_"), + ], + "*mut _", + ); + } + + #[test] + fn test_type_ref() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: None, + mutable: false, + type_: Box::new(Type::Infer), + }) + }, + vec![Token::symbol("&"), Token::symbol("_")], + "&_", + ); + } + + #[test] + fn test_type_ref_mut() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: None, + mutable: true, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("&"), + Token::keyword("mut"), + ws!(), + Token::symbol("_"), + ], + "&mut _", + ); + } + + #[test] + fn test_type_ref_lt() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: Some(s!("'a")), + mutable: false, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("&"), + Token::lifetime("'a"), + ws!(), + Token::symbol("_"), + ], + "&'a _", + ); + } + + #[test] + fn test_type_ref_lt_mut() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: Some(s!("'a")), + mutable: true, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("&"), + Token::lifetime("'a"), + ws!(), + Token::keyword("mut"), + ws!(), + Token::symbol("_"), + ], + "&'a mut _", + ); + } + + #[test] + fn test_type_path() { + assert_render( + |context| { + context.render_type(&Type::QualifiedPath { + name: s!("name"), + args: Box::new(GenericArgs::AngleBracketed { + args: vec![], + bindings: vec![], + }), + self_type: Box::new(Type::Generic(s!("type"))), + trait_: Some(Path { + name: String::from("trait"), + args: None, + id: Id(s!("id")), + }), + }) + }, + vec![ + Token::symbol("<"), + Token::generic("type"), + ws!(), + Token::keyword("as"), + ws!(), + Token::type_("trait"), + Token::symbol(">"), + Token::symbol("::"), + Token::identifier("name"), + ], + "::name", + ); + } + + fn assert_render( + render_fn: impl Fn(RenderingContext) -> Vec, + expected: Vec, + expected_string: &str, + ) { + let crate_ = Crate { + root: Id(String::from("1:2:3")), + crate_version: None, + includes_private: false, + index: HashMap::new(), + paths: HashMap::new(), + external_crates: HashMap::new(), + format_version: 0, + }; + let context = RenderingContext { + crate_: &crate_, + id_to_items: HashMap::new(), + }; + + let actual = render_fn(context); + + assert_eq!(actual, expected); + assert_eq!( + tokens::tokens_to_string(&actual), + expected_string.to_string() + ); + } +} diff --git a/crux_cli/src/codegen/tokens.rs b/crux_cli/src/codegen/tokens.rs new file mode 100644 index 000000000..2f7147b66 --- /dev/null +++ b/crux_cli/src/codegen/tokens.rs @@ -0,0 +1,110 @@ +//! Contains all token handling logic. +#[cfg(doc)] +use crate::public_item::PublicItem; + +/// A token in a rendered [`PublicItem`], used to apply syntax coloring in downstream applications. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Token { + /// A symbol, like `=` or `::<` + Symbol(String), + /// A qualifier, like `pub` or `const` + Qualifier(String), + /// The kind of an item, like `function` or `trait` + Kind(String), + /// Whitespace, a single space + Whitespace, + /// An identifier, like variable names or parts of the path of an item + Identifier(String), + /// An annotation, used e.g. for Rust attributes. + Annotation(String), + /// The identifier self, the text can be `self` or `Self` + Self_(String), + /// The identifier for a function, like `fn_arg` in `comprehensive_api::functions::fn_arg` + Function(String), + /// A lifetime including the apostrophe `'`, like `'a` + Lifetime(String), + /// A keyword, like `impl`, `where`, or `dyn` + Keyword(String), + /// A generic parameter, like `T` + Generic(String), + /// A primitive type, like `usize` + Primitive(String), + /// A non-primitive type, like the name of a struct or a trait + Type(String), +} + +impl Token { + /// A symbol, like `=` or `::<` + pub(crate) fn symbol(text: impl Into) -> Self { + Self::Symbol(text.into()) + } + /// A qualifier, like `pub` or `const` + pub(crate) fn qualifier(text: impl Into) -> Self { + Self::Qualifier(text.into()) + } + /// The kind of an item, like `function` or `trait` + pub(crate) fn kind(text: impl Into) -> Self { + Self::Kind(text.into()) + } + /// An identifier, like variable names or parts of the path of an item + pub(crate) fn identifier(text: impl Into) -> Self { + Self::Identifier(text.into()) + } + /// The identifier self, the text can be `self` or `Self` + pub(crate) fn self_(text: impl Into) -> Self { + Self::Self_(text.into()) + } + /// The identifier for a function, like `fn_arg` in `comprehensive_api::functions::fn_arg` + pub(crate) fn function(text: impl Into) -> Self { + Self::Function(text.into()) + } + /// A lifetime including the apostrophe `'`, like `'a` + pub(crate) fn lifetime(text: impl Into) -> Self { + Self::Lifetime(text.into()) + } + /// A keyword, like `impl` + pub(crate) fn keyword(text: impl Into) -> Self { + Self::Keyword(text.into()) + } + /// A generic, like `T` + pub(crate) fn generic(text: impl Into) -> Self { + Self::Generic(text.into()) + } + /// A primitive type, like `usize` + pub(crate) fn primitive(text: impl Into) -> Self { + Self::Primitive(text.into()) + } + /// A type, like `Iterator` + pub(crate) fn type_(text: impl Into) -> Self { + Self::Type(text.into()) + } + /// Give the length of the inner text of this token + #[allow(clippy::len_without_is_empty)] + #[must_use] + pub fn len(&self) -> usize { + self.text().len() + } + /// Get the inner text of this token + #[must_use] + pub fn text(&self) -> &str { + match self { + Self::Symbol(l) + | Self::Qualifier(l) + | Self::Kind(l) + | Self::Identifier(l) + | Self::Annotation(l) + | Self::Self_(l) + | Self::Function(l) + | Self::Lifetime(l) + | Self::Keyword(l) + | Self::Generic(l) + | Self::Primitive(l) + | Self::Type(l) => l, + Self::Whitespace => " ", + } + } +} + +pub(crate) fn tokens_to_string(tokens: &[Token]) -> String { + tokens.iter().map(Token::text).collect() +} From 7814e16602f738e0b255e83b07366c02c2767347 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Tue, 4 Jun 2024 16:37:01 +0100 Subject: [PATCH 09/27] dig into generic type params --- crux_cli/src/codegen/item_processor.rs | 61 ++++++++++++++++++-------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/item_processor.rs index d9e39c35e..61a67f225 100644 --- a/crux_cli/src/codegen/item_processor.rs +++ b/crux_cli/src/codegen/item_processor.rs @@ -1,13 +1,10 @@ use super::nameable_item::NameableItem; use super::CrateWrapper; -use super::{ - intermediate_public_item::IntermediatePublicItem, path_component::PathComponent, - public_item::PublicItem, render::RenderingContext, -}; +use super::{intermediate_public_item::IntermediatePublicItem, path_component::PathComponent}; use rustdoc_types::{ - Crate, Id, Impl, Import, Item, ItemEnum, Module, Struct, StructKind, Type, Variant, VariantKind, + Crate, GenericArg, GenericArgs, Id, Impl, Import, Item, ItemEnum, Module, Path, Struct, + StructKind, Type, Variant, VariantKind, }; -use std::convert::identity; use std::{ collections::{HashMap, VecDeque}, vec, @@ -227,9 +224,6 @@ impl<'c> ItemProcessor<'c> { let children = children_for_item(item); let impls = impls_for_item(item).into_iter().flatten(); - if item.id == Id("0:428:2145".to_string()) { - println!("Processing: {:?}", item.id); - } for id in children { let parent_path = finished_item.path().into(); self.add_to_work_queue(parent_path, id); @@ -418,10 +412,19 @@ fn children_for_item(item: &Item) -> Vec<&Id> { match &item.inner { ItemEnum::Module(m) => m.items.iter().collect(), ItemEnum::Union(u) => u.fields.iter().collect(), + ItemEnum::Struct(Struct { + kind: StructKind::Unit, + .. + }) => vec![], + ItemEnum::Struct(Struct { + kind: StructKind::Tuple(fields), + .. + }) => fields.iter().flatten().collect(), ItemEnum::Struct(Struct { kind: StructKind::Plain { fields, .. }, .. }) => fields.iter().collect(), + ItemEnum::Variant(Variant { kind: VariantKind::Struct { fields, .. }, .. @@ -434,20 +437,42 @@ fn children_for_item(item: &Item) -> Vec<&Id> { kind: VariantKind::Tuple(fields), .. }) => fields.iter().flatten().collect(), - ItemEnum::Struct(Struct { - kind: StructKind::Unit, - .. - }) => vec![], - ItemEnum::Struct(Struct { - kind: StructKind::Tuple(fields), - .. - }) => fields.iter().flatten().collect(), + ItemEnum::Enum(e) => e.variants.iter().collect(), ItemEnum::Trait(t) => t.items.iter().collect(), ItemEnum::Impl(i) => i.items.iter().collect(), ItemEnum::ExternCrate { .. } => vec![], ItemEnum::Import(_) => vec![], - ItemEnum::StructField(_) => vec![], + ItemEnum::StructField(ty) => match ty { + Type::ResolvedPath(Path { + args: Some(args), .. + }) => match args.as_ref() { + GenericArgs::AngleBracketed { args, .. } => args + .iter() + .filter_map(|a| { + if let GenericArg::Type(Type::ResolvedPath(p)) = a { + Some(&p.id) + } else { + None + } + }) + .collect(), + GenericArgs::Parenthesized { .. } => vec![], + }, + Type::ResolvedPath(_) => vec![], + Type::DynTrait(_) => vec![], + Type::Generic(_) => vec![], + Type::Primitive(_) => vec![], + Type::FunctionPointer(_) => vec![], + Type::Tuple(_) => vec![], + Type::Slice(_) => vec![], + Type::Array { .. } => vec![], + Type::ImplTrait(_) => vec![], + Type::Infer => vec![], + Type::RawPointer { .. } => vec![], + Type::BorrowedRef { .. } => vec![], + Type::QualifiedPath { .. } => vec![], + }, ItemEnum::Function(_) => vec![], ItemEnum::TraitAlias(_) => vec![], ItemEnum::TypeAlias(_) => vec![], From 292c9c7c46573d501e30ff7dafe48701df782291 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Wed, 5 Jun 2024 10:23:34 +0100 Subject: [PATCH 10/27] nested generics --- crux_cli/src/codegen/item_processor.rs | 71 ++++++++------------------ 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/item_processor.rs index 61a67f225..aebf4f8f7 100644 --- a/crux_cli/src/codegen/item_processor.rs +++ b/crux_cli/src/codegen/item_processor.rs @@ -412,10 +412,6 @@ fn children_for_item(item: &Item) -> Vec<&Id> { match &item.inner { ItemEnum::Module(m) => m.items.iter().collect(), ItemEnum::Union(u) => u.fields.iter().collect(), - ItemEnum::Struct(Struct { - kind: StructKind::Unit, - .. - }) => vec![], ItemEnum::Struct(Struct { kind: StructKind::Tuple(fields), .. @@ -424,68 +420,45 @@ fn children_for_item(item: &Item) -> Vec<&Id> { kind: StructKind::Plain { fields, .. }, .. }) => fields.iter().collect(), - ItemEnum::Variant(Variant { kind: VariantKind::Struct { fields, .. }, .. }) => fields.iter().collect(), - ItemEnum::Variant(Variant { - kind: VariantKind::Plain, - .. - }) => vec![], ItemEnum::Variant(Variant { kind: VariantKind::Tuple(fields), .. }) => fields.iter().flatten().collect(), - ItemEnum::Enum(e) => e.variants.iter().collect(), ItemEnum::Trait(t) => t.items.iter().collect(), ItemEnum::Impl(i) => i.items.iter().collect(), - ItemEnum::ExternCrate { .. } => vec![], - ItemEnum::Import(_) => vec![], - ItemEnum::StructField(ty) => match ty { - Type::ResolvedPath(Path { - args: Some(args), .. - }) => match args.as_ref() { - GenericArgs::AngleBracketed { args, .. } => args + ItemEnum::StructField(ty) => items_for_type(ty), + _ => vec![], + } +} + +fn items_for_type(ty: &Type) -> Vec<&Id> { + let mut ids = Vec::new(); + if let Type::ResolvedPath(Path { id, args, name: _ }) = ty { + ids.push(id); + if let Some(args) = args { + if let GenericArgs::AngleBracketed { args, .. } = args.as_ref() { + let nested: Vec<&Id> = args .iter() .filter_map(|a| { - if let GenericArg::Type(Type::ResolvedPath(p)) = a { - Some(&p.id) + if let GenericArg::Type(ty) = a { + let ids = items_for_type(ty); + (!ids.is_empty()).then_some(ids) } else { None } }) - .collect(), - GenericArgs::Parenthesized { .. } => vec![], - }, - Type::ResolvedPath(_) => vec![], - Type::DynTrait(_) => vec![], - Type::Generic(_) => vec![], - Type::Primitive(_) => vec![], - Type::FunctionPointer(_) => vec![], - Type::Tuple(_) => vec![], - Type::Slice(_) => vec![], - Type::Array { .. } => vec![], - Type::ImplTrait(_) => vec![], - Type::Infer => vec![], - Type::RawPointer { .. } => vec![], - Type::BorrowedRef { .. } => vec![], - Type::QualifiedPath { .. } => vec![], - }, - ItemEnum::Function(_) => vec![], - ItemEnum::TraitAlias(_) => vec![], - ItemEnum::TypeAlias(_) => vec![], - ItemEnum::OpaqueTy(_) => vec![], - ItemEnum::Constant(_) => vec![], - ItemEnum::Static(_) => vec![], - ItemEnum::ForeignType => vec![], - ItemEnum::Macro(_) => vec![], - ItemEnum::ProcMacro(_) => vec![], - ItemEnum::Primitive(_) => vec![], - ItemEnum::AssocConst { .. } => vec![], - ItemEnum::AssocType { .. } => vec![], - } + .flatten() + .collect(); + ids.extend(nested) + } + } + }; + ids } pub fn impls_for_item(item: &Item) -> Option<&[Id]> { From 7052f3e4669ed30799e8f0bdf2e40de66b4358e8 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Thu, 6 Jun 2024 10:58:53 +0100 Subject: [PATCH 11/27] respect serde skip --- crux_cli/src/codegen/item_processor.rs | 4 +- crux_cli/src/codegen/parser.rs | 56 ++++++++------------------ 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/item_processor.rs index aebf4f8f7..8b6bb40fc 100644 --- a/crux_cli/src/codegen/item_processor.rs +++ b/crux_cli/src/codegen/item_processor.rs @@ -207,7 +207,9 @@ impl<'c> ItemProcessor<'c> { item: &'c Item, overridden_name: Option, ) { - self.process_item_for_type(unprocessed_item, item, overridden_name, None); + if !item.attrs.contains(&"#[serde(skip)]".to_string()) { + self.process_item_for_type(unprocessed_item, item, overridden_name, None); + } } /// Process an item. Setup jobs for its children and impls and and then put diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 932301f37..cd15d399c 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,18 +1,13 @@ use std::collections::HashMap; use anyhow::Result; -use rustdoc_types::{ - Crate, GenericArg, GenericArgs, Id, Impl, ItemEnum, Path, StructKind, Type, VariantKind, -}; +use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, Type}; use crate::codegen::{ - item_processor::ItemProcessor, - public_item::PublicItem, - render::RenderingContext, - rust_types::{RustField, SpecialRustType}, + item_processor::ItemProcessor, public_item::PublicItem, render::RenderingContext, }; -use super::rust_types::{RustEnum, RustStruct, RustType, RustTypeAlias}; +use super::rust_types::{RustEnum, RustStruct, RustTypeAlias}; /// The results of parsing Rust source input. #[derive(Default, Debug)] @@ -61,41 +56,24 @@ pub fn parse(crate_: &Crate) -> Result { crate_, id_to_items: item_processor.id_to_items(), }; - let items = item_processor + let items: Vec = item_processor .output .iter() .filter_map(|item| { - if match &item.item().inner { - ItemEnum::Union(_) => true, - ItemEnum::Struct(_) => true, - ItemEnum::StructField(_) => true, - ItemEnum::Enum(_) => true, - ItemEnum::Variant(_) => true, - ItemEnum::Primitive(_) => true, - ItemEnum::TypeAlias(_) => true, - ItemEnum::ForeignType => true, - - ItemEnum::Module(_) => false, - ItemEnum::ExternCrate { .. } => false, - ItemEnum::Import(_) => false, - ItemEnum::Function(_) => false, - ItemEnum::Trait(_) => false, - ItemEnum::TraitAlias(_) => false, - ItemEnum::Impl(_) => false, - ItemEnum::OpaqueTy(_) => false, - ItemEnum::Constant(_) => true, - ItemEnum::Static(_) => false, - ItemEnum::Macro(_) => false, - ItemEnum::ProcMacro(_) => false, - ItemEnum::AssocConst { .. } => false, - ItemEnum::AssocType { .. } => false, - } { - Some(PublicItem::from_intermediate_public_item(&context, item)) - } else { - None - } + matches!( + &item.item().inner, + ItemEnum::Union(_) + | ItemEnum::Struct(_) + | ItemEnum::StructField(_) + | ItemEnum::Enum(_) + | ItemEnum::Variant(_) + | ItemEnum::Primitive(_) + | ItemEnum::TypeAlias(_) + | ItemEnum::ForeignType + ) + .then_some(PublicItem::from_intermediate_public_item(&context, item)) }) - .collect::>(); + .collect(); println!("{items:#?}"); Ok(ParsedData::new()) } From 389cdedfcd18cd6953284652eaec1de5f524c08b Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Thu, 6 Jun 2024 11:13:50 +0100 Subject: [PATCH 12/27] fully qualified names --- crux_cli/src/codegen/parser.rs | 57 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index cd15d399c..2620b31aa 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -4,7 +4,11 @@ use anyhow::Result; use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, Type}; use crate::codegen::{ - item_processor::ItemProcessor, public_item::PublicItem, render::RenderingContext, + item_processor::{sorting_prefix, ItemProcessor}, + nameable_item::NameableItem, + path_component::PathComponent, + public_item::PublicItem, + render::RenderingContext, }; use super::rust_types::{RustEnum, RustStruct, RustTypeAlias}; @@ -28,28 +32,8 @@ impl ParsedData { pub fn parse(crate_: &Crate) -> Result { let mut item_processor = ItemProcessor::new(crate_); - for (id, associated_items) in find_roots(crate_, "Effect", &["Ffi"]) { - println!( - "\nThe struct that implements crux_core::Effect is {}", - crate_.paths[id].path.join("::") - ); - - for id in associated_items { - item_processor.add_to_work_queue(vec![], id); - } - } - - for (id, associated_items) in find_roots(crate_, "App", &["Event", "ViewModel"]) { - println!( - "\nThe struct that implements crux_core::App is {}", - crate_.paths[id].path.join("::") - ); - - for id in associated_items { - item_processor.add_to_work_queue(vec![], id); - } - } - + add_items(crate_, "Effect", &["Ffi"], &mut item_processor); + add_items(crate_, "App", &["Event", "ViewModel"], &mut item_processor); item_processor.run(); let context = RenderingContext { @@ -78,6 +62,33 @@ pub fn parse(crate_: &Crate) -> Result { Ok(ParsedData::new()) } +fn add_items<'c: 'p, 'p>( + crate_: &'c Crate, + trait_name: &'c str, + filter: &'c [&'c str], + item_processor: &'p mut ItemProcessor<'c>, +) { + for (id, associated_items) in find_roots(crate_, trait_name, filter) { + let path = &crate_.paths[id].path; + println!("{} implements {trait_name}", path.join("::")); + + let item = &crate_.index[id]; + let module = path[..path.len() - 1].join("::"); + for id in associated_items { + let parent = PathComponent { + item: NameableItem { + item, + overridden_name: Some(module.clone()), + sorting_prefix: sorting_prefix(item), + }, + type_: None, + hide: false, + }; + item_processor.add_to_work_queue(vec![parent], id); + } + } +} + fn find_roots<'a>( crate_: &'a Crate, trait_name: &'a str, From 78c6d57440c8d05abfa0c4212f5660fd38a1225e Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Thu, 6 Jun 2024 12:11:19 +0100 Subject: [PATCH 13/27] sorted items --- crux_cli/src/codegen/item_processor.rs | 4 +- crux_cli/src/codegen/mod.rs | 41 +-------- crux_cli/src/codegen/parser.rs | 113 +++++++++++++++---------- crux_cli/src/codegen/public_api.rs | 79 +++++++++++++++++ 4 files changed, 154 insertions(+), 83 deletions(-) create mode 100644 crux_cli/src/codegen/public_api.rs diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/item_processor.rs index 8b6bb40fc..8966b7801 100644 --- a/crux_cli/src/codegen/item_processor.rs +++ b/crux_cli/src/codegen/item_processor.rs @@ -1,5 +1,5 @@ +use super::crate_wrapper::CrateWrapper; use super::nameable_item::NameableItem; -use super::CrateWrapper; use super::{intermediate_public_item::IntermediatePublicItem, path_component::PathComponent}; use rustdoc_types::{ Crate, GenericArg, GenericArgs, Id, Impl, Import, Item, ItemEnum, Module, Path, Struct, @@ -37,7 +37,7 @@ struct UnprocessedItem<'c> { /// supported. pub struct ItemProcessor<'c> { /// The original and unmodified rustdoc JSON, in deserialized form. - crate_: CrateWrapper<'c>, + pub crate_: CrateWrapper<'c>, /// A queue of unprocessed items to process. work_queue: VecDeque>, diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs index 6f81af850..18bdd4d13 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -5,52 +5,19 @@ mod item_processor; mod nameable_item; mod parser; mod path_component; +mod public_api; mod public_item; -use rustdoc_types::{Crate, Id, Item}; - -/// The [`Crate`] type represents the deserialized form of the rustdoc JSON -/// input. This wrapper adds some helpers and state on top. -pub struct CrateWrapper<'c> { - crate_: &'c Crate, - - /// Normally, an item referenced by [`Id`] is present in the rustdoc JSON. - /// If [`Self::crate_.index`] is missing an [`Id`], then we add it here, to - /// aid with debugging. It will typically be missing because of bugs (or - /// borderline bug such as re-exports of foreign items like discussed in - /// ) - /// We do not report it to users by default, because they can't do anything - /// about it. Missing IDs will be printed with `--verbose` however. - missing_ids: Vec<&'c Id>, -} - -impl<'c> CrateWrapper<'c> { - pub fn new(crate_: &'c Crate) -> Self { - Self { - crate_, - missing_ids: vec![], - } - } - - pub fn get_item(&mut self, id: &'c Id) -> Option<&'c Item> { - self.crate_.index.get(id).or_else(|| { - self.missing_ids.push(id); - None - }) - } - - pub fn missing_item_ids(&self) -> Vec { - self.missing_ids.iter().map(|m| m.0.clone()).collect() - } -} mod render; mod rust_types; mod tokens; -use anyhow::{bail, Result}; use std::{ fs::File, io::{stdout, IsTerminal}, }; + +use anyhow::{bail, Result}; +use rustdoc_types::Crate; use tokio::{process::Command, task::spawn_blocking}; use crate::{args::CodegenArgs, command_runner}; diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 2620b31aa..4a0e40937 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -3,6 +3,10 @@ use std::collections::HashMap; use anyhow::Result; use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, Type}; +use super::{ + public_api::PublicApi, + rust_types::{RustEnum, RustStruct, RustTypeAlias}, +}; use crate::codegen::{ item_processor::{sorting_prefix, ItemProcessor}, nameable_item::NameableItem, @@ -11,8 +15,6 @@ use crate::codegen::{ render::RenderingContext, }; -use super::rust_types::{RustEnum, RustStruct, RustTypeAlias}; - /// The results of parsing Rust source input. #[derive(Default, Debug)] pub struct ParsedData { @@ -40,7 +42,8 @@ pub fn parse(crate_: &Crate) -> Result { crate_, id_to_items: item_processor.id_to_items(), }; - let items: Vec = item_processor + + let items: Vec<_> = item_processor .output .iter() .filter_map(|item| { @@ -58,8 +61,23 @@ pub fn parse(crate_: &Crate) -> Result { .then_some(PublicItem::from_intermediate_public_item(&context, item)) }) .collect(); - println!("{items:#?}"); - Ok(ParsedData::new()) + + let mut public_api = PublicApi { + items, + missing_item_ids: item_processor.crate_.missing_item_ids(), + }; + + public_api.items.sort_by(PublicItem::grouping_cmp); + + let mut parsed_data = ParsedData::new(); + + println!(); + + for item in public_api.items { + println!("{:?}", item.sortable_path); + println!("{}\n", item); + } + Ok(parsed_data) } fn add_items<'c: 'p, 'p>( @@ -68,17 +86,13 @@ fn add_items<'c: 'p, 'p>( filter: &'c [&'c str], item_processor: &'p mut ItemProcessor<'c>, ) { - for (id, associated_items) in find_roots(crate_, trait_name, filter) { - let path = &crate_.paths[id].path; - println!("{} implements {trait_name}", path.join("::")); - - let item = &crate_.index[id]; - let module = path[..path.len() - 1].join("::"); - for id in associated_items { + for root in find_roots(crate_, trait_name, filter) { + let item = &crate_.index[root.parent]; + for id in root.assoc_types { let parent = PathComponent { item: NameableItem { item, - overridden_name: Some(module.clone()), + overridden_name: None, sorting_prefix: sorting_prefix(item), }, type_: None, @@ -89,47 +103,58 @@ fn add_items<'c: 'p, 'p>( } } +struct Root<'a> { + parent: &'a Id, + assoc_types: Vec<&'a Id>, +} + fn find_roots<'a>( crate_: &'a Crate, trait_name: &'a str, filter: &'a [&'a str], -) -> impl Iterator)> { - crate_.index.iter().filter_map(move |(_k, v)| { - if let ItemEnum::Impl(Impl { - trait_: Some(Path { name, .. }), - for_: Type::ResolvedPath(Path { id, .. }), - items, - .. - }) = &v.inner - { - if name.as_str() == trait_name { - let assoc_types = items - .iter() - .filter_map(|id| { - let item = &crate_.index[id]; - item.name.as_deref().and_then(|name| { - if filter.contains(&name) { - if let ItemEnum::AssocType { - default: Some(Type::ResolvedPath(Path { id, .. })), - .. - } = &item.inner - { - Some(id) +) -> impl Iterator> { + crate_ + .index + .iter() + .filter_map(move |(parent, parent_item)| { + if let ItemEnum::Impl(Impl { + trait_: Some(Path { name, .. }), + // for_: Type::ResolvedPath(_), + items, + .. + }) = &parent_item.inner + { + if name.as_str() == trait_name { + let assoc_types = items + .iter() + .filter_map(|id| { + let item = &crate_.index[id]; + item.name.as_deref().and_then(|name| { + if filter.contains(&name) { + if let ItemEnum::AssocType { + default: Some(Type::ResolvedPath(Path { id, .. })), + .. + } = &item.inner + { + Some(id) + } else { + None + } } else { None } - } else { - None - } + }) }) + .collect(); + Some(Root { + parent, + assoc_types, }) - .collect(); - Some((id, assoc_types)) + } else { + None + } } else { None } - } else { - None - } - }) + }) } diff --git a/crux_cli/src/codegen/public_api.rs b/crux_cli/src/codegen/public_api.rs new file mode 100644 index 000000000..8162ca9e4 --- /dev/null +++ b/crux_cli/src/codegen/public_api.rs @@ -0,0 +1,79 @@ +use super::public_item::PublicItem; + +/// The public API of a crate +/// +/// Create an instance with [`Builder`]. +/// +/// ## Rendering the items +/// +/// To render the items in the public API you can iterate over the [items](PublicItem). +/// +/// You get the `rustdoc_json_str` in the example below as explained in the [crate] documentation, either via +/// [`rustdoc_json`](https://crates.io/crates/rustdoc_json) or by calling `cargo rustdoc` yourself. +/// +/// ```no_run +/// use public_api::PublicApi; +/// use std::path::PathBuf; +/// +/// # let rustdoc_json: PathBuf = todo!(); +/// // Gather the rustdoc content as described in this crates top-level documentation. +/// let public_api = public_api::Builder::from_rustdoc_json(&rustdoc_json).build()?; +/// +/// for public_item in public_api.items() { +/// // here we print the items to stdout, we could also write to a string or a file. +/// println!("{}", public_item); +/// } +/// +/// // If you want all items of the public API in a single big multi-line String then +/// // you can do like this: +/// let public_api_string = public_api.to_string(); +/// # Ok::<(), Box>(()) +/// ``` +#[derive(Debug)] +#[non_exhaustive] // More fields might be added in the future +pub struct PublicApi { + /// The items that constitutes the public API. An "item" is for example a + /// function, a struct, a struct field, an enum, an enum variant, a module, + /// etc... + pub(crate) items: Vec, + + /// See [`Self::missing_item_ids()`] + pub(crate) missing_item_ids: Vec, +} + +impl PublicApi { + /// Returns an iterator over all public items in the public API + pub fn items(&self) -> impl Iterator { + self.items.iter() + } + + /// Like [`Self::items()`], but ownership of all `PublicItem`s are + /// transferred to the caller. + pub fn into_items(self) -> impl Iterator { + self.items.into_iter() + } + + /// The rustdoc JSON IDs of missing but referenced items. Intended for use + /// with `--verbose` flags or similar. + /// + /// In some cases, a public item might be referenced from another public + /// item (e.g. a `mod`), but is missing from the rustdoc JSON file. This + /// occurs for example in the case of re-exports of external modules (see + /// ). The entries + /// in this Vec are what IDs that could not be found. + /// + /// The exact format of IDs are to be considered an implementation detail + /// and must not be be relied on. + pub fn missing_item_ids(&self) -> impl Iterator { + self.missing_item_ids.iter() + } +} + +impl std::fmt::Display for PublicApi { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for item in self.items() { + writeln!(f, "{item}")?; + } + Ok(()) + } +} From 95001834d5654f4f86d00d2d7b56504548b406ad Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Thu, 6 Jun 2024 16:11:23 +0100 Subject: [PATCH 14/27] add MIT license --- LICENSE => LICENSE-APACHE | 0 LICENSE-MIT | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+) rename LICENSE => LICENSE-APACHE (100%) create mode 100644 LICENSE-MIT diff --git a/LICENSE b/LICENSE-APACHE similarity index 100% rename from LICENSE rename to LICENSE-APACHE diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..4ee041a72 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 RedBadger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a013e9c9e4b4a0300da875dd979223e1c1c04e71 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 7 Jun 2024 09:45:09 +0100 Subject: [PATCH 15/27] deps --- Cargo.lock | 12 +- crux_cli/Cargo.toml | 8 +- crux_cli/src/codegen/item_processor.rs | 2 +- crux_cli/src/codegen/render.rs | 15 +- examples/cat_facts/cli/Cargo.lock | 2324 ------------------------ examples/counter/server/Cargo.lock | 1163 ------------ examples/counter/server/Cargo.toml | 4 +- 7 files changed, 22 insertions(+), 3506 deletions(-) delete mode 100644 examples/cat_facts/cli/Cargo.lock delete mode 100644 examples/counter/server/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index b341849d8..7b50d230d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -925,9 +925,9 @@ dependencies = [ [[package]] name = "guppy" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bff2f6a9d515cf6453282af93363f93bdf570792a6f4f619756e46696d773fa" +checksum = "bf47f1dcacf93614a4181e308b8341f5bf5d4f7ae2f67cc5a078df8e7023ea63" dependencies = [ "ahash", "camino", @@ -1310,9 +1310,9 @@ dependencies = [ [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" dependencies = [ "camino", ] @@ -1653,9 +1653,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustdoc-types" -version = "0.24.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0904b9147011800e63763fb9e49bbeaf76c1b6ab8982824c659dce5433712559" +checksum = "0b9be1bc4a0ec3445cfa2e4ba112827544890d43d68b7d1eda5359a9c09d2cd8" dependencies = [ "serde", ] diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index 2b346720e..809974e6b 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -15,16 +15,16 @@ path = "src/main.rs" [dependencies] anyhow.workspace = true -clap = { version = "4.3.24", features = ["derive"] } +clap = { version = "4.4.18", features = ["derive"] } console = "0.15.8" guppy = "0.17.5" ignore = "0.4.23" libc = "0.2.155" ramhorns = "1.0.0" -rustdoc-types = "0.24.0" +rustdoc-types = "0.26.0" serde = { workspace = true, features = ["derive"] } +serde_json = "1.0.117" similar = { version = "2.6.0", features = ["inline"] } -serde_json = "1.0.114" -tokio = { version = "1.37.0", features = ["full"] } +tokio = { version = "1.38.0", features = ["full"] } tokio-fd = "0.3.0" toml = "0.8.19" diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/item_processor.rs index 8966b7801..35eea08e3 100644 --- a/crux_cli/src/codegen/item_processor.rs +++ b/crux_cli/src/codegen/item_processor.rs @@ -332,7 +332,7 @@ pub(crate) fn sorting_prefix(item: &Item) -> u8 { ItemEnum::StructField(_) => 10, ItemEnum::Variant(_) => 11, - ItemEnum::Constant(_) => 12, + ItemEnum::Constant { .. } => 12, ItemEnum::Static(_) => 13, diff --git a/crux_cli/src/codegen/render.rs b/crux_cli/src/codegen/render.rs index 0dcab81ae..812e923b9 100644 --- a/crux_cli/src/codegen/render.rs +++ b/crux_cli/src/codegen/render.rs @@ -123,10 +123,10 @@ impl<'c> RenderingContext<'c> { output } ItemEnum::OpaqueTy(_) => self.render_simple(&["opaque", "type"], item_path), - ItemEnum::Constant(con) => { + ItemEnum::Constant { const_, type_ } => { let mut output = self.render_simple(&["const"], item_path); output.extend(colon()); - output.extend(self.render_constant(con)); + output.extend(self.render_constant(const_, Some(type_))); output } ItemEnum::AssocConst { type_, .. } => { @@ -355,6 +355,9 @@ impl<'c> RenderingContext<'c> { self_type, trait_, } => self.render_qualified_path(self_type, trait_.as_ref(), name), + Type::Pat { .. } => vec![Token::symbol( + "https://github.com/rust-lang/rust/issues/123646 is unstable and not supported", + )], } } @@ -742,7 +745,7 @@ impl<'c> RenderingContext<'c> { fn render_term(&self, term: &Term) -> Vec { match term { Term::Type(ty) => self.render_type(ty), - Term::Constant(c) => self.render_constant(c), + Term::Constant(c) => self.render_constant(c, None), } } @@ -756,7 +759,7 @@ impl<'c> RenderingContext<'c> { match arg { GenericArg::Lifetime(name) => vec![Token::lifetime(name)], GenericArg::Type(ty) => self.render_type(ty), - GenericArg::Const(c) => self.render_constant(c), + GenericArg::Const(c) => self.render_constant(c, None), GenericArg::Infer => vec![Token::symbol("_")], } } @@ -776,10 +779,10 @@ impl<'c> RenderingContext<'c> { output } - fn render_constant(&self, constant: &Constant) -> Vec { + fn render_constant(&self, constant: &Constant, type_: Option<&Type>) -> Vec { let mut output = vec![]; if constant.is_literal { - output.extend(self.render_type(&constant.type_)); + output.extend(self.render_type(type_.expect("constant literals have a type"))); if let Some(value) = &constant.value { output.extend(equals()); if constant.is_literal { diff --git a/examples/cat_facts/cli/Cargo.lock b/examples/cat_facts/cli/Cargo.lock deleted file mode 100644 index 88889bb1c..000000000 --- a/examples/cat_facts/cli/Cargo.lock +++ /dev/null @@ -1,2324 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher", -] - -[[package]] -name = "aes-gcm" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher", - "opaque-debug", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" - -[[package]] -name = "askama" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139" -dependencies = [ - "askama_derive", - "askama_escape", - "askama_shared", -] - -[[package]] -name = "askama_derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71" -dependencies = [ - "askama_shared", - "proc-macro2", - "syn", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "askama_shared" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0" -dependencies = [ - "askama_escape", - "mime", - "mime_guess", - "nom", - "proc-macro2", - "quote", - "serde", - "syn", - "toml", -] - -[[package]] -name = "async-channel" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" -dependencies = [ - "async-lock", - "autocfg", - "concurrent-queue", - "futures-lite", - "libc", - "log", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi", -] - -[[package]] -name = "async-lock" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" -dependencies = [ - "event-listener", - "futures-lite", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-task" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" - -[[package]] -name = "async-trait" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - -[[package]] -name = "bumpalo" -version = "3.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" - -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - -[[package]] -name = "bytes" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - -[[package]] -name = "camino" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.14", - "serde", - "serde_json", -] - -[[package]] -name = "cc" -version = "1.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-integer", - "num-traits", - "time 0.1.44", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim 0.8.0", - "textwrap 0.11.0", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim 0.10.0", - "termcolor", - "textwrap 0.16.0", -] - -[[package]] -name = "clap_derive" -version = "3.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" -dependencies = [ - "heck 0.4.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "cli" -version = "0.1.0" -dependencies = [ - "async-std", - "chrono", - "shared", - "structopt", - "surf", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "const_fn" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" - -[[package]] -name = "cookie" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" -dependencies = [ - "aes-gcm", - "base64", - "hkdf", - "hmac", - "percent-encoding", - "rand 0.8.5", - "sha2", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - -[[package]] -name = "crossbeam-utils" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" -dependencies = [ - "cipher", -] - -[[package]] -name = "curl" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.58+curl-7.86.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430e2ecf0c5f4445334472cf8f9611a6eea404b4135ca5500f38a97a128c913e" -dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi", -] - -[[package]] -name = "cxx" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "flume" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bebadab126f8120d410b677ed95eee4ba6eb7c6dd8e34a5ec88a08050e26132" -dependencies = [ - "futures-core", - "futures-sink", - "spinning_top", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs-err" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" - -[[package]] -name = "futures-channel" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" - -[[package]] -name = "futures-io" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" - -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" - -[[package]] -name = "futures-task" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" - -[[package]] -name = "futures-util" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" -dependencies = [ - "futures-core", - "futures-io", - "futures-macro", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gloo-timers" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "goblin" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" -dependencies = [ - "log", - "plain", - "scroll", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac", - "digest", -] - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes 1.2.1", - "fnv", - "itoa", -] - -[[package]] -name = "http-client" -version = "6.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" -dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "http-types", - "isahc", - "log", -] - -[[package]] -name = "http-types" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel", - "async-std", - "base64", - "cookie", - "futures-lite", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "isahc" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2948a0ce43e2c2ef11d7edf6816508998d99e13badd1150be0914205df9388a" -dependencies = [ - "bytes 0.5.6", - "crossbeam-utils", - "curl", - "curl-sys", - "flume", - "futures-lite", - "http", - "log", - "once_cell", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - -[[package]] -name = "itoa" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.137" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" - -[[package]] -name = "libnghttp2-sys" -version = "0.1.7+1.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "libz-sys" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", - "value-bag", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "os_str_bytes" -version = "6.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "paste" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "polling" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" -dependencies = [ - "autocfg", - "cfg-if", - "libc", - "log", - "wepoll-ffi", - "winapi", -] - -[[package]] -name = "polyval" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" -dependencies = [ - "cpuid-bool", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro2" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.8", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "ryu" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "schannel" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" -dependencies = [ - "lazy_static", - "windows-sys", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - -[[package]] -name = "scroll" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" -dependencies = [ - "scroll_derive", -] - -[[package]] -name = "scroll_derive" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" -dependencies = [ - "serde", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "shared" -version = "0.1.0" -dependencies = [ - "anyhow", - "serde", - "serde_json", - "thiserror", - "uniffi", - "uniffi_build", - "uniffi_macros", - "uuid", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - -[[package]] -name = "socket2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spinning_top" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" -dependencies = [ - "lock_api", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "surf" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718b1ae6b50351982dedff021db0def601677f2120938b070eadb10ba4038dd7" -dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "encoding_rs", - "futures-util", - "getrandom 0.2.8", - "http-client", - "http-types", - "log", - "mime_guess", - "once_cell", - "pin-project-lite", - "serde", - "serde_json", - "web-sys", -] - -[[package]] -name = "syn" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "thiserror" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-ident" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "uniffi" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54af5ada67d1173457a99a7bb44a7917f63e7466764cb4714865c7a6678b830" -dependencies = [ - "anyhow", - "bytes 1.2.1", - "camino", - "cargo_metadata", - "log", - "once_cell", - "paste", - "static_assertions", - "uniffi_macros", -] - -[[package]] -name = "uniffi_bindgen" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc4af3c0180c7e86c4a3acf2b6587af04ba2567b1e948993df10f421796621" -dependencies = [ - "anyhow", - "askama", - "bincode", - "camino", - "clap 3.2.23", - "fs-err", - "goblin", - "heck 0.4.0", - "once_cell", - "paste", - "serde", - "serde_json", - "toml", - "uniffi_meta", - "weedle2", -] - -[[package]] -name = "uniffi_build" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510287c368a9386eb731ebe824a6fc6c82a105e20d020af1aa20519c1c1561db" -dependencies = [ - "anyhow", - "camino", - "uniffi_bindgen", -] - -[[package]] -name = "uniffi_macros" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8604503caa2cbcf271578dc51ca236d40e3b22e1514ffa2e638e2c39f6ad10" -dependencies = [ - "bincode", - "camino", - "fs-err", - "once_cell", - "proc-macro2", - "quote", - "serde", - "syn", - "toml", - "uniffi_build", - "uniffi_meta", -] - -[[package]] -name = "uniffi_meta" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9417cc653937681436b93838d8c5f97a5b8c58697813982ee8810bd1dc3b57" -dependencies = [ - "serde", -] - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "uuid" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" -dependencies = [ - "getrandom 0.2.8", - "wasm-bindgen", -] - -[[package]] -name = "value-bag" -version = "1.0.0-alpha.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weedle2" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e79c5206e1f43a2306fd64bdb95025ee4228960f2e6c5a8b173f3caaf807741" -dependencies = [ - "nom", -] - -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/examples/counter/server/Cargo.lock b/examples/counter/server/Cargo.lock deleted file mode 100644 index f2c08caac..000000000 --- a/examples/counter/server/Cargo.lock +++ /dev/null @@ -1,1163 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "async-trait" -version = "0.1.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bytes" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - -[[package]] -name = "cc" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-signals" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b175f2f6600dd81d92d20cf10872b03ea9df6b2513ca7f672341260dacb1ab2" -dependencies = [ - "discard", - "futures-channel", - "futures-core", - "futures-util", - "gensym", - "log", - "pin-project", - "serde", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "gensym" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "uuid", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking_lot" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.5", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.3", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.202" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.202" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "server" -version = "0.1.0" -dependencies = [ - "axum", - "futures", - "futures-signals", - "rand", - "serde", - "serde_json", - "tokio", - "tower-http", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "syn" -version = "2.0.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tokio" -version = "1.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags", - "bytes", - "http", - "http-body", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "getrandom", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/examples/counter/server/Cargo.toml b/examples/counter/server/Cargo.toml index b71c1d41c..c560a8eef 100644 --- a/examples/counter/server/Cargo.toml +++ b/examples/counter/server/Cargo.toml @@ -8,9 +8,9 @@ axum = "0.7.5" futures = "0.3.30" futures-signals = "0.3.33" rand = "0.8.5" -serde = "1.0.202" +serde = "1.0.203" serde_json = "1.0.117" -tokio = { version = "1.37.0", features = ["full"] } +tokio = { version = "1.38.0", features = ["full"] } tower-http = { version = "0.5.2", features = ["cors"] } tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } From e142d15c2b7d776cd6a99064fbc64103deb49c45 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 27 Sep 2024 10:07:41 +0100 Subject: [PATCH 16/27] remove typeshare --- crux_cli/codegen.fish | 6 + crux_cli/src/codegen/mod.rs | 4 +- crux_cli/src/codegen/parser.rs | 30 +-- crux_cli/src/codegen/rust_types.rs | 311 ----------------------------- 4 files changed, 10 insertions(+), 341 deletions(-) create mode 100755 crux_cli/codegen.fish delete mode 100644 crux_cli/src/codegen/rust_types.rs diff --git a/crux_cli/codegen.fish b/crux_cli/codegen.fish new file mode 100755 index 000000000..f79e11ffb --- /dev/null +++ b/crux_cli/codegen.fish @@ -0,0 +1,6 @@ +#!/usr/bin/env fish + +cargo build +pushd ../examples/counter +../../target/debug/crux codegen --lib shared +popd diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs index 18bdd4d13..15e98bf7b 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -8,7 +8,6 @@ mod path_component; mod public_api; mod public_item; mod render; -mod rust_types; mod tokens; use std::{ @@ -58,8 +57,7 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { }) .await??; - let data = parser::parse(&crate_)?; - println!("\n\ndata: {data:?}"); + parser::parse(&crate_)?; Ok(()) } diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 4a0e40937..7c4b92ff1 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,12 +1,7 @@ -use std::collections::HashMap; - use anyhow::Result; use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, Type}; -use super::{ - public_api::PublicApi, - rust_types::{RustEnum, RustStruct, RustTypeAlias}, -}; +use super::public_api::PublicApi; use crate::codegen::{ item_processor::{sorting_prefix, ItemProcessor}, nameable_item::NameableItem, @@ -15,24 +10,7 @@ use crate::codegen::{ render::RenderingContext, }; -/// The results of parsing Rust source input. -#[derive(Default, Debug)] -pub struct ParsedData { - /// Structs defined in the source - pub structs: HashMap, - /// Enums defined in the source - pub enums: HashMap, - /// Type aliases defined in the source - pub aliases: HashMap, -} - -impl ParsedData { - pub fn new() -> Self { - Default::default() - } -} - -pub fn parse(crate_: &Crate) -> Result { +pub fn parse(crate_: &Crate) -> Result<()> { let mut item_processor = ItemProcessor::new(crate_); add_items(crate_, "Effect", &["Ffi"], &mut item_processor); add_items(crate_, "App", &["Event", "ViewModel"], &mut item_processor); @@ -69,15 +47,13 @@ pub fn parse(crate_: &Crate) -> Result { public_api.items.sort_by(PublicItem::grouping_cmp); - let mut parsed_data = ParsedData::new(); - println!(); for item in public_api.items { println!("{:?}", item.sortable_path); println!("{}\n", item); } - Ok(parsed_data) + Ok(()) } fn add_items<'c: 'p, 'p>( diff --git a/crux_cli/src/codegen/rust_types.rs b/crux_cli/src/codegen/rust_types.rs deleted file mode 100644 index e6fe7655f..000000000 --- a/crux_cli/src/codegen/rust_types.rs +++ /dev/null @@ -1,311 +0,0 @@ -/// Identifier used in Rust structs, enums, and fields. It includes the `original` name and the `renamed` value after the transformation based on `serde` attributes. -#[derive(Debug, Clone, PartialEq)] -pub struct Id { - // the identifier from the rustdoc json - pub id: rustdoc_types::Id, - /// The original identifier name - pub original: String, - /// The renamed identifier, based on serde attributes. - /// If there is no re-naming going on, this will be identical to - /// `original`. - pub renamed: String, -} - -impl Id { - pub fn new(id: rustdoc_types::Id) -> Self { - Self { - id, - original: String::new(), - renamed: String::new(), - } - } -} - -impl From for Id { - fn from(id: rustdoc_types::Id) -> Self { - Self::new(id) - } -} - -// Rust struct. -#[derive(Debug, Clone, PartialEq)] -pub struct RustStruct { - /// The identifier for the struct. - pub id: Id, - /// The generic parameters that come after the struct name. - pub generic_types: Vec, - /// The fields of the struct. - pub fields: Vec, - /// Comments that were in the struct source. - /// We copy comments over to the typeshared files, - /// so we need to collect them here. - pub comments: Vec, -} - -impl RustStruct { - pub fn new(id: Id) -> Self { - Self { - id, - generic_types: Vec::new(), - fields: Vec::new(), - comments: Vec::new(), - } - } -} - -/// Rust type alias. -/// ``` -/// pub struct MasterPassword(String); -/// ``` -#[derive(Debug, Clone, PartialEq)] -pub struct RustTypeAlias { - /// The identifier for the alias. - pub id: Id, - /// The generic parameters that come after the type alias name. - pub generic_types: Vec, - /// The type identifier that this type alias is aliasing - pub r#type: RustType, - /// Comments that were in the type alias source. - pub comments: Vec, -} - -/// Rust field definition. -#[derive(Debug, Clone, PartialEq)] -pub struct RustField { - /// Identifier for the field. - pub id: Id, - /// Type of the field. - pub ty: RustType, - /// Comments that were in the original source. - pub comments: Vec, - /// This will be true if the field has a `serde(default)` decorator. - /// Even if the field's type is not optional, we need to make it optional - /// for the languages we generate code for. - pub has_default: bool, -} - -/// A Rust type. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RustType { - /// A type with generic parameters. Consists of a type ID + parameters that come - /// after in angled brackets. Examples include: - /// - `SomeStruct` - /// - `SomeEnum` - /// - `SomeTypeAlias<(), &str>` - /// However, there are some generic types that are considered to be _special_. These - /// include `Vec` `HashMap`, and `Option`, which are part of `SpecialRustType` instead - /// of `RustType::Generic`. - Generic { - #[allow(missing_docs)] - id: String, - #[allow(missing_docs)] - parameters: Vec, - }, - /// A type that requires a special transformation to its respective language. This includes - /// many core types, like string types, basic container types, numbers, and other primitives. - Special(SpecialRustType), - /// A type with no generic parameters that is not considered a **special** type. This includes - /// all user-generated types and some types from the standard library or third-party crates. - Simple { - #[allow(missing_docs)] - id: String, - }, -} - -/// A special rust type that needs a manual type conversion -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SpecialRustType { - /// Represents `Vec` from the standard library - Vec(Box), - /// Represents `[T; N]` from the standard library - Array(Box, usize), - /// Represents `&[T]` from the standard library - Slice(Box), - /// Represents `HashMap` from the standard library - HashMap(Box, Box), - /// Represents `Option` from the standard library - Option(Box), - /// Represents `()` - Unit, - /// Represents `String` from the standard library - String, - /// Represents `char` - Char, - /// Represents `i8` - I8, - /// Represents `i16` - I16, - /// Represents `i32` - I32, - /// Represents `i64` - I64, - /// Represents `u8` - U8, - /// Represents `u16` - U16, - /// Represents `u32` - U32, - /// Represents `u64` - U64, - /// Represents `isize` - ISize, - /// Represents `usize` - USize, - /// Represents `bool` - Bool, - /// Represents `f32` - F32, - /// Represents `f64` - F64, - /// Represents `I54` from `typeshare::I54` - I54, - /// Represents `U53` from `typeshare::U53` - U53, -} - -impl<'a> TryFrom<&'a str> for SpecialRustType { - type Error = &'a str; - - fn try_from(value: &'a str) -> Result { - match value { - "String" => Ok(SpecialRustType::String), - "char" => Ok(SpecialRustType::Char), - "i8" => Ok(SpecialRustType::I8), - "i16" => Ok(SpecialRustType::I16), - "i32" => Ok(SpecialRustType::I32), - "i64" => Ok(SpecialRustType::I64), - "u8" => Ok(SpecialRustType::U8), - "u16" => Ok(SpecialRustType::U16), - "u32" => Ok(SpecialRustType::U32), - "u64" => Ok(SpecialRustType::U64), - "isize" => Ok(SpecialRustType::ISize), - "usize" => Ok(SpecialRustType::USize), - "bool" => Ok(SpecialRustType::Bool), - "f32" => Ok(SpecialRustType::F32), - "f64" => Ok(SpecialRustType::F64), - "I54" => Ok(SpecialRustType::I54), - "U53" => Ok(SpecialRustType::U53), - _ => Err(value), - } - } -} - -/// Parsed information about a Rust enum definition -#[derive(Debug, Clone, PartialEq)] -pub enum RustEnum { - /// A unit enum - /// - /// An example of such an enum: - /// - /// ``` - /// enum UnitEnum { - /// Variant, - /// AnotherVariant, - /// Yay, - /// } - /// ``` - Unit(RustEnumShared), - /// An algebraic enum - /// - /// An example of such an enum: - /// - /// ``` - /// struct AssociatedData { /* ... */ } - /// - /// enum AlgebraicEnum { - /// UnitVariant, - /// TupleVariant(AssociatedData), - /// AnonymousStruct { - /// field: String, - /// another_field: bool, - /// }, - /// } - /// ``` - Algebraic { - /// The parsed value of the `#[serde(tag = "...")]` attribute - tag_key: String, - /// The parsed value of the `#[serde(content = "...")]` attribute - content_key: String, - /// Shared context for this enum. - shared: RustEnumShared, - }, -} - -impl RustEnum { - /// Get a reference to the inner shared content - pub fn shared(&self) -> &RustEnumShared { - match self { - Self::Unit(shared) | Self::Algebraic { shared, .. } => shared, - } - } -} - -/// Enum information shared among different enum types -#[derive(Debug, Clone, PartialEq)] -pub struct RustEnumShared { - /// The enum's ident - pub id: Id, - /// Generic parameters for the enum, e.g. `SomeEnum` would produce `vec!["T"]` - pub generic_types: Vec, - /// Comments on the enum definition itself - pub comments: Vec, - /// The enum's variants - pub variants: Vec, - /// True if this enum references itself in any field of any variant - /// Swift needs the special keyword `indirect` for this case - pub is_recursive: bool, -} - -/// Parsed information about a Rust enum variant -#[derive(Debug, Clone, PartialEq)] -pub enum RustEnumVariant { - /// A unit variant - Unit(RustEnumVariantShared), - /// A tuple variant - Tuple { - /// The type of the single tuple field - ty: RustType, - /// Shared context for this enum. - shared: RustEnumVariantShared, - }, - /// An anonymous struct variant - AnonymousStruct { - /// The fields of the anonymous struct - fields: Vec, - /// Shared context for this enum. - shared: RustEnumVariantShared, - }, -} - -impl RustEnumVariant { - /// Get a reference to the inner shared content - pub fn shared(&self) -> &RustEnumVariantShared { - match self { - Self::Unit(shared) - | Self::Tuple { shared, .. } - | Self::AnonymousStruct { shared, .. } => shared, - } - } -} - -/// Variant information shared among different variant types -#[derive(Debug, Clone, PartialEq)] -pub struct RustEnumVariantShared { - /// The variant's ident - pub id: Id, - /// Comments applied to the variant - pub comments: Vec, -} - -/// An enum that encapsulates units of code generation for Typeshare. -/// Analogous to `syn::Item`, even though our variants are more limited. -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -pub enum RustItem { - /// A `struct` definition - Struct(RustStruct), - /// An `enum` definition - Enum(RustEnum), - /// A `type` definition or newtype struct. - Alias(RustTypeAlias), -} From d285c167cf426304b7386a607200c3111d091ef7 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 27 Sep 2024 10:23:42 +0100 Subject: [PATCH 17/27] public_api mod --- crux_cli/src/codegen/mod.rs | 8 -------- crux_cli/src/codegen/parser.rs | 6 +++--- .../src/codegen/{ => public_api}/crate_wrapper.rs | 0 .../{ => public_api}/intermediate_public_item.rs | 0 .../src/codegen/{ => public_api}/item_processor.rs | 0 .../src/codegen/{public_api.rs => public_api/mod.rs} | 11 ++++++++++- .../src/codegen/{ => public_api}/nameable_item.rs | 0 .../src/codegen/{ => public_api}/path_component.rs | 0 crux_cli/src/codegen/{ => public_api}/public_item.rs | 0 crux_cli/src/codegen/{ => public_api}/render.rs | 2 +- crux_cli/src/codegen/{ => public_api}/tokens.rs | 0 11 files changed, 14 insertions(+), 13 deletions(-) rename crux_cli/src/codegen/{ => public_api}/crate_wrapper.rs (100%) rename crux_cli/src/codegen/{ => public_api}/intermediate_public_item.rs (100%) rename crux_cli/src/codegen/{ => public_api}/item_processor.rs (100%) rename crux_cli/src/codegen/{public_api.rs => public_api/mod.rs} (93%) rename crux_cli/src/codegen/{ => public_api}/nameable_item.rs (100%) rename crux_cli/src/codegen/{ => public_api}/path_component.rs (100%) rename crux_cli/src/codegen/{ => public_api}/public_item.rs (100%) rename crux_cli/src/codegen/{ => public_api}/render.rs (99%) rename crux_cli/src/codegen/{ => public_api}/tokens.rs (100%) diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs index 15e98bf7b..dbdfdc553 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -1,14 +1,6 @@ -mod crate_wrapper; mod graph; -mod intermediate_public_item; -mod item_processor; -mod nameable_item; mod parser; -mod path_component; mod public_api; -mod public_item; -mod render; -mod tokens; use std::{ fs::File, diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 7c4b92ff1..89df6f9f8 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,14 +1,14 @@ use anyhow::Result; use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, Type}; -use super::public_api::PublicApi; -use crate::codegen::{ +use super::public_api::{ item_processor::{sorting_prefix, ItemProcessor}, nameable_item::NameableItem, path_component::PathComponent, public_item::PublicItem, - render::RenderingContext, + PublicApi, }; +use crate::codegen::public_api::render::RenderingContext; pub fn parse(crate_: &Crate) -> Result<()> { let mut item_processor = ItemProcessor::new(crate_); diff --git a/crux_cli/src/codegen/crate_wrapper.rs b/crux_cli/src/codegen/public_api/crate_wrapper.rs similarity index 100% rename from crux_cli/src/codegen/crate_wrapper.rs rename to crux_cli/src/codegen/public_api/crate_wrapper.rs diff --git a/crux_cli/src/codegen/intermediate_public_item.rs b/crux_cli/src/codegen/public_api/intermediate_public_item.rs similarity index 100% rename from crux_cli/src/codegen/intermediate_public_item.rs rename to crux_cli/src/codegen/public_api/intermediate_public_item.rs diff --git a/crux_cli/src/codegen/item_processor.rs b/crux_cli/src/codegen/public_api/item_processor.rs similarity index 100% rename from crux_cli/src/codegen/item_processor.rs rename to crux_cli/src/codegen/public_api/item_processor.rs diff --git a/crux_cli/src/codegen/public_api.rs b/crux_cli/src/codegen/public_api/mod.rs similarity index 93% rename from crux_cli/src/codegen/public_api.rs rename to crux_cli/src/codegen/public_api/mod.rs index 8162ca9e4..0daf551f8 100644 --- a/crux_cli/src/codegen/public_api.rs +++ b/crux_cli/src/codegen/public_api/mod.rs @@ -1,4 +1,13 @@ -use super::public_item::PublicItem; +mod crate_wrapper; +mod intermediate_public_item; +pub mod item_processor; +pub mod nameable_item; +pub mod path_component; +pub mod public_item; +pub mod render; +mod tokens; + +use public_item::PublicItem; /// The public API of a crate /// diff --git a/crux_cli/src/codegen/nameable_item.rs b/crux_cli/src/codegen/public_api/nameable_item.rs similarity index 100% rename from crux_cli/src/codegen/nameable_item.rs rename to crux_cli/src/codegen/public_api/nameable_item.rs diff --git a/crux_cli/src/codegen/path_component.rs b/crux_cli/src/codegen/public_api/path_component.rs similarity index 100% rename from crux_cli/src/codegen/path_component.rs rename to crux_cli/src/codegen/public_api/path_component.rs diff --git a/crux_cli/src/codegen/public_item.rs b/crux_cli/src/codegen/public_api/public_item.rs similarity index 100% rename from crux_cli/src/codegen/public_item.rs rename to crux_cli/src/codegen/public_api/public_item.rs diff --git a/crux_cli/src/codegen/render.rs b/crux_cli/src/codegen/public_api/render.rs similarity index 99% rename from crux_cli/src/codegen/render.rs rename to crux_cli/src/codegen/public_api/render.rs index 812e923b9..3b7972e88 100644 --- a/crux_cli/src/codegen/render.rs +++ b/crux_cli/src/codegen/public_api/render.rs @@ -1012,7 +1012,7 @@ mod test { }; } - use crate::codegen::tokens; + use crate::codegen::public_api::tokens; use super::*; diff --git a/crux_cli/src/codegen/tokens.rs b/crux_cli/src/codegen/public_api/tokens.rs similarity index 100% rename from crux_cli/src/codegen/tokens.rs rename to crux_cli/src/codegen/public_api/tokens.rs From 6058e2fa4460b90be939bbfb9b16d74f6d7d2ea5 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 27 Sep 2024 14:05:37 +0100 Subject: [PATCH 18/27] update public_api from origin --- Cargo.lock | 31 +- crux_cli/Cargo.toml | 1 + .../src/codegen/public_api/item_processor.rs | 68 +--- crux_cli/src/codegen/public_api/mod.rs | 3 + .../src/codegen/public_api/public_item.rs | 1 + crux_cli/src/codegen/public_api/render.rs | 353 +----------------- crux_cli/src/codegen/public_api/tokens.rs | 3 +- 7 files changed, 57 insertions(+), 403 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b50d230d..c522728b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,8 +436,9 @@ dependencies = [ "guppy", "ignore", "libc", + "public-api", "ramhorns", - "rustdoc-types", + "rustdoc-types 0.26.0", "serde", "serde_json", "similar", @@ -956,6 +957,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" +[[package]] +name = "hashbag" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f494b2060b2a8f5e63379e1e487258e014cee1b1725a735816c0107a2e9d93" + [[package]] name = "hashbrown" version = "0.15.0" @@ -1494,6 +1501,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "public-api" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9c3424091bd95654453c01003276fb9f2690be8290e1f179018eefbea24b39" +dependencies = [ + "hashbag", + "rustdoc-types 0.30.0", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "pulldown-cmark" version = "0.12.2" @@ -1660,6 +1680,15 @@ dependencies = [ "serde", ] +[[package]] +name = "rustdoc-types" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e3124c5a7883153fe3e762da9998cf36c302dbf1cc237869d297f9713e5655" +dependencies = [ + "serde", +] + [[package]] name = "rustix" version = "0.38.37" diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index 809974e6b..7b8b3b01b 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -20,6 +20,7 @@ console = "0.15.8" guppy = "0.17.5" ignore = "0.4.23" libc = "0.2.155" +public-api = "0.38.0" ramhorns = "1.0.0" rustdoc-types = "0.26.0" serde = { workspace = true, features = ["derive"] } diff --git a/crux_cli/src/codegen/public_api/item_processor.rs b/crux_cli/src/codegen/public_api/item_processor.rs index 35eea08e3..70a155a99 100644 --- a/crux_cli/src/codegen/public_api/item_processor.rs +++ b/crux_cli/src/codegen/public_api/item_processor.rs @@ -1,9 +1,10 @@ -use super::crate_wrapper::CrateWrapper; use super::nameable_item::NameableItem; -use super::{intermediate_public_item::IntermediatePublicItem, path_component::PathComponent}; +use super::{ + crate_wrapper::CrateWrapper, intermediate_public_item::IntermediatePublicItem, + path_component::PathComponent, +}; use rustdoc_types::{ - Crate, GenericArg, GenericArgs, Id, Impl, Import, Item, ItemEnum, Module, Path, Struct, - StructKind, Type, Variant, VariantKind, + Crate, Id, Impl, Import, Item, ItemEnum, Module, Struct, StructKind, Type, VariantKind, }; use std::{ collections::{HashMap, VecDeque}, @@ -223,12 +224,11 @@ impl<'c> ItemProcessor<'c> { ) { let finished_item = unprocessed_item.finish(item, overridden_name, type_); - let children = children_for_item(item); + let children = children_for_item(item).into_iter().flatten(); let impls = impls_for_item(item).into_iter().flatten(); for id in children { - let parent_path = finished_item.path().into(); - self.add_to_work_queue(parent_path, id); + self.add_to_work_queue(finished_item.path().into(), id); } // As usual, impls are special. We want impl items to appear grouped @@ -410,59 +410,25 @@ impl ImplKind { /// Some items contain other items, which is relevant for analysis. Keep track /// of such relationships. -fn children_for_item(item: &Item) -> Vec<&Id> { +const fn children_for_item(item: &Item) -> Option<&Vec> { match &item.inner { - ItemEnum::Module(m) => m.items.iter().collect(), - ItemEnum::Union(u) => u.fields.iter().collect(), - ItemEnum::Struct(Struct { - kind: StructKind::Tuple(fields), - .. - }) => fields.iter().flatten().collect(), + ItemEnum::Module(m) => Some(&m.items), + ItemEnum::Union(u) => Some(&u.fields), ItemEnum::Struct(Struct { kind: StructKind::Plain { fields, .. }, .. - }) => fields.iter().collect(), - ItemEnum::Variant(Variant { + }) + | ItemEnum::Variant(rustdoc_types::Variant { kind: VariantKind::Struct { fields, .. }, .. - }) => fields.iter().collect(), - ItemEnum::Variant(Variant { - kind: VariantKind::Tuple(fields), - .. - }) => fields.iter().flatten().collect(), - ItemEnum::Enum(e) => e.variants.iter().collect(), - ItemEnum::Trait(t) => t.items.iter().collect(), - ItemEnum::Impl(i) => i.items.iter().collect(), - ItemEnum::StructField(ty) => items_for_type(ty), - _ => vec![], + }) => Some(fields), + ItemEnum::Enum(e) => Some(&e.variants), + ItemEnum::Trait(t) => Some(&t.items), + ItemEnum::Impl(i) => Some(&i.items), + _ => None, } } -fn items_for_type(ty: &Type) -> Vec<&Id> { - let mut ids = Vec::new(); - if let Type::ResolvedPath(Path { id, args, name: _ }) = ty { - ids.push(id); - if let Some(args) = args { - if let GenericArgs::AngleBracketed { args, .. } = args.as_ref() { - let nested: Vec<&Id> = args - .iter() - .filter_map(|a| { - if let GenericArg::Type(ty) = a { - let ids = items_for_type(ty); - (!ids.is_empty()).then_some(ids) - } else { - None - } - }) - .flatten() - .collect(); - ids.extend(nested) - } - } - }; - ids -} - pub fn impls_for_item(item: &Item) -> Option<&[Id]> { match &item.inner { ItemEnum::Union(u) => Some(&u.impls), diff --git a/crux_cli/src/codegen/public_api/mod.rs b/crux_cli/src/codegen/public_api/mod.rs index 0daf551f8..4b0c40a0e 100644 --- a/crux_cli/src/codegen/public_api/mod.rs +++ b/crux_cli/src/codegen/public_api/mod.rs @@ -47,6 +47,7 @@ pub struct PublicApi { pub(crate) items: Vec, /// See [`Self::missing_item_ids()`] + #[allow(dead_code)] pub(crate) missing_item_ids: Vec, } @@ -58,6 +59,7 @@ impl PublicApi { /// Like [`Self::items()`], but ownership of all `PublicItem`s are /// transferred to the caller. + #[allow(dead_code)] pub fn into_items(self) -> impl Iterator { self.items.into_iter() } @@ -73,6 +75,7 @@ impl PublicApi { /// /// The exact format of IDs are to be considered an implementation detail /// and must not be be relied on. + #[allow(dead_code)] pub fn missing_item_ids(&self) -> impl Iterator { self.missing_item_ids.iter() } diff --git a/crux_cli/src/codegen/public_api/public_item.rs b/crux_cli/src/codegen/public_api/public_item.rs index 74ba630c2..9c0e820e8 100644 --- a/crux_cli/src/codegen/public_api/public_item.rs +++ b/crux_cli/src/codegen/public_api/public_item.rs @@ -38,6 +38,7 @@ impl PublicItem { } /// The rendered item as a stream of [`Token`]s + #[allow(dead_code)] pub fn tokens(&self) -> impl Iterator { self.tokens.iter() } diff --git a/crux_cli/src/codegen/public_api/render.rs b/crux_cli/src/codegen/public_api/render.rs index 3b7972e88..73b547a66 100644 --- a/crux_cli/src/codegen/public_api/render.rs +++ b/crux_cli/src/codegen/public_api/render.rs @@ -1,11 +1,11 @@ #![allow(clippy::unused_self)] -use std::ops::Deref; -use std::{cmp::Ordering, collections::HashMap, vec}; use super::intermediate_public_item::IntermediatePublicItem; use super::nameable_item::NameableItem; use super::path_component::PathComponent; use super::tokens::Token; +use std::ops::Deref; +use std::{cmp::Ordering, collections::HashMap, vec}; use rustdoc_types::{ Abi, Constant, Crate, FnDecl, FunctionPointer, GenericArg, GenericArgs, GenericBound, @@ -609,7 +609,7 @@ impl<'c> RenderingContext<'c> { pub(crate) fn render_impl( &self, impl_: &Impl, - path: &[PathComponent], + _path: &[PathComponent], disregard_negativity: bool, ) -> Vec { let mut output = vec![]; @@ -1003,350 +1003,3 @@ fn equals() -> Vec { fn arrow() -> Vec { vec![ws!(), Token::symbol("->"), ws!()] } - -#[cfg(test)] -mod test { - macro_rules! s { - ($value:literal) => { - $value.to_string() - }; - } - - use crate::codegen::public_api::tokens; - - use super::*; - - #[test] - fn test_type_infer() { - assert_render( - |context| context.render_type(&Type::Infer), - vec![Token::symbol("_")], - "_", - ); - } - - #[test] - fn test_type_generic() { - assert_render( - |context| context.render_type(&Type::Generic(s!("name"))), - vec![Token::generic("name")], - "name", - ); - } - - #[test] - fn test_type_primitive() { - assert_render( - |context| context.render_type(&Type::Primitive(s!("name"))), - vec![Token::primitive("name")], - "name", - ); - } - - #[test] - fn test_type_resolved_simple() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("name"), - args: None, - id: Id(s!("id")), - })) - }, - vec![Token::type_("name")], - "name", - ); - } - - #[test] - fn test_type_resolved_long_name() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("name::with::parts"), - args: None, - id: Id(s!("id")), - })) - }, - vec![ - Token::identifier("name"), - Token::symbol("::"), - Token::identifier("with"), - Token::symbol("::"), - Token::type_("parts"), - ], - "name::with::parts", - ); - } - - #[test] - fn test_type_resolved_crate_name() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("$crate::name"), - args: None, - id: Id(s!("id")), - })) - }, - vec![ - Token::identifier("$crate"), - Token::symbol("::"), - Token::type_("name"), - ], - "$crate::name", - ); - } - - #[test] - fn test_type_resolved_name_crate() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("name::$crate"), - args: None, - id: Id(s!("id")), - })) - }, - vec![ - Token::identifier("name"), - Token::symbol("::"), - Token::type_("$crate"), - ], - "name::$crate", - ); - } - - #[test] - fn test_type_tuple_empty() { - assert_render( - |context| context.render_type(&Type::Tuple(vec![])), - vec![Token::symbol("("), Token::symbol(")")], - "()", - ); - } - - #[test] - fn test_type_tuple() { - assert_render( - |context| { - context.render_type(&Type::Tuple(vec![Type::Infer, Type::Generic(s!("gen"))])) - }, - vec![ - Token::symbol("("), - Token::symbol("_"), - Token::symbol(","), - ws!(), - Token::generic("gen"), - Token::symbol(")"), - ], - "(_, gen)", - ); - } - - #[test] - fn test_type_slice() { - assert_render( - |context| context.render_type(&Type::Slice(Box::new(Type::Infer))), - vec![Token::symbol("["), Token::symbol("_"), Token::symbol("]")], - "[_]", - ); - } - - #[test] - fn test_type_array() { - assert_render( - |context| { - context.render_type(&Type::Array { - type_: Box::new(Type::Infer), - len: s!("20"), - }) - }, - vec![ - Token::symbol("["), - Token::symbol("_"), - Token::symbol(";"), - ws!(), - Token::primitive("20"), - Token::symbol("]"), - ], - "[_; 20]", - ); - } - - #[test] - fn test_type_pointer() { - assert_render( - |context| { - context.render_type(&Type::RawPointer { - mutable: false, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("*"), - Token::keyword("const"), - ws!(), - Token::symbol("_"), - ], - "*const _", - ); - } - - #[test] - fn test_type_pointer_mut() { - assert_render( - |context| { - context.render_type(&Type::RawPointer { - mutable: true, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("*"), - Token::keyword("mut"), - ws!(), - Token::symbol("_"), - ], - "*mut _", - ); - } - - #[test] - fn test_type_ref() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: None, - mutable: false, - type_: Box::new(Type::Infer), - }) - }, - vec![Token::symbol("&"), Token::symbol("_")], - "&_", - ); - } - - #[test] - fn test_type_ref_mut() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: None, - mutable: true, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("&"), - Token::keyword("mut"), - ws!(), - Token::symbol("_"), - ], - "&mut _", - ); - } - - #[test] - fn test_type_ref_lt() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: Some(s!("'a")), - mutable: false, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("&"), - Token::lifetime("'a"), - ws!(), - Token::symbol("_"), - ], - "&'a _", - ); - } - - #[test] - fn test_type_ref_lt_mut() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: Some(s!("'a")), - mutable: true, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("&"), - Token::lifetime("'a"), - ws!(), - Token::keyword("mut"), - ws!(), - Token::symbol("_"), - ], - "&'a mut _", - ); - } - - #[test] - fn test_type_path() { - assert_render( - |context| { - context.render_type(&Type::QualifiedPath { - name: s!("name"), - args: Box::new(GenericArgs::AngleBracketed { - args: vec![], - bindings: vec![], - }), - self_type: Box::new(Type::Generic(s!("type"))), - trait_: Some(Path { - name: String::from("trait"), - args: None, - id: Id(s!("id")), - }), - }) - }, - vec![ - Token::symbol("<"), - Token::generic("type"), - ws!(), - Token::keyword("as"), - ws!(), - Token::type_("trait"), - Token::symbol(">"), - Token::symbol("::"), - Token::identifier("name"), - ], - "::name", - ); - } - - fn assert_render( - render_fn: impl Fn(RenderingContext) -> Vec, - expected: Vec, - expected_string: &str, - ) { - let crate_ = Crate { - root: Id(String::from("1:2:3")), - crate_version: None, - includes_private: false, - index: HashMap::new(), - paths: HashMap::new(), - external_crates: HashMap::new(), - format_version: 0, - }; - let context = RenderingContext { - crate_: &crate_, - id_to_items: HashMap::new(), - }; - - let actual = render_fn(context); - - assert_eq!(actual, expected); - assert_eq!( - tokens::tokens_to_string(&actual), - expected_string.to_string() - ); - } -} diff --git a/crux_cli/src/codegen/public_api/tokens.rs b/crux_cli/src/codegen/public_api/tokens.rs index 2f7147b66..f911bf5fc 100644 --- a/crux_cli/src/codegen/public_api/tokens.rs +++ b/crux_cli/src/codegen/public_api/tokens.rs @@ -1,6 +1,6 @@ //! Contains all token handling logic. #[cfg(doc)] -use crate::public_item::PublicItem; +use super::public_item::PublicItem; /// A token in a rendered [`PublicItem`], used to apply syntax coloring in downstream applications. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -80,6 +80,7 @@ impl Token { } /// Give the length of the inner text of this token #[allow(clippy::len_without_is_empty)] + #[allow(dead_code)] #[must_use] pub fn len(&self) -> usize { self.text().len() From 5237e6bc61d78e3b15ca222da3aa8b0effdba330 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 27 Sep 2024 14:50:19 +0100 Subject: [PATCH 19/27] add back tests --- Cargo.lock | 31 +- crux_cli/Cargo.toml | 1 - crux_cli/src/codegen/public_api/render.rs | 347 ++++++++++++++++++++++ 3 files changed, 348 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c522728b7..7b50d230d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,9 +436,8 @@ dependencies = [ "guppy", "ignore", "libc", - "public-api", "ramhorns", - "rustdoc-types 0.26.0", + "rustdoc-types", "serde", "serde_json", "similar", @@ -957,12 +956,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" -[[package]] -name = "hashbag" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98f494b2060b2a8f5e63379e1e487258e014cee1b1725a735816c0107a2e9d93" - [[package]] name = "hashbrown" version = "0.15.0" @@ -1501,19 +1494,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "public-api" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9c3424091bd95654453c01003276fb9f2690be8290e1f179018eefbea24b39" -dependencies = [ - "hashbag", - "rustdoc-types 0.30.0", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "pulldown-cmark" version = "0.12.2" @@ -1680,15 +1660,6 @@ dependencies = [ "serde", ] -[[package]] -name = "rustdoc-types" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e3124c5a7883153fe3e762da9998cf36c302dbf1cc237869d297f9713e5655" -dependencies = [ - "serde", -] - [[package]] name = "rustix" version = "0.38.37" diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index 7b8b3b01b..809974e6b 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -20,7 +20,6 @@ console = "0.15.8" guppy = "0.17.5" ignore = "0.4.23" libc = "0.2.155" -public-api = "0.38.0" ramhorns = "1.0.0" rustdoc-types = "0.26.0" serde = { workspace = true, features = ["derive"] } diff --git a/crux_cli/src/codegen/public_api/render.rs b/crux_cli/src/codegen/public_api/render.rs index 73b547a66..5ceba5d6f 100644 --- a/crux_cli/src/codegen/public_api/render.rs +++ b/crux_cli/src/codegen/public_api/render.rs @@ -1003,3 +1003,350 @@ fn equals() -> Vec { fn arrow() -> Vec { vec![ws!(), Token::symbol("->"), ws!()] } + +#[cfg(test)] +mod test { + macro_rules! s { + ($value:literal) => { + $value.to_string() + }; + } + + use crate::codegen::public_api::tokens; + + use super::*; + + #[test] + fn test_type_infer() { + assert_render( + |context| context.render_type(&Type::Infer), + vec![Token::symbol("_")], + "_", + ); + } + + #[test] + fn test_type_generic() { + assert_render( + |context| context.render_type(&Type::Generic(s!("name"))), + vec![Token::generic("name")], + "name", + ); + } + + #[test] + fn test_type_primitive() { + assert_render( + |context| context.render_type(&Type::Primitive(s!("name"))), + vec![Token::primitive("name")], + "name", + ); + } + + #[test] + fn test_type_resolved_simple() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("name"), + args: None, + id: Id(s!("id")), + })) + }, + vec![Token::type_("name")], + "name", + ); + } + + #[test] + fn test_type_resolved_long_name() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("name::with::parts"), + args: None, + id: Id(s!("id")), + })) + }, + vec![ + Token::identifier("name"), + Token::symbol("::"), + Token::identifier("with"), + Token::symbol("::"), + Token::type_("parts"), + ], + "name::with::parts", + ); + } + + #[test] + fn test_type_resolved_crate_name() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("$crate::name"), + args: None, + id: Id(s!("id")), + })) + }, + vec![ + Token::identifier("$crate"), + Token::symbol("::"), + Token::type_("name"), + ], + "$crate::name", + ); + } + + #[test] + fn test_type_resolved_name_crate() { + assert_render( + |context| { + context.render_type(&Type::ResolvedPath(Path { + name: s!("name::$crate"), + args: None, + id: Id(s!("id")), + })) + }, + vec![ + Token::identifier("name"), + Token::symbol("::"), + Token::type_("$crate"), + ], + "name::$crate", + ); + } + + #[test] + fn test_type_tuple_empty() { + assert_render( + |context| context.render_type(&Type::Tuple(vec![])), + vec![Token::symbol("("), Token::symbol(")")], + "()", + ); + } + + #[test] + fn test_type_tuple() { + assert_render( + |context| { + context.render_type(&Type::Tuple(vec![Type::Infer, Type::Generic(s!("gen"))])) + }, + vec![ + Token::symbol("("), + Token::symbol("_"), + Token::symbol(","), + ws!(), + Token::generic("gen"), + Token::symbol(")"), + ], + "(_, gen)", + ); + } + + #[test] + fn test_type_slice() { + assert_render( + |context| context.render_type(&Type::Slice(Box::new(Type::Infer))), + vec![Token::symbol("["), Token::symbol("_"), Token::symbol("]")], + "[_]", + ); + } + + #[test] + fn test_type_array() { + assert_render( + |context| { + context.render_type(&Type::Array { + type_: Box::new(Type::Infer), + len: s!("20"), + }) + }, + vec![ + Token::symbol("["), + Token::symbol("_"), + Token::symbol(";"), + ws!(), + Token::primitive("20"), + Token::symbol("]"), + ], + "[_; 20]", + ); + } + + #[test] + fn test_type_pointer() { + assert_render( + |context| { + context.render_type(&Type::RawPointer { + mutable: false, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("*"), + Token::keyword("const"), + ws!(), + Token::symbol("_"), + ], + "*const _", + ); + } + + #[test] + fn test_type_pointer_mut() { + assert_render( + |context| { + context.render_type(&Type::RawPointer { + mutable: true, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("*"), + Token::keyword("mut"), + ws!(), + Token::symbol("_"), + ], + "*mut _", + ); + } + + #[test] + fn test_type_ref() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: None, + mutable: false, + type_: Box::new(Type::Infer), + }) + }, + vec![Token::symbol("&"), Token::symbol("_")], + "&_", + ); + } + + #[test] + fn test_type_ref_mut() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: None, + mutable: true, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("&"), + Token::keyword("mut"), + ws!(), + Token::symbol("_"), + ], + "&mut _", + ); + } + + #[test] + fn test_type_ref_lt() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: Some(s!("'a")), + mutable: false, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("&"), + Token::lifetime("'a"), + ws!(), + Token::symbol("_"), + ], + "&'a _", + ); + } + + #[test] + fn test_type_ref_lt_mut() { + assert_render( + |context| { + context.render_type(&Type::BorrowedRef { + lifetime: Some(s!("'a")), + mutable: true, + type_: Box::new(Type::Infer), + }) + }, + vec![ + Token::symbol("&"), + Token::lifetime("'a"), + ws!(), + Token::keyword("mut"), + ws!(), + Token::symbol("_"), + ], + "&'a mut _", + ); + } + + #[test] + fn test_type_path() { + assert_render( + |context| { + context.render_type(&Type::QualifiedPath { + name: s!("name"), + args: Box::new(GenericArgs::AngleBracketed { + args: vec![], + bindings: vec![], + }), + self_type: Box::new(Type::Generic(s!("type"))), + trait_: Some(Path { + name: String::from("trait"), + args: None, + id: Id(s!("id")), + }), + }) + }, + vec![ + Token::symbol("<"), + Token::generic("type"), + ws!(), + Token::keyword("as"), + ws!(), + Token::type_("trait"), + Token::symbol(">"), + Token::symbol("::"), + Token::identifier("name"), + ], + "::name", + ); + } + + fn assert_render( + render_fn: impl Fn(RenderingContext) -> Vec, + expected: Vec, + expected_string: &str, + ) { + let crate_ = Crate { + root: Id(String::from("1:2:3")), + crate_version: None, + includes_private: false, + index: HashMap::new(), + paths: HashMap::new(), + external_crates: HashMap::new(), + format_version: 0, + }; + let context = RenderingContext { + crate_: &crate_, + id_to_items: HashMap::new(), + }; + + let actual = render_fn(context); + + assert_eq!(actual, expected); + assert_eq!( + tokens::tokens_to_string(&actual), + expected_string.to_string() + ); + } +} From 356d9fc42a47417ecf30a9b05bf63dfc5984df18 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Fri, 27 Sep 2024 15:12:27 +0100 Subject: [PATCH 20/27] all examples in script, and lockfiles --- crux_cli/codegen.fish | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crux_cli/codegen.fish b/crux_cli/codegen.fish index f79e11ffb..a51f53253 100755 --- a/crux_cli/codegen.fish +++ b/crux_cli/codegen.fish @@ -1,6 +1,12 @@ #!/usr/bin/env fish cargo build -pushd ../examples/counter -../../target/debug/crux codegen --lib shared -popd + +for d in ../examples/* + echo "" + echo "---------------" + echo "Public API for $d" + pushd $d + ../../target/debug/crux codegen --lib shared + popd +end From b3a540a0d6741d798f749e1b9096881037f2e3e9 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sat, 26 Oct 2024 12:49:28 +0100 Subject: [PATCH 21/27] build graph wip --- Cargo.lock | 63 +- crux_cli/Cargo.toml | 14 +- crux_cli/codegen.fish | 2 +- crux_cli/src/codegen/graph.rs | 8 - crux_cli/src/codegen/mod.rs | 46 +- crux_cli/src/codegen/parser.rs | 238 ++- .../src/codegen/public_api/crate_wrapper.rs | 36 - .../public_api/intermediate_public_item.rs | 54 - .../src/codegen/public_api/item_processor.rs | 441 ------ crux_cli/src/codegen/public_api/mod.rs | 91 -- .../src/codegen/public_api/nameable_item.rs | 92 -- .../src/codegen/public_api/path_component.rs | 23 - .../src/codegen/public_api/public_item.rs | 97 -- crux_cli/src/codegen/public_api/render.rs | 1352 ----------------- crux_cli/src/codegen/public_api/tokens.rs | 111 -- crux_cli/src/command_runner.rs | 68 - crux_cli/src/main.rs | 1 - 17 files changed, 199 insertions(+), 2538 deletions(-) delete mode 100644 crux_cli/src/codegen/graph.rs delete mode 100644 crux_cli/src/codegen/public_api/crate_wrapper.rs delete mode 100644 crux_cli/src/codegen/public_api/intermediate_public_item.rs delete mode 100644 crux_cli/src/codegen/public_api/item_processor.rs delete mode 100644 crux_cli/src/codegen/public_api/mod.rs delete mode 100644 crux_cli/src/codegen/public_api/nameable_item.rs delete mode 100644 crux_cli/src/codegen/public_api/path_component.rs delete mode 100644 crux_cli/src/codegen/public_api/public_item.rs delete mode 100644 crux_cli/src/codegen/public_api/render.rs delete mode 100644 crux_cli/src/codegen/public_api/tokens.rs delete mode 100644 crux_cli/src/command_runner.rs diff --git a/Cargo.lock b/Cargo.lock index 7b50d230d..695b8aabf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,6 +255,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-manifest" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fa8484a7f2eef80e6dd2e2be90b322b9c29aeb1bbc206013d6eb2104db7241" +dependencies = [ + "serde", + "thiserror", + "toml", +] + [[package]] name = "cargo-platform" version = "0.1.8" @@ -436,7 +447,9 @@ dependencies = [ "guppy", "ignore", "libc", + "petgraph", "ramhorns", + "rustdoc-json", "rustdoc-types", "serde", "serde_json", @@ -1651,11 +1664,25 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustdoc-json" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "182c2e4c76cc5e19dd718721036d82a59d396e35c6882a3f2b54e8b928e1b36a" +dependencies = [ + "cargo-manifest", + "cargo_metadata", + "serde", + "thiserror", + "toml", + "tracing", +] + [[package]] name = "rustdoc-types" -version = "0.26.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9be1bc4a0ec3445cfa2e4ba112827544890d43d68b7d1eda5359a9c09d2cd8" +checksum = "7fd5cb7a0c0a5a4f6bc429274fc1073d395237c83ef13d0ac728e0f95f5499ca" dependencies = [ "serde", ] @@ -2020,6 +2047,7 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -2048,6 +2076,37 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "typeid" version = "1.0.2" diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index 809974e6b..c7f289ae0 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -17,14 +17,16 @@ path = "src/main.rs" anyhow.workspace = true clap = { version = "4.4.18", features = ["derive"] } console = "0.15.8" -guppy = "0.17.5" +guppy = "0.17.4" ignore = "0.4.23" -libc = "0.2.155" -ramhorns = "1.0.0" -rustdoc-types = "0.26.0" +libc = "0.2.161" +petgraph = "0.6.5" +ramhorns = "1.0.1" +rustdoc-json = "0.9.2" +rustdoc-types = "0.32.2" serde = { workspace = true, features = ["derive"] } -serde_json = "1.0.117" +serde_json = "1.0.132" similar = { version = "2.6.0", features = ["inline"] } -tokio = { version = "1.38.0", features = ["full"] } +tokio = { version = "1.41.0", features = ["full"] } tokio-fd = "0.3.0" toml = "0.8.19" diff --git a/crux_cli/codegen.fish b/crux_cli/codegen.fish index a51f53253..4064d165f 100755 --- a/crux_cli/codegen.fish +++ b/crux_cli/codegen.fish @@ -2,7 +2,7 @@ cargo build -for d in ../examples/* +for d in ../examples/hello_world echo "" echo "---------------" echo "Public API for $d" diff --git a/crux_cli/src/codegen/graph.rs b/crux_cli/src/codegen/graph.rs deleted file mode 100644 index 4cd1efa08..000000000 --- a/crux_cli/src/codegen/graph.rs +++ /dev/null @@ -1,8 +0,0 @@ -use anyhow::Result; -use guppy::{graph::PackageGraph, MetadataCommand}; - -pub(crate) fn compute_package_graph() -> Result { - let mut cmd = MetadataCommand::new(); - let package_graph = PackageGraph::from_command(&mut cmd)?; - Ok(package_graph) -} diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs index dbdfdc553..da7bd9c09 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -1,46 +1,26 @@ -mod graph; mod parser; -mod public_api; -use std::{ - fs::File, - io::{stdout, IsTerminal}, -}; +use std::fs::File; use anyhow::{bail, Result}; +use guppy::{graph::PackageGraph, MetadataCommand}; use rustdoc_types::Crate; -use tokio::{process::Command, task::spawn_blocking}; +use tokio::task::spawn_blocking; -use crate::{args::CodegenArgs, command_runner}; +use crate::args::CodegenArgs; pub async fn codegen(args: &CodegenArgs) -> Result<()> { - let graph = graph::compute_package_graph()?; + let mut cmd = MetadataCommand::new(); + let package_graph = PackageGraph::from_command(&mut cmd)?; - let Ok(lib) = graph.workspace().member_by_path(&args.lib) else { + let Ok(lib) = package_graph.workspace().member_by_path(&args.lib) else { bail!("Could not find workspace package with path {}", args.lib) }; - let mut cmd = Command::new("cargo"); - cmd.env("RUSTC_BOOTSTRAP", "1") - .env( - "RUSTDOCFLAGS", - "-Z unstable-options --output-format=json --cap-lints=allow", - ) - .arg("doc") - .arg("--no-deps") - .arg("--manifest-path") - .arg(lib.manifest_path()) - .arg("--lib"); - if stdout().is_terminal() { - cmd.arg("--color=always"); - } - - command_runner::run(&mut cmd).await?; - - let target_directory = graph.workspace().target_directory().as_std_path(); - let json_path = target_directory - .join("doc") - .join(format!("{}.json", lib.name().replace('-', "_"))); + let json_path = rustdoc_json::Builder::default() + .toolchain("nightly") + .manifest_path(lib.manifest_path()) + .build()?; let crate_: Crate = spawn_blocking(move || -> Result { let file = File::open(json_path)?; @@ -49,7 +29,9 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { }) .await??; - parser::parse(&crate_)?; + println!("Parsing rustdoc JSON, version {}", crate_.format_version); + let out = parser::parse(&crate_)?; + println!("{}", out); Ok(()) } diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 89df6f9f8..aac68ecea 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,136 +1,128 @@ -use anyhow::Result; -use rustdoc_types::{Crate, Id, Impl, ItemEnum, Path, Type}; +use std::collections::HashMap; -use super::public_api::{ - item_processor::{sorting_prefix, ItemProcessor}, - nameable_item::NameableItem, - path_component::PathComponent, - public_item::PublicItem, - PublicApi, -}; -use crate::codegen::public_api::render::RenderingContext; +use anyhow::{anyhow, Result}; +use petgraph::{dot::Dot, graph::NodeIndex, Graph}; +use rustdoc_types::{Crate, Enum, Id, Impl, Item, ItemEnum, Type, VariantKind}; -pub fn parse(crate_: &Crate) -> Result<()> { - let mut item_processor = ItemProcessor::new(crate_); - add_items(crate_, "Effect", &["Ffi"], &mut item_processor); - add_items(crate_, "App", &["Event", "ViewModel"], &mut item_processor); - item_processor.run(); +pub fn parse(crate_: &Crate) -> Result { + let mut graph = Graph::new(); + let mut nodes = HashMap::new(); - let context = RenderingContext { - crate_, - id_to_items: item_processor.id_to_items(), - }; - - let items: Vec<_> = item_processor - .output - .iter() - .filter_map(|item| { - matches!( - &item.item().inner, - ItemEnum::Union(_) - | ItemEnum::Struct(_) - | ItemEnum::StructField(_) - | ItemEnum::Enum(_) - | ItemEnum::Variant(_) - | ItemEnum::Primitive(_) - | ItemEnum::TypeAlias(_) - | ItemEnum::ForeignType - ) - .then_some(PublicItem::from_intermediate_public_item(&context, item)) - }) - .collect(); + // nodes + for (id, item) in crate_.index.clone() { + nodes.insert(id, graph.add_node(Node { id, item })); + } - let mut public_api = PublicApi { - items, - missing_item_ids: item_processor.crate_.missing_item_ids(), + let node = |id| -> Result<&NodeIndex> { + nodes + .get(id) + .ok_or_else(|| anyhow!("Could not find node with id {:?}", id)) }; - public_api.items.sort_by(PublicItem::grouping_cmp); - - println!(); - - for item in public_api.items { - println!("{:?}", item.sortable_path); - println!("{}\n", item); - } - Ok(()) -} - -fn add_items<'c: 'p, 'p>( - crate_: &'c Crate, - trait_name: &'c str, - filter: &'c [&'c str], - item_processor: &'p mut ItemProcessor<'c>, -) { - for root in find_roots(crate_, trait_name, filter) { - let item = &crate_.index[root.parent]; - for id in root.assoc_types { - let parent = PathComponent { - item: NameableItem { - item, - overridden_name: None, - sorting_prefix: sorting_prefix(item), - }, - type_: None, - hide: false, - }; - item_processor.add_to_work_queue(vec![parent], id); + // edges + for (id, item) in &crate_.index { + let source = node(id)?; + match &item.inner { + ItemEnum::Module(_module) => (), + ItemEnum::ExternCrate { name: _, rename: _ } => (), + ItemEnum::Use(_) => (), + ItemEnum::Union(_union) => (), + ItemEnum::Struct(s) => { + match &s.kind { + rustdoc_types::StructKind::Unit => (), + rustdoc_types::StructKind::Tuple(fields) => { + for field in fields { + if let Some(id) = field { + graph.add_edge(*source, *node(&id)?, Edge::HasField); + } + } + } + rustdoc_types::StructKind::Plain { + fields, + has_stripped_fields: _, + } => { + for id in fields { + graph.add_edge(*source, *node(&id)?, Edge::HasField); + } + } + }; + for id in &s.impls { + graph.add_edge(*source, *node(&id)?, Edge::Implements); + } + } + ItemEnum::StructField(_) => (), + ItemEnum::Enum(Enum { variants, .. }) => { + for id in variants { + graph.add_edge(*source, *node(&id)?, Edge::HasVariant); + } + } + ItemEnum::Variant(v) => { + match &v.kind { + VariantKind::Plain => (), + VariantKind::Tuple(_vec) => (), + VariantKind::Struct { + fields, + has_stripped_fields: _, + } => { + for field in fields { + graph.add_edge(*source, *node(&field)?, Edge::HasField); + } + } + }; + } + ItemEnum::Function(_function) => (), + ItemEnum::Trait(_) => (), + ItemEnum::TraitAlias(_trait_alias) => (), + ItemEnum::Impl(Impl { + for_: Type::ResolvedPath(target), + items, + .. + }) => { + graph.add_edge(*source, *node(&target.id)?, Edge::ImplFor); + for id in items { + graph.add_edge(*source, *node(&id)?, Edge::AssociatedItem); + } + } + ItemEnum::Impl(_) => (), + ItemEnum::TypeAlias(_type_alias) => (), + ItemEnum::Constant { + type_: _, + const_: _, + } => (), + ItemEnum::Static(_) => (), + ItemEnum::ExternType => (), + ItemEnum::Macro(_) => (), + ItemEnum::ProcMacro(_proc_macro) => (), + ItemEnum::Primitive(_primitive) => (), + ItemEnum::AssocConst { type_: _, value: _ } => (), + ItemEnum::AssocType { + generics: _, + bounds: _, + type_: Some(Type::ResolvedPath(target)), + } => { + if let Ok(dest) = node(&target.id) { + graph.add_edge(*source, *dest, Edge::AssociatedType); + } + } + ItemEnum::AssocType { .. } => (), } } + let out = Dot::new(&graph); + Ok(format!("{:?}", out)) } -struct Root<'a> { - parent: &'a Id, - assoc_types: Vec<&'a Id>, +#[derive(Debug)] +struct Node { + id: Id, + item: Item, } -fn find_roots<'a>( - crate_: &'a Crate, - trait_name: &'a str, - filter: &'a [&'a str], -) -> impl Iterator> { - crate_ - .index - .iter() - .filter_map(move |(parent, parent_item)| { - if let ItemEnum::Impl(Impl { - trait_: Some(Path { name, .. }), - // for_: Type::ResolvedPath(_), - items, - .. - }) = &parent_item.inner - { - if name.as_str() == trait_name { - let assoc_types = items - .iter() - .filter_map(|id| { - let item = &crate_.index[id]; - item.name.as_deref().and_then(|name| { - if filter.contains(&name) { - if let ItemEnum::AssocType { - default: Some(Type::ResolvedPath(Path { id, .. })), - .. - } = &item.inner - { - Some(id) - } else { - None - } - } else { - None - } - }) - }) - .collect(); - Some(Root { - parent, - assoc_types, - }) - } else { - None - } - } else { - None - } - }) +#[derive(Debug)] +enum Edge { + ImplFor, + HasField, + Implements, + AssociatedItem, + AssociatedType, + HasVariant, } diff --git a/crux_cli/src/codegen/public_api/crate_wrapper.rs b/crux_cli/src/codegen/public_api/crate_wrapper.rs deleted file mode 100644 index 7d698573c..000000000 --- a/crux_cli/src/codegen/public_api/crate_wrapper.rs +++ /dev/null @@ -1,36 +0,0 @@ -use rustdoc_types::{Crate, Id, Item}; - -/// The [`Crate`] type represents the deserialized form of the rustdoc JSON -/// input. This wrapper adds some helpers and state on top. -pub struct CrateWrapper<'c> { - crate_: &'c Crate, - - /// Normally, an item referenced by [`Id`] is present in the rustdoc JSON. - /// If [`Self::crate_.index`] is missing an [`Id`], then we add it here, to - /// aid with debugging. It will typically be missing because of bugs (or - /// borderline bug such as re-exports of foreign items like discussed in - /// ) - /// We do not report it to users by default, because they can't do anything - /// about it. Missing IDs will be printed with `--verbose` however. - missing_ids: Vec<&'c Id>, -} - -impl<'c> CrateWrapper<'c> { - pub fn new(crate_: &'c Crate) -> Self { - Self { - crate_, - missing_ids: vec![], - } - } - - pub fn get_item(&mut self, id: &'c Id) -> Option<&'c Item> { - self.crate_.index.get(id).or_else(|| { - self.missing_ids.push(id); - None - }) - } - - pub fn missing_item_ids(&self) -> Vec { - self.missing_ids.iter().map(|m| m.0.clone()).collect() - } -} diff --git a/crux_cli/src/codegen/public_api/intermediate_public_item.rs b/crux_cli/src/codegen/public_api/intermediate_public_item.rs deleted file mode 100644 index 2e6dfd3f0..000000000 --- a/crux_cli/src/codegen/public_api/intermediate_public_item.rs +++ /dev/null @@ -1,54 +0,0 @@ -use rustdoc_types::Item; - -use super::nameable_item::NameableItem; -use super::path_component::PathComponent; -use super::public_item::PublicItemPath; -use super::render::RenderingContext; -use super::tokens::Token; - -/// This struct represents one public item of a crate, but in intermediate form. -/// Conceptually it wraps a single [`Item`] even though the path to the item -/// consists of many [`Item`]s. Later, one [`Self`] will be converted to exactly -/// one [`crate::PublicItem`]. -#[derive(Clone, Debug)] -pub struct IntermediatePublicItem<'c> { - path: Vec>, -} - -impl<'c> IntermediatePublicItem<'c> { - pub fn new(path: Vec>) -> Self { - Self { path } - } - - #[must_use] - pub fn item(&self) -> &'c Item { - self.path() - .last() - .expect("path must not be empty") - .item - .item - } - - #[must_use] - pub fn path(&self) -> &[PathComponent<'c>] { - &self.path - } - - /// See [`crate::item_processor::sorting_prefix()`] docs for an explanation why we have this. - #[must_use] - pub fn sortable_path(&self, context: &RenderingContext) -> PublicItemPath { - self.path() - .iter() - .map(|p| NameableItem::sortable_name(&p.item, context)) - .collect() - } - - #[must_use] - pub fn path_contains_renamed_item(&self) -> bool { - self.path().iter().any(|m| m.item.overridden_name.is_some()) - } - - pub fn render_token_stream(&self, context: &RenderingContext) -> Vec { - context.token_stream(self) - } -} diff --git a/crux_cli/src/codegen/public_api/item_processor.rs b/crux_cli/src/codegen/public_api/item_processor.rs deleted file mode 100644 index 70a155a99..000000000 --- a/crux_cli/src/codegen/public_api/item_processor.rs +++ /dev/null @@ -1,441 +0,0 @@ -use super::nameable_item::NameableItem; -use super::{ - crate_wrapper::CrateWrapper, intermediate_public_item::IntermediatePublicItem, - path_component::PathComponent, -}; -use rustdoc_types::{ - Crate, Id, Impl, Import, Item, ItemEnum, Module, Struct, StructKind, Type, VariantKind, -}; -use std::{ - collections::{HashMap, VecDeque}, - vec, -}; - -/// Items in rustdoc JSON reference each other by Id. The [`ItemProcessor`] -/// essentially takes one Id at a time and figure out what to do with it. Once -/// complete, the item is ready to be listed as part of the public API, and -/// optionally can also be used as part of a path to another (child) item. -/// -/// This struct contains a (processed) path to an item that is about to be -/// processed further, and the Id of that item. -#[derive(Debug)] -struct UnprocessedItem<'c> { - /// The path to the item to process. - parent_path: Vec>, - - /// The Id of the item to process. - id: &'c Id, -} - -/// Processes items to find more items and to figure out the path to each item. -/// Some non-obvious cases to take into consideration are: -/// 1. A single item is imported several times. -/// 2. An item is (publicly) imported from another crate -/// -/// Note that this implementation iterates over everything, so if the rustdoc -/// JSON is generated with `--document-private-items`, then private items will -/// also be included in the output. Use with `--document-private-items` is not -/// supported. -pub struct ItemProcessor<'c> { - /// The original and unmodified rustdoc JSON, in deserialized form. - pub crate_: CrateWrapper<'c>, - - /// A queue of unprocessed items to process. - work_queue: VecDeque>, - - /// The output. A list of processed items. Note that the order is - /// intentionally "logical", so that e.g. struct fields items follows from - /// struct items. - pub output: Vec>, -} - -impl<'c> ItemProcessor<'c> { - pub(crate) fn new(crate_: &'c Crate) -> Self { - ItemProcessor { - crate_: CrateWrapper::new(crate_), - work_queue: VecDeque::new(), - output: vec![], - } - } - - /// Adds an item to the front of the work queue. We want to add items to the - /// front of the work queue since we process items from the top, and since - /// we want to "fully" process an item before we move on to the next one. So - /// when we encounter a struct, we also encounter its struct fields, and we - /// want to insert the struct fields BEFORE everything else, so that these - /// items remain grouped together. And the same applies for many kinds of - /// groupings (enums, impls, etc). - pub fn add_to_work_queue(&mut self, parent_path: Vec>, id: &'c Id) { - self.work_queue - .push_front(UnprocessedItem { parent_path, id }); - } - - /// Processes the entire work queue. Adds more items based on items it - /// processes. When this returns, all items and their children and impls - /// have been recursively processed. - pub fn run(&mut self) { - while let Some(unprocessed_item) = self.work_queue.pop_front() { - if let Some(item) = self.crate_.get_item(unprocessed_item.id) { - self.process_any_item(item, unprocessed_item); - } - } - } - - /// Process any item. In particular, does the right thing if the item is an - /// impl or an import. - fn process_any_item(&mut self, item: &'c Item, unprocessed_item: UnprocessedItem<'c>) { - match &item.inner { - ItemEnum::Import(import) => { - if import.glob { - self.process_import_glob_item(import, unprocessed_item, item); - } else { - self.process_import_item(item, import, unprocessed_item); - } - } - ItemEnum::Impl(impl_) => { - self.process_impl_item(unprocessed_item, item, impl_); - } - _ => { - self.process_item_unless_recursive(unprocessed_item, item, None); - } - } - } - - /// We need to handle `pub use foo::*` specially. In case of such wildcard - /// imports, `glob` will be `true` and `id` will be the module we should - /// import all items from, but we should NOT add the module itself. Before - /// we inline this wildcard import, make sure that the module is not - /// indirectly trying to import itself. If we allow that, we'll get a stack - /// overflow. - fn process_import_glob_item( - &mut self, - import: &'c Import, - unprocessed_item: UnprocessedItem<'c>, - item: &'c Item, - ) { - if let Some(Item { - inner: ItemEnum::Module(Module { items, .. }), - .. - }) = import - .id - .as_ref() - .and_then(|id| self.get_item_if_not_in_path(&unprocessed_item.parent_path, id)) - { - for item_id in items { - self.add_to_work_queue(unprocessed_item.parent_path.clone(), item_id); - } - } else { - self.process_item( - unprocessed_item, - item, - Some(format!("<<{}::*>>", import.source)), - ); - } - } - - /// Since public imports are part of the public API, we inline them, i.e. - /// replace the item corresponding to an import with the item that is - /// imported. If we didn't do this, publicly imported items would show up as - /// just e.g. `pub use some::function`, which is not sufficient for the use - /// cases of this tool. We want to show the actual API, and thus also show - /// type information! There is one exception; for re-exports of primitive - /// types, there is no item Id to inline with, so they remain as e.g. `pub - /// use my_i32` in the output. - fn process_import_item( - &mut self, - item: &'c Item, - import: &'c Import, - unprocessed_item: UnprocessedItem<'c>, - ) { - let mut actual_item = item; - - if let Some(imported_item) = import - .id - .as_ref() - .and_then(|id| self.get_item_if_not_in_path(&unprocessed_item.parent_path, id)) - { - actual_item = imported_item; - } - - self.process_item(unprocessed_item, actual_item, Some(import.name.clone())); - } - - /// Processes impls. Impls are special because we support filtering out e.g. - /// blanket implementations to reduce output noise. - fn process_impl_item( - &mut self, - unprocessed_item: UnprocessedItem<'c>, - item: &'c Item, - impl_: &'c Impl, - ) { - if !ImplKind::from(item, impl_).is_active() { - return; - } - - self.process_item_for_type(unprocessed_item, item, None, Some(&impl_.for_)); - } - - /// Make sure the item we are about to process is not already part of the - /// item path. If it is, we have encountered recursion. Stop processing in - /// that case. - fn process_item_unless_recursive( - &mut self, - unprocessed_item: UnprocessedItem<'c>, - item: &'c Item, - overridden_name: Option, - ) { - if unprocessed_item - .parent_path - .iter() - .any(|m| m.item.item.id == item.id) - { - let recursion_breaker = unprocessed_item.finish( - item, - Some(format!("<<{}>>", item.name.as_deref().unwrap_or(""))), - None, - ); - self.output.push(recursion_breaker); - } else { - self.process_item(unprocessed_item, item, overridden_name); - } - } - - /// Process an item. Setup jobs for its children and impls and and then put - /// it in the output. - fn process_item( - &mut self, - unprocessed_item: UnprocessedItem<'c>, - item: &'c Item, - overridden_name: Option, - ) { - if !item.attrs.contains(&"#[serde(skip)]".to_string()) { - self.process_item_for_type(unprocessed_item, item, overridden_name, None); - } - } - - /// Process an item. Setup jobs for its children and impls and and then put - /// it in the output. - fn process_item_for_type( - &mut self, - unprocessed_item: UnprocessedItem<'c>, - item: &'c Item, - overridden_name: Option, - type_: Option<&'c Type>, - ) { - let finished_item = unprocessed_item.finish(item, overridden_name, type_); - - let children = children_for_item(item).into_iter().flatten(); - let impls = impls_for_item(item).into_iter().flatten(); - - for id in children { - self.add_to_work_queue(finished_item.path().into(), id); - } - - // As usual, impls are special. We want impl items to appear grouped - // with the trait or type it involves. But when _rendering_ we want to - // use the type that we implement for, so that e.g. generic arguments - // can be shown. So hide the "sorting path" of the impl. We'll instead - // render the path to the type the impl is for. - for id in impls { - let mut path = finished_item.path().to_vec(); - for a in &mut path { - a.hide = true; - } - self.add_to_work_queue(path, id); - } - - self.output.push(finished_item); - } - - /// Get the rustdoc JSON item with `id`, but only if it is not already part - /// of the path. This can happen in the case of recursive re-exports, in - /// which case we need to break the recursion. - fn get_item_if_not_in_path( - &mut self, - parent_path: &[PathComponent<'c>], - id: &'c Id, - ) -> Option<&'c Item> { - if parent_path.iter().any(|m| m.item.item.id == *id) { - // The item is already in the path! Break import recursion... - return None; - } - - self.crate_.get_item(id) - } - - // Returns a HashMap where a rustdoc JSON Id is mapped to what public items - // that have this ID. The reason this is a one-to-many mapping is because of - // re-exports. If an API re-exports a public item in a different place, the - // same item will be reachable by different paths, and thus the Vec will - // contain many [`IntermediatePublicItem`]s for that ID. - // - // You might think this is rare, but it is actually a common thing in - // real-world code. - pub fn id_to_items(&self) -> HashMap<&Id, Vec<&IntermediatePublicItem>> { - let mut id_to_items: HashMap<&Id, Vec<&IntermediatePublicItem>> = HashMap::new(); - for finished_item in &self.output { - id_to_items - .entry(&finished_item.item().id) - .or_default() - .push(finished_item); - } - id_to_items - } -} - -impl<'c> UnprocessedItem<'c> { - /// Turns an [`UnprocessedItem`] into a finished [`IntermediatePublicItem`]. - fn finish( - self, - item: &'c Item, - overridden_name: Option, - type_: Option<&'c Type>, - ) -> IntermediatePublicItem<'c> { - // Transfer path ownership to output item - let mut path = self.parent_path; - - // Complete the path with the last item - path.push(PathComponent { - item: NameableItem { - item, - overridden_name, - sorting_prefix: sorting_prefix(item), - }, - type_, - hide: false, - }); - - // Done - IntermediatePublicItem::new(path) - } -} - -/// In order for items in the output to be nicely grouped, we add a prefix to -/// each item in the path to an item. That way, sorting on the name (with this -/// prefix) will group items. But we don't want this prefix to be be visible to -/// users of course, so we do this "behind the scenes". -pub(crate) fn sorting_prefix(item: &Item) -> u8 { - match &item.inner { - ItemEnum::ExternCrate { .. } => 1, - ItemEnum::Import(_) => 2, - - ItemEnum::Primitive(_) => 3, - - ItemEnum::Module(_) => 4, - - ItemEnum::Macro(_) => 5, - ItemEnum::ProcMacro(_) => 6, - - ItemEnum::Enum(_) => 7, - ItemEnum::Union(_) => 8, - ItemEnum::Struct(_) => 9, - ItemEnum::StructField(_) => 10, - ItemEnum::Variant(_) => 11, - - ItemEnum::Constant { .. } => 12, - - ItemEnum::Static(_) => 13, - - ItemEnum::Trait(_) => 14, - - ItemEnum::AssocType { .. } => 15, - ItemEnum::AssocConst { .. } => 16, - - ItemEnum::Function(_) => 17, - - ItemEnum::TypeAlias(_) => 19, - - ItemEnum::Impl(impl_) => match ImplKind::from(item, impl_) { - // To not cause a diff when changing a manual impl to an - // auto-derived impl (or vice versa), we put them in the same group. - ImplKind::Inherent => 20, - ImplKind::Trait | ImplKind::AutoDerived => 21, - ImplKind::AutoTrait => 23, - ImplKind::Blanket => 24, - }, - - ItemEnum::ForeignType => 25, - - ItemEnum::OpaqueTy(_) => 26, - - ItemEnum::TraitAlias(_) => 27, - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum ImplKind { - /// E.g. `impl Foo` or `impl<'a> Foo<'a>` - Inherent, - - /// E.g. `impl Bar for Foo {}` - Trait, - - /// Auto-generated by `#[derive(...)]` - AutoDerived, - - /// Auto-trait impl such as `impl Sync for Foo` - AutoTrait, - - /// Blanket impls such as `impl Any for T` - Blanket, -} - -impl ImplKind { - fn from(impl_item: &Item, impl_: &Impl) -> Self { - let has_blanket_impl = impl_.blanket_impl.is_some(); - let is_automatically_derived = impl_item - .attrs - .iter() - .any(|a| a == "#[automatically_derived]"); - - // See https://github.com/rust-lang/rust/blob/54f20bbb8a7aeab93da17c0019c1aaa10329245a/src/librustdoc/json/conversions.rs#L589-L590 - match (impl_.synthetic, has_blanket_impl) { - (true, false) => ImplKind::AutoTrait, - (false, true) => ImplKind::Blanket, - _ if is_automatically_derived => ImplKind::AutoDerived, - _ if impl_.trait_.is_none() => ImplKind::Inherent, - _ => ImplKind::Trait, - } - } -} - -impl ImplKind { - fn is_active(&self) -> bool { - match self { - ImplKind::Blanket | ImplKind::AutoTrait | ImplKind::AutoDerived => false, - ImplKind::Inherent | ImplKind::Trait => true, - } - } -} - -/// Some items contain other items, which is relevant for analysis. Keep track -/// of such relationships. -const fn children_for_item(item: &Item) -> Option<&Vec> { - match &item.inner { - ItemEnum::Module(m) => Some(&m.items), - ItemEnum::Union(u) => Some(&u.fields), - ItemEnum::Struct(Struct { - kind: StructKind::Plain { fields, .. }, - .. - }) - | ItemEnum::Variant(rustdoc_types::Variant { - kind: VariantKind::Struct { fields, .. }, - .. - }) => Some(fields), - ItemEnum::Enum(e) => Some(&e.variants), - ItemEnum::Trait(t) => Some(&t.items), - ItemEnum::Impl(i) => Some(&i.items), - _ => None, - } -} - -pub fn impls_for_item(item: &Item) -> Option<&[Id]> { - match &item.inner { - ItemEnum::Union(u) => Some(&u.impls), - ItemEnum::Struct(s) => Some(&s.impls), - ItemEnum::Enum(e) => Some(&e.impls), - ItemEnum::Primitive(p) => Some(&p.impls), - ItemEnum::Trait(t) => Some(&t.implementations), - _ => None, - } -} diff --git a/crux_cli/src/codegen/public_api/mod.rs b/crux_cli/src/codegen/public_api/mod.rs deleted file mode 100644 index 4b0c40a0e..000000000 --- a/crux_cli/src/codegen/public_api/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -mod crate_wrapper; -mod intermediate_public_item; -pub mod item_processor; -pub mod nameable_item; -pub mod path_component; -pub mod public_item; -pub mod render; -mod tokens; - -use public_item::PublicItem; - -/// The public API of a crate -/// -/// Create an instance with [`Builder`]. -/// -/// ## Rendering the items -/// -/// To render the items in the public API you can iterate over the [items](PublicItem). -/// -/// You get the `rustdoc_json_str` in the example below as explained in the [crate] documentation, either via -/// [`rustdoc_json`](https://crates.io/crates/rustdoc_json) or by calling `cargo rustdoc` yourself. -/// -/// ```no_run -/// use public_api::PublicApi; -/// use std::path::PathBuf; -/// -/// # let rustdoc_json: PathBuf = todo!(); -/// // Gather the rustdoc content as described in this crates top-level documentation. -/// let public_api = public_api::Builder::from_rustdoc_json(&rustdoc_json).build()?; -/// -/// for public_item in public_api.items() { -/// // here we print the items to stdout, we could also write to a string or a file. -/// println!("{}", public_item); -/// } -/// -/// // If you want all items of the public API in a single big multi-line String then -/// // you can do like this: -/// let public_api_string = public_api.to_string(); -/// # Ok::<(), Box>(()) -/// ``` -#[derive(Debug)] -#[non_exhaustive] // More fields might be added in the future -pub struct PublicApi { - /// The items that constitutes the public API. An "item" is for example a - /// function, a struct, a struct field, an enum, an enum variant, a module, - /// etc... - pub(crate) items: Vec, - - /// See [`Self::missing_item_ids()`] - #[allow(dead_code)] - pub(crate) missing_item_ids: Vec, -} - -impl PublicApi { - /// Returns an iterator over all public items in the public API - pub fn items(&self) -> impl Iterator { - self.items.iter() - } - - /// Like [`Self::items()`], but ownership of all `PublicItem`s are - /// transferred to the caller. - #[allow(dead_code)] - pub fn into_items(self) -> impl Iterator { - self.items.into_iter() - } - - /// The rustdoc JSON IDs of missing but referenced items. Intended for use - /// with `--verbose` flags or similar. - /// - /// In some cases, a public item might be referenced from another public - /// item (e.g. a `mod`), but is missing from the rustdoc JSON file. This - /// occurs for example in the case of re-exports of external modules (see - /// ). The entries - /// in this Vec are what IDs that could not be found. - /// - /// The exact format of IDs are to be considered an implementation detail - /// and must not be be relied on. - #[allow(dead_code)] - pub fn missing_item_ids(&self) -> impl Iterator { - self.missing_item_ids.iter() - } -} - -impl std::fmt::Display for PublicApi { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for item in self.items() { - writeln!(f, "{item}")?; - } - Ok(()) - } -} diff --git a/crux_cli/src/codegen/public_api/nameable_item.rs b/crux_cli/src/codegen/public_api/nameable_item.rs deleted file mode 100644 index d67fda469..000000000 --- a/crux_cli/src/codegen/public_api/nameable_item.rs +++ /dev/null @@ -1,92 +0,0 @@ -use rustdoc_types::{Item, ItemEnum}; - -use super::render::RenderingContext; - -/// Wraps an [`Item`] and allows us to override its name. -#[derive(Clone, Debug)] -pub struct NameableItem<'c> { - /// The item we are effectively wrapping. - pub item: &'c Item, - - /// If `Some`, this overrides [Item::name], which happens in the case of - /// renamed imports (`pub use other::Item as Foo;`). - /// - /// We can't calculate this on-demand, because we can't know the final name - /// until we have checked if we need to break import recursion. - pub overridden_name: Option, - - /// See [`crate::item_processor::sorting_prefix()`] docs for an explanation why we have this. - pub sorting_prefix: u8, -} - -impl<'c> NameableItem<'c> { - /// The regular name of the item. Shown to users. - pub fn name(&self) -> Option<&str> { - self.overridden_name - .as_deref() - .or(self.item.name.as_deref()) - } - - /// The name that, when sorted on, will group items nicely. Is never shown - /// to a user. - pub fn sortable_name(&self, context: &RenderingContext) -> String { - // Note that in order for the prefix to sort properly lexicographically, - // we need to pad it with leading zeroes. - let mut sortable_name = format!("{:0>3}-", self.sorting_prefix); - - if let Some(name) = self.name() { - sortable_name.push_str(name); - } else if let ItemEnum::Impl(impl_) = &self.item.inner { - // In order for items of impls to be grouped together with its impl, - // add the "name" of the impl to the sorting prefix. Ignore `!` when - // sorting however, because that just messes the expected order up. - sortable_name.push_str(&super::tokens::tokens_to_string(&context.render_impl( - impl_, - &[], - true, /* disregard_negativity */ - ))); - - // If this is an inherent impl, additionally add the concatenated - // names of all associated items to the "name" of the impl. This makes - // multiple inherent impls group together, even if they have the - // same "name". - // - // For example, consider this code: - // - // pub struct MultipleInherentImpls; - // - // impl MultipleInherentImpls { - // pub fn impl_one() {} - // } - // - // impl MultipleInherentImpls { - // pub fn impl_two() {} - // } - // - // In this case, we want to group the two impls together. So - // the name of the first impl should be - // - // impl MultipleInherentImpls-impl_one - // - // and the second one - // - // impl MultipleInherentImpls-impl_two - // - if impl_.trait_.is_none() { - let mut assoc_item_names: Vec<&str> = impl_ - .items - .iter() - .filter_map(|id| context.crate_.index.get(id)) - .filter_map(|item| item.name.as_ref()) - .map(String::as_str) - .collect(); - assoc_item_names.sort_unstable(); - - sortable_name.push('-'); - sortable_name.push_str(&assoc_item_names.join("-")); - } - } - - sortable_name - } -} diff --git a/crux_cli/src/codegen/public_api/path_component.rs b/crux_cli/src/codegen/public_api/path_component.rs deleted file mode 100644 index ab508f093..000000000 --- a/crux_cli/src/codegen/public_api/path_component.rs +++ /dev/null @@ -1,23 +0,0 @@ -use rustdoc_types::Type; - -use super::nameable_item::NameableItem; - -/// A public item in a public API can only be referenced via a path. For example -/// `mod_a::mod_b::StructC`. A `PathComponent` represents one component of such -/// a path. A path component can either be a Rust item, or a Rust type. Normally -/// it is an item. The typical case when it is a type is when there are generic -/// arguments involved. For example, `Option` is a type. The -/// corresponding item is `Option` (no specific generic args has been -/// specified for the generic parameter T). -#[derive(Clone, Debug)] -pub struct PathComponent<'c> { - /// The item for this path component. - pub item: NameableItem<'c>, - - /// The type, if applicable. If we have a type, we'll want to use that - /// instead of `item`, since the type might have specific generic args. - pub type_: Option<&'c Type>, - - /// If `true`, do not render this path component to users. - pub hide: bool, -} diff --git a/crux_cli/src/codegen/public_api/public_item.rs b/crux_cli/src/codegen/public_api/public_item.rs deleted file mode 100644 index 9c0e820e8..000000000 --- a/crux_cli/src/codegen/public_api/public_item.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::cmp::Ordering; -use std::fmt::Display; -use std::hash::Hash; - -use super::intermediate_public_item::IntermediatePublicItem; -use super::render::RenderingContext; -use super::tokens::tokens_to_string; -use super::tokens::Token; - -/// Each public item (except `impl`s) have a path that is displayed like -/// `first::second::third`. Internally we represent that with a `vec!["first", -/// "second", "third"]`. This is a type alias for that internal representation -/// to make the code easier to read. -pub(crate) type PublicItemPath = Vec; - -/// Represent a public item of an analyzed crate, i.e. an item that forms part -/// of the public API of a crate. Implements [`Display`] so it can be printed. It -/// also implements [`Ord`], but how items are ordered are not stable yet, and -/// will change in later versions. -#[derive(Clone)] -pub struct PublicItem { - /// Read [`crate::item_processor::sorting_prefix()`] docs for more info - pub(crate) sortable_path: PublicItemPath, - - /// The rendered item as a stream of [`Token`]s - pub(crate) tokens: Vec, -} - -impl PublicItem { - pub(crate) fn from_intermediate_public_item( - context: &RenderingContext, - public_item: &IntermediatePublicItem<'_>, - ) -> PublicItem { - PublicItem { - sortable_path: public_item.sortable_path(context), - tokens: public_item.render_token_stream(context), - } - } - - /// The rendered item as a stream of [`Token`]s - #[allow(dead_code)] - pub fn tokens(&self) -> impl Iterator { - self.tokens.iter() - } - - /// Special version of [`cmp`](Ord::cmp) that is used to sort public items in a way that - /// makes them grouped logically. For example, struct fields will be put - /// right after the struct they are part of. - #[must_use] - pub fn grouping_cmp(&self, other: &Self) -> std::cmp::Ordering { - // This will make e.g. struct and struct fields be grouped together. - if let Some(ordering) = different_or_none(&self.sortable_path, &other.sortable_path) { - return ordering; - } - - // Fall back to lexical sorting if the above is not sufficient - self.to_string().cmp(&other.to_string()) - } -} - -impl PartialEq for PublicItem { - fn eq(&self, other: &Self) -> bool { - self.tokens == other.tokens - } -} - -impl Eq for PublicItem {} - -impl Hash for PublicItem { - fn hash(&self, state: &mut H) { - self.tokens.hash(state); - } -} - -/// We want pretty-printing (`"{:#?}"`) of [`crate::diff::PublicApiDiff`] to print -/// each public item as `Display`, so implement `Debug` with `Display`. -impl std::fmt::Debug for PublicItem { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self, f) - } -} - -/// One of the basic uses cases is printing a sorted `Vec` of `PublicItem`s. So -/// we implement `Display` for it. -impl Display for PublicItem { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", tokens_to_string(&self.tokens)) - } -} - -/// Returns `None` if two items are equal. Otherwise their ordering is returned. -fn different_or_none(a: &T, b: &T) -> Option { - match a.cmp(b) { - Ordering::Equal => None, - c => Some(c), - } -} diff --git a/crux_cli/src/codegen/public_api/render.rs b/crux_cli/src/codegen/public_api/render.rs deleted file mode 100644 index 5ceba5d6f..000000000 --- a/crux_cli/src/codegen/public_api/render.rs +++ /dev/null @@ -1,1352 +0,0 @@ -#![allow(clippy::unused_self)] - -use super::intermediate_public_item::IntermediatePublicItem; -use super::nameable_item::NameableItem; -use super::path_component::PathComponent; -use super::tokens::Token; -use std::ops::Deref; -use std::{cmp::Ordering, collections::HashMap, vec}; - -use rustdoc_types::{ - Abi, Constant, Crate, FnDecl, FunctionPointer, GenericArg, GenericArgs, GenericBound, - GenericParamDef, GenericParamDefKind, Generics, Header, Id, Impl, Item, ItemEnum, MacroKind, - Path, PolyTrait, StructKind, Term, Trait, Type, TypeBinding, TypeBindingKind, VariantKind, - WherePredicate, -}; - -/// A simple macro to write `Token::Whitespace` in less characters. -macro_rules! ws { - () => { - Token::Whitespace - }; -} - -/// When we render an item, it might contain references to other parts of the -/// public API. For such cases, the rendering code can use the fields in this -/// struct. -pub struct RenderingContext<'c> { - /// The original and unmodified rustdoc JSON, in deserialized form. - pub crate_: &'c Crate, - - /// Given a rustdoc JSON ID, keeps track of what public items that have this Id. - pub id_to_items: HashMap<&'c Id, Vec<&'c IntermediatePublicItem<'c>>>, -} - -impl<'c> RenderingContext<'c> { - pub fn token_stream(&self, public_item: &IntermediatePublicItem<'c>) -> Vec { - let item = public_item.item(); - let item_path = public_item.path(); - - let mut tokens = vec![]; - - for attr in &item.attrs { - if attr_relevant_for_public_apis(attr) { - tokens.push(Token::Annotation(attr.clone())); - tokens.push(ws!()); - } - } - - let inner_tokens = match &item.inner { - ItemEnum::Module(_) => self.render_simple(&["mod"], item_path), - ItemEnum::ExternCrate { .. } => self.render_simple(&["extern", "crate"], item_path), - ItemEnum::Import(_) => self.render_simple(&["use"], item_path), - ItemEnum::Union(_) => self.render_simple(&["union"], item_path), - ItemEnum::Struct(s) => { - let mut output = self.render_simple(&["struct"], item_path); - output.extend(self.render_generics(&s.generics)); - if let StructKind::Tuple(fields) = &s.kind { - output.extend( - self.render_option_tuple(&self.resolve_tuple_fields(fields), Some(&pub_())), - ); - } - output - } - ItemEnum::StructField(inner) => { - let mut output = self.render_simple(&[], item_path); - output.extend(colon()); - output.extend(self.render_type(inner)); - output - } - ItemEnum::Enum(e) => { - let mut output = self.render_simple(&["enum"], item_path); - output.extend(self.render_generics(&e.generics)); - output - } - ItemEnum::Variant(inner) => { - let mut output = self.render_simple(&[], item_path); - match &inner.kind { - VariantKind::Struct { .. } => {} // Each struct field is printed individually - VariantKind::Plain => { - if let Some(discriminant) = &inner.discriminant { - output.extend(equals()); - output.push(Token::identifier(&discriminant.value)); - } - } - VariantKind::Tuple(fields) => { - output.extend( - self.render_option_tuple(&self.resolve_tuple_fields(fields), None), - ); - } - } - output - } - ItemEnum::Function(inner) => self.render_function( - self.render_path(item_path), - &inner.decl, - &inner.generics, - &inner.header, - ), - ItemEnum::Trait(trait_) => self.render_trait(trait_, item_path), - ItemEnum::TraitAlias(_) => self.render_simple(&["trait", "alias"], item_path), - ItemEnum::Impl(impl_) => { - self.render_impl(impl_, item_path, false /* disregard_negativity */) - } - ItemEnum::TypeAlias(inner) => { - let mut output = self.render_simple(&["type"], item_path); - output.extend(self.render_generics(&inner.generics)); - output.extend(equals()); - output.extend(self.render_type(&inner.type_)); - output - } - ItemEnum::AssocType { - generics, - bounds, - default, - } => { - let mut output = self.render_simple(&["type"], item_path); - output.extend(self.render_generics(generics)); - output.extend(self.render_generic_bounds_with_colon(bounds)); - if let Some(ty) = default { - output.extend(equals()); - output.extend(self.render_type(ty)); - } - output - } - ItemEnum::OpaqueTy(_) => self.render_simple(&["opaque", "type"], item_path), - ItemEnum::Constant { const_, type_ } => { - let mut output = self.render_simple(&["const"], item_path); - output.extend(colon()); - output.extend(self.render_constant(const_, Some(type_))); - output - } - ItemEnum::AssocConst { type_, .. } => { - let mut output = self.render_simple(&["const"], item_path); - output.extend(colon()); - output.extend(self.render_type(type_)); - output - } - ItemEnum::Static(inner) => { - let tags = if inner.mutable { - vec!["mut", "static"] - } else { - vec!["static"] - }; - let mut output = self.render_simple(&tags, item_path); - output.extend(colon()); - output.extend(self.render_type(&inner.type_)); - output - } - ItemEnum::ForeignType => self.render_simple(&["type"], item_path), - ItemEnum::Macro(_definition) => { - // TODO: _definition contains the whole definition, it would be really neat to get out all possible ways to invoke it - let mut output = self.render_simple(&["macro"], item_path); - output.push(Token::symbol("!")); - output - } - ItemEnum::ProcMacro(inner) => { - let mut output = self.render_simple(&["proc", "macro"], item_path); - output.pop(); // Remove name of macro to possibly wrap it in `#[]` - let name = Token::identifier(item.name.as_deref().unwrap_or("")); - match inner.kind { - MacroKind::Bang => output.extend(vec![name, Token::symbol("!()")]), - MacroKind::Attr => { - output.extend(vec![Token::symbol("#["), name, Token::symbol("]")]); - } - MacroKind::Derive => { - output.extend(vec![Token::symbol("#[derive("), name, Token::symbol(")]")]); - } - } - output - } - ItemEnum::Primitive(primitive) => { - // This is hard to write tests for since only Rust `core` is - // allowed to define primitives. So you have to test this code - // using the pre-built rustdoc JSON for core: - // - // rustup component add rust-docs-json --toolchain nightly - // cargo run -- --rustdoc-json ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/share/doc/rust/json/core.json - let mut output = pub_(); - output.extend([ - Token::kind("type"), - ws!(), - Token::primitive(&primitive.name), - ]); - output - } - }; - - tokens.extend(inner_tokens); - - tokens - } - - /// Tuple fields are referenced by ID in JSON, but we need to look up the - /// actual types that the IDs correspond to, in order to render the fields. - /// This helper does that for a slice of fields. - fn resolve_tuple_fields(&self, fields: &[Option]) -> Vec> { - let mut resolved_fields: Vec> = vec![]; - - for id in fields { - resolved_fields.push( - if let Some(Item { - inner: ItemEnum::StructField(type_), - .. - }) = id.as_ref().and_then(|id| self.crate_.index.get(id)) - { - Some(type_) - } else { - None - }, - ); - } - - resolved_fields - } - - fn render_simple(&self, tags: &[&str], path: &[PathComponent]) -> Vec { - let mut output = pub_(); - output.extend( - tags.iter() - .flat_map(|t| [Token::kind(*t), ws!()]) - .collect::>(), - ); - output.extend(self.render_path(path)); - output - } - - fn render_path(&self, path: &[PathComponent]) -> Vec { - let mut output = vec![]; - for component in path { - if component.hide { - continue; - } - - let (tokens, push_a_separator) = component.type_.map_or_else( - || self.render_nameable_item(&component.item), - |ty| self.render_type_and_separator(ty), - ); - - output.extend(tokens); - - if push_a_separator { - output.push(Token::symbol("::")); - } - } - if !path.is_empty() { - output.pop(); // Remove last "::" so "a::b::c::" becomes "a::b::c" - } - output - } - - fn render_nameable_item(&self, item: &NameableItem) -> (Vec, bool) { - let mut push_a_separator = false; - let mut output = vec![]; - let token_fn = if matches!(item.item.inner, ItemEnum::Function(_)) { - Token::function - } else if matches!( - item.item.inner, - ItemEnum::Trait(_) - | ItemEnum::Struct(_) - | ItemEnum::Union(_) - | ItemEnum::Enum(_) - | ItemEnum::TypeAlias(_) - ) { - Token::type_ - } else { - Token::identifier - }; - - if let Some(name) = item.name() { - // If we are not debugging, some items (read: impls) do not have - // a name, so only push a name if it exists - output.push(token_fn(name.to_string())); - push_a_separator = true; - } - (output, push_a_separator) - } - - fn render_sequence( - &self, - start: Vec, - end: Vec, - between: Vec, - sequence: &[T], - render: impl Fn(&T) -> Vec, - ) -> Vec { - self.render_sequence_impl(start, end, between, false, sequence, render) - } - - fn render_sequence_if_not_empty( - &self, - start: Vec, - end: Vec, - between: Vec, - sequence: &[T], - render: impl Fn(&T) -> Vec, - ) -> Vec { - self.render_sequence_impl(start, end, between, true, sequence, render) - } - - fn render_sequence_impl( - &self, - start: Vec, - end: Vec, - between: Vec, - return_nothing_if_empty: bool, - sequence: &[T], - render: impl Fn(&T) -> Vec, - ) -> Vec { - if return_nothing_if_empty && sequence.is_empty() { - return vec![]; - } - let mut output = start; - for (index, seq) in sequence.iter().enumerate() { - output.extend(render(seq)); - if index < sequence.len() - 1 { - output.extend(between.clone()); - } - } - output.extend(end); - output - } - - fn render_type(&self, ty: &Type) -> Vec { - self.render_option_type(&Some(ty)) - } - - fn render_type_and_separator(&self, ty: &Type) -> (Vec, bool) { - (self.render_type(ty), true) - } - - fn render_option_type(&self, ty: &Option<&Type>) -> Vec { - let Some(ty) = ty else { - return vec![Token::symbol("_")]; - }; // The `_` in `EnumWithStrippedTupleVariants::DoubleFirstHidden(_, bool)` - match ty { - Type::ResolvedPath(path) => self.render_resolved_path(path), - Type::DynTrait(dyn_trait) => self.render_dyn_trait(dyn_trait), - Type::Generic(name) => vec![Token::generic(name)], - Type::Primitive(name) => vec![Token::primitive(name)], - Type::FunctionPointer(ptr) => self.render_function_pointer(ptr), - Type::Tuple(types) => self.render_tuple(types), - Type::Slice(ty) => self.render_slice(ty), - Type::Array { type_, len } => self.render_array(type_, len), - Type::ImplTrait(bounds) => self.render_impl_trait(bounds), - Type::Infer => vec![Token::symbol("_")], - Type::RawPointer { mutable, type_ } => self.render_raw_pointer(*mutable, type_), - Type::BorrowedRef { - lifetime, - mutable, - type_, - } => self.render_borrowed_ref(lifetime.as_deref(), *mutable, type_), - Type::QualifiedPath { - name, - args: _, - self_type, - trait_, - } => self.render_qualified_path(self_type, trait_.as_ref(), name), - Type::Pat { .. } => vec![Token::symbol( - "https://github.com/rust-lang/rust/issues/123646 is unstable and not supported", - )], - } - } - - fn render_trait(&self, trait_: &Trait, path: &[PathComponent]) -> Vec { - let mut output = pub_(); - if trait_.is_unsafe { - output.extend(vec![Token::qualifier("unsafe"), ws!()]); - }; - output.extend([Token::kind("trait"), ws!()]); - output.extend(self.render_path(path)); - output.extend(self.render_generics(&trait_.generics)); - output.extend(self.render_generic_bounds_with_colon(&trait_.bounds)); - output - } - - fn render_dyn_trait(&self, dyn_trait: &rustdoc_types::DynTrait) -> Vec { - let mut output = vec![]; - - let more_than_one = dyn_trait.traits.len() > 1 || dyn_trait.lifetime.is_some(); - if more_than_one { - output.push(Token::symbol("(")); - } - - output.extend(self.render_sequence_if_not_empty( - vec![Token::keyword("dyn"), ws!()], - vec![], - plus(), - &dyn_trait.traits, - |p| self.render_poly_trait(p), - )); - - if let Some(lt) = &dyn_trait.lifetime { - output.extend(plus()); - output.extend(vec![Token::lifetime(lt)]); - } - - if more_than_one { - output.push(Token::symbol(")")); - } - - output - } - - fn render_function( - &self, - name: Vec, - decl: &FnDecl, - generics: &Generics, - header: &Header, - ) -> Vec { - let mut output = pub_(); - if header.unsafe_ { - output.extend(vec![Token::qualifier("unsafe"), ws!()]); - }; - if header.const_ { - output.extend(vec![Token::qualifier("const"), ws!()]); - }; - if header.async_ { - output.extend(vec![Token::qualifier("async"), ws!()]); - }; - if header.abi != Abi::Rust { - output.push(match &header.abi { - Abi::C { .. } => Token::qualifier("c"), - Abi::Cdecl { .. } => Token::qualifier("cdecl"), - Abi::Stdcall { .. } => Token::qualifier("stdcall"), - Abi::Fastcall { .. } => Token::qualifier("fastcall"), - Abi::Aapcs { .. } => Token::qualifier("aapcs"), - Abi::Win64 { .. } => Token::qualifier("win64"), - Abi::SysV64 { .. } => Token::qualifier("sysV64"), - Abi::System { .. } => Token::qualifier("system"), - Abi::Other(text) => Token::qualifier(text), - Abi::Rust => unreachable!(), - }); - output.push(ws!()); - } - - output.extend(vec![Token::kind("fn"), ws!()]); - output.extend(name); - - // Generic parameters - output.extend(self.render_generic_param_defs(&generics.params)); - - // Regular parameters and return type - output.extend(self.render_fn_decl(decl)); - - // Where predicates - output.extend(self.render_where_predicates(&generics.where_predicates)); - - output - } - - fn render_fn_decl(&self, decl: &FnDecl) -> Vec { - let mut output = vec![]; - // Main arguments - output.extend(self.render_sequence( - vec![Token::symbol("(")], - vec![Token::symbol(")")], - comma(), - &decl.inputs, - |(name, ty)| { - self.simplified_self(name, ty).unwrap_or_else(|| { - let mut output = vec![]; - if name != "_" { - output.extend(vec![Token::identifier(name), Token::symbol(":"), ws!()]); - } - output.extend(self.render_type(ty)); - output - }) - }, - )); - // Return type - if let Some(ty) = &decl.output { - output.extend(arrow()); - output.extend(self.render_type(ty)); - } - output - } - - fn simplified_self(&self, name: &str, ty: &Type) -> Option> { - if name == "self" { - match ty { - Type::Generic(name) if name == "Self" => Some(vec![Token::self_("self")]), - Type::BorrowedRef { - lifetime, - mutable, - type_, - } => match type_.as_ref() { - Type::Generic(name) if name == "Self" => { - let mut output = vec![Token::symbol("&")]; - if let Some(lt) = lifetime { - output.extend(vec![Token::lifetime(lt), ws!()]); - } - if *mutable { - output.extend(vec![Token::keyword("mut"), ws!()]); - } - output.push(Token::self_("self")); - Some(output) - } - _ => None, - }, - _ => None, - } - } else { - None - } - } - - fn render_resolved_path(&self, path: &Path) -> Vec { - let mut output = vec![]; - if let Some(item) = self.best_item_for_id(&path.id) { - output.extend(self.render_path(item.path())); - } else if let Some(item) = self.crate_.paths.get(&path.id) { - output.extend(self.render_path_components(item.path.iter().map(Deref::deref))); - } else if !path.name.is_empty() { - // If we get here it means there was no item for this Path in the - // rustdoc JSON. Examples of when this happens: - // - // * The resolved path is for a public item inside a private mod - // (and thus effectively the item is not public) - // - // In these cases we simply use the `name` verbatim, which typically - // is equal to how it appears in the source text. It might not be - // ideal and end up identical to the corresponding rustdoc HTML, but - // it is good enough given the edge-case nature of this code path. - output.extend(self.render_path_name(&path.name)); - } - if let Some(args) = &path.args { - output.extend(self.render_generic_args(args)); - } - output - } - - fn render_path_name(&self, name: &str) -> Vec { - self.render_path_components(name.split("::")) - } - - fn render_path_components( - &self, - path_iter: impl Iterator>, - ) -> Vec { - let mut output = vec![]; - let path: Vec<_> = path_iter.collect(); - let len = path.len(); - for (index, part) in path.into_iter().enumerate() { - if index == len - 1 { - output.push(Token::type_(part.as_ref())); - } else { - output.push(Token::identifier(part.as_ref())); - } - output.push(Token::symbol("::")); - } - if len > 0 { - output.pop(); - } - output - } - - fn render_function_pointer(&self, ptr: &FunctionPointer) -> Vec { - let mut output = self.render_higher_rank_trait_bounds(&ptr.generic_params); - output.push(Token::kind("fn")); - output.extend(self.render_fn_decl(&ptr.decl)); - output - } - - fn render_tuple(&self, types: &[Type]) -> Vec { - let option_tuple: Vec> = types.iter().map(Some).collect(); - self.render_option_tuple(&option_tuple, None) - } - - /// `prefix` is to handle the difference between tuple structs and enum variant - /// tuple structs. The former marks public fields as `pub ` whereas all fields - /// of enum tuple structs are always implicitly `pub`. - fn render_option_tuple(&self, types: &[Option<&Type>], prefix: Option<&[Token]>) -> Vec { - self.render_sequence( - vec![Token::symbol("(")], - vec![Token::symbol(")")], - comma(), - types, - |type_| { - let mut output: Vec = vec![]; - if let (Some(prefix), Some(_)) = (prefix, type_) { - output.extend(prefix.to_owned()); - } - output.extend(self.render_option_type(type_)); - output - }, - ) - } - - fn render_slice(&self, ty: &Type) -> Vec { - let mut output = vec![Token::symbol("[")]; - output.extend(self.render_type(ty)); - output.push(Token::symbol("]")); - output - } - - fn render_array(&self, type_: &Type, len: &str) -> Vec { - let mut output = vec![Token::symbol("[")]; - output.extend(self.render_type(type_)); - output.extend(vec![ - Token::symbol(";"), - ws!(), - Token::primitive(len), - Token::symbol("]"), - ]); - output - } - - pub(crate) fn render_impl( - &self, - impl_: &Impl, - _path: &[PathComponent], - disregard_negativity: bool, - ) -> Vec { - let mut output = vec![]; - - if impl_.is_unsafe { - output.extend(vec![Token::keyword("unsafe"), ws!()]); - } - - output.push(Token::keyword("impl")); - - output.extend(self.render_generic_param_defs(&impl_.generics.params)); - - output.push(ws!()); - - if let Some(trait_) = &impl_.trait_ { - if !disregard_negativity && impl_.negative { - output.push(Token::symbol("!")); - } - output.extend(self.render_resolved_path(trait_)); - output.extend(vec![ws!(), Token::keyword("for"), ws!()]); - output.extend(self.render_type(&impl_.for_)); - } else { - output.extend(self.render_type(&impl_.for_)); - } - - output.extend(self.render_where_predicates(&impl_.generics.where_predicates)); - - output - } - - fn render_impl_trait(&self, bounds: &[GenericBound]) -> Vec { - let mut output = vec![Token::keyword("impl")]; - output.push(ws!()); - output.extend(self.render_generic_bounds(bounds)); - output - } - - fn render_raw_pointer(&self, mutable: bool, type_: &Type) -> Vec { - let mut output = vec![Token::symbol("*")]; - output.push(Token::keyword(if mutable { "mut" } else { "const" })); - output.push(ws!()); - output.extend(self.render_type(type_)); - output - } - - fn render_borrowed_ref( - &self, - lifetime: Option<&str>, - mutable: bool, - type_: &Type, - ) -> Vec { - let mut output = vec![Token::symbol("&")]; - if let Some(lt) = lifetime { - output.extend(vec![Token::lifetime(lt), ws!()]); - } - if mutable { - output.extend(vec![Token::keyword("mut"), ws!()]); - } - output.extend(self.render_type(type_)); - output - } - - fn render_qualified_path(&self, type_: &Type, trait_: Option<&Path>, name: &str) -> Vec { - let mut output = vec![]; - match (type_, trait_) { - (Type::Generic(name), Some(trait_)) if name == "Self" && trait_.name.is_empty() => { - output.push(Token::keyword("Self")); - } - (_, trait_) => { - if trait_.is_some() { - output.push(Token::symbol("<")); - } - output.extend(self.render_type(type_)); - if let Some(trait_) = trait_ { - output.extend(vec![ws!(), Token::keyword("as"), ws!()]); - output.extend(self.render_resolved_path(trait_)); - output.push(Token::symbol(">")); - } - } - } - output.push(Token::symbol("::")); - output.push(Token::identifier(name)); - output - } - - fn render_generic_args(&self, args: &GenericArgs) -> Vec { - match args { - GenericArgs::AngleBracketed { args, bindings } => { - self.render_angle_bracketed(args, bindings) - } - GenericArgs::Parenthesized { inputs, output } => { - self.render_parenthesized(inputs, output) - } - } - } - - fn render_parenthesized(&self, inputs: &[Type], return_ty: &Option) -> Vec { - let mut output = self.render_sequence( - vec![Token::symbol("(")], - vec![Token::symbol(")")], - comma(), - inputs, - |type_| self.render_type(type_), - ); - if let Some(return_ty) = return_ty { - output.extend(arrow()); - output.extend(self.render_type(return_ty)); - } - output - } - - fn render_angle_bracketed(&self, args: &[GenericArg], bindings: &[TypeBinding]) -> Vec { - enum Arg<'c> { - GenericArg(&'c GenericArg), - TypeBinding(&'c TypeBinding), - } - self.render_sequence_if_not_empty( - vec![Token::symbol("<")], - vec![Token::symbol(">")], - comma(), - &args - .iter() - .map(Arg::GenericArg) - .chain(bindings.iter().map(Arg::TypeBinding)) - .collect::>(), - |arg| match arg { - Arg::GenericArg(arg) => self.render_generic_arg(arg), - Arg::TypeBinding(binding) => self.render_type_binding(binding), - }, - ) - } - - fn render_term(&self, term: &Term) -> Vec { - match term { - Term::Type(ty) => self.render_type(ty), - Term::Constant(c) => self.render_constant(c, None), - } - } - - fn render_poly_trait(&self, poly_trait: &PolyTrait) -> Vec { - let mut output = self.render_higher_rank_trait_bounds(&poly_trait.generic_params); - output.extend(self.render_resolved_path(&poly_trait.trait_)); - output - } - - fn render_generic_arg(&self, arg: &GenericArg) -> Vec { - match arg { - GenericArg::Lifetime(name) => vec![Token::lifetime(name)], - GenericArg::Type(ty) => self.render_type(ty), - GenericArg::Const(c) => self.render_constant(c, None), - GenericArg::Infer => vec![Token::symbol("_")], - } - } - - fn render_type_binding(&self, binding: &TypeBinding) -> Vec { - let mut output = vec![Token::identifier(&binding.name)]; - output.extend(self.render_generic_args(&binding.args)); - match &binding.binding { - TypeBindingKind::Equality(term) => { - output.extend(equals()); - output.extend(self.render_term(term)); - } - TypeBindingKind::Constraint(bounds) => { - output.extend(self.render_generic_bounds(bounds)); - } - } - output - } - - fn render_constant(&self, constant: &Constant, type_: Option<&Type>) -> Vec { - let mut output = vec![]; - if constant.is_literal { - output.extend(self.render_type(type_.expect("constant literals have a type"))); - if let Some(value) = &constant.value { - output.extend(equals()); - if constant.is_literal { - output.push(Token::primitive(value)); - } else { - output.push(Token::identifier(value)); - } - } - } else { - output.push(Token::identifier(&constant.expr)); - } - output - } - - fn render_generics(&self, generics: &Generics) -> Vec { - let mut output = vec![]; - output.extend(self.render_generic_param_defs(&generics.params)); - output.extend(self.render_where_predicates(&generics.where_predicates)); - output - } - - fn render_generic_param_defs(&self, params: &[GenericParamDef]) -> Vec { - let params_without_synthetics: Vec<_> = params - .iter() - .filter(|p| { - if let GenericParamDefKind::Type { synthetic, .. } = p.kind { - !synthetic - } else { - true - } - }) - .collect(); - - self.render_sequence_if_not_empty( - vec![Token::symbol("<")], - vec![Token::symbol(">")], - comma(), - ¶ms_without_synthetics, - |param| self.render_generic_param_def(param), - ) - } - - fn render_generic_param_def(&self, generic_param_def: &GenericParamDef) -> Vec { - let mut output = vec![]; - match &generic_param_def.kind { - GenericParamDefKind::Lifetime { outlives } => { - output.push(Token::lifetime(&generic_param_def.name)); - if !outlives.is_empty() { - output.extend(colon()); - output.extend(self.render_sequence(vec![], vec![], plus(), outlives, |s| { - vec![Token::lifetime(s)] - })); - } - } - GenericParamDefKind::Type { bounds, .. } => { - output.push(Token::generic(&generic_param_def.name)); - output.extend(self.render_generic_bounds_with_colon(bounds)); - } - GenericParamDefKind::Const { type_, .. } => { - output.push(Token::qualifier("const")); - output.push(ws!()); - output.push(Token::identifier(&generic_param_def.name)); - output.extend(colon()); - output.extend(self.render_type(type_)); - } - } - output - } - - fn render_where_predicates(&self, where_predicates: &[WherePredicate]) -> Vec { - let mut output = vec![]; - if !where_predicates.is_empty() { - output.push(ws!()); - output.push(Token::Keyword("where".to_owned())); - output.push(ws!()); - output.extend( - self.render_sequence(vec![], vec![], comma(), where_predicates, |p| { - self.render_where_predicate(p) - }), - ); - } - output - } - - fn render_where_predicate(&self, where_predicate: &WherePredicate) -> Vec { - let mut output = vec![]; - match where_predicate { - WherePredicate::BoundPredicate { - type_, - bounds, - generic_params, - } => { - output.extend(self.render_higher_rank_trait_bounds(generic_params)); - output.extend(self.render_type(type_)); - output.extend(self.render_generic_bounds_with_colon(bounds)); - } - WherePredicate::RegionPredicate { - lifetime, - bounds: _, - } => output.push(Token::Lifetime(lifetime.clone())), - WherePredicate::EqPredicate { lhs, rhs } => { - output.extend(self.render_type(lhs)); - output.extend(equals()); - output.extend(self.render_term(rhs)); - } - } - output - } - - fn render_generic_bounds_with_colon(&self, bounds: &[GenericBound]) -> Vec { - let mut output = vec![]; - if !bounds.is_empty() { - output.extend(colon()); - output.extend(self.render_generic_bounds(bounds)); - } - output - } - - fn render_generic_bounds(&self, bounds: &[GenericBound]) -> Vec { - self.render_sequence_if_not_empty(vec![], vec![], plus(), bounds, |bound| match bound { - GenericBound::TraitBound { - trait_, - generic_params, - .. - } => { - let mut output = vec![]; - output.extend(self.render_higher_rank_trait_bounds(generic_params)); - output.extend(self.render_resolved_path(trait_)); - output - } - GenericBound::Outlives(id) => vec![Token::lifetime(id)], - }) - } - - fn render_higher_rank_trait_bounds(&self, generic_params: &[GenericParamDef]) -> Vec { - let mut output = vec![]; - if !generic_params.is_empty() { - output.push(Token::keyword("for")); - output.extend(self.render_generic_param_defs(generic_params)); - output.push(ws!()); - } - output - } - - fn best_item_for_id(&self, id: &'c Id) -> Option<&'c IntermediatePublicItem<'c>> { - match self.id_to_items.get(&id) { - None => None, - Some(items) => { - items - .iter() - .max_by(|a, b| { - // If there is any item in the path that has been - // renamed/re-exported, i.e. that is not the original - // path, prefer that less than an item with a path where - // all items are original. - let mut ordering = match ( - a.path_contains_renamed_item(), - b.path_contains_renamed_item(), - ) { - (true, false) => Ordering::Less, - (false, true) => Ordering::Greater, - _ => Ordering::Equal, - }; - - // If we still can't make up our mind, go with the shortest path - if ordering == Ordering::Equal { - ordering = b.path().len().cmp(&a.path().len()); - } - - ordering - }) - .copied() - } - } - } -} - -/// Our list of allowed attributes comes from -/// -fn attr_relevant_for_public_apis>(attr: S) -> bool { - let prefixes = [ - "#[export_name", - "#[link_section", - "#[no_mangle", - "#[non_exhaustive", - "#[repr", - ]; - - for prefix in prefixes { - if attr.as_ref().starts_with(prefix) { - return true; - } - } - - false -} - -fn pub_() -> Vec { - vec![Token::qualifier("pub"), ws!()] -} - -fn plus() -> Vec { - vec![ws!(), Token::symbol("+"), ws!()] -} - -fn colon() -> Vec { - vec![Token::symbol(":"), ws!()] -} - -fn comma() -> Vec { - vec![Token::symbol(","), ws!()] -} - -fn equals() -> Vec { - vec![ws!(), Token::symbol("="), ws!()] -} - -fn arrow() -> Vec { - vec![ws!(), Token::symbol("->"), ws!()] -} - -#[cfg(test)] -mod test { - macro_rules! s { - ($value:literal) => { - $value.to_string() - }; - } - - use crate::codegen::public_api::tokens; - - use super::*; - - #[test] - fn test_type_infer() { - assert_render( - |context| context.render_type(&Type::Infer), - vec![Token::symbol("_")], - "_", - ); - } - - #[test] - fn test_type_generic() { - assert_render( - |context| context.render_type(&Type::Generic(s!("name"))), - vec![Token::generic("name")], - "name", - ); - } - - #[test] - fn test_type_primitive() { - assert_render( - |context| context.render_type(&Type::Primitive(s!("name"))), - vec![Token::primitive("name")], - "name", - ); - } - - #[test] - fn test_type_resolved_simple() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("name"), - args: None, - id: Id(s!("id")), - })) - }, - vec![Token::type_("name")], - "name", - ); - } - - #[test] - fn test_type_resolved_long_name() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("name::with::parts"), - args: None, - id: Id(s!("id")), - })) - }, - vec![ - Token::identifier("name"), - Token::symbol("::"), - Token::identifier("with"), - Token::symbol("::"), - Token::type_("parts"), - ], - "name::with::parts", - ); - } - - #[test] - fn test_type_resolved_crate_name() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("$crate::name"), - args: None, - id: Id(s!("id")), - })) - }, - vec![ - Token::identifier("$crate"), - Token::symbol("::"), - Token::type_("name"), - ], - "$crate::name", - ); - } - - #[test] - fn test_type_resolved_name_crate() { - assert_render( - |context| { - context.render_type(&Type::ResolvedPath(Path { - name: s!("name::$crate"), - args: None, - id: Id(s!("id")), - })) - }, - vec![ - Token::identifier("name"), - Token::symbol("::"), - Token::type_("$crate"), - ], - "name::$crate", - ); - } - - #[test] - fn test_type_tuple_empty() { - assert_render( - |context| context.render_type(&Type::Tuple(vec![])), - vec![Token::symbol("("), Token::symbol(")")], - "()", - ); - } - - #[test] - fn test_type_tuple() { - assert_render( - |context| { - context.render_type(&Type::Tuple(vec![Type::Infer, Type::Generic(s!("gen"))])) - }, - vec![ - Token::symbol("("), - Token::symbol("_"), - Token::symbol(","), - ws!(), - Token::generic("gen"), - Token::symbol(")"), - ], - "(_, gen)", - ); - } - - #[test] - fn test_type_slice() { - assert_render( - |context| context.render_type(&Type::Slice(Box::new(Type::Infer))), - vec![Token::symbol("["), Token::symbol("_"), Token::symbol("]")], - "[_]", - ); - } - - #[test] - fn test_type_array() { - assert_render( - |context| { - context.render_type(&Type::Array { - type_: Box::new(Type::Infer), - len: s!("20"), - }) - }, - vec![ - Token::symbol("["), - Token::symbol("_"), - Token::symbol(";"), - ws!(), - Token::primitive("20"), - Token::symbol("]"), - ], - "[_; 20]", - ); - } - - #[test] - fn test_type_pointer() { - assert_render( - |context| { - context.render_type(&Type::RawPointer { - mutable: false, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("*"), - Token::keyword("const"), - ws!(), - Token::symbol("_"), - ], - "*const _", - ); - } - - #[test] - fn test_type_pointer_mut() { - assert_render( - |context| { - context.render_type(&Type::RawPointer { - mutable: true, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("*"), - Token::keyword("mut"), - ws!(), - Token::symbol("_"), - ], - "*mut _", - ); - } - - #[test] - fn test_type_ref() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: None, - mutable: false, - type_: Box::new(Type::Infer), - }) - }, - vec![Token::symbol("&"), Token::symbol("_")], - "&_", - ); - } - - #[test] - fn test_type_ref_mut() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: None, - mutable: true, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("&"), - Token::keyword("mut"), - ws!(), - Token::symbol("_"), - ], - "&mut _", - ); - } - - #[test] - fn test_type_ref_lt() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: Some(s!("'a")), - mutable: false, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("&"), - Token::lifetime("'a"), - ws!(), - Token::symbol("_"), - ], - "&'a _", - ); - } - - #[test] - fn test_type_ref_lt_mut() { - assert_render( - |context| { - context.render_type(&Type::BorrowedRef { - lifetime: Some(s!("'a")), - mutable: true, - type_: Box::new(Type::Infer), - }) - }, - vec![ - Token::symbol("&"), - Token::lifetime("'a"), - ws!(), - Token::keyword("mut"), - ws!(), - Token::symbol("_"), - ], - "&'a mut _", - ); - } - - #[test] - fn test_type_path() { - assert_render( - |context| { - context.render_type(&Type::QualifiedPath { - name: s!("name"), - args: Box::new(GenericArgs::AngleBracketed { - args: vec![], - bindings: vec![], - }), - self_type: Box::new(Type::Generic(s!("type"))), - trait_: Some(Path { - name: String::from("trait"), - args: None, - id: Id(s!("id")), - }), - }) - }, - vec![ - Token::symbol("<"), - Token::generic("type"), - ws!(), - Token::keyword("as"), - ws!(), - Token::type_("trait"), - Token::symbol(">"), - Token::symbol("::"), - Token::identifier("name"), - ], - "::name", - ); - } - - fn assert_render( - render_fn: impl Fn(RenderingContext) -> Vec, - expected: Vec, - expected_string: &str, - ) { - let crate_ = Crate { - root: Id(String::from("1:2:3")), - crate_version: None, - includes_private: false, - index: HashMap::new(), - paths: HashMap::new(), - external_crates: HashMap::new(), - format_version: 0, - }; - let context = RenderingContext { - crate_: &crate_, - id_to_items: HashMap::new(), - }; - - let actual = render_fn(context); - - assert_eq!(actual, expected); - assert_eq!( - tokens::tokens_to_string(&actual), - expected_string.to_string() - ); - } -} diff --git a/crux_cli/src/codegen/public_api/tokens.rs b/crux_cli/src/codegen/public_api/tokens.rs deleted file mode 100644 index f911bf5fc..000000000 --- a/crux_cli/src/codegen/public_api/tokens.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Contains all token handling logic. -#[cfg(doc)] -use super::public_item::PublicItem; - -/// A token in a rendered [`PublicItem`], used to apply syntax coloring in downstream applications. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Token { - /// A symbol, like `=` or `::<` - Symbol(String), - /// A qualifier, like `pub` or `const` - Qualifier(String), - /// The kind of an item, like `function` or `trait` - Kind(String), - /// Whitespace, a single space - Whitespace, - /// An identifier, like variable names or parts of the path of an item - Identifier(String), - /// An annotation, used e.g. for Rust attributes. - Annotation(String), - /// The identifier self, the text can be `self` or `Self` - Self_(String), - /// The identifier for a function, like `fn_arg` in `comprehensive_api::functions::fn_arg` - Function(String), - /// A lifetime including the apostrophe `'`, like `'a` - Lifetime(String), - /// A keyword, like `impl`, `where`, or `dyn` - Keyword(String), - /// A generic parameter, like `T` - Generic(String), - /// A primitive type, like `usize` - Primitive(String), - /// A non-primitive type, like the name of a struct or a trait - Type(String), -} - -impl Token { - /// A symbol, like `=` or `::<` - pub(crate) fn symbol(text: impl Into) -> Self { - Self::Symbol(text.into()) - } - /// A qualifier, like `pub` or `const` - pub(crate) fn qualifier(text: impl Into) -> Self { - Self::Qualifier(text.into()) - } - /// The kind of an item, like `function` or `trait` - pub(crate) fn kind(text: impl Into) -> Self { - Self::Kind(text.into()) - } - /// An identifier, like variable names or parts of the path of an item - pub(crate) fn identifier(text: impl Into) -> Self { - Self::Identifier(text.into()) - } - /// The identifier self, the text can be `self` or `Self` - pub(crate) fn self_(text: impl Into) -> Self { - Self::Self_(text.into()) - } - /// The identifier for a function, like `fn_arg` in `comprehensive_api::functions::fn_arg` - pub(crate) fn function(text: impl Into) -> Self { - Self::Function(text.into()) - } - /// A lifetime including the apostrophe `'`, like `'a` - pub(crate) fn lifetime(text: impl Into) -> Self { - Self::Lifetime(text.into()) - } - /// A keyword, like `impl` - pub(crate) fn keyword(text: impl Into) -> Self { - Self::Keyword(text.into()) - } - /// A generic, like `T` - pub(crate) fn generic(text: impl Into) -> Self { - Self::Generic(text.into()) - } - /// A primitive type, like `usize` - pub(crate) fn primitive(text: impl Into) -> Self { - Self::Primitive(text.into()) - } - /// A type, like `Iterator` - pub(crate) fn type_(text: impl Into) -> Self { - Self::Type(text.into()) - } - /// Give the length of the inner text of this token - #[allow(clippy::len_without_is_empty)] - #[allow(dead_code)] - #[must_use] - pub fn len(&self) -> usize { - self.text().len() - } - /// Get the inner text of this token - #[must_use] - pub fn text(&self) -> &str { - match self { - Self::Symbol(l) - | Self::Qualifier(l) - | Self::Kind(l) - | Self::Identifier(l) - | Self::Annotation(l) - | Self::Self_(l) - | Self::Function(l) - | Self::Lifetime(l) - | Self::Keyword(l) - | Self::Generic(l) - | Self::Primitive(l) - | Self::Type(l) => l, - Self::Whitespace => " ", - } - } -} - -pub(crate) fn tokens_to_string(tokens: &[Token]) -> String { - tokens.iter().map(Token::text).collect() -} diff --git a/crux_cli/src/command_runner.rs b/crux_cli/src/command_runner.rs deleted file mode 100644 index 761b47b6d..000000000 --- a/crux_cli/src/command_runner.rs +++ /dev/null @@ -1,68 +0,0 @@ -// inspired by @fasterthanlime's brilliant post https://fasterthanli.me/articles/a-terminal-case-of-linux -// and Jakub Kądziołka's great follow up https://compilercrim.es/amos-nerdsniped-me/ - -use anyhow::{bail, Result}; -use std::convert::TryFrom; -use tokio::{io::AsyncReadExt, process::Command}; -use tokio_fd::AsyncFd; - -pub async fn run(cmd: &mut Command) -> Result<()> { - let (primary_fd, secondary_fd) = open_terminal(); - - unsafe { - cmd.pre_exec(move || { - if libc::login_tty(secondary_fd) != 0 { - panic!("couldn't set the controlling terminal or something"); - } - Ok(()) - }) - }; - let mut child = cmd.spawn()?; - - let mut buf = vec![0u8; 1024]; - let mut primary = AsyncFd::try_from(primary_fd)?; - - loop { - tokio::select! { - n = primary.read(&mut buf) => { - let n = n?; - let slice = &buf[..n]; - - let s = std::str::from_utf8(slice)?; - print!("{}", s); - }, - - status = child.wait() => { - match status { - Ok(s) => { - if s.success() { - break; - } - bail!("command failed with {}", s) - } - Err(e) => bail!(e), - } - }, - } - } - - Ok(()) -} - -fn open_terminal() -> (i32, i32) { - let mut primary_fd: i32 = -1; - let mut secondary_fd: i32 = -1; - unsafe { - let ret = libc::openpty( - &mut primary_fd, - &mut secondary_fd, - std::ptr::null_mut(), - std::ptr::null_mut(), - std::ptr::null_mut(), - ); - if ret != 0 { - panic!("Failed to openpty!"); - } - }; - (primary_fd, secondary_fd) -} diff --git a/crux_cli/src/main.rs b/crux_cli/src/main.rs index 2443c6ab0..7e5ec43d4 100644 --- a/crux_cli/src/main.rs +++ b/crux_cli/src/main.rs @@ -6,7 +6,6 @@ use args::Cli; mod args; mod codegen; -mod command_runner; mod config; mod diff; mod doctor; From 71932c60d0ef1c8e0ff08028bac53ae4433beb28 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sun, 27 Oct 2024 14:17:06 +0000 Subject: [PATCH 22/27] ascent wip --- Cargo.lock | 239 ++++++++++++++---- crux_cli/Cargo.toml | 1 + crux_cli/codegen.fish | 5 +- crux_cli/src/codegen/mod.rs | 13 +- crux_cli/src/codegen/parser.rs | 182 ++++++++++--- examples/cat_facts/shared/src/app.rs | 10 +- examples/cat_facts/shared/src/app/platform.rs | 6 +- examples/hello_world/shared/src/app.rs | 2 +- 8 files changed, 360 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 695b8aabf..a35b731ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -56,9 +62,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -71,36 +77,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -115,6 +121,51 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "ascent" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542b90d362e62ae00b8a9f1c8887919fe5cb4a07c64423b9ab72ab78f4b27f41" +dependencies = [ + "ascent_base", + "ascent_macro", + "boxcar", + "cfg-if", + "dashmap", + "hashbrown 0.14.5", + "instant", + "once_cell", + "paste", + "rayon", + "rustc-hash", +] + +[[package]] +name = "ascent_base" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "606412229e80de366935b461eaef5016a9725064151e52ea35f200e7541c2912" +dependencies = [ + "paste", +] + +[[package]] +name = "ascent_macro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67685ce7f6568cfd9671322ed3d7c7322ff8ec6a81e07d5323f997e118885972" +dependencies = [ + "ascent_base", + "derive-syn-parse", + "duplicate", + "itertools 0.12.1", + "lazy_static", + "petgraph", + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "assert_fs" version = "1.1.2" @@ -167,7 +218,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -218,6 +269,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "boxcar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c99613cb3cd7429889a08dfcf651721ca971c86afa30798461f8eee994de47" + [[package]] name = "bstr" version = "1.10.0" @@ -360,7 +417,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -371,9 +428,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "concurrent-queue" @@ -442,6 +499,7 @@ name = "crux_cli" version = "0.1.0" dependencies = [ "anyhow", + "ascent", "clap", "console", "guppy", @@ -534,7 +592,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -577,7 +635,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -588,7 +646,21 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.82", + "syn 2.0.85", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "rayon", ] [[package]] @@ -597,6 +669,17 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" +[[package]] +name = "derive-syn-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -615,7 +698,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -625,7 +708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -648,6 +731,12 @@ dependencies = [ "serde", ] +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" + [[package]] name = "either" version = "1.13.0" @@ -662,9 +751,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -829,7 +918,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -950,7 +1039,7 @@ dependencies = [ "fixedbitset", "guppy-workspace-hack", "indexmap", - "itertools", + "itertools 0.13.0", "nested", "once_cell", "pathdiff", @@ -969,6 +1058,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "rayon", +] + [[package]] name = "hashbrown" version = "0.15.0" @@ -1102,7 +1202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -1138,6 +1238,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1223,7 +1332,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -1321,6 +1430,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.2" @@ -1392,29 +1507,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1460,12 +1575,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -1558,7 +1673,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -1632,6 +1747,26 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -1664,6 +1799,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustdoc-json" version = "0.9.2" @@ -1689,9 +1830,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags", "errno", @@ -1781,7 +1922,7 @@ checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -1911,9 +2052,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -1984,7 +2125,7 @@ checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -2038,7 +2179,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -2095,7 +2236,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] [[package]] @@ -2236,7 +2377,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", "wasm-bindgen-shared", ] @@ -2258,7 +2399,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2406,5 +2547,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.85", ] diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index c7f289ae0..07e8a7272 100644 --- a/crux_cli/Cargo.toml +++ b/crux_cli/Cargo.toml @@ -15,6 +15,7 @@ path = "src/main.rs" [dependencies] anyhow.workspace = true +ascent = "0.7.0" clap = { version = "4.4.18", features = ["derive"] } console = "0.15.8" guppy = "0.17.4" diff --git a/crux_cli/codegen.fish b/crux_cli/codegen.fish index 4064d165f..b472f8fe4 100755 --- a/crux_cli/codegen.fish +++ b/crux_cli/codegen.fish @@ -2,10 +2,7 @@ cargo build -for d in ../examples/hello_world - echo "" - echo "---------------" - echo "Public API for $d" +for d in ../examples/simple_counter pushd $d ../../target/debug/crux codegen --lib shared popd diff --git a/crux_cli/src/codegen/mod.rs b/crux_cli/src/codegen/mod.rs index da7bd9c09..65072d5ad 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -21,6 +21,15 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { .toolchain("nightly") .manifest_path(lib.manifest_path()) .build()?; + // let json_path = lib + // .manifest_path() + // .parent() + // .unwrap() + // .parent() + // .unwrap() + // .join("target") + // .join("doc") + // .join("shared.json"); let crate_: Crate = spawn_blocking(move || -> Result { let file = File::open(json_path)?; @@ -29,9 +38,7 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { }) .await??; - println!("Parsing rustdoc JSON, version {}", crate_.format_version); - let out = parser::parse(&crate_)?; - println!("{}", out); + parser::parse(&crate_)?; Ok(()) } diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index aac68ecea..faf832e6b 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,27 +1,72 @@ use std::collections::HashMap; use anyhow::{anyhow, Result}; -use petgraph::{dot::Dot, graph::NodeIndex, Graph}; -use rustdoc_types::{Crate, Enum, Id, Impl, Item, ItemEnum, Type, VariantKind}; +use ascent::ascent; +use rustdoc_types::{ + Crate, Enum, Id, Impl, Item, ItemEnum, ItemSummary, Path, StructKind, Type, VariantKind, +}; +use serde::Serialize; + +ascent! { + relation edge(Node, Node, Edge); + + relation implements(Node, Node); + relation struct_fields(Node, Node); + relation enum_variants(Node, Node); + relation associated_type(Node, Node); + + implements(type_, trait_) <-- + edge(impl_, type_, Edge::ForType), + edge(impl_, trait_, Edge::Trait); + + associated_type(impl_, type_) <-- + edge(impl_, trait_, Edge::Trait), + edge(impl_, item, Edge::AssociatedItem), + edge(item, type_, Edge::AssociatedType); + + struct_fields(struct_, field) <-- + associated_type(impl_, struct_), + (edge(struct_, field, Edge::HasField) || edge(struct_, field, Edge::Unit)); + + enum_variants(enum_, variant) <-- + associated_type(impl_, enum_), + edge(enum_, variant, Edge::HasVariant); + + enum_variants(variant, field) <-- + enum_variants(enum_, variant), + edge(variant, field, Edge::HasField); +} pub fn parse(crate_: &Crate) -> Result { - let mut graph = Graph::new(); - let mut nodes = HashMap::new(); + let mut prog = AscentProgram::default(); + let mut nodes_by_id = HashMap::new(); - // nodes - for (id, item) in crate_.index.clone() { - nodes.insert(id, graph.add_node(Node { id, item })); + // items + for (id, item) in &crate_.index { + nodes_by_id + .entry(*id) + .or_insert_with(|| Node::new(*id)) + .item = Some(item.clone()); + } + + // paths + for (id, path) in &crate_.paths { + nodes_by_id + .entry(*id) + .or_insert_with(|| Node::new(*id)) + .path = Some(path.clone()); } - let node = |id| -> Result<&NodeIndex> { - nodes + let node_by_id = |id: &Id| -> Result<&Node> { + nodes_by_id .get(id) .ok_or_else(|| anyhow!("Could not find node with id {:?}", id)) }; // edges for (id, item) in &crate_.index { - let source = node(id)?; + let source = node_by_id(id)?.clone(); + match &item.inner { ItemEnum::Module(_module) => (), ItemEnum::ExternCrate { name: _, rename: _ } => (), @@ -29,43 +74,65 @@ pub fn parse(crate_: &Crate) -> Result { ItemEnum::Union(_union) => (), ItemEnum::Struct(s) => { match &s.kind { - rustdoc_types::StructKind::Unit => (), - rustdoc_types::StructKind::Tuple(fields) => { + StructKind::Unit => { + prog.edge.push((source.clone(), source, Edge::Unit)); + } + StructKind::Tuple(fields) => { for field in fields { if let Some(id) = field { - graph.add_edge(*source, *node(&id)?, Edge::HasField); + prog.edge.push(( + source.clone(), + node_by_id(&id)?.clone(), + Edge::HasField, + )); } } } - rustdoc_types::StructKind::Plain { + StructKind::Plain { fields, has_stripped_fields: _, } => { for id in fields { - graph.add_edge(*source, *node(&id)?, Edge::HasField); + prog.edge.push(( + source.clone(), + node_by_id(&id)?.clone(), + Edge::HasField, + )); } } }; - for id in &s.impls { - graph.add_edge(*source, *node(&id)?, Edge::Implements); - } } ItemEnum::StructField(_) => (), ItemEnum::Enum(Enum { variants, .. }) => { for id in variants { - graph.add_edge(*source, *node(&id)?, Edge::HasVariant); + prog.edge + .push((source.clone(), node_by_id(&id)?.clone(), Edge::HasVariant)); } } ItemEnum::Variant(v) => { match &v.kind { VariantKind::Plain => (), - VariantKind::Tuple(_vec) => (), + VariantKind::Tuple(fields) => { + for id in fields { + if let Some(id) = id { + prog.edge.push(( + source.clone(), + node_by_id(id)?.clone(), + Edge::HasField, + )); + } + } + } VariantKind::Struct { fields, has_stripped_fields: _, } => { for field in fields { - graph.add_edge(*source, *node(&field)?, Edge::HasField); + prog.edge.push(( + source.clone(), + node_by_id(field)?.clone(), + Edge::HasField, + )); } } }; @@ -74,13 +141,32 @@ pub fn parse(crate_: &Crate) -> Result { ItemEnum::Trait(_) => (), ItemEnum::TraitAlias(_trait_alias) => (), ItemEnum::Impl(Impl { + trait_: + Some(Path { + id: trait_id, + name, + args: _, + }), for_: Type::ResolvedPath(target), items, .. }) => { - graph.add_edge(*source, *node(&target.id)?, Edge::ImplFor); + if !["App", "Effect"].contains(&name.as_str()) { + continue; + } + prog.edge.push(( + source.clone(), + node_by_id(&target.id)?.clone(), + Edge::ForType, + )); + prog.edge + .push((source.clone(), node_by_id(trait_id)?.clone(), Edge::Trait)); for id in items { - graph.add_edge(*source, *node(&id)?, Edge::AssociatedItem); + prog.edge.push(( + source.clone(), + node_by_id(&id)?.clone(), + Edge::AssociatedItem, + )); } } ItemEnum::Impl(_) => (), @@ -100,29 +186,59 @@ pub fn parse(crate_: &Crate) -> Result { bounds: _, type_: Some(Type::ResolvedPath(target)), } => { - if let Ok(dest) = node(&target.id) { - graph.add_edge(*source, *dest, Edge::AssociatedType); + if let Ok(dest) = node_by_id(&target.id) { + prog.edge + .push((source.clone(), dest.clone(), Edge::AssociatedType)); } } ItemEnum::AssocType { .. } => (), } } - let out = Dot::new(&graph); - Ok(format!("{:?}", out)) + + prog.run(); + + std::fs::write( + "/tmp/struct_fields.json", + serde_json::to_string(&prog.struct_fields).unwrap(), + )?; + std::fs::write( + "/tmp/enum_variants.json", + serde_json::to_string(&prog.enum_variants).unwrap(), + )?; + + Ok(format!("")) } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] struct Node { id: Id, - item: Item, + item: Option, + path: Option, } -#[derive(Debug)] +impl Node { + fn new(id: Id) -> Self { + Self { + id, + item: None, + path: None, + } + } +} + +impl std::hash::Hash for Node { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] enum Edge { - ImplFor, - HasField, - Implements, AssociatedItem, AssociatedType, + ForType, + HasField, HasVariant, + Trait, + Unit, } diff --git a/examples/cat_facts/shared/src/app.rs b/examples/cat_facts/shared/src/app.rs index a13854278..3dff67511 100644 --- a/examples/cat_facts/shared/src/app.rs +++ b/examples/cat_facts/shared/src/app.rs @@ -88,11 +88,11 @@ pub struct CatFacts { #[cfg_attr(feature = "typegen", derive(crux_core::macros::Export))] #[derive(crux_core::macros::Effect)] pub struct CatFactCapabilities { - pub http: Http, - pub key_value: KeyValue, - pub platform: Platform, - pub render: Render, - pub time: Time, + http: Http, + key_value: KeyValue, + platform: Platform, + render: Render, + time: Time, } // Allow easily using Platform as a submodule diff --git a/examples/cat_facts/shared/src/app/platform.rs b/examples/cat_facts/shared/src/app/platform.rs index a83af205b..8ca81e753 100644 --- a/examples/cat_facts/shared/src/app/platform.rs +++ b/examples/cat_facts/shared/src/app/platform.rs @@ -7,7 +7,7 @@ pub struct App {} #[derive(Default, Serialize, Deserialize)] pub struct Model { - pub platform: String, + pub(crate) platform: String, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -18,8 +18,8 @@ pub enum Event { #[derive(crux_core::macros::Effect)] pub struct Capabilities { - pub platform: Platform, - pub render: Render, + pub(crate) platform: Platform, + pub(crate) render: Render, } impl crux_core::App for App { diff --git a/examples/hello_world/shared/src/app.rs b/examples/hello_world/shared/src/app.rs index 220e3cb7a..69b9d7cbb 100644 --- a/examples/hello_world/shared/src/app.rs +++ b/examples/hello_world/shared/src/app.rs @@ -13,7 +13,7 @@ pub struct Model; #[derive(Serialize, Deserialize)] pub struct ViewModel { - data: String, + pub data: String, } #[derive(crux_core::macros::Effect)] From 3b7ef60b48bbab87a1815426c76346a9c9573b01 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sun, 27 Oct 2024 19:44:58 +0000 Subject: [PATCH 23/27] struct recursion wip --- crux_cli/codegen.fish | 2 +- crux_cli/src/codegen/parser.rs | 42 +++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/crux_cli/codegen.fish b/crux_cli/codegen.fish index b472f8fe4..06d09793d 100755 --- a/crux_cli/codegen.fish +++ b/crux_cli/codegen.fish @@ -2,7 +2,7 @@ cargo build -for d in ../examples/simple_counter +for d in ../examples/cat_facts pushd $d ../../target/debug/crux codegen --lib shared popd diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index faf832e6b..977b453b6 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -3,22 +3,18 @@ use std::collections::HashMap; use anyhow::{anyhow, Result}; use ascent::ascent; use rustdoc_types::{ - Crate, Enum, Id, Impl, Item, ItemEnum, ItemSummary, Path, StructKind, Type, VariantKind, + Crate, Enum, GenericArg, GenericArgs, Id, Impl, Item, ItemEnum, ItemSummary, Path, StructKind, + Type, VariantKind, }; use serde::Serialize; ascent! { relation edge(Node, Node, Edge); - relation implements(Node, Node); relation struct_fields(Node, Node); relation enum_variants(Node, Node); relation associated_type(Node, Node); - implements(type_, trait_) <-- - edge(impl_, type_, Edge::ForType), - edge(impl_, trait_, Edge::Trait); - associated_type(impl_, type_) <-- edge(impl_, trait_, Edge::Trait), edge(impl_, item, Edge::AssociatedItem), @@ -27,11 +23,14 @@ ascent! { struct_fields(struct_, field) <-- associated_type(impl_, struct_), (edge(struct_, field, Edge::HasField) || edge(struct_, field, Edge::Unit)); + struct_fields(struct2, field2) <-- + struct_fields(struct1, field1), + edge(field1, struct2, Edge::ForType), + edge(struct2, field2, Edge::HasField); enum_variants(enum_, variant) <-- associated_type(impl_, enum_), edge(enum_, variant, Edge::HasVariant); - enum_variants(variant, field) <-- enum_variants(enum_, variant), edge(variant, field, Edge::HasField); @@ -67,6 +66,12 @@ pub fn parse(crate_: &Crate) -> Result { for (id, item) in &crate_.index { let source = node_by_id(id)?.clone(); + if let Some(x) = &source.item { + if x.attrs.contains(&"#[serde(skip)]".to_string()) { + continue; + } + } + match &item.inner { ItemEnum::Module(_module) => (), ItemEnum::ExternCrate { name: _, rename: _ } => (), @@ -102,7 +107,27 @@ pub fn parse(crate_: &Crate) -> Result { } }; } - ItemEnum::StructField(_) => (), + ItemEnum::StructField(type_) => match type_ { + // TODO: make recursive + Type::ResolvedPath(path) => { + if let Some(args) = &path.args { + if let GenericArgs::AngleBracketed { args, .. } = args.as_ref() { + for arg in args { + if let GenericArg::Type(t) = arg { + if let Type::ResolvedPath(path) = t { + prog.edge.push(( + source.clone(), + node_by_id(&path.id)?.clone(), + Edge::ForType, + )); + }; + } + } + } + } + } + _ => (), + }, ItemEnum::Enum(Enum { variants, .. }) => { for id in variants { prog.edge @@ -197,6 +222,7 @@ pub fn parse(crate_: &Crate) -> Result { prog.run(); + std::fs::write("/tmp/edge.json", serde_json::to_string(&prog.edge).unwrap())?; std::fs::write( "/tmp/struct_fields.json", serde_json::to_string(&prog.struct_fields).unwrap(), From 428fbb8d1c15d92aa8188ea4ffb6a825d0ac6d85 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Sun, 27 Oct 2024 20:02:19 +0000 Subject: [PATCH 24/27] serde skip --- crux_cli/src/codegen/parser.rs | 121 +++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 977b453b6..c861d2339 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use anyhow::{anyhow, Result}; +use anyhow::Result; use ascent::ascent; use rustdoc_types::{ Crate, Enum, GenericArg, GenericArgs, Id, Impl, Item, ItemEnum, ItemSummary, Path, StructKind, @@ -56,21 +56,22 @@ pub fn parse(crate_: &Crate) -> Result { .path = Some(path.clone()); } - let node_by_id = |id: &Id| -> Result<&Node> { - nodes_by_id + let node_by_id = |id: &Id| -> Option<&Node> { + let node = nodes_by_id .get(id) - .ok_or_else(|| anyhow!("Could not find node with id {:?}", id)) + .expect("node should exist for all items and paths"); + let skip = match &node.item { + Some(x) => x.attrs.contains(&"#[serde(skip)]".to_string()), + _ => false, + }; + (!skip).then_some(node) }; // edges for (id, item) in &crate_.index { - let source = node_by_id(id)?.clone(); - - if let Some(x) = &source.item { - if x.attrs.contains(&"#[serde(skip)]".to_string()) { - continue; - } - } + let Some(source) = node_by_id(id) else { + continue; + }; match &item.inner { ItemEnum::Module(_module) => (), @@ -80,16 +81,16 @@ pub fn parse(crate_: &Crate) -> Result { ItemEnum::Struct(s) => { match &s.kind { StructKind::Unit => { - prog.edge.push((source.clone(), source, Edge::Unit)); + prog.edge.push((source.clone(), source.clone(), Edge::Unit)); } StructKind::Tuple(fields) => { for field in fields { if let Some(id) = field { - prog.edge.push(( - source.clone(), - node_by_id(&id)?.clone(), - Edge::HasField, - )); + let Some(dest) = node_by_id(id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::HasField)); } } } @@ -98,11 +99,11 @@ pub fn parse(crate_: &Crate) -> Result { has_stripped_fields: _, } => { for id in fields { - prog.edge.push(( - source.clone(), - node_by_id(&id)?.clone(), - Edge::HasField, - )); + let Some(dest) = node_by_id(id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::HasField)); } } }; @@ -115,9 +116,12 @@ pub fn parse(crate_: &Crate) -> Result { for arg in args { if let GenericArg::Type(t) = arg { if let Type::ResolvedPath(path) = t { + let Some(dest) = node_by_id(&path.id) else { + continue; + }; prog.edge.push(( source.clone(), - node_by_id(&path.id)?.clone(), + dest.clone(), Edge::ForType, )); }; @@ -130,8 +134,11 @@ pub fn parse(crate_: &Crate) -> Result { }, ItemEnum::Enum(Enum { variants, .. }) => { for id in variants { + let Some(dest) = node_by_id(id) else { + continue; + }; prog.edge - .push((source.clone(), node_by_id(&id)?.clone(), Edge::HasVariant)); + .push((source.clone(), dest.clone(), Edge::HasVariant)); } } ItemEnum::Variant(v) => { @@ -140,11 +147,11 @@ pub fn parse(crate_: &Crate) -> Result { VariantKind::Tuple(fields) => { for id in fields { if let Some(id) = id { - prog.edge.push(( - source.clone(), - node_by_id(id)?.clone(), - Edge::HasField, - )); + let Some(dest) = node_by_id(id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::HasField)); } } } @@ -152,12 +159,12 @@ pub fn parse(crate_: &Crate) -> Result { fields, has_stripped_fields: _, } => { - for field in fields { - prog.edge.push(( - source.clone(), - node_by_id(field)?.clone(), - Edge::HasField, - )); + for id in fields { + let Some(dest) = node_by_id(id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::HasField)); } } }; @@ -179,19 +186,32 @@ pub fn parse(crate_: &Crate) -> Result { if !["App", "Effect"].contains(&name.as_str()) { continue; } - prog.edge.push(( - source.clone(), - node_by_id(&target.id)?.clone(), - Edge::ForType, - )); + + let Some(dest) = node_by_id(&target.id) else { + continue; + }; prog.edge - .push((source.clone(), node_by_id(trait_id)?.clone(), Edge::Trait)); + .push((source.clone(), dest.clone(), Edge::ForType)); + + let Some(dest) = node_by_id(trait_id) else { + continue; + }; + prog.edge.push((source.clone(), dest.clone(), Edge::Trait)); for id in items { - prog.edge.push(( - source.clone(), - node_by_id(&id)?.clone(), - Edge::AssociatedItem, - )); + let Some(dest) = node_by_id(id) else { + continue; + }; + if let Some(Item { + name: Some(name), .. + }) = &dest.item + { + if !["Event", "ViewModel"].contains(&name.as_str()) { + continue; + } + } + + prog.edge + .push((source.clone(), dest.clone(), Edge::AssociatedItem)); } } ItemEnum::Impl(_) => (), @@ -211,10 +231,11 @@ pub fn parse(crate_: &Crate) -> Result { bounds: _, type_: Some(Type::ResolvedPath(target)), } => { - if let Ok(dest) = node_by_id(&target.id) { - prog.edge - .push((source.clone(), dest.clone(), Edge::AssociatedType)); - } + let Some(dest) = node_by_id(&target.id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::AssociatedType)); } ItemEnum::AssocType { .. } => (), } From b99d67add0909ce030351f9cc83cb3b0aec0c465 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Mon, 28 Oct 2024 11:02:52 +0000 Subject: [PATCH 25/27] effect variants --- crux_cli/src/codegen/parser.rs | 49 ++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index c861d2339..90cbccf23 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -11,28 +11,35 @@ use serde::Serialize; ascent! { relation edge(Node, Node, Edge); - relation struct_fields(Node, Node); - relation enum_variants(Node, Node); - relation associated_type(Node, Node); + relation field(Node, Node); + relation variant(Node, Node); + relation root(Node, Node); - associated_type(impl_, type_) <-- + // root for Event and ViewModel + root(impl_, type_) <-- edge(impl_, trait_, Edge::Trait), edge(impl_, item, Edge::AssociatedItem), edge(item, type_, Edge::AssociatedType); + // root for Effect + root(impl_, type_) <-- + edge(impl_, type_, Edge::AssociatedType), + if let Some(i) = impl_.item.as_ref(), + if let Some(n) = i.name.as_ref(), + if n == &"Ffi".to_string(); - struct_fields(struct_, field) <-- - associated_type(impl_, struct_), - (edge(struct_, field, Edge::HasField) || edge(struct_, field, Edge::Unit)); - struct_fields(struct2, field2) <-- - struct_fields(struct1, field1), + field(struct_, field) <-- + root(impl_, struct_), + edge(struct_, field, ?Edge::HasField|Edge::Unit); + field(struct2, field2) <-- + field(struct1, field1), edge(field1, struct2, Edge::ForType), edge(struct2, field2, Edge::HasField); - enum_variants(enum_, variant) <-- - associated_type(impl_, enum_), + variant(enum_, variant) <-- + root(impl_, enum_), edge(enum_, variant, Edge::HasVariant); - enum_variants(variant, field) <-- - enum_variants(enum_, variant), + variant(variant, field) <-- + variant(enum_, variant), edge(variant, field, Edge::HasField); } @@ -231,6 +238,14 @@ pub fn parse(crate_: &Crate) -> Result { bounds: _, type_: Some(Type::ResolvedPath(target)), } => { + if let Item { + name: Some(name), .. + } = &item + { + if !["Event", "ViewModel", "Ffi"].contains(&name.as_str()) { + continue; + } + } let Some(dest) = node_by_id(&target.id) else { continue; }; @@ -245,12 +260,12 @@ pub fn parse(crate_: &Crate) -> Result { std::fs::write("/tmp/edge.json", serde_json::to_string(&prog.edge).unwrap())?; std::fs::write( - "/tmp/struct_fields.json", - serde_json::to_string(&prog.struct_fields).unwrap(), + "/tmp/field.json", + serde_json::to_string(&prog.field).unwrap(), )?; std::fs::write( - "/tmp/enum_variants.json", - serde_json::to_string(&prog.enum_variants).unwrap(), + "/tmp/variant.json", + serde_json::to_string(&prog.variant).unwrap(), )?; Ok(format!("")) From b4aff89ba16ced29d4f670a3c03e52bbe60f09bf Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Mon, 28 Oct 2024 11:43:41 +0000 Subject: [PATCH 26/27] nested structs --- crux_cli/codegen.fish | 2 +- crux_cli/src/codegen/parser.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crux_cli/codegen.fish b/crux_cli/codegen.fish index 06d09793d..5ef3f181f 100755 --- a/crux_cli/codegen.fish +++ b/crux_cli/codegen.fish @@ -2,7 +2,7 @@ cargo build -for d in ../examples/cat_facts +for d in ../examples/hello_world pushd $d ../../target/debug/crux codegen --lib shared popd diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 90cbccf23..0a950a956 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -33,7 +33,7 @@ ascent! { field(struct2, field2) <-- field(struct1, field1), edge(field1, struct2, Edge::ForType), - edge(struct2, field2, Edge::HasField); + edge(struct2, field2, ?Edge::HasField|Edge::Unit); variant(enum_, variant) <-- root(impl_, enum_), @@ -105,6 +105,11 @@ pub fn parse(crate_: &Crate) -> Result { fields, has_stripped_fields: _, } => { + // unit struct + if fields.is_empty() { + prog.edge.push((source.clone(), source.clone(), Edge::Unit)); + } + for id in fields { let Some(dest) = node_by_id(id) else { continue; @@ -116,8 +121,14 @@ pub fn parse(crate_: &Crate) -> Result { }; } ItemEnum::StructField(type_) => match type_ { - // TODO: make recursive Type::ResolvedPath(path) => { + let Some(dest) = node_by_id(&path.id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::ForType)); + + // TODO: make recursive if let Some(args) = &path.args { if let GenericArgs::AngleBracketed { args, .. } = args.as_ref() { for arg in args { From bbfb4549a022d9cffaf2ebcac136cfe9acbfb620 Mon Sep 17 00:00:00 2001 From: Stuart Harris Date: Mon, 28 Oct 2024 14:29:58 +0000 Subject: [PATCH 27/27] recursively process generic args --- crux_cli/src/codegen/parser.rs | 72 +++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/crux_cli/src/codegen/parser.rs b/crux_cli/src/codegen/parser.rs index 0a950a956..93c2f4cc1 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -105,11 +105,6 @@ pub fn parse(crate_: &Crate) -> Result { fields, has_stripped_fields: _, } => { - // unit struct - if fields.is_empty() { - prog.edge.push((source.clone(), source.clone(), Edge::Unit)); - } - for id in fields { let Some(dest) = node_by_id(id) else { continue; @@ -128,24 +123,8 @@ pub fn parse(crate_: &Crate) -> Result { prog.edge .push((source.clone(), dest.clone(), Edge::ForType)); - // TODO: make recursive if let Some(args) = &path.args { - if let GenericArgs::AngleBracketed { args, .. } = args.as_ref() { - for arg in args { - if let GenericArg::Type(t) = arg { - if let Type::ResolvedPath(path) = t { - let Some(dest) = node_by_id(&path.id) else { - continue; - }; - prog.edge.push(( - source.clone(), - dest.clone(), - Edge::ForType, - )); - }; - } - } - } + process_args(source, args.as_ref(), &node_by_id, &mut prog); } } _ => (), @@ -191,34 +170,43 @@ pub fn parse(crate_: &Crate) -> Result { ItemEnum::Trait(_) => (), ItemEnum::TraitAlias(_trait_alias) => (), ItemEnum::Impl(Impl { + for_: + Type::ResolvedPath(Path { + id: for_type_id, .. + }), trait_: Some(Path { id: trait_id, - name, + name: trait_name, args: _, }), - for_: Type::ResolvedPath(target), items, .. }) => { - if !["App", "Effect"].contains(&name.as_str()) { + if !["App", "Effect"].contains(&trait_name.as_str()) { continue; } - let Some(dest) = node_by_id(&target.id) else { + // record an edge for the type the impl is for + let Some(dest) = node_by_id(&for_type_id) else { continue; }; prog.edge .push((source.clone(), dest.clone(), Edge::ForType)); + // record an edge for the trait the impl is of let Some(dest) = node_by_id(trait_id) else { continue; }; prog.edge.push((source.clone(), dest.clone(), Edge::Trait)); + + // record edges for the associated items in the impl for id in items { let Some(dest) = node_by_id(id) else { continue; }; + + // skip everything except the Event and ViewModel associated types if let Some(Item { name: Some(name), .. }) = &dest.item @@ -245,10 +233,10 @@ pub fn parse(crate_: &Crate) -> Result { ItemEnum::Primitive(_primitive) => (), ItemEnum::AssocConst { type_: _, value: _ } => (), ItemEnum::AssocType { - generics: _, - bounds: _, type_: Some(Type::ResolvedPath(target)), + .. } => { + // skip everything except the Event, ViewModel and Ffi associated types if let Item { name: Some(name), .. } = &item @@ -257,6 +245,8 @@ pub fn parse(crate_: &Crate) -> Result { continue; } } + + // record an edge for the associated type let Some(dest) = node_by_id(&target.id) else { continue; }; @@ -282,6 +272,32 @@ pub fn parse(crate_: &Crate) -> Result { Ok(format!("")) } +fn process_args<'a>( + source: &Node, + args: &GenericArgs, + node_by_id: &impl Fn(&Id) -> Option<&'a Node>, + prog: &mut AscentProgram, +) { + if let GenericArgs::AngleBracketed { args, .. } = args { + for arg in args { + if let GenericArg::Type(t) = arg { + if let Type::ResolvedPath(path) = t { + let Some(dest) = node_by_id(&path.id) else { + continue; + }; + prog.edge + .push((source.clone(), dest.clone(), Edge::ForType)); + + if let Some(args) = &path.args { + let generic_args = args.as_ref(); + process_args(source, generic_args, node_by_id, prog); + } + }; + } + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize)] struct Node { id: Id,