1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use ploidy_core::ir::{
ContainerView, EdgeKind, EnumVariant, EnumView, InlineTypeView, ParameterView, PrimitiveType,
QueryParameter, Reach, SchemaTypeView, Traversal, TypeView, View,
};
/// Rust-specific extensions to [`View`].
pub(crate) trait ViewExt {
/// Returns `true` if this type implements `Eq` and `Hash`.
fn hashable(&self) -> bool;
/// Returns `true` if this type implements `Default`.
fn defaultable(&self) -> bool;
}
impl<'a, T: View<'a>> ViewExt for T {
fn hashable(&self) -> bool {
self.dependencies().all(|view| match view {
TypeView::Inline(InlineTypeView::Primitive(_, view))
| TypeView::Schema(SchemaTypeView::Primitive(_, view)) => {
!matches!(view.ty(), PrimitiveType::F32 | PrimitiveType::F64)
}
_ => true,
})
}
fn defaultable(&self) -> bool {
self.traverse(Reach::Dependencies, |kind, view| {
match (kind, view) {
(
EdgeKind::Reference,
TypeView::Schema(SchemaTypeView::Container(_, _))
| TypeView::Inline(InlineTypeView::Container(_, _)),
) => {
// All container types implement `Default`;
// no need to explore them.
Traversal::Ignore
}
(
EdgeKind::Reference,
TypeView::Schema(SchemaTypeView::Struct(_, _))
| TypeView::Inline(InlineTypeView::Struct(_, _)),
) => {
// Structs may or may not implement `Default`, depending on
// their fields. Skip the struct itself, but explore all
// its fields to determine which ones are defaultable.
Traversal::Skip
}
(EdgeKind::Inherits, _) => {
// Explore parents to check their defaultability.
Traversal::Skip
}
(EdgeKind::Reference, _) => {
// Any other type that this struct references must be defaultable
// for this struct to derive `Default`.
Traversal::Visit
}
}
})
.all(|view| {
match view {
// `serde_json::Value` implements `Default`.
TypeView::Inline(InlineTypeView::Any(..))
| TypeView::Schema(SchemaTypeView::Any(..)) => true,
// `Url` doesn't implement `Default`, but other primitives do.
TypeView::Inline(InlineTypeView::Primitive(_, prim))
| TypeView::Schema(SchemaTypeView::Primitive(_, prim))
if matches!(prim.ty(), PrimitiveType::Url) =>
{
false
}
TypeView::Inline(InlineTypeView::Primitive(..))
| TypeView::Schema(SchemaTypeView::Primitive(..)) => true,
// Representable enums derive `Default` via their
// `Other` variants; unrepresentable enums become
// `String` type aliases, which also implement `Default`.
TypeView::Inline(InlineTypeView::Enum(..))
| TypeView::Schema(SchemaTypeView::Enum(..)) => true,
// Other types aren't defaultable.
_ => false,
}
})
}
}
/// Rust-specific extensions to [`EnumView`].
pub(crate) trait EnumViewExt {
/// Returns `true` if all variants of this enum can be represented as
/// unit variants in Rust. Enums with unrepresentable variants become
/// Rust strings instead.
fn representable(&self) -> bool;
}
impl EnumViewExt for EnumView<'_> {
fn representable(&self) -> bool {
self.variants().iter().all(|variant| match variant {
// Only non-empty string variants with at least one identifier
// character are representable as Rust enum variants.
EnumVariant::String(s) => s.chars().any(unicode_ident::is_xid_continue),
_ => false,
})
}
}
/// Rust-specific extensions to [`ParameterView`].
pub(crate) trait ParameterViewExt {
/// Returns `true` if the struct field for this parameter
/// should be wrapped in an [`Option`]. This is the case when
/// the parameter isn't required and its schema type isn't
/// already [`Optional`][ContainerView::Optional].
fn optional(&self) -> bool;
}
impl<'view, 'a> ParameterViewExt for ParameterView<'view, 'a, QueryParameter> {
fn optional(&self) -> bool {
!self.required() && !matches!(self.ty().as_container(), Some(ContainerView::Optional(_)))
}
}