bsn1/
lib.rs

1// Copyright 2021-2024 Shin Yoshida
2//
3// "GPL-3.0-only"
4//
5// This is part of BSN1
6//
7// BSN1 is free software: you can redistribute it and/or modify it under the terms of the
8// GNU General Public License as published by the Free Software Foundation, version 3.
9//
10// BSN1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
11// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License along with this program. If
15// not, see <https://www.gnu.org/licenses/>.
16
17#![deny(missing_docs)]
18#![doc = include_str!("../README.md")]
19
20mod ber;
21mod ber_ref;
22mod buffer;
23mod contents;
24mod contents_ref;
25mod der;
26mod der_ref;
27mod id_tags;
28mod identifier;
29mod identifier_ref;
30mod length;
31mod length_buffer;
32mod misc;
33
34pub use ber::Ber;
35pub use ber_ref::BerRef;
36pub use buffer::Buffer;
37pub use contents::Contents;
38pub use contents_ref::ContentsRef;
39pub use der::Der;
40pub use der_ref::DerRef;
41pub use id_tags::{ClassTag, PCTag, TagNumber};
42pub use identifier::Id;
43pub use identifier_ref::IdRef;
44pub use length::Length;
45use length_buffer::LengthBuffer;
46use std::fmt;
47
48/// Errors for this crate.
49#[derive(Debug)]
50#[non_exhaustive]
51pub enum Error {
52    /// The bytes finish before the last octet.
53    UnterminatedBytes,
54    /// The bytes include some redundant octet(s).
55    /// ('ASN.1' does not allow such bytes.)
56    RedundantBytes,
57    /// Over flow is occurred to parse bytes as a number.
58    OverFlow,
59    /// 'Indefinite length' is used where not allowed.
60    /// (It is only for BER of some type, but not for DER, nor for CER.)
61    IndefiniteLength,
62    /// The contents of 'EOC' of the 'Indefinite Length BER' must be empty.
63    BadEoc,
64    /// The contents include (an) invalid octet(s) at the end.
65    ExtraContentsOctet,
66    /// The identifier does not match to that of data type when deserialized.
67    UnmatchedId,
68    /// Invarid as UTF-8.
69    InvalidUtf8,
70    /// The contents of DER BOOLEAN must be 0x00 or 0xFF.
71    InvalidDerBooleanContents,
72    /// The key-value pair is invalid.
73    InvalidKeyValuePair,
74    /// IO Error for serialization/deserialization.
75    ///
76    /// Note that this error cannot be compared with others.
77    /// `PartialEq::eq` always returns `false` for this error.
78    Io(std::io::Error),
79    /// Wrapper of [`anyhow::Error`].
80    ///
81    /// Note that this error cannot be compared with others.
82    /// `PartialEq::eq` always returns `false` for this error.
83    Anyhow(anyhow::Error),
84}
85
86impl fmt::Display for Error {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        match self {
89            Self::UnterminatedBytes => f.write_str("The bytes finish before the last octet."),
90            Self::RedundantBytes => f.write_str("The bytes include some redundant octet(s)."),
91            Self::OverFlow => f.write_str("Over flow is occurred to parse bytes as a number."),
92            Self::IndefiniteLength => f.write_str("'Indefinite Length' is used where not allowed."),
93            Self::BadEoc => f.write_str("'Indefinite Length BER' includes a bad 'EOC.'"),
94            Self::ExtraContentsOctet => {
95                f.write_str("Contents include (an) invlid octet(s) at the end.")
96            }
97            Self::UnmatchedId => f.write_str("The identifier does not match to that of data type."),
98            Self::InvalidUtf8 => f.write_str("Invalid as UTF-8."),
99            Self::InvalidDerBooleanContents => {
100                f.write_str("The contents of DER BOOLEAN must be 0x00 or 0xFF.")
101            }
102            Self::InvalidKeyValuePair => f.write_str("SEQUENCE of key-value pair is required."),
103            Self::Io(err) => err.fmt(f),
104            Self::Anyhow(err) => err.fmt(f),
105        }
106    }
107}
108
109impl std::error::Error for Error {}
110
111impl PartialEq for Error {
112    fn eq(&self, other: &Self) -> bool {
113        match self {
114            Self::UnterminatedBytes => matches!(other, Self::UnterminatedBytes),
115            Self::RedundantBytes => matches!(other, Self::RedundantBytes),
116            Self::OverFlow => matches!(other, Self::OverFlow),
117            Self::IndefiniteLength => matches!(other, Self::IndefiniteLength),
118            Self::BadEoc => matches!(other, Self::BadEoc),
119            Self::ExtraContentsOctet => matches!(other, Self::ExtraContentsOctet),
120            Self::UnmatchedId => matches!(other, Self::UnmatchedId),
121            Self::InvalidUtf8 => matches!(other, Self::InvalidUtf8),
122            Self::InvalidDerBooleanContents => matches!(other, Self::InvalidDerBooleanContents),
123            Self::InvalidKeyValuePair => matches!(other, Self::InvalidKeyValuePair),
124            Self::Io(_) => false,
125            Self::Anyhow(_) => false,
126        }
127    }
128}
129
130impl From<std::io::Error> for Error {
131    fn from(err: std::io::Error) -> Self {
132        Self::Io(err)
133    }
134}
135
136impl From<anyhow::Error> for Error {
137    fn from(err: anyhow::Error) -> Self {
138        Self::Anyhow(err)
139    }
140}
141
142impl Error {
143    /// Consumes `self`, converting it into an [`anyhow::Error`].
144    ///
145    /// If `self` matches `Error::Anyhow`, returns the inner value;
146    /// otherwise, wraps `self` and returns.
147    pub fn into_anyhow(self) -> anyhow::Error {
148        match self {
149            Self::Anyhow(err) => err,
150            _ => anyhow::Error::new(self),
151        }
152    }
153
154    /// Returns a reference to the inner [`anyhow::Error`] if `self` is `Error::Anyhow`;
155    /// otherwise, returns `None`.
156    pub fn as_anyhow(&self) -> Option<&anyhow::Error> {
157        match self {
158            Self::Anyhow(err) => Some(err),
159            _ => None,
160        }
161    }
162
163    /// Consumes `self`, wrapping it with `context`.
164    pub fn context<C>(self, context: C) -> Self
165    where
166        C: fmt::Display + Send + Sync + 'static,
167    {
168        self.into_anyhow().context(context).into()
169    }
170
171    /// If `self` matches `Error::Anyhow`, returns a reference to the first `Self` type error
172    /// in the chain; otherwise, returns `self`.
173    pub fn root_cause(&self) -> &Self {
174        match self {
175            Self::Anyhow(err) => {
176                let mut ret = self;
177                for cause in err.chain() {
178                    if let Some(e) = cause.downcast_ref::<Self>() {
179                        ret = e;
180                    } else {
181                        break;
182                    }
183                }
184                return ret;
185            }
186            _ => self,
187        }
188    }
189}