diff --git a/Cargo.lock b/Cargo.lock index 695b8aab..73b3bbcc 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" @@ -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.82", +] + [[package]] name = "assert_fs" version = "1.1.2" @@ -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" @@ -442,6 +499,7 @@ name = "crux_cli" version = "0.1.0" dependencies = [ "anyhow", + "ascent", "clap", "console", "guppy", @@ -591,12 +649,37 @@ dependencies = [ "syn 2.0.82", ] +[[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]] name = "debug-ignore" 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.82", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -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" @@ -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" @@ -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" @@ -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" diff --git a/crux_cli/Cargo.toml b/crux_cli/Cargo.toml index c7f289ae..07e8a727 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 4064d165..5ef3f181 100755 --- a/crux_cli/codegen.fish +++ b/crux_cli/codegen.fish @@ -3,9 +3,6 @@ cargo build for d in ../examples/hello_world - echo "" - echo "---------------" - echo "Public API for $d" 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 da7bd9c0..51a51679 100644 --- a/crux_cli/src/codegen/mod.rs +++ b/crux_cli/src/codegen/mod.rs @@ -17,10 +17,19 @@ pub async fn codegen(args: &CodegenArgs) -> Result<()> { bail!("Could not find workspace package with path {}", args.lib) }; - let json_path = rustdoc_json::Builder::default() - .toolchain("nightly") - .manifest_path(lib.manifest_path()) - .build()?; + // let json_path = rustdoc_json::Builder::default() + // .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 aac68ece..817f6455 100644 --- a/crux_cli/src/codegen/parser.rs +++ b/crux_cli/src/codegen/parser.rs @@ -1,27 +1,59 @@ 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, Type, VariantKind}; +use serde::Serialize; + +ascent! { + relation edge(Node, Node, Edge); + + relation for_type(Node, Node); + for_type(impl_, type_) <-- edge(impl_, type_, Edge::ForType); + + relation of_trait(Node, Node); + of_trait(impl_, trait_) <-- edge(impl_, trait_, Edge::Trait); + + relation implements(Node, Node); + implements(type_, trait_) <-- for_type(impl_, type_), of_trait(impl_, trait_); + + relation struct_fields(Node, Node); + struct_fields(struct_, field) <-- edge(struct_, field, Edge::HasField); + + relation enum_variants(Node, Node); + enum_variants(enum_, variant) <-- edge(enum_, variant, Edge::HasVariant); +} 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: _ } => (), @@ -33,7 +65,11 @@ pub fn parse(crate_: &Crate) -> Result { rustdoc_types::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, + )); } } } @@ -42,18 +78,20 @@ pub fn parse(crate_: &Crate) -> Result { 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) => { @@ -65,7 +103,11 @@ pub fn parse(crate_: &Crate) -> Result { 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 +116,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 +161,62 @@ 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/implements.json", + serde_json::to_string(&prog.implements).unwrap(), + )?; + 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, +} + +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)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] enum Edge { - ImplFor, - HasField, - Implements, AssociatedItem, AssociatedType, + ForType, + HasField, HasVariant, + Trait, }