Skip to main content

wildboar_asn1/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(non_camel_case_types)]
3// #![allow(non_snake_case)]
4#![allow(non_upper_case_globals)]
5
6pub mod bitstring;
7pub mod constants;
8pub mod construction;
9pub mod date;
10pub mod datetime;
11pub mod display;
12pub mod duration;
13pub mod error;
14pub mod external;
15pub mod gentime;
16pub mod oid;
17pub mod roid;
18pub mod strings;
19pub mod tag;
20pub mod time_of_day;
21pub mod utctime;
22pub mod utils;
23
24pub use bitstring::*;
25pub use constants::*;
26pub use construction::*;
27pub use date::*;
28pub use datetime::*;
29pub use display::*;
30pub use duration::*;
31pub use error::*;
32pub use external::*;
33pub use gentime::*;
34pub use oid::*;
35pub use roid::*;
36pub use strings::*;
37pub use tag::*;
38pub use time_of_day::*;
39pub use utctime::*;
40pub use utils::*;
41
42/// Alias to make `true` look like ASN.1
43pub const TRUE: bool = true;
44
45/// Alias to make `false` look like ASN.1
46pub const FALSE: bool = false;
47
48/// How this library represents borrowed "bytes"
49pub type ByteSlice<'a> = &'a [u8];
50
51/// An alias to make `Option<>` look more like ASN.1.
52pub type OPTIONAL<T> = Option<T>;
53
54/// Coordinated Universal Time (UTC) Offset
55#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
56pub struct UTCOffset {
57    /// The hour offset from Coordinated Universal Time (UTC)
58    /// This may be between -15 to +15 inclusively.
59    pub hour: i8,
60    /// The minute offset from Coordinated Universal Time (UTC)
61    pub minute: u8,
62}
63
64impl UTCOffset {
65
66    /// Construct a new Coordinated Universal Time (UTC) Offset
67    #[inline]
68    pub const fn new(hour: i8, minute: u8) -> Self {
69        UTCOffset { hour, minute }
70    }
71
72    /// Returns `true` if the Construct a new Coordinated Universal Time (UTC)
73    /// Offset is 0 hours and 0 minutes.
74    #[inline]
75    pub const fn is_zero(&self) -> bool {
76        self.hour == 0 && self.minute == 0
77    }
78
79    /// Construct a new zeroed Coordinated Universal Time (UTC) Offset
80    #[inline]
81    pub const fn utc() -> Self {
82        UTCOffset{ hour: 0, minute: 0 }
83    }
84}
85
86impl Default for UTCOffset {
87
88    /// Construct a new zeroed Coordinated Universal Time (UTC) Offset
89    #[inline]
90    fn default() -> Self {
91        UTCOffset::utc()
92    }
93}
94
95/// Decimal digits fractional part
96#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
97#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
98pub struct FractionalPart {
99    /// Number of digits of precision
100    pub number_of_digits: u8,
101
102    /// The fractional value that is to be converted to a decimal string and
103    /// left padded with zeroes until it is `number_of_digits` digits long to
104    /// produce the fractional part.
105    pub fractional_value: u32,
106}
107
108impl FractionalPart {
109
110    /// Construct a new [FractionalPart]
111    #[inline]
112    pub const fn new(number_of_digits: u8, fractional_value: u32) -> Self {
113        FractionalPart {
114            number_of_digits,
115            fractional_value,
116        }
117    }
118}
119
120// type END_OF_CONTENT = None;
121
122/// ASN.1 `BOOLEAN`
123pub type BOOLEAN = bool;
124
125/// ASN.1 `INTEGER`
126pub type INTEGER = Vec<u8>;
127
128/// Index into an ASN.1 `BIT STRING`
129pub type BIT_INDEX = usize;
130
131/// An ASN.1 `OCTET STRING`
132pub type OCTET_STRING = Vec<u8>;
133
134/// An ASN.1 `NULL` value
135pub type NULL = ();
136
137/// An arc within an ASN.1 `OBJECT IDENTIFIER` or `RELATIVE-OID`
138pub type OID_ARC = u32;
139
140/// ASN.1 `ObjectDescriptor`, which is defined as
141///
142/// ```asn1
143/// ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString
144/// ```
145///
146pub type ObjectDescriptor = GraphicString;
147
148/// ASN.1 `EXTERNAL`
149pub type EXTERNAL = crate::external::External;
150
151/// ASN.1 `REAL`
152pub type REAL = f64;
153
154/// ASN.1 `ENUMERATED`
155pub type ENUMERATED = i64;
156
157/// ASN.1 `EMBEDDED PDV`
158pub type EMBEDDED_PDV = crate::external::EmbeddedPDV;
159
160/// ASN.1 `UTF8String`
161pub type UTF8String = String;
162
163/// ASN.1 `TIME`
164pub type TIME = String;
165// type Reserved15 = None;
166
167/// ASN.1 `SEQUENCE`
168pub type SEQUENCE = Vec<ASN1Value>;
169
170/// ASN.1 `SEQUENCE OF`
171pub type SEQUENCE_OF<T> = Vec<T>;
172
173/// ASN.1 `SET`
174pub type SET = Vec<ASN1Value>;
175
176/// ASN.1 `SET OF`
177pub type SET_OF<T> = Vec<T>;
178
179/// ASN.1 `NumericString`
180pub type NumericString = String;
181
182/// ASN.1 `PrintableString`
183pub type PrintableString = String;
184
185/// ASN.1 `T61String` / `TeletexString`
186pub type T61String = Vec<u8>;
187
188/// ASN.1 `T61String` / `TeletexString`
189pub type TeletexString = T61String;
190
191/// ASN.1 `VideotexString`
192pub type VideotexString = Vec<u8>;
193
194/// ASN.1 `IA5String`
195pub type IA5String = String;
196
197/// ASN.1 `GraphicString`
198pub type GraphicString = String;
199
200/// ASN.1 `VisibleString`
201pub type VisibleString = String;
202
203/// ASN.1 `GeneralString`
204pub type GeneralString = String;
205
206/// ASN.1 `UniversalString`: Unicode code points encoded on four bytes each
207///
208/// Each quartet of bytes can represent all Unicode code points.
209///
210#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
211pub struct UniversalString(pub Vec<u32>);
212
213/// ASN.1 `CharacterString`
214///
215/// A completely unrestricted string type that can use the presentation layer
216/// to identify its syntax.
217pub type CHARACTER_STRING = crate::external::CharacterString;
218
219/// ASN.1 `BMPString`: Unicode code points encoded on two bytes each.
220///
221/// The `BMPString` type is capable of encoding the Basic Multilingual Plane
222/// from Unicode, which has the letters used by most languages.
223///
224/// See: <https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane>
225#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
226pub struct BMPString(pub Vec<u16>);
227
228/// ASN.1 `DURATION`
229///
230/// This is based off of the ISO 8601 duration syntax.
231///
232/// See: <https://en.wikipedia.org/wiki/ISO_8601#Durations>
233pub type DURATION = crate::duration::DURATION_EQUIVALENT;
234
235/// ASN.1 `OBJECT IDENTIFIER` Internationalized Resource Identifier (OID-IRI)
236pub type OID_IRI = String;
237/// ASN.1 `RELATIVE-OID` Internationalized Resource Identifier (Relative-OID-IRI)
238pub type RELATIVE_OID_IRI = String;
239
240/// ASN.1 `INSTANCE OF`
241///
242/// Definition:
243///
244/// ```asn1
245/// SEQUENCE {
246///     type-id     <DefinedObjectClass>.&id,
247///     value [0]   <DefinedObjectClass>.&Type
248/// }
249/// ```
250pub type INSTANCE_OF = crate::external::InstanceOf;
251
252/// ASN.1 `TYPE-IDENTIFIER`
253///
254/// Definition:
255///
256/// ```asn1
257/// TYPE-IDENTIFIER ::= CLASS {
258///     &id     OBJECT IDENTIFIER UNIQUE,
259///     &Type
260/// }
261/// WITH SYNTAX {
262///     &Type
263///     IDENTIFIED BY &id
264/// }
265/// ```
266///
267/// This class is defined as a "useful" information object class, and is
268/// available in any ASN.1 module without the necessity for importing it.
269#[derive(Debug, Clone, PartialEq, Eq, Hash)]
270pub struct TYPE_IDENTIFIER {
271    /// The `type-id` field.
272    pub id: OBJECT_IDENTIFIER,
273}
274
275/// ASN.1 value
276/// 
277/// This cannot implement `Eq` because (at least) the `RealValue` variant
278/// does not.
279#[derive(Debug, Clone, PartialEq)]
280pub enum ASN1Value {
281    // BuiltInValue
282    BitStringValue(crate::bitstring::BIT_STRING),
283    BooleanValue(BOOLEAN),
284    ChoiceValue(Box<ASN1Value>),
285    // ChoiceValue (&'a ChoiceValue<'a>),
286    EmbeddedPDVValue(EMBEDDED_PDV),
287    EnumeratedValue(ENUMERATED),
288    ExternalValue(EXTERNAL),
289    InstanceOfValue(INSTANCE_OF),
290    IntegerValue(INTEGER),
291    IRIValue(OID_IRI),
292    NullValue,
293    ObjectIdentifierValue(OBJECT_IDENTIFIER),
294    ObjectDescriptor(ObjectDescriptor),
295    OctetStringValue(OCTET_STRING),
296    RealValue(REAL),
297    RelativeIRIValue(RELATIVE_OID_IRI),
298    RelativeOIDValue(crate::roid::RELATIVE_OID),
299    SequenceValue(SEQUENCE),
300    SequenceOfValue(SEQUENCE_OF<ASN1Value>),
301    SetValue(SET),
302    SetOfValue(SET_OF<ASN1Value>),
303    // CharacterStringValue
304    UnrestrictedCharacterStringValue(CHARACTER_STRING),
305    // RestrictedCharacterStringType
306    BMPString(BMPString),
307    GeneralString(GeneralString),
308    GraphicString(GraphicString),
309    IA5String(IA5String),
310    ISO646String(VisibleString), // Same as VisibleString.
311    NumericString(NumericString),
312    PrintableString(PrintableString),
313    TeletexString(T61String), // Same as TeletexString.
314    T61String(T61String),
315    UniversalString(UniversalString),
316    UTF8String(UTF8String),
317    VideotexString(VideotexString),
318    VisibleString(VisibleString),
319    // PrefixedValue (&'a ASN1Value<'a>),
320    TaggedValue(TaggedASN1Value),
321    TimeValue(TIME),
322    UTCTime(crate::utctime::UTCTime),
323    GeneralizedTime(GeneralizedTime),
324    DATE(DATE),
325    TIME_OF_DAY(TIME_OF_DAY),
326    DATE_TIME(crate::datetime::DATE_TIME),
327    DURATION(DURATION),
328
329    /* This is a type that stores the value bytes of values that were encoded
330    with an implicit tag and decoded as ANY. Since we cannot know what the
331    actual encoded ASN.1 value was, we just have to store raw bytes. */
332    UnknownBytes(std::sync::Arc<Vec<u8>>),
333}
334
335impl ASN1Value {
336
337    /// Get the tag number of this ASN.1 value
338    pub fn tag_number (&self) -> TagNumber {
339        match self {
340            ASN1Value::BitStringValue(_) => UNIV_TAG_BIT_STRING,
341            ASN1Value::BooleanValue(_) => UNIV_TAG_BOOLEAN,
342            ASN1Value::ChoiceValue(v) => v.tag_number(),
343            ASN1Value::EmbeddedPDVValue(_) => UNIV_TAG_EMBEDDED_PDV,
344            ASN1Value::EnumeratedValue(_) => UNIV_TAG_ENUMERATED,
345            ASN1Value::ExternalValue(_) => UNIV_TAG_EXTERNAL,
346            ASN1Value::InstanceOfValue(_) => UNIV_TAG_INSTANCE_OF,
347            ASN1Value::IntegerValue(_) => UNIV_TAG_INTEGER,
348            ASN1Value::IRIValue(_) => UNIV_TAG_OID_IRI,
349            ASN1Value::NullValue => UNIV_TAG_NULL,
350            ASN1Value::ObjectIdentifierValue(_) => UNIV_TAG_OBJECT_IDENTIFIER,
351            ASN1Value::ObjectDescriptor(_) => UNIV_TAG_OBJECT_DESCRIPTOR,
352            ASN1Value::OctetStringValue(_) => UNIV_TAG_OCTET_STRING,
353            ASN1Value::RealValue(_) => UNIV_TAG_REAL,
354            ASN1Value::RelativeIRIValue(_) => UNIV_TAG_RELATIVE_OID_IRI,
355            ASN1Value::RelativeOIDValue(_) => UNIV_TAG_RELATIVE_OID,
356            ASN1Value::SequenceValue(_) => UNIV_TAG_SEQUENCE,
357            ASN1Value::SequenceOfValue(_) => UNIV_TAG_SEQUENCE_OF,
358            ASN1Value::SetValue(_) => UNIV_TAG_SET,
359            ASN1Value::SetOfValue(_) => UNIV_TAG_SET_OF,
360            ASN1Value::UnrestrictedCharacterStringValue(_) => UNIV_TAG_CHARACTER_STRING,
361            ASN1Value::BMPString(_) => UNIV_TAG_BMP_STRING,
362            ASN1Value::GeneralString(_) => UNIV_TAG_GENERAL_STRING,
363            ASN1Value::GraphicString(_) => UNIV_TAG_GRAPHIC_STRING,
364            ASN1Value::IA5String(_) => UNIV_TAG_IA5_STRING,
365            ASN1Value::ISO646String(_) => UNIV_TAG_VISIBLE_STRING,
366            ASN1Value::NumericString(_) => UNIV_TAG_NUMERIC_STRING,
367            ASN1Value::PrintableString(_) => UNIV_TAG_PRINTABLE_STRING,
368            ASN1Value::TeletexString(_) => UNIV_TAG_T61_STRING,
369            ASN1Value::T61String(_) => UNIV_TAG_T61_STRING,
370            ASN1Value::UniversalString(_) => UNIV_TAG_UNIVERSAL_STRING,
371            ASN1Value::UTF8String(_) => UNIV_TAG_UTF8_STRING,
372            ASN1Value::VideotexString(_) => UNIV_TAG_VIDEOTEX_STRING,
373            ASN1Value::VisibleString(_) => UNIV_TAG_VISIBLE_STRING,
374            ASN1Value::TaggedValue(v) => v.tag.tag_number,
375            ASN1Value::TimeValue(_) => UNIV_TAG_TIME,
376            ASN1Value::UTCTime(_) => UNIV_TAG_UTC_TIME,
377            ASN1Value::GeneralizedTime(_) => UNIV_TAG_GENERALIZED_TIME,
378            ASN1Value::DATE(_) => UNIV_TAG_DATE,
379            ASN1Value::TIME_OF_DAY(_) => UNIV_TAG_TIME_OF_DAY,
380            ASN1Value::DATE_TIME(_) => UNIV_TAG_DATE_TIME,
381            ASN1Value::DURATION(_) => UNIV_TAG_DURATION,
382            // Not sure what to do here.
383            ASN1Value::UnknownBytes(_) => UNIV_TAG_OCTET_STRING,
384        }
385    }
386
387    pub fn tag (&self) -> Tag {
388        match self {
389            ASN1Value::TaggedValue(v) => v.tag,
390            _ => Tag::new(TagClass::UNIVERSAL, self.tag_number()),
391        }
392    }
393
394}
395/// The `UNIVERSAL` tag number for `END-OF-CONTENT`
396pub const UNIV_TAG_END_OF_CONTENT: TagNumber = 0;
397/// The `UNIVERSAL` tag number for `BOOLEAN`
398pub const UNIV_TAG_BOOLEAN: TagNumber = 1;
399/// The `UNIVERSAL` tag number for `INTEGER`
400pub const UNIV_TAG_INTEGER: TagNumber = 2;
401/// The `UNIVERSAL` tag number for `BIT STRING`
402pub const UNIV_TAG_BIT_STRING: TagNumber = 3;
403/// The `UNIVERSAL` tag number for `OCTET STRING`
404pub const UNIV_TAG_OCTET_STRING: TagNumber = 4;
405/// The `UNIVERSAL` tag number for `NULL`
406pub const UNIV_TAG_NULL: TagNumber = 5;
407/// The `UNIVERSAL` tag number for `OBJECT IDENTIFIER`
408pub const UNIV_TAG_OBJECT_IDENTIFIER: TagNumber = 6;
409/// The `UNIVERSAL` tag number for `ObjectDescriptor`
410pub const UNIV_TAG_OBJECT_DESCRIPTOR: TagNumber = 7;
411/// The `UNIVERSAL` tag number for `EXTERNAL`
412pub const UNIV_TAG_EXTERNAL: TagNumber = 8;
413/// The `UNIVERSAL` tag number for `INSTANCE OF`
414pub const UNIV_TAG_INSTANCE_OF: TagNumber = UNIV_TAG_EXTERNAL;
415/// The `UNIVERSAL` tag number for `REAL`
416pub const UNIV_TAG_REAL: TagNumber = 9;
417/// The `UNIVERSAL` tag number for `ENUMERATED`
418pub const UNIV_TAG_ENUMERATED: TagNumber = 10;
419/// The `UNIVERSAL` tag number for `EMBEDDED PDV`
420pub const UNIV_TAG_EMBEDDED_PDV: TagNumber = 11;
421/// The `UNIVERSAL` tag number for `UTF8String`
422pub const UNIV_TAG_UTF8_STRING: TagNumber = 12;
423/// The `UNIVERSAL` tag number for `RELATIVE-OID`
424pub const UNIV_TAG_RELATIVE_OID: TagNumber = 13;
425/// The `UNIVERSAL` tag number for `TIME`
426pub const UNIV_TAG_TIME: TagNumber = 14;
427/// The reserved `UNIVERSAL` tag number 15
428pub const UNIV_TAG_RESERVED_15: TagNumber = 15;
429/// The `UNIVERSAL` tag number for `SEQUENCE`
430pub const UNIV_TAG_SEQUENCE: TagNumber = 16;
431/// The `UNIVERSAL` tag number for `SEQUENCE OF`
432pub const UNIV_TAG_SEQUENCE_OF: TagNumber = UNIV_TAG_SEQUENCE;
433/// The `UNIVERSAL` tag number for `SET`
434pub const UNIV_TAG_SET: TagNumber = 17;
435/// The `UNIVERSAL` tag number for `SET OF`
436pub const UNIV_TAG_SET_OF: TagNumber = UNIV_TAG_SET;
437/// The `UNIVERSAL` tag number for `NumericString`
438pub const UNIV_TAG_NUMERIC_STRING: TagNumber = 18;
439/// The `UNIVERSAL` tag number for `PrintableString`
440pub const UNIV_TAG_PRINTABLE_STRING: TagNumber = 19;
441/// The `UNIVERSAL` tag number for `T61String` / `TeletexString`
442pub const UNIV_TAG_T61_STRING: TagNumber = 20;
443/// The `UNIVERSAL` tag number for `VideotexString`
444pub const UNIV_TAG_VIDEOTEX_STRING: TagNumber = 21;
445/// The `UNIVERSAL` tag number for `IA5String`
446pub const UNIV_TAG_IA5_STRING: TagNumber = 22;
447/// The `UNIVERSAL` tag number for `UTCTime`
448pub const UNIV_TAG_UTC_TIME: TagNumber = 23;
449/// The `UNIVERSAL` tag number for `GeneralizedTime`
450pub const UNIV_TAG_GENERALIZED_TIME: TagNumber = 24;
451/// The `UNIVERSAL` tag number for `GraphicString`
452pub const UNIV_TAG_GRAPHIC_STRING: TagNumber = 25;
453/// The `UNIVERSAL` tag number for `VisibleString`
454pub const UNIV_TAG_VISIBLE_STRING: TagNumber = 26;
455/// The `UNIVERSAL` tag number for `GeneralString`
456pub const UNIV_TAG_GENERAL_STRING: TagNumber = 27;
457/// The `UNIVERSAL` tag number for `UniversalString`
458pub const UNIV_TAG_UNIVERSAL_STRING: TagNumber = 28;
459/// The `UNIVERSAL` tag number for `CharacterString`
460pub const UNIV_TAG_CHARACTER_STRING: TagNumber = 29;
461/// The `UNIVERSAL` tag number for `BMPString`
462pub const UNIV_TAG_BMP_STRING: TagNumber = 30;
463/// The `UNIVERSAL` tag number for `DATE`
464pub const UNIV_TAG_DATE: TagNumber = 31;
465/// The `UNIVERSAL` tag number for `TIME-OF-DAY`
466pub const UNIV_TAG_TIME_OF_DAY: TagNumber = 32;
467/// The `UNIVERSAL` tag number for `DATE-TIME`
468pub const UNIV_TAG_DATE_TIME: TagNumber = 33;
469/// The `UNIVERSAL` tag number for `DURATION`
470pub const UNIV_TAG_DURATION: TagNumber = 34;
471/// The `UNIVERSAL` tag number for `OID-IRI`
472pub const UNIV_TAG_OID_IRI: TagNumber = 35;
473/// The `UNIVERSAL` tag number for `RELATIVE-OID-IRI`
474pub const UNIV_TAG_RELATIVE_OID_IRI: TagNumber = 36;
475
476/// An ASN.1 Codec
477pub trait ASN1Codec {
478
479    /// Get an `OBJECT IDENTIFIER` representing this codec as a transfer syntax
480    fn transfer_syntax_oid (&self) -> OBJECT_IDENTIFIER;
481
482    /// Get an OID-IRI representing this codec as a transfer syntax
483    fn transfer_syntax_oid_iri (&self) -> Option<OID_IRI> {
484        None
485    }
486
487}
488
489/// Something that can be converted into an ISO 8601 Timestamp
490pub trait ISO8601Timestampable {
491
492    /// Convert this into an ISO 8601 Timestamp
493    fn to_iso_8601_string (&self) -> String;
494
495}
496
497/// Trait for a type whose X.690 content octets can be validated in such a way
498/// that holds true for all X.690 codecs.
499///
500/// These are:
501///
502/// - The Basic Encoding Rules (BER)
503/// - The Distinguished Encoding Rules (DER)
504/// - The Canonical Encoding Rules (CER)
505///
506/// These functions are often useful for other codecs, such as the Packed
507/// Encoding Rules (PER) or the Octet Encoding Rules (OER).
508pub trait X690Validate {
509
510    /// Validate that the `content_octets` are a valid X.690 encoding of this
511    /// data type.
512    fn validate_x690_encoding (content_octets: &[u8]) -> ASN1Result<()>;
513
514}
515
516/// A Named Type, such as would appear in the component type lists in a
517/// `SET` or `SEQUENCE`
518#[derive(Debug, Clone)]
519pub struct NamedType <'a, Type = ASN1Value> {
520
521    /// The identifier, such as `subjectPublicKeyInfo`
522    pub identifier: &'a str,
523
524    /// The value
525    pub value: Type,
526}
527
528/// Anything that, when encoded as the content octets ("value") of an X.690
529/// Tag-Length-Value (TLV), will be encoded on a number of octets that can be
530/// trivially calculated, and does not vary with the choice of concrete syntax
531/// (BER, CER, or DER). This is so a codec can know in advance how many bytes
532/// a value will take up and pre-allocate them.
533///
534/// `OBJECT IDENTIFIER` values are an example: they are encoded the same way
535/// for BER, CER, and DER, and it isn't too hard to calculate how many bytes
536/// they will take up in advance (in this library's implementation, there is
537/// _no_ calculation that needs to be done: just reading a `.len()`). An example
538/// of a type that MUST NOT implement this trait would be a `BIT STRING` because
539/// it would have a different length if encoded using DER or CER. All of the
540/// context-switching types (e.g. `EXTERNAL`, `EMBEDDED PDV`, `CharacterString`)
541/// are also transitively disqualified for this reason and more.
542///
543pub trait X690KnownSize {
544
545    /// Get the size of the content octets ("value") of an X.690
546    /// Tag-Length-Value (TLV) encoding when this value is encoded.
547    fn x690_size (&self) -> usize;
548
549}
550
551/// Create an `OCTET STRING`
552///
553/// This is really just an alias for vec![], but it is defined for future-proofing.
554#[macro_export]
555macro_rules! octs {
556    () => {
557        std::vec![]
558    };
559    ( $( $x:expr ),+ ) => {
560        std::vec![$($x,)*]
561    };
562}
563
564
565#[cfg(test)]
566mod tests {
567
568    #[test]
569    fn test_octs_macro () {
570        let octets = octs!(1,3,6,4,1);
571        assert_eq!(octets.len(), 5);
572    }
573
574}