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
32
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28
32
        self.1(self.0, f)
29
32
    }
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 information.
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
          // Are we skipping encoding default values?
83
          ${defcond WANT_SKIP_DEFAULT fmeta(netdoc(default(skip)))}
84
          // If so, provide a function (bound method call) to test defaultness.
85
          ${define IS_DEFAULT { selector.${paste_spanned $fname is_default} }}
86

            
87
          // Bind `selector` to an appropriate selector ZST.
88
          ${define LET_SELECTOR {
89
              ${if WANT_SKIP_DEFAULT {
90
                         let selector = SingletonMultiplicitySelector::<$ftype>::default();
91
              } else {
92
                         let selector = MultiplicitySelector::<$ftype>::default();
93
              }}
94
                         let selector = selector.selector();
95
          }}
96

            
97
          ${for fields {
98
                    { // Rust block for bindings for this field (notably `selector`, `item`
99

            
100
            // ignore #[deftly(netdoc(default))] precisely like NetdocSomeItemsParseableCommon
101
            ${if not(F_INTRO) {
102
                ${if fmeta(netdoc(default)) {}}
103
            }}
104

            
105
            ${select1
106
              F_INTRO {
107
                        #[allow(unused)] // `with` can make this unused
108
                        let selector = SingletonMultiplicitySelector::<$ftype>::default();
109
                        let item = &self.$fname;
110
                        $ENCODE_ITEM_VALUE
111
              }
112
              F_NORMAL {
113
                        $LET_SELECTOR
114
                        for item in selector.${paste_spanned $fname iter_ordered}(&self.$fname) {
115
                  ${if WANT_SKIP_DEFAULT {
116
                            if !$IS_DEFAULT(item)
117
                  }}
118
                            {
119
                                $ENCODE_ITEM_VALUE
120
                            }
121
                        }
122
              }
123
              F_FLATTEN {
124
                        <$ftype as NetdocEncodableFields>::encode_fields
125
                            (&self.$fname, out)
126
                            .$BUG_CONTEXT?;
127
              }
128
              F_SUBDOC {
129
                        $LET_SELECTOR
130
                        selector.${paste_spanned $fname check_netdoc_encodable}();
131
                        for subdoc in selector.iter_ordered(&self.$fname) {
132
                  ${if WANT_SKIP_DEFAULT {
133
                            if !$IS_DEFAULT(subdoc)
134
                  }}
135
                            {
136
                                NetdocEncodable::encode_unsigned(subdoc, out)
137
                                    .$BUG_CONTEXT?;
138
                            }
139
                        }
140
              }
141
              F_SKIP {
142
              }
143
            }
144
                    } // field block.
145
          }} // ${for fields ..}
146

            
147
                    Ok(())
148
    }}
