Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
StuartHarris committed Dec 6, 2024
1 parent 64cb0d2 commit b9caf63
Show file tree
Hide file tree
Showing 11 changed files with 1,085 additions and 1,027 deletions.
106 changes: 106 additions & 0 deletions crux_cli/src/codegen/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use ascent::ascent;

use super::node::Node;

ascent! {
pub struct Filter;

// ------- facts ------------------
relation node(Node);

// ------- rules ------------------

relation is_struct(Node);
is_struct(s) <-- node(s), if s.is_struct();

relation is_enum(Node);
is_enum(e) <-- node(e), if e.is_enum();

relation variant(Node, Node);
variant(e, v) <-- is_enum(e), node(v), if e.has_variant(v);

relation field(Node, Node);
field(s, f) <-- is_struct(s), node(f), if s.has_field(f);
field(v, f) <-- variant(e, v), node(f), if v.has_field(f);

relation type_of(Node, Node);
type_of(f, t) <-- node(f), node(t), if f.is_of_type(t);

// app structs have an implementation of the App trait
relation app(Node, Node);
app(imp, app) <--
is_struct(app),
node(imp),
if imp.is_impl_for(app, "App");

// app hierarchy
relation parent(Node, Node);
parent(parent, child) <--
app(_, parent),
app(_, child),
field(parent, field),
type_of(field, child);

relation root_app(Node, Node);
root_app(impl_, app) <--
app(impl_, app),
!parent(_, app);

// a view model is an associated type of an app
relation view_model(Node, Node);
view_model(app, view_model) <--
root_app(impl_, app),
type_of(item, view_model),
if impl_.has_associated_item(item, "ViewModel");

// an event is an associated type of an app
relation event(Node, Node);
event(app, event) <--
root_app(impl_, app),
type_of(item, event),
if impl_.has_associated_item(item, "Event");

// effect enums have an implementation of the Effect trait
// and an associated Ffi type, which is the FFI representation of the effect
relation effect(Node, Node);
effect(app, effect_ffi) <--
root_app(app_impl, app),
is_enum(effect),
node(effect_impl),
if effect_impl.is_impl_for(effect, "Effect"),
if app.is_in_same_module_as(effect),
type_of(effect_ffi_item, effect_ffi),
if effect_impl.has_associated_item(effect_ffi_item, "Ffi");

relation root(Node);
root(x) <-- view_model(app, x);
root(x) <-- event(app, x);
root(x) <-- effect(app, x);

// set of all the edges we are interested in
relation edge(Node, Node);

// root fields
edge(root, field) <--
root(root),
field(root, field);
// root variants
edge(root, variant) <--
root(root),
variant(root, variant);

edge(type_, field) <--
edge(_, type_),
field(type_, field);
edge(type_, variant) <--
edge(_, type_),
variant(type_, variant);

edge(field, type_) <--
edge(_, field),
type_of(field, type_);
}

#[cfg(test)]
#[path = "filter_tests.rs"]
mod filter_tests;
Original file line number Diff line number Diff line change
@@ -1,71 +1,6 @@
use std::fs::File;
use rustdoc_types::Crate;

use pretty_assertions::assert_eq;
use rustdoc_types::{Crate, Generics, Id, Item, ItemEnum, Struct, StructKind, Visibility};

use super::*;
use crate::codegen::{parse, Registry};

fn test_node(name: Option<String>, attrs: Vec<String>) -> Node {
Node {
item: Some(Item {
name,
attrs,
inner: ItemEnum::Struct(Struct {
kind: StructKind::Plain {
fields: vec![],
has_stripped_fields: false,
},
generics: Generics {
params: vec![],
where_predicates: vec![],
},
impls: vec![],
}),
id: Id(0),
crate_id: 0,
span: None,
visibility: Visibility::Public,
docs: None,
links: Default::default(),
deprecation: None,
}),
id: Id(0),
summary: None,
}
}

#[test]
fn test_get_name() {
let name = Some("Foo".to_string());
let attrs = vec![];
let node = test_node(name, attrs);
assert_eq!(name_of(&node), Some("Foo"));
}

