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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
//! The macros of the inspection of the protocol crates, written once: the tables of their
//! trees and their views, and the functions that read them.
/// The shape that the list of the types of a protocol writes.
#[doc(hidden)]
#[macro_export]
macro_rules! __shape {
(_) => {
$crate::inspect::Shape::Plain
};
(null) => {
$crate::inspect::Shape::Null
};
(bits) => {
$crate::inspect::Shape::Bits
};
(ies) => {
$crate::inspect::Shape::Ies(None)
};
(ie) => {
$crate::inspect::Shape::Ie(None)
};
(extensions) => {
$crate::inspect::Shape::Extensions("")
};
(extension) => {
$crate::inspect::Shape::Extension(None, "")
};
([$of:tt]) => {
$crate::inspect::Shape::List(&$crate::__shape!($of))
};
(()) => {
$crate::inspect::Shape::Transfer("")
};
(($contained:ident)) => {
$crate::inspect::Shape::Transfer(stringify!($contained))
};
($name:ident) => {
$crate::inspect::Shape::Named(stringify!($name))
};
}
/// Whether the list of the types writes a member as one that may be absent.
#[doc(hidden)]
#[macro_export]
macro_rules! __optional {
(:) => {
false
};
(?) => {
true
};
}
/// What the list of the types says a type is.
#[doc(hidden)]
#[macro_export]
macro_rules! __kind {
(= $is:tt) => {
$crate::inspect::Kind::Is($crate::__shape!($is))
};
(sequence { $($member:literal $presence:tt $shape:tt),* }) => {
$crate::inspect::Kind::Sequence(&[$((
$member,
$crate::__shape!($shape),
$crate::__optional!($presence),
)),*])
};
(choice { $($member:literal $presence:tt $shape:tt),* }) => {
$crate::inspect::Kind::Choice(&[$((
$member,
$crate::__shape!($shape),
$crate::__optional!($presence),
)),*])
};
}
/// The types of a protocol, which its `src/inspect_registry.rs` lists from the bindings in
/// the order of their names: each SEQUENCE with its members and each CHOICE with its
/// alternatives, by the names that ASN.1 gives them, and each type that is a list or a
/// bit string. A member is written with `?` when a value may be without it, and its value
/// as `_` when it has no members, `bits` for a bit string whose size varies, the name of
/// its type, a list in brackets, a container by its kind, or in parentheses the type that
/// the octets of a transfer contain.
#[doc(hidden)]
#[macro_export]
macro_rules! __types {
($($name:ident $kind:tt $body:tt;)*) => {
pub(crate) const TYPES: &[(&str, $crate::inspect::Kind)] = &[
$((stringify!($name), $crate::__kind!($kind $body)),)*
];
};
}
/// The functions of a type of the bindings of a protocol for its trees and its views,
/// which is written with the lifetime `'a` when it borrows, or without one in a function.
#[doc(hidden)]
#[macro_export]
// A protocol calls it in its crate, whose `inspect` module `crate::` names.
#[allow(clippy::crate_in_macro_def)]
macro_rules! __typed {
($type:ty) => {{
// A type that borrows nothing leaves the lifetime without a use.
#[allow(clippy::needless_lifetimes)]
fn decode<'a>(raw: &'a [u8]) -> Result<$crate::inspect::Decoded, $crate::Error> {
let name = $crate::protocol::__type_name(stringify!($type));
crate::inspect::__decode_typed::<$type>(name, raw)
}
#[allow(clippy::needless_lifetimes)]
fn encode<'a>(value: &'a $crate::json::Value) -> Result<Vec<u8>, $crate::Error> {
crate::inspect::__encode_typed::<$type>(value)
}
$crate::inspect::Typed { decode, encode }
}};
}
/// The transfers of a protocol: the member that holds each, and the type that its octets
/// contain. A member that has several types has its name alone.
#[doc(hidden)]
#[macro_export]
macro_rules! __transfers {
($($field:literal $($transfer:path)?;)*) => {
pub(crate) const TRANSFER_FIELDS: &[&str] = &[$($field,)*];
#[allow(clippy::match_single_binding)]
pub(crate) fn transfer(field: &str) -> Result<$crate::inspect::Typed, $crate::Error> {
match field {
$($($field => Ok($crate::__typed!($transfer)),)?)*
_ => Err($crate::Error::form(format!(
"{PROTOCOL} has several types for {field}, or none: it is read and \
written as its octets"
))),
}
}
};
}
/// The IEs of a protocol whose octets may carry a type that the specification names and
/// the ASN.1 does not: the identifier of each, the name that ASN.1 gives the type, and the
/// type.
#[doc(hidden)]
#[macro_export]
macro_rules! __carried {
($($id:literal $name:literal $carried:path;)*) => {
pub(crate) const CARRIED: &[(u16, &str, &str)] =
&[$(($id, $name, stringify!($carried)),)*];
pub(crate) fn carried(name: &str) -> Option<$crate::inspect::Typed> {
$(if $crate::inspect::same_name($name, name) {
return Some($crate::__typed!($carried));
})*
None
}
};
}
/// The tables of a protocol for its trees, its views and their paths, and the functions of
/// its module `inspect` that read them, written once: the protocol calls it in that module,
/// with the module of its bindings, the type of its PDU, the cfg of its hooks, a message
/// that is a container of protocol IEs and nothing else, and the documentation of
/// `item_criticality`, which names what the protocol has in single containers.
#[doc(hidden)]
#[macro_export]
// A protocol calls it in its crate, whose items `crate::` names.
#[allow(clippy::crate_in_macro_def)]
macro_rules! __inspect {
(bindings $bindings:ident, pdu $pdu:ident, hooks $hooks:meta, any $any:ty;
$(#[$item_doc:meta])*) => {
use std::collections::HashMap;
use std::sync::LazyLock;
use $crate::inspect::{self as shared, Protocol};
pub(crate) use $crate::inspect::{Kind, Shape, same_name};
use $crate::json::{self, Tree};
use $crate::{AsnType, Decode, Encode};
use crate::Error;
// The name of the protocol, the first segment of the paths of the IEs of the message of
// a PDU, and what the ASN.1 says of the messages, the IEs and the types.
use crate::inspect_registry::{
CARRIED, ENUMERATIONS, IE_NAMES, IE_TYPES, ITEM_CRITICALITIES, MESSAGE_IE_CRITICALITIES,
MESSAGES, PROCEDURE_CRITICALITIES, PROTOCOL, ROOT, TRANSFER_FIELDS, TYPES,
};
// The type that an IE of an extension container has where its identifier alone does not
// say it: the one that the type which the container extends gives it.
use crate::registry::EXTENSION_TYPES;
// The transfers of protocol IEs, with their object sets.
use crate::registry::TRANSFERS;
/// A tree and a view: the JSON values of `oxirush-utils`.
pub use $crate::json::Value;
/// The tables of the protocol, as the paths read them.
pub(crate) static TABLES: Protocol = Protocol {
name: PROTOCOL,
root: ROOT,
types: TYPES,
messages: MESSAGES,
ie_names: IE_NAMES,
ie_types: IE_TYPES,
extension_types: EXTENSION_TYPES,
carried: CARRIED,
enumerations: ENUMERATIONS,
item_criticalities: ITEM_CRITICALITIES,
message_ie_criticalities: MESSAGE_IE_CRITICALITIES,
transfers: TRANSFERS,
ie_id,
kind_of,
procedure_criticalities: PROCEDURE_CRITICALITIES,
ie_typed: crate::inspect_registry::ie,
extension_typed: crate::registry::extension,
ie_contents: crate::inspect_registry::ie_contents,
type_form: crate::inspect_registry::type_form,
transfer_fields: TRANSFER_FIELDS,
transfer_typed: crate::inspect_registry::transfer,
carried_typed: crate::inspect_registry::carried,
any_message,
};
/// The letters and the digits of a name, in lower case.
fn letters(name: &str) -> impl Iterator<Item = u8> + '_ {
let letters = name.bytes().filter(u8::is_ascii_alphanumeric);
letters.map(|letter| letter.to_ascii_lowercase())
}
/// The number of the IE that ASN.1 names `id-<name>`, however the name is written.
pub(crate) fn ie_id(name: &str) -> Option<u16> {
/// The identifier of each IE by the letters and the digits of its name, in lower
/// case, which is how two names are the same: the first IE of a name, as a search
/// of the names in their order finds it.
static IDS: LazyLock<HashMap<Vec<u8>, u16>> = LazyLock::new(|| {
let mut ids = HashMap::new();
for (id, name) in IE_NAMES {
ids.entry(letters(name).collect()).or_insert(*id);
}
ids
});
// The letters of the name, on the stack for a name of the length that an IE has:
// the longest has 51 of them.
let mut short = [0; 64];
let mut length = 0;
for letter in letters(name) {
let Some(place) = short.get_mut(length) else {
let long: Vec<u8> = letters(name).collect();
return IDS.get(&long).copied();
};
*place = letter;
length += 1;
}
IDS.get(&short[..length]).copied()
}
/// What a type of the list of the types is; `None` for a name that the list does not
/// have, which is that of a value without members.
fn kind_of(name: &str) -> Option<&'static Kind> {
static KINDS: LazyLock<HashMap<&'static str, &'static Kind>> =
LazyLock::new(|| TYPES.iter().map(|(name, kind)| (*name, kind)).collect());
KINDS.get(name).copied()
}
/// The values at `path` in `view`, in the order of the view.
///
/// An IE that the message does not have selects nothing, and so does a member that its
/// type can have and the value does not have: an OPTIONAL member that is absent, or
/// another alternative of a CHOICE. A name that the type of the value cannot have is an
/// error, which says the members that it has. `*` is each entry of a list or each member
/// of a value, without those that start with `_`; after it, a segment is an error when
/// none of the values can have it.
///
/// A segment that names no member of an IE is taken in its value, and one that names no
/// member of a transfer in its decoded value, as if `value` or `decoded` stood before
/// it. A name that neither has is an error, which says the members of both.
///
/// After an IE, a segment that is the name of that IE stays on it: a path may say which
/// IE an entry is, as `0/Cause` for the first IE of a list, and one that ends so selects
/// the IE. The name of another IE, where the value has no such member, is an error that
/// says which IE the entry is.
///
/// The `octets` of an IE or of a transfer are what it was received as: those of one whose
/// value [`set`], [`remove`] or [`insert`] edited since are an error, as `from_view` of
/// the PDU has yet to give it others. So is a path into a value that did not decode.
pub fn select<'a>(view: &'a Value, path: &str) -> Result<Vec<&'a Value>, Error> {
shared::select(&TABLES, view, path)
}
/// Whether `path` can select a value in a message that ASN.1 names `message`: what
/// [`select`] says of a path without a view to select in.
///
/// The name is taken as [`message_named`](crate::inspect::message_named) takes it. Under
/// the root, the name of an IE has to be that of an IE of the object set of the message;
/// under an IE, each segment has to be a member that the type of the value has, through
/// the lists, the alternatives of a CHOICE and the decoded value of a transfer. A position
/// or `*` stands for each entry that a list could have, and `-` for the end of a list.
///
/// An IE of an extension container is read against the type that the type which the
/// container extends gives it, where its identifier alone does not say its type.
///
/// What is not known is not refused: the value of an IE selected by `@id=N` when the
/// identifier is unknown or has no type where it is, the entries of a container that is
/// not that of the message when they are selected by position, a segment under such an
/// IE, which may name a member of its value, and whether a position is within the bounds
/// of its list. Such an entry is the IE that the next segment names, and the path is
/// then read against the type of that IE: unless some value has a member of that name
/// and of another type, which the segment may be.
pub fn check_path(message: &str, path: &str) -> Result<(), Error> {
shared::check_path(&TABLES, message, path)
}
/// The names that the value at `path` can have in a message that ASN.1 names `message`,
/// when its type is an ENUMERATED: the names of its values as the specification spells
/// them, in the order of its definition. A name is compared as [`select`] compares one,
/// whatever its case and whatever is between its letters. A name that is not one of them
/// is never the value of a message, so a caller can refuse it before it compares one.
///
/// `None` for a value of another type, and for one whose type is not known: what
/// [`check_path`] does not refuse for that reason. A path that it refuses is an error
/// here too.
pub fn enumerated_at(message: &str, path: &str) -> Result<Option<&'static [&'static str]>, Error> {
shared::enumerated_at(&TABLES, message, path)
}
/// Each value of `view` with the path that selects it, in the order of the view. A list
/// of plain values is one value. The IEs of the message go by name under the root, and
/// those of a message that the view contains by name in its place; an IE goes by its
/// position when it has no name or its identifier is there twice.
///
/// The members that start with `_` repeat what was received and are left out, except
/// `_decode_error`. The `octets` of an IE or of a transfer are listed when it has no
/// value beside them: those of a value repeat it, and are selected all the same.
///
/// What is nested deeper than 64 levels, the most segments that a path has, is one
/// value at the deepest path, whatever it holds: no view of a PDU is that deep, and
/// every path that is listed is one that [`select`] takes.
pub fn paths(view: &Value) -> Vec<(String, Value)> {
shared::paths(&TABLES, view)
}
/// Give the member or the list entry at `path` the value `value`. `null` takes an
/// optional member out. An IE is written as `{id, criticality, value}` with its typed
/// value, or with its `octets` in hexadecimal in place of `value`; its `id` is a number
/// or the name of the IE. The `octets` of an IE or of a transfer are set in place of
/// its value. An alternative of a CHOICE takes the place of the one that is there.
///
/// A path that selects nothing is an error, and the view is then as it was. So is a
/// name that the type of a value cannot have, a member that its type always has and
/// that would be taken out, and an edit that would send nothing else: the value of an IE
/// or the decoded value of a transfer is not taken out, as the octets received would go
/// out in its place.
pub fn set(view: &mut Value, path: &str, value: Value) -> Result<(), Error> {
shared::set(&TABLES, view, path, value)
}
/// Take the member or the list entries at `path` out of `view`. A path that selects
/// nothing is an error, and so is a member that its type always has.
pub fn remove(view: &mut Value, path: &str) -> Result<(), Error> {
shared::remove(&TABLES, view, path)
}
/// Add `value` to a list of `view`: before each entry that `path` selects, or at the end
/// for a path that ends with `-`. An IE is written as [`set`] takes it.
pub fn insert(view: &mut Value, path: &str, value: Value) -> Result<(), Error> {
shared::insert(&TABLES, view, path, value)
}
/// The entry shape of a list of `shape`, which the tests of the tables read.
#[cfg(test)]
fn entry_shape(shape: Shape) -> Shape {
TABLES.entry_shape(shape)
}
/// The IEs of the message that ASN.1 names `message`, which a view has as `ies`, as a
/// tree has them, which the tests of the tables read.
#[cfg(test)]
fn named_ies(message: &str, ies: Value) -> Result<Value, Error> {
shared::named_ies(&TABLES, message, ies)
}
/// The entries that a view has for the IEs of the message that ASN.1 names `message`,
/// which `ies` has as a tree has them, which the tests of the tables read.
#[cfg(test)]
fn listed_ies(message: &str, ies: &Value) -> Result<Value, Error> {
shared::listed_ies(&TABLES, message, ies)
}
#[cfg(test)]
mod tests {
use super::*;
use $crate::inspect::{EDITED, Member, type_name};
use oxirush_utils::tree;
/// The member of a view that holds the message of the PDU, and the member of a
/// message that holds its IEs.
const MESSAGE: &str = "message";
const IES: &str = "protocolIEs";
/// The members that hold the value of an IE or of a transfer.
const VALUES: [&str; 3] = ["value", "extensionValue", "decoded"];
/// Every member and every alternative of every type of the list.
fn all_members() -> impl Iterator<Item = (&'static str, &'static [Member])> {
TYPES.iter().filter_map(|(name, kind)| match kind {
Kind::Sequence(members) | Kind::Choice(members) => Some((*name, *members)),
Kind::Is(_) => None,
})
}
/// The member of `members` that a segment names.
fn member<'m>(members: &'m [Member], part: &str) -> Option<&'m Member> {
let exact = members.iter().find(|(name, ..)| *name == part);
exact.or_else(|| members.iter().find(|(name, ..)| same_name(name, part)))
}
/// The name that ASN.1 gives the IE of an identifier.
fn ie_name(id: u16) -> String {
TABLES.ie_name_of(id).unwrap().to_string()
}
#[test]
fn the_types_are_listed_in_the_order_of_their_names_and_name_types_of_the_list() {
assert!(TYPES.windows(2).all(|pair| pair[0].0 < pair[1].0));
fn check(of: &str, shape: Shape) {
match shape {
// A name that the list does not have is that of an ENUMERATED, which
// has no members and has its names, or of a well-known type, which has
// its readable form.
Shape::Named(name) => assert!(
kind_of(name).is_some()
|| TABLES.enumeration(name).is_some()
|| (TABLES.type_form)(name).is_some(),
"{of}: {name}"
),
Shape::Transfer(name) if !name.is_empty() => {
assert!(kind_of(name).is_some(), "{of}: {name}")
}
Shape::List(inner) => check(of, *inner),
_ => {}
}
}
for (name, kind) in TYPES {
match kind {
Kind::Is(shape) => check(name, *shape),
Kind::Sequence(members) | Kind::Choice(members) => {
members.iter().for_each(|(_, shape, _)| check(name, *shape))
}
}
}
// The types of the messages and of the IEs are types of the bindings.
for (.., ty) in MESSAGES {
assert!(kind_of(type_name(ty)).is_some(), "{ty}");
}
assert!(
IE_TYPES
.iter()
.any(|(_, ty, _)| kind_of(type_name(ty)).is_some())
);
// The identifiers of the IEs are in their order, which their names are read in.
assert!(IE_NAMES.windows(2).all(|pair| pair[0].0 < pair[1].0));
}
#[test]
fn an_enumerated_value_has_the_names_of_its_type_wherever_a_path_finds_it() {
assert!(ENUMERATIONS.windows(2).all(|pair| pair[0].0 < pair[1].0));
let names = |message: &str, path: &str| enumerated_at(message, &format!("/{ROOT}/{path}"));
let release = |path: &str| names("UEContextReleaseCommand", path).unwrap();
// An alternative of a CHOICE, however the path is written.
let nas = release("Cause/value/nas").expect("an ENUMERATED");
assert!(nas.contains(&"normal-release") && !nas.contains(&"unspecified-failure"));
assert_eq!(release("cause/VALUE/Nas"), Some(nas));
// The value of the IE may be left out of the path.
assert_eq!(release("Cause/nas"), Some(nas));
// A CHOICE, and the identifier of an IE.
assert_eq!(release("Cause/value"), None);
assert_eq!(release("Cause/id"), None);
// A member of the entries of a list, through an optional member.
let error = names(
"ErrorIndication",
"CriticalityDiagnostics/value/iEsCriticalityDiagnostics/0/typeOfError",
);
assert_eq!(error.unwrap(), Some(&["not-understood", "missing"][..]));
// What `check_path` refuses is refused.
assert!(names("UEContextReleaseCommand", "Cause/value/nsa").is_err());
assert!(names("NoSuchMessage", "Cause/value/nas").is_err());
}
#[test]
fn no_two_names_that_a_path_takes_are_the_same() {
let distinct = |of: &str, names: &mut dyn Iterator<Item = &str>| {
let names: Vec<_> = names.collect();
for (at, name) in names.iter().enumerate() {
let other = names[..at].iter().find(|other| same_name(other, name));
assert!(other.is_none(), "{of}: {name} and {other:?}");
}
};
for (name, members) in all_members() {
distinct(name, &mut members.iter().map(|(member, ..)| *member));
}
distinct("the IEs", &mut IE_NAMES.iter().map(|(_, name)| *name));
let mut messages = MESSAGES.iter().map(|(_, _, name, ..)| *name);
distinct("the messages", &mut messages);
// The names of an IE and of a member do not meet: a message that a view contains
// has its IEs alone.
for (name, members) in all_members() {
if members.iter().any(|(member, ..)| *member == IES) {
assert_eq!(members.len(), 1, "{name}");
}
}
// A name is read whatever its case and its separators, and a name of an IE
// finds its identifier.
assert!(same_name("AMF-UE-NGAP-ID", "amf_ue ngap.id"));
assert!(!same_name("-", "_") && !same_name("", ""));
for (id, name) in IE_NAMES {
assert_eq!(ie_id(&name.to_lowercase()), Some(*id), "{name}");
}
}
#[test]
fn no_two_names_of_an_enumerated_are_the_same() {
// A name is read whatever its case and its separators: no two values of one
// type are then one name.
for (ty, names) in ENUMERATIONS {
for (at, name) in names.iter().enumerate() {
let other = names[..at].iter().find(|other| same_name(other, name));
assert!(other.is_none(), "{ty}: {name} and {other:?}");
}
}
}
/// The view of a message that ASN.1 names `name` and that has `ies` for IEs.
fn view_of(name: &str, ies: Value) -> Value {
let mut messages = MESSAGES.iter();
let (direction, code, ..) = messages.find(|(_, _, known, ..)| *known == name).unwrap();
tree!({
"direction": direction,
"procedure_code": code,
"criticality": "reject",
MESSAGE: {IES: ies},
})
}
#[test]
fn a_segment_that_names_no_member_of_an_ie_is_taken_in_its_value() {
let cause = ie_id("Cause").expect("an IE of the protocol");
let value = tree!({"radioNetwork": "unspecified"});
let entry = tree!({"id": cause, "criticality": "ignore", "value": &value});
let mut view = view_of("UEContextReleaseCommand", tree!([&entry]));
let path = |rest: &str| format!("/{ROOT}/Cause/{rest}");
// The path that leaves `value` out selects what the one that has it selects.
let short = select(&view, &path("radioNetwork")).unwrap();
assert_eq!(short, [&tree!("unspecified")]);
assert_eq!(short, select(&view, &path("value/radioNetwork")).unwrap());
assert!(select(&view, &path("nas")).unwrap().is_empty());
// What the IE has itself comes first, and `*` is each of its members.
assert_eq!(select(&view, &path("value")).unwrap(), [&value]);
assert_eq!(select(&view, &path("id")).unwrap(), [&tree!(cause)]);
assert_eq!(select(&view, &path("*")).unwrap().len(), 3);
// A name that neither has is refused with the members of both, with a view and
// without one.
let refused = select(&view, &path("misspelled")).unwrap_err();
assert_eq!(refused.kind(), crate::ErrorKind::NoSuchMember);
let text = refused.to_string();
assert!(text.contains("is not a member of Cause, which has radioNetwork"));
assert!(text.contains("; an IE itself has id, criticality, value, octets, at /"));
let checked = |rest: &str| check_path("UEContextReleaseCommand", &path(rest));
assert_eq!(checked("misspelled"), Err(refused.clone()));
assert_eq!(checked("nas"), Ok(()));
let deeper = checked("radioNetwork/deeper").unwrap_err().to_string();
assert!(deeper.contains("which has none") && !deeper.contains("itself"));
// An edit takes the same path, and marks the value that it changed.
view[MESSAGE][IES][0]["_raw_value"] = tree!("0000");
set(&mut view, &path("nas"), tree!("normal-release")).unwrap();
let edited = &view[MESSAGE][IES][0];
assert_eq!(edited["value"], tree!({"nas": "normal-release"}));
assert_eq!(edited[EDITED], tree!(true));
let before = view.clone();
assert_eq!(set(&mut view, &path("misspelled"), tree!(1)), Err(refused));
let kept = remove(&mut view, &path("nas")).unwrap_err();
assert_eq!(kept.kind(), crate::ErrorKind::Edit);
assert!(
kept.to_string().contains("a CHOICE has one alternative"),
"{kept}"
);
assert_eq!(view, before);
}
#[test]
fn the_name_of_the_ie_that_an_entry_is_stays_on_the_entry() {
let cause = ie_id("Cause").expect("an IE of the protocol");
let value = tree!({"radioNetwork": "unspecified"});
let entry = tree!({"id": cause, "criticality": "ignore", "value": &value});
let mut view = view_of("UEContextReleaseCommand", tree!([&entry]));
let path = |rest: &str| format!("/{ROOT}/{rest}");
// After its position, and after its name, which selects it already.
for named in ["0/Cause", "0/cause", "Cause/Cause", "*/Cause"] {
assert_eq!(select(&view, &path(named)).unwrap(), [&entry], "{named}");
let member = select(&view, &path(&format!("{named}/radioNetwork"))).unwrap();
assert_eq!(member, [&tree!("unspecified")], "{named}");
let whole = select(&view, &path(&format!("{named}/value"))).unwrap();
assert_eq!(whole, [&value], "{named}");
}
// The name of another IE says which IE the entry is, with a view and without.
let other = IE_NAMES.iter().find(|(id, _)| *id != cause);
let other = path(&format!("0/{}/value", other.unwrap().1));
let refused = select(&view, &other).unwrap_err();
let text = refused.to_string();
assert!(text.starts_with("this IE is Cause, not "), "{refused}");
assert!(text.contains("is not a member of Cause, which has radioNetwork"));
let checked = |rest: &str| check_path("UEContextReleaseCommand", &path(rest));
assert_eq!(checked("Cause/Cause/nas"), Ok(()));
assert!(checked("Cause/Cause/misspelled").is_err());
let names = |rest: &str| enumerated_at("UEContextReleaseCommand", &path(rest));
assert_eq!(names("*/Cause/nas"), names("Cause/value/nas"));
assert!(names("*/Cause/nas").unwrap().is_some());
// An edit takes the same path, and the entry itself where the path ends with
// its name.
assert_eq!(set(&mut view, &other, tree!(1)), Err(refused));
set(&mut view, &path("0/Cause/nas"), tree!("normal-release")).unwrap();
let edited = select(&view, &path("Cause/value")).unwrap();
assert_eq!(edited, [&tree!({"nas": "normal-release"})]);
insert(&mut view, &path("0/Cause"), entry.clone()).unwrap();
assert_eq!(select(&view, &path("*/Cause")).unwrap().len(), 2);
remove(&mut view, &path("1/Cause")).unwrap();
assert_eq!(select(&view, &path("*/Cause")).unwrap(), [&entry]);
}
#[test]
fn an_entry_that_is_not_known_is_the_ie_that_a_segment_names_where_no_member_is() {
// Where a view is not there to say which IE an entry is, a segment that names
// an IE is taken for it, and the path goes on in the type of that IE: unless a
// value has a member of that name and of another type, which it may be.
let cause = ie_id("Cause").expect("an IE of the protocol");
for (shape, stays) in [
(Shape::Ie(None), Shape::Ie(Some(cause))),
(
Shape::Extension(None, ""),
Shape::Extension(Some(cause), ""),
),
] {
assert_eq!(TABLES.named_ie(shape, "cause", true), Some(stays));
assert_eq!(TABLES.named_ie(shape, "cause", false), None);
}
assert_eq!(
TABLES.named_ie(Shape::Ie(Some(cause + 1)), "Cause", true),
None
);
assert_eq!(TABLES.named_ie(Shape::Plain, "Cause", true), None);
let (mut taken, mut left, mut spelled) = (0, 0, 0);
for (of, members) in all_members() {
for (member, shape, _) in members {
let Some(id) = ie_id(member) else {
continue;
};
// A member of the value of an IE that is named as the IE itself is
// spelled otherwise, which is how a path tells the two.
let own = match TABLES.resolved(TABLES.ie_value(Some(id), "")) {
Shape::Transfer(contained) => Shape::Named(contained),
own => own,
};
if own == Shape::Named(of) {
assert_ne!(*member, ie_name(id), "{of}.{member}");
assert!(
!TABLES.stays_on(id, "", member) && TABLES.stays_on(id, "", &ie_name(id))
);
spelled += 1;
}
let own = TABLES.resolved(TABLES.ie_value(Some(id), ""));
match TABLES.named_ie(Shape::Ie(None), member, true) {
Some(_) => {
let same = TABLES.resolved(*shape) == own;
assert!(same, "{of}.{member}");
taken += 1;
}
None => left += 1,
}
}
}
assert!(taken > 0 && left > 0, "{taken} {left}");
// TS 36.413 has one such member, and TS 38.413 none.
assert!(spelled <= 1, "{spelled}");
}
#[test]
fn only_a_string_of_bits_names_a_member_as_the_ie_that_holds_it_does() {
// A segment under an IE that names a member of the IE is that member, and under
// a transfer one of the transfer: of the values that they hold, a string of
// bits whose size varies alone has a member of such a name, its `value`, which
// a path reaches after the `value` of the IE.
let mut bits = 0;
// Each IE with the type of its identifier, and with the one that each type which
// it extends gives it.
let of_their_own = IE_TYPES.iter().map(|(id, ..)| (id, ""));
let extensions = EXTENSION_TYPES
.iter()
.map(|(id, extended, _)| (id, *extended));
for (id, extended) in of_their_own.chain(extensions) {
let mut shape = TABLES.resolved(TABLES.ie_value(Some(*id), extended));
if let Shape::Transfer(contained) = shape {
shape = TABLES.resolved(Shape::Named(contained));
}
let members = TABLES.members(shape, None).unwrap_or_default();
for own in VALUES.into_iter().chain(["id", "criticality", "octets"]) {
let same = members.iter().any(|(name, ..)| same_name(name, own));
assert_eq!(same, shape == Shape::Bits && own == "value", "{id}: {own}");
}
bits += usize::from(shape == Shape::Bits);
}
assert!(bits > 0);
}
#[test]
fn an_extension_has_the_type_that_the_type_it_extends_gives_it() {
// An extension container extends the type that has it, whatever type that is.
let mut containers = 0;
for (name, _) in all_members() {
let members = TABLES.members(Shape::Named(name), None).unwrap_or_default();
for (_, shape, _) in members {
if let Shape::Extensions(extended) = shape {
assert_eq!(extended, name);
assert_eq!(entry_shape(shape), Shape::Extension(None, name));
containers += 1;
}
}
}
assert!(containers > 0);
// An IE whose identifier has a type for each type that it extends is of that type
// there, and of none elsewhere: its identifier alone does not say its type.
for (id, extended, ty) in EXTENSION_TYPES {
assert!(kind_of(extended).is_some(), "{extended}");
assert!(IE_TYPES.iter().all(|(known, ..)| known != id), "{id}");
assert_eq!(
TABLES.ie_value(Some(*id), extended),
Shape::Named(type_name(ty))
);
assert_eq!(TABLES.ie_value(Some(*id), ""), Shape::Unknown);
let extension = Shape::Extension(Some(*id), extended);
let value = TABLES.members(extension, None).unwrap();
let (_, value, _) = member(&value, "extensionValue").unwrap();
assert_eq!(*value, Shape::Named(type_name(ty)), "{id} in {extended}");
// Its name stays on it, as that of any IE does.
let named = TABLES.named_ie(Shape::Extension(None, extended), &ie_name(*id), true);
assert_eq!(named, Some(extension));
}
}
#[test]
fn the_ies_that_a_value_holds_are_written_by_name_and_read_back() {
let cause = ie_id("Cause").expect("an IE of the protocol");
let value = tree!({"radioNetwork": "unspecified"});
// The entry of a view, with what it keeps of what was received, and as it is
// written.
let entry = |received: bool| {
let mut entry = tree!({"id": cause, "criticality": "ignore", "value": &value});
if received {
entry["_raw_value"] = tree!("0000");
entry["_original_id"] = tree!(cause);
}
entry
};
// No table has the criticality of the IEs of a message that is not one of the
// protocol, as that of a transfer: it is written, and asked for.
let shown = named_ies("no message", tree!([entry(true)])).unwrap();
assert_eq!(shown, tree!([{"Cause": &value, "criticality": "ignore"}]));
let listed = listed_ies("no message", &shown).unwrap();
assert_eq!(listed, tree!([entry(false)]));
let refused = listed_ies("no message", &tree!([{"Cause": &value}])).unwrap_err();
assert_eq!(refused.kind(), crate::ErrorKind::Form);
let text = refused.to_string();
assert!(text.contains("says its \"criticality\""), "{refused}");
// An entry that is written as a view has it stays as it is.
assert_eq!(listed_ies("no message", &listed), Ok(listed));
// One that did not decode has its octets, which are sent as they are.
let mut undecoded = entry(true);
undecoded["value"] = tree!("0000");
undecoded["_decode_error"] = tree!("why");
let shown = named_ies("no message", tree!([undecoded])).unwrap();
let octets = tree!({"Cause": {"octets": "0000"}, "criticality": "ignore"});
assert_eq!(shown, tree!([octets]));
let listed = listed_ies("no message", &shown).unwrap();
let octets = tree!({"id": cause, "criticality": "ignore", "octets": "0000"});
assert_eq!(listed, tree!([octets]));
// A tree is not read deeper than a view is encoded.
let mut deep = tree!(1);
for _ in 0..258 {
deep = tree!([deep]);
}
let deep = tree!([{"Cause": deep, "criticality": "ignore"}]);
let refused = listed_ies("no message", &deep).unwrap_err();
assert_eq!(refused.kind(), crate::ErrorKind::Limit);
assert!(refused.to_string().contains("nests deeper"), "{refused}");
}
#[test]
fn the_octets_of_an_ie_carry_a_type_of_the_list_by_a_name_that_no_member_has() {
for (id, name, ty) in CARRIED {
// The value of the IE is octets, and what they carry a type of the list, by
// the name that ASN.1 gives it.
let (_, octets, contained) = IE_TYPES.iter().find(|(known, ..)| known == id).unwrap();
assert!(contained.is_empty() && kind_of(type_name(octets)).is_none());
assert!(kind_of(type_name(ty)).is_some() && same_name(name, type_name(ty)));
assert_eq!(TABLES.ie_value(Some(*id), ""), Shape::Carrier(*id));
// A view is encoded with any member of that name as that type.
for (of, members) in all_members() {
let same = members.iter().any(|(member, ..)| same_name(member, name));
assert!(!same, "{of}: {name}");
}
}
}
#[test]
fn what_is_nested_deeper_than_a_path_goes_is_one_value() {
// Lists in lists, ten levels deeper than a path goes: the path of the one value
// has a segment for each level that is walked, and the value is what is under
// them, whole.
const SEGMENTS: usize = 64;
let mut deep = tree!([{"a": 1}]);
for _ in 0..SEGMENTS + 10 {
deep = tree!([deep]);
}
let found = paths(&deep);
assert_eq!(found.len(), 1);
let (path, value) = &found[0];
assert_eq!(path.matches('/').count(), SEGMENTS);
assert_eq!(select(&deep, path), Ok(vec![value]));
// What is not that deep is listed value by value.
let mut shallow = tree!([{"a": 1}]);
for _ in 0..SEGMENTS - 10 {
shallow = tree!([shallow]);
}
let found = paths(&shallow);
assert_eq!(found.len(), 1);
assert!(found[0].0.ends_with("/0/a") && found[0].1 == 1);
}
#[test]
fn an_entry_of_a_list_of_ies_is_known_by_its_members() {
// An IE is written with a number or a name as its `id`: no type has a member of
// that name with such a value, nor one named as the members of an IE or of a
// transfer in a view.
for (name, members) in all_members() {
for (member, shape, _) in members {
assert!(
!["octets", "decoded", "extensionValue"].contains(member),
"{name}.{member}"
);
if *member == "id" {
assert!(
matches!(TABLES.resolved(*shape), Shape::Named(_)),
"{name}.id"
);
}
}
}
}
}
/// The identifier and the name of each IE that an object set of the ASN.1 has, those of
/// the extension containers included. An identifier that the ASN.1 defines and that no
/// object set uses has no type, and is not among them.
pub fn ie_names() -> &'static [(u16, &'static str)] {
crate::inspect_registry::IE_NAMES
}
/// The messages, each as the `direction` and the `procedure_code` that a view has for it
/// and the name that ASN.1 gives it.
pub fn message_names() -> impl Iterator<Item = (&'static str, u8, &'static str)> {
let messages = crate::inspect_registry::MESSAGES.iter();
messages.map(|(direction, code, name, ..)| (*direction, *code, *name))
}
/// The name that ASN.1 gives the message of a PDU, as `InitialContextSetupResponse`; `None`
/// for a procedure that the specification does not have in that direction.
pub fn message_name(pdu: &crate::$bindings::$pdu<'_>) -> Option<&'static str> {
let (direction, code) = (pdu.direction(), pdu.procedure_code());
message_names()
.find(|(of, procedure, _)| *procedure == code && *of == direction)
.map(|(.., name)| name)
}
/// The `direction` and the `procedure_code` of the message that `name` names: a name is
/// its letters and its digits, whatever their case and whatever is between them.
pub fn message_named(name: &str) -> Option<(&'static str, u8)> {
message_names()
.find(|(.., known)| same_name(known, name))
.map(|(direction, code, _)| (direction, code))
}
/// The IEs that the message `name` can have, as its ASN.1 object set lists them: the
/// identifier of each and whether its presence is mandatory, in the order of the set. The
/// name is the one that ASN.1 gives the message, taken as [`message_named`] takes it.
/// `None` for a name that is no message; a message without an object set of IEs, as a
/// private message, has none.
pub fn message_ies(name: &str) -> Option<&'static [(u16, bool)]> {
let mut messages = crate::inspect_registry::MESSAGES.iter();
let message = messages.find(|(_, _, known, ..)| same_name(known, name));
message.map(|(_, _, _, _, ies, _)| *ies)
}
/// The criticality that the ASN.1 object set of the message `name` assigns to each of its
/// IEs: the identifier of each and `"reject"`, `"ignore"` or `"notify"`, in the order of
/// the set, which is that of [`message_ies`]. The name is the one that ASN.1 gives the
/// message, taken as [`message_named`] takes it. `None` for a name that is no message; a
/// message without an object set of IEs, as a private message, has none.
pub fn message_ie_criticalities(name: &str) -> Option<&'static [(u16, &'static str)]> {
let mut messages = crate::inspect_registry::MESSAGE_IE_CRITICALITIES.iter();
let message = messages.find(|(known, _)| same_name(known, name));
message.map(|(_, ies)| *ies)
}
/// The criticality that ASN.1 assigns to the procedure of the message `name`, which a PDU
/// has beside its procedure code: `"reject"`, `"ignore"` or `"notify"`. The name is the
/// one that ASN.1 gives the message, taken as [`message_named`] takes it. `None` for a
/// name that is no message.
pub fn message_criticality(name: &str) -> Option<&'static str> {
let (_, code) = message_named(name)?;
let mut procedures = crate::inspect_registry::PROCEDURE_CRITICALITIES.iter();
let procedure = procedures.find(|(known, _)| *known == code);
procedure.map(|(_, criticality)| *criticality)
}
$(#[$item_doc])*
pub fn item_criticality(id: u16) -> Option<&'static str> {
let mut items = crate::inspect_registry::ITEM_CRITICALITIES.iter();
let item = items.find(|(known, _)| *known == id);
item.map(|(_, criticality)| *criticality)
}
/// The functions of the message of a procedure that the specification does not have in
/// that direction, which is read and written as what every message of the protocol but
/// the private one is: a container of protocol IEs and nothing else.
pub(crate) fn any_message() -> Typed {
$crate::__typed!($any)
}
/// The value of the type `T` that the octets `raw` are, in its JSON form, and whether it
/// encodes back to them: it does not when they hold what a later version of the type
/// adds, which the value has no place for, or are in another form than the canonical
/// one. `name` is the name of the type in the bindings.
pub(crate) fn __decode_typed<'a, T>(
name: &'static str,
raw: &'a [u8],
) -> Result<$crate::inspect::Decoded, Error>
where
T: Decode<'a> + Encode + Tree,
{
let read: Result<T, Error> = $crate::decode(raw);
let shown = read.map(|value| {
let exact = $crate::encode(&value).is_ok_and(|octets| octets == raw);
// Octets that do not come back are read again by the reader of the one encoding,
// which says what of them is in another form.
let refused = match exact {
true => None,
false => $crate::decode_strict::<T>(raw).err(),
};
(value.to_tree(), exact, refused)
});
// The oracle writes the form of the JSON Encoding Rules, which a tree has but for the
// well-known types.
#[cfg($hooks)]
if let Some(observer) = OBSERVER.get() {
let jer = |(value, ..): &$crate::inspect::Decoded| TABLES.jer(value.clone(), name);
observer(name, raw, &shown.as_ref().map(jer).map_err(Clone::clone));
}
let _ = name;
shown
}
/// The octets of the value of the type `T` that `value` writes in its JSON form.
pub(crate) fn __encode_typed<T>(value: &Value) -> Result<Vec<u8>, Error>
where
T: Encode + Tree + AsnType,
{
let typed: T = json::from_value(value)?;
// A value that is read and does not encode is outside a constraint of its type.
$crate::encode(&typed).map_err(|error| error.outside_its_type(<T as AsnType>::NAME))
}
#[cfg($hooks)]
thread_local! {
/// What is told of each value that this thread decodes as a type of the registry:
/// the name of the type in the bindings, the octets, and the JSON form of the
/// value or why the octets are none. The tests of the oracle and the fuzz targets
/// compare them with what another codec makes of the same octets.
pub(crate) static OBSERVER: core::cell::Cell<Option<Observer>> =
const { core::cell::Cell::new(None) };
}
/// What observes the values that are decoded as a type of the registry.
#[cfg($hooks)]
pub(crate) type Observer = fn(&'static str, &[u8], &Result<Value, Error>);
/// How a tree shows the values of a well-known type.
pub(crate) use $crate::inspect::Form;
/// The functions of a type of the registry.
pub(crate) use $crate::inspect::Typed;
/// Decode, in `view`, the octets that `path` takes as a type that they may carry: those
/// of an IE whose content the specification gives by reference, as the transparent
/// container of a handover, where the segment after the IE or its `value` is the name
/// that ASN.1 gives one of these types. See
/// [Octets that carry a type](self#octets-that-carry-a-type).
///
/// The IE then has, in place of its octets, the transfer of that type under that name,
/// which [`select`], [`paths`] and the edits go into and
/// `from_view` of the PDU takes back: its decoded value beside the octets, or
/// why they are not of that type. Nothing in a message says which type octets carry, so
/// `to_view` leaves them as they are, and nothing but this function
/// decodes them: on octets that were not opened, a path through the name of a type is an
/// error that says so.
///
/// A path that goes through no such name changes nothing. A name that the type of a
/// value cannot have is an error, and the view is then as it was.
pub fn open(view: &mut Value, path: &str) -> Result<(), Error> {
shared::open(&TABLES, view, path)
}
/// The view, as `to_view` writes views, of a view whose values another implementation
/// wrote in the JSON Encoding Rules of ITU-T X.697, as pycrate writes them: `from_view` of
/// the PDU reads what it gives, where it would refuse a string of decimal digits that is
/// octets in those rules and a number or digits in a view. The tests read the values of
/// their reference with it.
#[doc(hidden)]
pub fn __view_of_jer(view: &Value) -> Result<Value, Error> {
shared::view_of_jer(&TABLES, view)
}
};
}
/// The module `hooks` of a protocol crate, which its fuzz targets and the tests of its
/// oracle take from the inside of the crate: the protocol calls it at the root of the
/// crate, with the configuration name that it has the module with, the module of its
/// bindings and the types of its PDU.
#[doc(hidden)]
#[macro_export]
// A protocol calls it in its crate, whose items `crate::` names.
#[allow(clippy::crate_in_macro_def)]
macro_rules! __hooks {
(cfg $cfg:ident, bindings $bindings:ident, pdu $pdu:ident, owned $owned:ident) => {
#[cfg($cfg)]
#[doc(hidden)]
#[doc = concat!(
"What the fuzz targets of `fuzz/` and the tests of `oracle/` take from the inside of \
the crate. It is compiled with `--cfg ", stringify!($cfg), "`, which those two \
packages are built with, and is no part of the crate without: the fuzz build of \
another program, for which cargo-fuzz sets `fuzzing`, has the crate as every other \
build has it.\n\n\
Both compare what the crate does with what the codec of rasn does, which the oracle \
has. The oracle is no dependency of the crate, so the comparison is in them: this \
module gives them what needs the type of a message, which no function gives from a \
PDU, each value that a view decodes, and the two ways in which the crate writes a \
message."
)]
pub mod hooks {
use crate::Error;
use crate::$bindings::{ProtocolIEContainer, $pdu};
/// The bindings, by a name that is the same for both protocols.
pub use crate::$bindings as bindings;
/// A PDU that owns its octets, by a name that is the same for both protocols.
pub use crate::$bindings::$owned as Pdu;
/// A PDU that borrows its message from the octets that it is decoded from.
pub use crate::$bindings::$pdu as BorrowedPdu;
/// The name of the type of a PDU in the bindings.
pub const PDU_TYPE: &str = stringify!($pdu);
/// The root of the paths of a view, which the module of the bindings names.
pub const ROOT: &str = concat!("/", stringify!($bindings));
/// The octets of the message of `pdu`.
pub fn message_octets<'p>(pdu: &'p $pdu<'_>) -> &'p [u8] {
pdu.value()
}
/// The octets of the message that has these protocol IEs, as `set_protocol_ies`
/// writes them, octet by octet, and as the codec of the type writes them.
pub fn message_octets_both_ways(
ies: &ProtocolIEContainer<'_>,
) -> (Result<Vec<u8>, Error>, Result<Vec<u8>, Error>) {
let as_its_type = $crate::protocol::message_octets_as_its_type(&ies.0);
($crate::protocol::message_octets(&ies.0), as_its_type)
}
/// The message of `pdu` as its type: the name of the type in the bindings, and the
/// octets that the message encodes to once `decode_value` has read it, or why it does
/// not decode. `None` when the protocol has no message for the PDU.
pub fn message_read_and_written(
pdu: &$pdu<'_>,
) -> Option<(&'static str, Result<Vec<u8>, Error>)> {
let name =
crate::registry::message_type_name(pdu.direction(), pdu.procedure_code())?;
Some((name, crate::registry::message_read_and_written(pdu)?))
}
/// What is told of each value that a view decodes.
#[cfg(feature = "inspect")]
pub type Observer = fn(&'static str, &[u8], &Result<$crate::json::Value, Error>);
/// From here on, `observer` is told of each value that a view of this thread decodes
/// as a type of the registry: the name of the type in the bindings, the octets, and
/// the value in the form of the JSON Encoding Rules, which the oracle writes, or why
/// the octets are none. `None` ends it.
#[cfg(feature = "inspect")]
pub fn observe(observer: Option<Observer>) {
crate::inspect::OBSERVER.set(observer);
}
}
};
}