1
//! Deriving `NetdocEncodable`
2

            
3
use super::*;
4

            
5
use derive_deftly::{define_derive_deftly, define_derive_deftly_module};
6

            
7
/// Not `Copy`, used to detect when other arguments come after a `rest` field
8
///
9
/// Implementation detail of the encoding derives.
10
#[doc(hidden)]
11
#[allow(clippy::exhaustive_structs)]
12
pub struct RestMustComeLastMarker;
13

            
14
/// Displays `T` using the `fmt` function `F`
15
///
16
/// Implementation detail of the encoding derives.
17
#[doc(hidden)]
18
#[allow(clippy::exhaustive_structs)]
19
pub struct DisplayHelper<'t, T, F>(pub &'t T, pub F)
20
where
21
    F: Fn(&T, &mut fmt::Formatter) -> fmt::Result;
22

            
23
impl<'t, T, F> Display for DisplayHelper<'t, T, F>
24
where
25
    F: Fn(&T, &mut fmt::Formatter) -> fmt::Result,
26
{
27
6
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28
6
        self.1(self.0, f)
29
6
    }
30
}
31

            
32
define_derive_deftly_module! {
33
    /// Common definitions for `NetdocEncodable` and `NetdocEncodableFields`
34
    ///
35
    /// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
36
    NetdocSomeItemsEncodableCommon beta_deftly:
37

            
38
    ${define P { $crate::encode }}
39

            
40
    // Suffix for error handling - specifically to add field informaton.
41
    //
42
    // Usage:
43
    //    some_function().$BUG_CONTEXT?;
44
    ${define BUG_CONTEXT {
45
        // We use .map_err() rather than .bug_context() so that we nail down the error type
46
        map_err(|bug: Bug| bug.bug_context(
47
            ${concat "in netdoc " $ttype ", in field " $F_KEYWORD_REPORT}
48
        ))
49
    }}
50

            
51
    // Body of an encoding function.
52
    //
53
    //    | <- macro conditions and loops are aligned starting here
54
    //    |         | <- we line up the normal Rust statements starting here
55
    ${define ENCODE_ITEMS_BODY {
56
                    $EMIT_DEBUG_PLACEHOLDER
57

            
58
          // Add an item with keyword $F_KEYWORD_STR and value `item`
59
          ${define ENCODE_ITEM_VALUE {
60
                    #[allow(unused_mut)]
61
                    let mut item_out = out.item($F_KEYWORD_STR);
62
            ${if fmeta(netdoc(with)) {
63
                    ${fmeta(netdoc(with)) as path}
64
                        ::${paste_spanned $fname write_item_value_onto}
65
                        (item, item_out)
66
                        .$BUG_CONTEXT?;
67
            } else if fmeta(netdoc(single_arg)) {
68
                    selector.${paste_spanned $fname check_item_argument_encodable}();
69
                    ItemArgument::${paste_spanned $fname write_arg_onto}
70
                        (item, &mut item_out)
71
                        .$BUG_CONTEXT?;
72
                    item_out.finish();
73
            } else {
74
                    selector.${paste_spanned $fname check_item_value_encodable}();
75
                    ItemValueEncodable
76
                        ::${paste_spanned $fname write_item_value_onto}
77
                        (item, item_out)
78
                        .$BUG_CONTEXT?;
79
            }}
80
          }}
81

            
82
          // Bind `selector` to an appropriate selector ZST.
83
          ${define LET_SELECTOR {
84
                         let selector = MultiplicitySelector::<$ftype>::default();
85
                         let selector = selector.selector();
86
          }}
87

            
88
          ${for fields {
89
                    { // Rust block for bindings for this field (notably `selector`, `item`
90

            
91
            // ignore #[deftly(netdoc(default))] precisely like NetdocSomeItemsParseableCommon
92
            ${if not(F_INTRO) {
93
                ${if fmeta(netdoc(default)) {}}
94
            }}
95

            
96
            ${select1
97
              F_INTRO {
98
                        #[allow(unused)] // `with` can make this unused
99
                        let selector = SingletonMultiplicitySelector::<$ftype>::default();
100
                        let item = &self.$fname;
101
                        $ENCODE_ITEM_VALUE
102
              }
103
              F_NORMAL {
104
                        $LET_SELECTOR
105
                        for item in selector.iter_ordered(&self.$fname) {
106
                            $ENCODE_ITEM_VALUE
107
                        }
108
              }
109
              F_FLATTEN {
110
                        <$ftype as NetdocEncodableFields>::encode_fields
111
                            (&self.$fname, out)
112
                            .$BUG_CONTEXT?;
113
              }
114
              F_SUBDOC {
115
                        $LET_SELECTOR
116
                        selector.${paste_spanned $fname check_netdoc_encodable}();
117
                        for subdoc in selector.iter_ordered(&self.$fname) {
118
                            NetdocEncodable::encode_unsigned(subdoc, out)
119
                                .$BUG_CONTEXT?;
120
                        }
121
              }
122
              F_SKIP {
123
              }
124
            }
125
                    } // field block.
126
          }} // ${for fields ..}
127

            
128
                    Ok(())
129
    }}
130
}
131

            
132
define_derive_deftly! {
133
    use NetdocDeriveAnyCommon;
134
    use NetdocEntireDeriveCommon;
135
    use NetdocSomeItemsDeriveCommon;
136
    use NetdocSomeItemsEncodableCommon;
137

            
138
    /// Derive [`NetdocEncodable`] for a document (or sub-document)
139
    ///
140
    // NB there is very similar wording in the NetdocParseable derive docs.
141
    // If editing any of this derive's documentation, considering editing that too.
142
    //
143
    // We could conceivably template this, but without a `$///` string templater in derive-deftly
144
    // that would be very tiresome, and it might be a bad idea anyway.
145
    //
146
    /// ### Expected input structure
147
    ///
148
    /// Should be applied named-field struct, where each field is
149
    /// an Item which may appear in the document,
150
    /// or a sub-document.
151
    ///
152
    /// The first field will be the document's intro Item.
153
    /// The output Keyword for each Item will be kebab-case of the field name.
154
    ///
155
    /// ### Field type
156
    ///
157
    /// Each field must be
158
    ///  * `impl `[`ItemValueEncodable`] for an "exactly once" field,
159
    ///  * `Vec<T: ItemValueEncodable>` for "zero or more", or
160
    ///  * `BTreeSet<T: ItemValueEncodable + Ord>`, or
161
    ///  * `Option<T: ItemValueEncodable>` for "zero or one".
162
    ///
163
    /// We don't directly support "at least once"; if the value is empty,
164
    /// the encoder will produce a structurally correct but semantically invalid document.
165
    ///
166
    /// (Multiplicity is implemented via types in the [`multiplicity`] module,
167
    /// specifically [`MultiplicitySelector`] and [`MultiplicityMethods`].)
168
    ///
169
    /// ### Signed documents
170
    ///
171
    /// TODO NETDOC ENCODE this is not yet supported.
172
    ///
173
    /// ### Top-level attributes:
174
    ///
175
    /// * **`#[deftly(netdoc(signatures))]`**:
176
    ///
177
    ///   This type is the signatures section of another document.
178
    ///   TODO NETDOC ENCODE This is not yet supported, and will fail to compile.
179
    ///
180
    $DOC_DEBUG_PLACEHOLDER
181
    ///
182
    /// # **`#[deftly(netdoc(doctype_for_error = "EXPRESSION"))]`**:
183
    ///
184
    ///   Ignored.  (The encoder does not report errors this way.)
185
    ///
186
    ///   Accepted for alignment with `NetdocParseable`,
187
    ///   so that a struct which only conditionally derives `NetdocParseable`
188
    ///   does not need to conditionally mark this attribute.
189
    ///
190
    /// ### Field-level attributes:
191
    ///
192
    /// * **`#[deftly(netdoc(keyword = STR))]`**:
193
    ///
194
    ///   Use `STR` as the Keyword for this Item.
195
    ///
196
    /// * **`#[deftly(netdoc(single_arg))]`**:
197
    ///
198
    ///   The field type implements `ItemArgument`,
199
    ///   instead of `ItemValueEncodable`,
200
    ///   and is encoded as if `(FIELD_TYPE,)` had been written.
201
    ///
202
    /// * **`#[deftly(netdoc(with = "MODULE"))]`**:
203
    ///
204
    ///   Instead of `ItemValueEncodable`, the item is parsed with
205
    ///   `MODULE::write_item_value_onto`,
206
    ///   which must have the same signature as [`ItemValueEncodable::write_item_value_onto`].
207
    ///
208
    ///   (Not supported for sub-documents, signature items, or field collections.)
209
    ///
210
    /// * **`#[deftly(netdoc(flatten))]`**:
211
    ///
212
    ///   This field is a struct containing further individual normal fields.
213
    ///   The Items for those individual fields appear in this
214
    ///   outer document here, so interspersed with other normal fields.
215
    ///
216
    ///   The field type must implement [`NetdocEncodableFields`].
217
    ///
218
    /// * **`#[deftly(netdoc(skip))]`**:
219
    ///
220
    ///   This field doesn't really appear in the network document.
221
    ///   It will be ignored during encoding.
222
    ///
223
    /// * **`#[deftly(netdoc(subdoc))]`**:
224
    ///
225
    ///   This field is a sub-document.
226
    ///   The value type `T` must implment [`NetdocEncodable`]
227
    ///   *instead of* `ItemValueEncodable`.
228
    ///
229
    ///   The field name is not used for parsging;
230
    ///   the sub-document's intro keyword is used instead.
231
    ///
232
    /// # **`#[deftly(netdoc(default))]`**:
233
    ///
234
    ///   Ignored.  (The encoder always encodes the field, regardless of the value.)
235
    ///
236
    ///   Accepted for alignment with `NetdocParseable`.
237
    ///
238
    /// # Example
239
    ///
240
    /// TODO NETDOC ENCODE provide an example when signatures are implemented.
241
    export NetdocEncodable beta_deftly, for struct, expect items:
242

            
243
    ${if T_SIGNATURES { ${error "Encoding of signatures sub-documents is not yet supported" }}}
244

            
245
    impl<$tgens> $P::NetdocEncodable for $ttype {
246
46
        fn encode_unsigned(&self, out: &mut $P::NetdocEncoder) -> Result<(), $P::Bug> {
247
            use $P::*;
248

            
249
            $FIELD_ORDERING_CHECK
250
            $ENCODE_ITEMS_BODY
251
46
        }
252
    }
253
}
254

            
255
define_derive_deftly! {
256
    use NetdocDeriveAnyCommon;
257
    use NetdocFieldsDeriveCommon;
258
    use NetdocSomeItemsDeriveCommon;
259
    use NetdocSomeItemsEncodableCommon;
260

            
261
    /// Derive [`NetdocEncodableFields`] for a struct with individual items
262
    ///
263
    /// Similar to
264
    /// [`#[derive_deftly(NetdocEncodable)]`](derive_deftly_template_NetdocEncodable),
265
    /// but:
266
    ///
267
    ///  * Derives [`NetdocEncodableFields`]
268
    $DOC_NETDOC_FIELDS_DERIVE_SUPPORTED
269
    ///
270
    export NetdocEncodableFields beta_deftly, for struct, expect items:
271

            
272
    impl<$tgens> $P::NetdocEncodableFields for $ttype {
273
12
        fn encode_fields(&self, out: &mut $P::NetdocEncoder) -> Result<(), $P::Bug> {
274
            use $P::*;
275

            
276
            $ENCODE_ITEMS_BODY
277
12
        }
278
    }
279
}
280

            
281
define_derive_deftly! {
282
    use NetdocDeriveAnyCommon;
283
    use NetdocItemDeriveCommon;
284

            
285
    /// Derive `ItemValueEncodable`
286
    ///
287
    // NB there is very similar wording in the ItemValuePareable derive docs.
288
    // If editing any of this derive's documentation, considering editing that too.
289
    //
290
    /// Fields in the struct are emitted as keyword line arguments,
291
    /// in the order they appear in the struct.
292
    ///
293
    /// ### Field type
294
    ///
295
    /// Each field should be:
296
    ///
297
    ///  * `impl `[`ItemArgument`] (one argument),
298
    ///  * `Option<impl ItemArgument>` (one optional argument), or
299
    ///  * `Vec<impl ItemArgument + EncodeOrd>` (zero or more arguments).
300
    ///  * `BTreeSet<impl ItemArgument>` (zero or more arguments).
301
    ///
302
    /// `ItemArgument` can be implemented via `impl Display`,
303
    /// by writing `impl NormalItemArgument`.
304
    ///
305
    /// (Multiplicity is implemented via types in the [`multiplicity`] module,
306
    /// specifically [`MultiplicitySelector`] and [`MultiplicityMethods`].)
307
    ///
308
    /// ### Top-level attributes:p
309
    ///
310
    ///  * **`#[deftly(netdoc(no_extra_args))]**:
311
    ///
312
    ///    Ignored.
313
    ///    (Obviously, the encoder never emits arguments that aren't in the document struct.)
314
    ///
315
    ///    Accepted for alignment with `ItemValueParseable`,
316
    ///    so that a struct which only conditionally derives `ItemValueParseable`
317
    ///    does not need to conditionally mark this attribute.
318
    ///
319
    ///    (May not be combined with `#[deftly(netdoc(rest))]`.)
320
    ///
321
    $DOC_DEBUG_PLACEHOLDER
322
    ///
323
    /// ### Field-level attributes:
324
    ///
325
    ///  * **`#[deftly(netdoc(rest))]**:
326
    ///
327
    ///    The field is the whole rest of the line.
328
    ///    Must come after any other normal argument fields.
329
    ///    Only allowed once.
330
    ///
331
    ///    The field type must implement `ToString` (normally, via `Display`).
332
    ///    (I.e. `Vec` , `Option` etc., are not allowed, and `ItemArgumen` is not used.)
333
    ///
334
    ///  * **`#[deftly(netdoc(object))]**:
335
    ///
336
    ///    The field is the Object.
337
    ///    It must implement [`ItemObjectEncodable`].
338
    ///    (or be `Option<impl ItemObjectEncodable>`).
339
    ///
340
    ///    Only allowed once.
341
    ///
342
    ///  * **`#[deftly(netdoc(object(label = "LABEL")))]**:
343
    ///
344
    ///    Sets the expected label for an Object.
345
    ///    If not supplied, uses [`ItemObjectEncodable::label`].
346
    ///
347
    ///  * **`#[deftly(netdoc(with = "MODULE")]**:
348
    ///
349
    ///    Instead of `ItemArgument`, the argument is encoded with `MODULE::write_arg_onto`,
350
    ///    which must have the same signature as [`ItemArgument::write_arg_onto`].
351
    ///
352
    ///    With `#[deftly(netdoc(rest))]`, `MODULE::fmt_args_rest` replaces `Display::fmt`.
353
    ///
354
    ///    With `#[deftly(netdoc(object))]`, uses `MODULE::write_object_onto`
355
    ///    instead of `tor_netdoc::Writeable::write_onto`.
356
    ///    LABEL must also be specified unless the object also implements `ItemObjectEncodable`.
357
    ///
358
    ///  * **`#[deftly(netdoc(sig_hash = "HASH_METHOD"))]**:
359
    ///
360
    ///    TODO NETDOC ENCODE.  Encoding of signed documents is not yet implemented.
361
    export ItemValueEncodable beta_deftly, for struct, expect items:
362

            
363
    ${define P { $crate::encode }}
364

            
365
    ${define LET_SELECTOR {
366
                    let selector = MultiplicitySelector::<$ftype>::default();
367
                    let selector = selector.selector();
368
    }}
369

            
370
    ${define BUG_CONTEXT {
371
        // We use .map_err() rather than .bug_context() so that we nail down the error type
372
        map_err(|bug: Bug| bug.bug_context(
373
            ${concat "in item " $ttype ", in field " $fname}
374
        ))
375
    }}
376

            
377
    impl<$tgens> $P::ItemValueEncodable for $ttype {
378
28
        fn write_item_value_onto(
379
28
            &self,
380
28
            #[allow(unused)]
381
28
            mut out: $P::ItemEncoder,
382
28
        ) -> $P::Result<(), $P::Bug> {
383
          //  | <- macro conditions and loops are aligned starting here
384
          //  |         | <- we line up the normal Rust statements starting here
385
                        #[allow(unused_imports)]
386
                        use $P::*;
387
                        #[allow(unused_imports)]
388
                        use tor_error::BugContext as _;
389

            
390
                        $EMIT_DEBUG_PLACEHOLDER
391

            
392
                        // ignore #[deftly(netdoc(doctype_for_error = EXPR))]
393
28
                        let _: &str = ${tmeta(netdoc(doctype_for_error)) as expr, default {""}};
394

            
395
                        #[allow(unused)]
396
28
                        let rest_must_come_last_marker = RestMustComeLastMarker;
397

            
398
              ${for fields {
399
18
                        {
400
18
                ${select1
401
18
                  F_NORMAL {
402
18
                            let _ = &rest_must_come_last_marker;
403
18
                            $LET_SELECTOR
404
18
                      ${if not(fmeta(netdoc(with))) {
405
18
                            selector.${paste_spanned $fname check_item_argument_encodable}();
406
18
                      }}
407
18
                            for arg in selector.iter_ordered(&self.$fname) {
408
18
                      ${fmeta(netdoc(with)) as path, default {
409
18
                                ItemArgument
410
18
                      }}
411
18
                                    ::${paste_spanned $fname write_arg_onto}
412
18
                                    (arg, &mut out)
413
18
                                    .$BUG_CONTEXT?;
414
18
                            }
415
18
                  }
416
18
                  F_REST {
417
18
                            let _moved = rest_must_come_last_marker;
418
18
                            out.args_raw_string(&DisplayHelper(
419
4
                                &self.$fname,
420
4
                      ${if fmeta(netdoc(with)) {
421
4
                                ${fmeta(netdoc(with)) as path}
422
4
                                ::${paste_spanned $fname fmt_args_rest}
423
4
                      } else {
424
4
                                <$ftype as Display>::fmt
425
4
                      }}
426
4
                                ));
427
18
                  }
428
18
                  F_SIG_HASH {
429
18
                    ${error "NYI"}
430
18
                  }
431
18
                  F_OBJECT {
432
18
                            // We do this one later, in case it's not last in the struct.
433
18
                            // It consumes `out`.
434
18
                  }
435
18
                }
436
18
                        } // per-field local variables scope
437
              }}
438

            
439
                // Look at some almost-entirely-ignored attributes.
440
                ${if tmeta(netdoc(no_extra_args)) {
441
12
                        let _consume = rest_must_come_last_marker;
442
                }}
443

            
444
              ${for fields {
445
                ${when F_OBJECT}
446

            
447
                        $LET_SELECTOR
448
22
                        if let Some(object) = selector.as_option(&self.$fname) {
449
                ${define CHECK_OBJECT_ENCODABLE {
450
6
                            selector.${paste_spanned $fname check_item_object_encodable}();
451
                }}
452
                            // Bind to `label`
453
8
                            let label =
454
                ${fmeta(netdoc(object(label))) as str, default {
455
                                {
456
                                    $CHECK_OBJECT_ENCODABLE
457
6
                                    ItemObjectEncodable::label(object)
458
                                }
459
                }}
460
                                ;
461

            
462
                            // Obtain the `data`
463
8
                            let mut data: Vec<u8> = vec![];
464
                      ${fmeta(netdoc(with)) as path, default {
465
6
                                ItemObjectEncodable
466
                      }}
467
                                ::${paste_spanned $fname write_object_onto}
468
6
                                (object, &mut data)
469
6
                                .map_err(into_internal!("failed to encode byte array!"))
470
2
                                .$BUG_CONTEXT?;
471

            
472
8
                            out.object(label, data);
473

            
474
14
                        } // if let Some(field)
475
              }}
476

            
477
28
                        Ok(())
478
28
            }
479
    }
480
}