149
}
150

            
151
define_derive_deftly! {
152
    use NetdocDeriveAnyCommon;
153
    use NetdocEntireDeriveCommon;
154
    use NetdocSomeItemsDeriveCommon;
155
    use NetdocSomeItemsEncodableCommon;
156

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

            
269
    impl<$tgens> $P::NetdocEncodable for $ttype {
270
170
        fn encode_unsigned(&self, out: &mut $P::NetdocEncoder) -> $P::Result<(), $P::Bug> {
271
            use $P::*;
272

            
273
            $FIELD_ORDERING_CHECK
274
            $ENCODE_ITEMS_BODY
275
170
        }
276
    }
277
}
278

            
279
define_derive_deftly! {
280
    use NetdocDeriveAnyCommon;
281
    use NetdocFieldsDeriveCommon;
282
    use NetdocSomeItemsDeriveCommon;
283
    use NetdocSomeItemsEncodableCommon;
284

            
285
    /// Derive [`NetdocEncodableFields`] for a struct with individual items
286
    ///
287
    /// Similar to
288
    /// [`#[derive_deftly(NetdocEncodable)]`](derive_deftly_template_NetdocEncodable),
289
    /// but:
290
    ///
291
    ///  * Derives [`NetdocEncodableFields`]
292
    $DOC_NETDOC_FIELDS_DERIVE_SUPPORTED
293
    ///
294
    export NetdocEncodableFields beta_deftly, for struct, meta_quoted rigorous, expect items:
295

            
296
    impl<$tgens> $P::NetdocEncodableFields for $ttype {
297
44
        fn encode_fields(
298
44
            &self,
299
44
            #[allow(unused)] // Not used if there are no fields.
300
44
            out: &mut $P::NetdocEncoder,
301
44
        ) -> $P::Result<(), $P::Bug> {
302
            #[allow(unused)] // Not used if there are no fields.
303
            use $P::*;
304

            
305
            $ENCODE_ITEMS_BODY
306
44
        }
307
    }
308
}
309

            
310
define_derive_deftly! {
311
    use NetdocDeriveAnyCommon;
312
    use NetdocItemDeriveCommon;
313

            
314
    /// Derive `ItemValueEncodable`
315
    ///
316
    // NB there is very similar wording in the ItemValuePareable derive docs.
317
    // If editing any of this derive's documentation, considering editing that too.
318
    //
319
    /// Fields in the struct are emitted as keyword line arguments,
320
    /// in the order they appear in the struct.
321
    ///
322
    /// ### Field type
323
    ///
324
    /// Each field should be:
325
    ///
326
    ///  * `impl `[`ItemArgument`] (one argument),
327
    ///  * `Option<impl ItemArgument>` (one optional argument), or
328
    ///  * `Vec<impl ItemArgument + EncodeOrd>` (zero or more arguments).
329
    ///  * `BTreeSet<impl ItemArgument>` (zero or more arguments).
330
    ///
331
    /// `ItemArgument` can be implemented via `impl Display`,
332
    /// by writing `impl NormalItemArgument`.
333
    ///
334
    /// (Multiplicity is implemented via types in the [`multiplicity`] module,
335
    /// specifically [`MultiplicitySelector`] and [`MultiplicityMethods`].)
336
    ///
337
    /// ### Top-level attributes:p
338
    ///
339
    ///  * **`#[deftly(netdoc(no_extra_args))]**:
340
    ///
341
    ///    Ignored.
342
    ///    (Obviously, the encoder never emits arguments that aren't in the document struct.)
343
    ///
344
    ///    Accepted for alignment with `ItemValueParseable`,
345
    ///    so that a struct which only conditionally derives `ItemValueParseable`
346
    ///    does not need to conditionally mark this attribute.
347
    ///
348
    ///    (May not be combined with `#[deftly(netdoc(rest))]`.)
349
    ///
350
    $DOC_DEBUG_PLACEHOLDER
351
    ///
352
    /// ### Field-level attributes:
353
    ///
354
    ///  * **`#[deftly(netdoc(rest))]**:
355
    ///
356
    ///    The field is the whole rest of the line.
357
    ///    Must come after any other normal argument fields.
358
    ///    Only allowed once.
359
    ///
360
    ///    The field type must implement `ToString` (normally, via `Display`).
361
    ///    (I.e. `Vec` , `Option` etc., are not allowed, and `ItemArgumen` is not used.)
362
    ///
363
    ///  * **`#[deftly(netdoc(object))]**:
364
    ///
365
    ///    The field is the Object.
366
    ///    It must implement [`ItemObjectEncodable`].
367
    ///    (or be `Option<impl ItemObjectEncodable>`).
368
    ///
369
    ///    Only allowed once.
370
    ///
371
    ///  * **`#[deftly(netdoc(object(label = "LABEL")))]**:
372
    ///
373
    ///    Sets the expected label for an Object.
374
    ///    If not supplied, uses [`ItemObjectEncodable::label`].
375
    ///
376
    ///  * **`#[deftly(netdoc(with = MODULE)]**:
377
    ///
378
    ///    Instead of `ItemArgument`, the argument is encoded with `MODULE::write_arg_onto`,
379
    ///    which must have the same signature as [`ItemArgument::write_arg_onto`].
380
    ///
381
    ///    With `#[deftly(netdoc(rest))]`, `MODULE::fmt_args_rest` replaces `Display::fmt`.
382
    ///
383
    ///    With `#[deftly(netdoc(object))]`, uses `MODULE::write_object_onto`
384
    ///    instead of `tor_netdoc::Writeable::write_onto`.
385
    ///    LABEL must also be specified unless the object also implements `ItemObjectEncodable`.
386
    ///
387
    ///  * **`#[deftly(netdoc(sig_hash = HASH_METHOD))]**:
388
    ///
389
    ///    TODO NETDOC ENCODE.  Encoding of signed documents is not yet implemented.
390
    ///
391
    ///  * **`#[deftly(netdoc(skip))]**:
392
    ///
393
    ///    Do not encode this field.
394
    export ItemValueEncodable beta_deftly, for struct, meta_quoted rigorous, expect items:
395

            
396
    ${define P { $crate::encode }}
397

            
398
    ${define BUG_CONTEXT {
399
        // We use .map_err() rather than .bug_context() so that we nail down the error type
400
        map_err(|bug: Bug| bug.bug_context(
401
            ${concat "in item " $ttype ", in field " $fname}
402
        ))
403
    }}
404

            
405
    impl<$tgens> $P::ItemValueEncodable for $ttype {
406
214
        fn write_item_value_onto(
407
214
            &self,
408
214
            #[allow(unused)]
409
214
            mut out: $P::ItemEncoder,
410
214
        ) -> $P::Result<(), $P::Bug> {
411
          //  | <- macro conditions and loops are aligned starting here
412
          //  |         | <- we line up the normal Rust statements starting here
413
                        #[allow(unused_imports)]
414
                        use $P::*;
415
                        #[allow(unused_imports)]
416
                        use tor_error::BugContext as _;
417

            
418
                        $EMIT_DEBUG_PLACEHOLDER
419

            
420
                        // ignore #[deftly(netdoc(doctype_for_error = EXPR))]
421
214
                        let _: &str = ${tmeta(netdoc(doctype_for_error)) as expr, default {""}};
422

            
423
                        #[allow(unused)]
424
214
                        let rest_must_come_last_marker = RestMustComeLastMarker;
425

            
426
              ${for fields {
427
62
                        {
428
62
                ${select1
429
62
                  F_NORMAL {
430
176
                            let _ = &rest_must_come_last_marker;
431
176
                            let selector = MultiplicitySelector::<$ftype>::default();
432
176
                            let selector = selector.selector();
433
62
                      ${if not(fmeta(netdoc(with))) {
434
130
                            selector.${paste_spanned $fname check_item_argument_encodable}();
435
62
                      }}
436
176
                            for arg in selector.iter_ordered(&self.$fname) {
437
62
                      ${fmeta(netdoc(with)) as path, default {
438
170
                                ItemArgument
439
33
                      }}
440
33
                                    ::${paste_spanned $fname write_arg_onto}
441
159
                                    (arg, &mut out)
442
71
                                    .$BUG_CONTEXT?;
443
62
                            }
444
62
                  }
445
62
                  F_REST {
446
62
                            let _moved = rest_must_come_last_marker;
447
62
                            out.args_raw_string(&DisplayHelper(
448
28
                                &self.$fname,
449
28
                      ${if fmeta(netdoc(with)) {
450
28
                                ${fmeta(netdoc(with)) as path}
451
28
                                ::${paste_spanned $fname fmt_args_rest}
452
28
                      } else {
453
28
                                <$ftype as Display>::fmt
454
28
                      }}
455
28
                                ));
456
62
                  }
457
62
                  F_OBJECT {
458
62
                            // We do this one later, in case it's not last in the struct.
459
62
                            // It consumes `out`.
460
62
                  }
461
62
                  F_SKIP {
462
62
                  }
463
62
                }
464
62
                        } // per-field local variables scope
465
              }}
466

            
467
                // Look at some almost-entirely-ignored attributes.
468
                ${if tmeta(netdoc(no_extra_args)) {
469
16
                        let _consume = rest_must_come_last_marker;
470
                }}
471

            
472
              ${for fields {
473
                ${when F_OBJECT}
474

            
475
58
                        let selector = MultiplicitySelector::<$ftype>::default();
476
51
                        if let Some(object) = selector
477
58
                            .${paste_spanned $fname as_option}(&self.$fname)
478
                        {
479
                ${define CHECK_OBJECT_ENCODABLE {
480
18
                            selector.${paste_spanned $fname check_item_object_encodable}();
481
                }}
482
                            // This is, sort of, a recapitulation of `ItemEncoder::object`.
483
                            // We can't conveniently just call that because we want to support
484
                            // overriding the label, even when we're using ItemObjectEncodable.
485

            
486
                            // Bind to `label`
487
44
                            let label =
488
                ${fmeta(netdoc(object(label))) as str, default {
489
                                {
490
                                    $CHECK_OBJECT_ENCODABLE
491
22
                                    ItemObjectEncodable::label(object)
492
                                }
493
                }}
494
                                ;
495

            
496
                            // Obtain the `data`
497
44
                            let mut data: Vec<u8> = vec![];
498
                      ${fmeta(netdoc(with)) as path, default {
499
15
                                ItemObjectEncodable
500
14
                      }}
501
14
                                ::${paste_spanned $fname write_object_onto}
502
32
                                (object, &mut data)
503
32
                                .map_err(into_internal!("failed to encode byte array!"))
504
22
                                .$BUG_CONTEXT?;
505

            
506
44
                            out.object_bytes(label, data);
507

            
508
14
                        } // if let Some(object)
509
              }}
510

            
511
214
                        Ok(())
512
214
            }
513
    }
514
}