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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use super::*;
pub trait Table {
/// Whether a field should be displayed as a table or a simple attribute.
///
/// For example, in the code below, the `name` field is displayed as a simple attribute,
/// while the `address` field is displayed as a table.
///
/// ```
/// use doku::Document;
///
/// #[derive(Document)]
/// struct Person {
/// name: String,
/// address: Address,
/// }
///
/// #[derive(Document)]
/// struct Address {
/// street: String,
/// number: u32
/// }
///
///
/// let doc = doku::to_toml::<Person>();
///
/// doku::assert_doc!(r#"
/// name = "string"
///
/// [address]
/// street = "string"
/// number = 123
/// "#, doc);
/// ```
fn is_table(&self) -> bool;
/// Whether we should write the name / title of the current table field.
///
/// There are currently 3 cases where the field is a table but we don't want to write its title:
///
/// 1. For optionals, since the table title will be written by the child type.
/// Writing the table title here would result in having the same title displayed twice.
///
/// 2. For arrays, similarly as above.
///
/// 3. For structs, when they have no simple fields, so every child field is a table.
/// In this case writing the table title is not wrong, but it's unneccessary since every child
/// field will write their own table.
fn should_write_table_name(&self) -> bool;
}
impl<'ty> Table for Ctxt<'_, 'ty, '_> {
fn is_table(&self) -> bool {
self.ty.is_table()
}
fn should_write_table_name(&self) -> bool {
self.has_array_parent() || self.ty.should_write_table_name()
}
}
impl Table for Fields {
fn is_table(&self) -> bool {
match self {
Fields::Named { .. } => true,
Fields::Unnamed { fields } => fields.iter().any(|f| f.is_table()),
Fields::Unit => false,
}
}
fn should_write_table_name(&self) -> bool {
match self {
Fields::Named { fields } => {
!fields.iter().all(|(_, f)| f.is_table())
}
_ => true,
}
}
}
impl Table for Field {
fn is_table(&self) -> bool {
!self.flattened && self.ty.is_table()
}
fn should_write_table_name(&self) -> bool {
self.ty.should_write_table_name()
}
}
impl Table for Type {
fn is_table(&self) -> bool {
match &self.kind {
TypeKind::Array { ty, .. } => ty.is_table(),
TypeKind::Enum {
tag: Tag::External,
variants,
..
} => variants.iter().any(|v| !matches!(v.fields, Fields::Unit)),
TypeKind::Enum {
tag: Tag::None,
variants,
..
} => variants.iter().any(|v| v.fields.is_table()),
TypeKind::Enum { .. } => true,
TypeKind::Struct {
fields,
transparent: false,
} => fields.is_table(),
TypeKind::Struct {
fields: Fields::Named { fields },
transparent: true,
} => fields.first().map(|(_, f)| f.is_table()).unwrap_or(false),
TypeKind::Struct {
fields: Fields::Unnamed { fields },
transparent: true,
} => fields.first().map(|f| f.is_table()).unwrap_or(false),
TypeKind::Tuple { fields } => fields.iter().any(|t| t.is_table()),
TypeKind::Optional { ty } => ty.is_table(),
TypeKind::Map { .. } => true,
_ => false,
}
}
fn should_write_table_name(&self) -> bool {
match &self.kind {
TypeKind::Struct {
fields,
transparent: false,
} => fields.should_write_table_name(),
TypeKind::Optional { .. } | TypeKind::Array { .. } => false,
_ => true,
}
}
}