#[test]
fn test_get_name_with_rename() {
let name = Some("Foo".to_string());
let attrs = vec![r#"#[serde(rename = "Bar")]"#.to_string()];
let node = test_node(name, attrs);
assert_eq!(name_of(&node), Some("Bar"));
}

#[test]
fn test_get_name_with_rename_no_whitespace() {
let name = Some("Foo".to_string());
let attrs = vec![r#"#[serde(rename="Bar")]"#.to_string()];
let node = test_node(name, attrs);
assert_eq!(name_of(&node), Some("Bar"));
}

#[test]
fn test_get_name_with_no_name() {
let name = None;
let attrs = vec![];
let node = test_node(name, attrs);
assert_eq!(name_of(&node), None);
}
use crate::codegen::parse;

#[test]
fn field_is_option_of_t() {
Expand Down Expand Up @@ -419,195 +354,3 @@ fn field_is_option_of_t() {
]
"#);
}

#[test]
#[ignore = "not yet fully implemented"]
fn cat_facts_json() {
static RUSTDOC: &'static [u8] = include_bytes!("fixtures/cat_facts/rustdoc.json");
let crate_: Crate = serde_json::from_slice(RUSTDOC).unwrap();
let nodes = parse(crate_);

let actual = super::run(nodes);

let writer = File::create("fixtures-cat_facts-actual.json").unwrap();
serde_json::to_writer_pretty(writer, &actual).unwrap();
let expected: Registry = serde_json::from_str(include_str!("fixtures/cat_facts/expected.json"))
.expect("should deserialize");
assert_eq!(actual, expected);
}

#[test]
#[ignore = "not yet fully implemented"]
fn notes_json() {
static RUSTDOC: &'static [u8] = include_bytes!("fixtures/notes/rustdoc.json");
let crate_: Crate = serde_json::from_slice(RUSTDOC).unwrap();
let nodes = parse(crate_);

let actual = super::run(nodes);

let writer = File::create("fixtures-notes-actual.json").unwrap();
serde_json::to_writer_pretty(writer, &actual).unwrap();
let expected: Registry = serde_json::from_str(include_str!("fixtures/notes/expected.json"))
.expect("should deserialize");
assert_eq!(actual, expected);
}

#[test]
fn cat_facts() {
static RUSTDOC: &'static [u8] = include_bytes!("fixtures/cat_facts/rustdoc.json");
let crate_: Crate = serde_json::from_slice(RUSTDOC).unwrap();
let nodes = parse(crate_);

let registry = super::run(nodes);

insta::assert_debug_snapshot!(&registry, @r#"
{
"CatImage": Struct(
[
Named {
name: "href",
value: Str,
},
],
),
"Effect": Enum(
{
0: Named {
name: "Http",
value: NewType(
TypeName(
"Operation",
),
),
},
1: Named {
name: "KeyValue",
value: NewType(
TypeName(
"Operation",
),
),
},
2: Named {
name: "Platform",
value: NewType(
TypeName(
"Operation",
),
),
},
3: Named {
name: "Render",
value: NewType(
TypeName(
"Operation",
),
),
},
4: Named {
name: "Time",
value: NewType(
TypeName(
"Operation",
),
),
},
},
),
"Event": Enum(
{
0: Named {
name: "None",
value: Unit,
},
1: Named {
name: "Clear",
value: Unit,
},
2: Named {
name: "Get",
value: Unit,
},
3: Named {
name: "Fetch",
value: Unit,
},
4: Named {
name: "GetPlatform",
value: Unit,
},
5: Named {
name: "Restore",
value: Unit,
},
},
),
"ViewModel": Struct(
[
Named {
name: "fact",
value: Str,
},
Named {
name: "image",
value: Option(
TypeName(
"CatImage",
),
),
},
Named {
name: "platform",
value: Str,
},
],
),
}
"#);
}

#[test]
fn bridge_echo() {
static RUSTDOC: &'static [u8] = include_bytes!("fixtures/bridge_echo_rustdoc.json");
let crate_: Crate = serde_json::from_slice(RUSTDOC).unwrap();
let nodes = parse(crate_);

let containers = super::run(nodes);

insta::assert_debug_snapshot!(&containers, @r#"
{
"Effect": Enum(
{
0: Named {
name: "Render",
value: NewType(
TypeName(
"Operation",
),
),
},
},
),
"Event": Enum(
{
0: Named {
name: "Tick",
value: Unit,
},
1: Named {
name: "NewPeriod",
value: Unit,
},
},
),
"ViewModel": Struct(
[
Named {
name: "count",
value: U64,
},
],
),
}
"#);
}
Loading

0 comments on commit b9caf63

Please sign in to comment.