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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! Mapstic allows [explicit Elasticsearch mappings][explicit] to be derived from Rust type
//! definitions.
//!
//! <div class="warning">
//! Note that it is the responsibility of the user to ensure that documents generated by the Rust
//! type and indexed by Elasticsearch have the same data types and shape as the mapping generated
//! by deriving <code>ToMapping</code>. In most normal cases where the types derive
//! <code>Serialize</code> or implement <code>Serialize</code> in an idiomatic way, the mapping and
//! data should match, but special care should be taken when using explicit mapping types with the
//! <code>#[mapstic(mapping_type = ...)]</code> attribute.
//! </div>
//!
//! ## Deriving mappings
//!
//! Mapstic can derive many common Rust types automatically:
//!
//! ```
//! use mapstic::ToMapping;
//!
//! #[derive(ToMapping)]
//! struct MyIndex {
//! id: u64,
//! score: f64,
//! time: std::time::SystemTime,
//! tags: Vec<Tag>,
//! }
//!
//! #[derive(ToMapping)]
//! struct Tag(String);
//!
//! # // We can't use insta here because it always prepends a header that we don't want below.
//! # let expected: serde_json::Value = serde_json::from_str(include_str!("lib.expected.json")).unwrap();
//! # let have = serde_json::to_value(&MyIndex::to_mapping()).unwrap();
//! # assert_eq!(expected, have);
//! ```
//!
//! This will generate a mapping that looks like this when serialised to JSON:
//!
//! ```json
//! ```
//!
//! It is also possible to control the type and parameters derived for each type and/or field: see
//! the documentation for [the `ToMapping` derive macro][derive@ToMapping] for more detail.
//!
//! ## Implicitly derived types
//!
//! The following [Elasticsearch types][types] are derived by default:
//!
//! | Elasticsearch type | Rust types | Notes |
//! |---|---|---|
//! | `binary` | [`[u8]`][u8], [`&CStr`][std::ffi::CStr], [`CString`][std::ffi::CString] |
//! | `boolean` | [`bool`] |
//! | `long` | [`i64`], [`NonZeroI64`][std::num::NonZeroI64], [`AtomicI64`][std::sync::atomic::AtomicI64] |
//! | `integer` | [`i32`], [`NonZeroI32`][std::num::NonZeroI32], [`AtomicI32`][std::sync::atomic::AtomicI32] |
//! | `short` | [`i16`], [`NonZeroI16`][std::num::NonZeroI16], [`AtomicI16`][std::sync::atomic::AtomicI16] |
//! | `byte` | [`i8`], [`NonZeroI8`][std::num::NonZeroI8], [`AtomicI8`][std::sync::atomic::AtomicI8] |
//! | `unsigned_long` | [`u64`], [`u32`], [`u16`], [`u8`] | (and their atomic and non-zero variants) |
//! | `double` | [`f64`] |
//! | `float` | [`f32`] |
//! | `half_float` | [`f16`] | (only on nightly, and with the `nightly` feature enabled) |
//! | `date` | [`SystemTime`][std::time::SystemTime] |
//! | `date` | [`chrono::DateTime`][chrono-datetime], [`chrono::Date`][chrono-date], [`chrono::NaiveDateTime`][chrono-naivedatetime], [`chrono::NaiveDate`][chrono-naivedate] | (with the `chrono` feature enabled) |
//! | `ip` | [`IpAddr`][std::net::IpAddr], [`Ipv4Addr`][std::net::Ipv4Addr], [`Ipv6Addr`][std::net::Ipv6Addr] |
//! | `text` | [`&str`][`str`], [`String`], [`Cow<str>`][std::borrow::Cow] | (the derived mapping will match the default behaviour when Elasticsearch uses [dynamic field mapping][dynamic]: specifically, by also specifying a `.keyword` sub-field with type `keyword`) |
//!
//! ### Options and collections and containers, oh my
//!
//! A number of container types will be automatically derived if their inner types (indicated as
//! `T` below) implement [`ToMapping`]:
//!
//! * [`Option<T>`]
//! * [`BTreeSet<T>`][std::collections::BTreeSet]
//! * [`Box<T>`]
//! * [`Cell<T>`][std::cell::Cell]
//! * [`Cow<T>`][std::borrow::Cow] (where `T` also implements [`Clone`])
//! * [`HashSet<T>`][std::collections::HashSet]
//! * [`LinkedList<T>`][std::collections::LinkedList]
//! * [`Mutex<T>`][std::sync::Mutex]
//! * [`RefCell<T>`][std::cell::RefCell]
//! * [`Reverse<T>`][std::cmp::Reverse]
//! * [`RwLock<T>`][std::sync::RwLock]
//! * [`Vec<T>`][Vec]
//! * [`VecDeque<T>`][std::collections::VecDeque]
//!
//! If the `rc` feature is enabled, the following types will also be automatically derived:
//!
//! * [`Arc<T>`][std::sync::Arc]
//! * [`Rc<T>`][std::rc::Rc]
//! * [`std::rc::Weak<T>`]
//! * [`std::sync::Weak<T>`]
//!
//! ## Usage with `elasticsearch`
//!
//! The [`Mapping`] type can be used directly as a body with the [`elasticsearch`
//! crate](https://crates.io/crates/elasticsearch). See [the `Mapping` documentation][Mapping] for
//! an example.
//!
//! ## Optional features
//!
//! * `chrono`: enables support for [the `chrono` crate](https://crates.io/crates/chrono)
//! * `nightly`: enables support for additional `std` types in nightly Rust
//! * `rc`: enables support for a handful of ref-counted types, much like Serde's `rc` feature
//!
//! [chrono-datetime]: https://docs.rs/chrono/latest/chrono/struct.DateTime.html
//! [chrono-date]: https://docs.rs/chrono/latest/chrono/struct.Date.html
//! [chrono-naivedatetime]: https://docs.rs/chrono/latest/chrono/struct.NaiveDateTime.html
//! [chrono-naivedate]: https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDate.html
//! [dynamic]: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html
//! [explicit]: https://www.elastic.co/guide/en/elasticsearch/reference/current/explicit-mapping.html
//! [types]: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
pub use ;
/// Derives [`ToMapping`] for the given type.
///
/// Mappings can be derived automatically for `struct`s with named fields, and tuple `struct`s with
/// one field. Other `struct` and `enum` types can only be derived if they specify the
/// `#[mapstic(mapping_type)]` container attribute.
///
/// ## Container attributes
///
/// The `#[mapstic(...)]` attribute can be optionally specified to control the mapping that is derived.
/// The following attributes are supported within this attribute:
///
/// ### `mapping_type`
///
/// Specifying `mapping_type` disables all type inference for the container, and will result in the
/// container being treated as a single [Elasticsearch mapping type][types], regardless of the
/// fields it contains.
///
/// For example, a `struct` that wraps a [`HashMap`][std::collections::HashMap] may want to
/// explicitly set its Elasticsearch mapping to [`flattened`][flattened] to prevent a [mappings
/// explosion][boom]:
///
/// ```
/// # use mapstic::ToMapping;
/// # use std::collections::HashMap;
/// # struct Value;
/// #[derive(ToMapping)]
/// #[mapstic(mapping_type = "flattened")]
/// struct KeyValueStore(HashMap<String, Value>);
///
/// # insta::assert_yaml_snapshot!(
/// # "derive-container-mapping-flattened",
/// # KeyValueStore::to_mapping(),
/// # );
/// ```
///
/// Or a string that is known to be a [`keyword`][keyword]:
///
/// ```
/// # use mapstic::ToMapping;
/// #[derive(ToMapping)]
/// #[mapstic(mapping_type = "keyword")]
/// struct Keyword(String);
///
/// # insta::assert_yaml_snapshot!("derive-container-mapping-keyword", Keyword::to_mapping());
/// ```
///
/// ### `params`
///
/// [Mapping parameters][params] can be specified using the `params` attribute.
///
/// For example, to change the analyzer used for a [text][text] field:
///
/// ```
/// # use mapstic::ToMapping;
/// #[derive(ToMapping)]
/// #[mapstic(params(analyzer = "whitespace"))]
/// struct Keyword(String);
///
/// # insta::assert_yaml_snapshot!("derive-container-param-analyzer", Keyword::to_mapping());
/// ```
///
/// Parameters can also be nested to allow for the specification of more complicated parameters,
/// such as [`multi-fields`][fields]:
///
/// ```
/// # use mapstic::ToMapping;
/// #[derive(ToMapping)]
/// // Note the use of r#type here: since type is a Rust keyword, we have to use the raw
/// // identifier.
/// #[mapstic(params(fields(completion(r#type="completion"), keyword(r#type = "keyword"))))]
/// struct Keyword(String);
///
/// # insta::assert_yaml_snapshot!("derive-container-param-keyword", Keyword::to_mapping());
/// ```
///
/// ## Field attributes
///
/// Each field within a `struct` also supports the aforementioned container attributes.
///
/// ### `mapping_type`
///
/// As above, any use of `mapping_type` disables further type inference for the field. It does not
/// matter if the field type implements [`ToMapping`].
///
/// ### `params`
///
/// The semantics of `params` are identical to parameters specified on the container.
///
/// In the event that both a field attribute and a container attribute specify `params`, they will
/// be merged together, with the lowest level parameter value winning.
///
/// ### `skip`
///
/// To ignore a field completely, you can apply the skip attribute. This is most useful when a
/// type contains a field that cannot be mapped, but also isn't indexed in Elasticsearch:
///
/// ```
/// # use mapstic::ToMapping;
/// #[derive(ToMapping)]
/// struct MyIndex {
/// id: i32,
/// author: String,
///
/// #[mapstic(skip)]
/// runtime_cache: Cache,
/// }
///
/// struct Cache {
/// // various fields that cannot derive a mapping, eg:
/// key: u128,
/// }
///
/// # insta::assert_yaml_snapshot!("derive-field-skip", MyIndex::to_mapping());
/// ```
///
/// ## Generic types
///
/// Unlike Serde, Mapstic does not attempt to automatically add bounds to generic types. In
/// practice, this means that any generic type will need to ensure that [`trait@ToMapping`] is
/// derived for all fields used in the mapping.
///
/// For example, a basic wrapper object around a generic type might look like this:
///
/// ```
/// # use mapstic::ToMapping;
/// # use std::fmt::Display;
/// #[derive(ToMapping)]
/// struct Wrapper<T>
/// where
/// T: ToMapping
/// {
/// id: i32,
/// inner: T,
/// }
///
/// #[derive(ToMapping)]
/// struct InnerVariant(String);
///
/// # insta::assert_yaml_snapshot!("derive-generic", Wrapper::<InnerVariant>::to_mapping());
/// ```
///
/// [boom]: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html#mapping-limit-settings
/// [fields]: https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-fields.html
/// [flattened]: https://www.elastic.co/guide/en/elasticsearch/reference/current/flattened.html
/// [keyword]: https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html
/// [params]: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html
/// [text]: https://www.elastic.co/guide/en/elasticsearch/reference/current/text.html
/// [types]: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
pub use ToMapping;
pub use ;