1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-11-25 08:31:00 +00:00

improvements to code

This commit is contained in:
Luro02 2020-02-10 13:21:48 +01:00
parent e6f5091f1b
commit 3a388e3985
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
22 changed files with 132 additions and 100 deletions

View file

@ -14,7 +14,7 @@
missing_docs,
missing_copy_implementations,
missing_debug_implementations,
trivial_casts, // TODO (needed?)
trivial_casts,
trivial_numeric_casts
)]
//! [HLS] m3u8 parser/generator.

View file

@ -391,6 +391,7 @@ fn parse_media_playlist(
segment = MediaSegment::builder();
has_partial_segment = false;
}
_ => {}
}
}

View file

@ -12,10 +12,10 @@ use crate::{Error, RequiredVersion};
/// # [4.4.5.1. EXT-X-MEDIA]
///
/// The [`ExtXMedia`] tag is used to relate [`Media Playlist`]s,
/// that contain alternative Renditions of the same content.
/// that contain alternative renditions of the same content.
///
/// For example, three [`ExtXMedia`] tags can be used to identify audio-only
/// [`Media Playlist`]s, that contain English, French, and Spanish Renditions
/// [`Media Playlist`]s, that contain English, French, and Spanish renditions
/// of the same presentation. Or, two [`ExtXMedia`] tags can be used to
/// identify video-only [`Media Playlist`]s that show two different camera
/// angles.
@ -35,7 +35,7 @@ pub struct ExtXMedia {
/// This attribute is **required**.
#[shorthand(enable(copy))]
media_type: MediaType,
/// The `URI` that identifies the [`Media Playlist`].
/// An `URI` to a [`Media Playlist`].
///
/// # Note
///
@ -184,14 +184,18 @@ impl ExtXMedia {
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA:";
/// Makes a new [`ExtXMedia`] tag.
pub fn new<T: ToString>(media_type: MediaType, group_id: T, name: T) -> Self {
pub fn new<T, K>(media_type: MediaType, group_id: T, name: K) -> Self
where
T: Into<String>,
K: Into<String>,
{
Self {
media_type,
uri: None,
group_id: group_id.to_string(),
group_id: group_id.into(),
language: None,
assoc_language: None,
name: name.to_string(),
name: name.into(),
is_default: false,
is_autoselect: false,
is_forced: false,

View file

@ -13,7 +13,7 @@ use crate::{Error, RequiredVersion};
#[derive(Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)]
pub enum SessionData {
/// A String, that contains the data identified by
/// [`data_id`].
/// [`data_id`](ExtXSessionData::data_id).
/// If a [`language`] is specified, the value
/// should contain a human-readable string written in the specified
/// language.
@ -51,7 +51,7 @@ pub struct ExtXSessionData {
///
/// [reverse DNS]: https://en.wikipedia.org/wiki/Reverse_domain_name_notation
data_id: String,
/// The data associated with the [`data_id`].
/// The data associated with the [`data_id`](ExtXSessionData::data_id).
/// For more information look [`here`](SessionData).
///
/// # Note
@ -70,6 +70,7 @@ impl ExtXSessionData {
/// Makes a new [`ExtXSessionData`] tag.
///
/// # Example
///
/// ```
/// use hls_m3u8::tags::{ExtXSessionData, SessionData};
///
@ -78,9 +79,9 @@ impl ExtXSessionData {
/// SessionData::Uri("https://www.example.com/".to_string()),
/// );
/// ```
pub fn new<T: ToString>(data_id: T, data: SessionData) -> Self {
pub fn new<T: Into<String>>(data_id: T, data: SessionData) -> Self {
Self {
data_id: data_id.to_string(),
data_id: data_id.into(),
data,
language: None,
}
@ -89,6 +90,7 @@ impl ExtXSessionData {
/// Returns a new Builder for [`ExtXSessionData`].
///
/// # Example
///
/// ```
/// use hls_m3u8::tags::{ExtXSessionData, SessionData};
///
@ -123,11 +125,15 @@ impl ExtXSessionData {
/// "english",
/// );
/// ```
pub fn with_language<T: ToString>(data_id: T, data: SessionData, language: T) -> Self {
pub fn with_language<T, K>(data_id: T, data: SessionData, language: K) -> Self
where
T: Into<String>,
K: Into<String>,
{
Self {
data_id: data_id.to_string(),
data_id: data_id.into(),
data,
language: Some(language.to_string()),
language: Some(language.into()),
}
}
}

View file

@ -40,7 +40,7 @@ impl ExtXSessionKey {
///
/// let session_key = ExtXSessionKey::new(EncryptionMethod::Aes128, "https://www.example.com/");
/// ```
pub fn new<T: ToString>(method: EncryptionMethod, uri: T) -> Self {
pub fn new<T: Into<String>>(method: EncryptionMethod, uri: T) -> Self {
if method == EncryptionMethod::None {
panic!("The EncryptionMethod is not allowed to be None");
}
@ -49,8 +49,8 @@ impl ExtXSessionKey {
}
}
/// This tag requires the version returned by
/// [`DecryptionKey::required_version`].
/// This tag requires the same [`ProtocolVersion`] that is returned by
/// `DecryptionKey::required_version`.
impl RequiredVersion for ExtXSessionKey {
fn required_version(&self) -> ProtocolVersion { self.0.required_version() }
}

View file

@ -1,6 +1,8 @@
use std::fmt;
use std::str::FromStr;
use shorthand::ShortHand;
use crate::types::ProtocolVersion;
use crate::utils::tag;
use crate::RequiredVersion;
@ -15,22 +17,9 @@ use crate::RequiredVersion;
/// [`Media Playlist`]: crate::MediaPlaylist
/// [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]:
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.3
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ExtXDiscontinuitySequence(u64);
impl ExtXDiscontinuitySequence {
pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:";
/// Makes a new [ExtXDiscontinuitySequence] tag.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
/// ```
pub const fn new(seq_num: u64) -> Self { Self(seq_num) }
#[derive(ShortHand, Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[shorthand(enable(must_use))]
pub struct ExtXDiscontinuitySequence {
/// Returns the discontinuity sequence number of
/// the first media segment that appears in the associated playlist.
///
@ -38,27 +27,26 @@ impl ExtXDiscontinuitySequence {
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
///
/// assert_eq!(discontinuity_sequence.seq_num(), 5);
/// ```
pub const fn seq_num(self) -> u64 { self.0 }
/// Sets the sequence number.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let mut discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
///
/// discontinuity_sequence.set_seq_num(10);
/// assert_eq!(discontinuity_sequence.seq_num(), 10);
/// ```
pub fn set_seq_num(&mut self, value: u64) -> &mut Self {
self.0 = value;
self
}
seq_num: u64,
}
impl ExtXDiscontinuitySequence {
pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:";
/// Makes a new [`ExtXDiscontinuitySequence`] tag.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
/// ```
pub const fn new(seq_num: u64) -> Self { Self { seq_num } }
}
/// This tag requires [`ProtocolVersion::V1`].
@ -69,7 +57,7 @@ impl RequiredVersion for ExtXDiscontinuitySequence {
impl fmt::Display for ExtXDiscontinuitySequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//
write!(f, "{}{}", Self::PREFIX, self.0)
write!(f, "{}{}", Self::PREFIX, self.seq_num)
}
}

View file

@ -121,7 +121,7 @@ pub struct ExtXDateRange {
impl ExtXDateRangeBuilder {
/// Inserts a key value pair.
pub fn insert_client_attribute<K: ToString, V: Into<Value>>(
pub fn insert_client_attribute<K: Into<String>, V: Into<Value>>(
&mut self,
key: K,
value: V,
@ -131,7 +131,7 @@ impl ExtXDateRangeBuilder {
}
if let Some(client_attributes) = &mut self.client_attributes {
client_attributes.insert(key.to_string(), value.into());
client_attributes.insert(key.into(), value.into());
} else {
unreachable!();
}
@ -160,9 +160,9 @@ impl ExtXDateRange {
/// .and_hms_milli(14, 54, 23, 31),
/// );
/// ```
pub fn new<T: ToString>(id: T, start_date: DateTime<FixedOffset>) -> Self {
pub fn new<T: Into<String>>(id: T, start_date: DateTime<FixedOffset>) -> Self {
Self {
id: id.to_string(),
id: id.into(),
class: None,
start_date,
end_date: None,
@ -281,7 +281,7 @@ impl fmt::Display for ExtXDateRange {
write!(
f,
",END-DATE={}",
quote(value.to_rfc3339_opts(SecondsFormat::AutoSi, true))
quote(&value.to_rfc3339_opts(SecondsFormat::AutoSi, true))
)?;
}

View file

@ -49,10 +49,10 @@ impl ExtInf {
///
/// let ext_inf = ExtInf::with_title(Duration::from_secs(5), "title");
/// ```
pub fn with_title<T: ToString>(duration: Duration, title: T) -> Self {
pub fn with_title<T: Into<String>>(duration: Duration, title: T) -> Self {
Self {
duration,
title: Some(title.to_string()),
title: Some(title.into()),
}
}
@ -156,9 +156,9 @@ impl FromStr for ExtInf {
let duration = Duration::from_secs_f64(input.next().unwrap().parse()?);
let title = input
.next()
.map(|value| value.trim())
.map(str::trim)
.filter(|value| !value.is_empty())
.map(|value| value.to_string());
.map(ToString::to_string);
Ok(Self { duration, title })
}

View file

@ -45,7 +45,7 @@ impl ExtXKey {
/// "#EXT-X-KEY:METHOD=AES-128,URI=\"https://www.example.com/\""
/// );
/// ```
pub fn new<T: ToString>(method: EncryptionMethod, uri: T) -> Self {
pub fn new<T: Into<String>>(method: EncryptionMethod, uri: T) -> Self {
Self(DecryptionKey::new(method, uri))
}
@ -87,8 +87,8 @@ impl ExtXKey {
pub fn is_empty(&self) -> bool { self.0.method() == EncryptionMethod::None }
}
/// This tag requires the [`ProtocolVersion`] returned by
/// [`DecryptionKey::required_version`].
/// This tag requires the same [`ProtocolVersion`] that is returned by
/// `DecryptionKey::required_version`.
impl RequiredVersion for ExtXKey {
fn required_version(&self) -> ProtocolVersion { self.0.required_version() }
}
@ -103,7 +103,10 @@ impl FromStr for ExtXKey {
}
impl fmt::Display for ExtXKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", Self::PREFIX, self.0) }
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//
write!(f, "{}{}", Self::PREFIX, self.0)
}
}
#[cfg(test)]

View file

@ -69,9 +69,9 @@ impl ExtXMap {
/// # use hls_m3u8::tags::ExtXMap;
/// let map = ExtXMap::new("https://prod.mediaspace.com/init.bin");
/// ```
pub fn new<T: ToString>(uri: T) -> Self {
pub fn new<T: Into<String>>(uri: T) -> Self {
Self {
uri: uri.to_string(),
uri: uri.into(),
range: None,
keys: vec![],
}
@ -90,9 +90,9 @@ impl ExtXMap {
/// ByteRange::new(9, Some(2)),
/// );
/// ```
pub fn with_range<T: ToString>(uri: T, range: ByteRange) -> Self {
pub fn with_range<T: Into<String>>(uri: T, range: ByteRange) -> Self {
Self {
uri: uri.to_string(),
uri: uri.into(),
range: Some(range),
keys: vec![],
}

View file

@ -7,8 +7,13 @@ use crate::{Error, RequiredVersion};
/// # [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]
///
/// The [`ExtXIndependentSegments`] tag signals that all media samples in a
/// [`MediaSegment`] can be decoded without information from other segments.
///
/// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]:
/// https://tools.ietf.org/html/rfc8216#section-4.3.5.1
///
/// [`MediaSegment`]: crate::MediaSegment
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ExtXIndependentSegments;

View file

@ -1,3 +1,11 @@
//! The tags in this section can appear in either Master Playlists or
//! Media Playlists. If one of these tags appears in a Master Playlist,
//! it should not appear in any Media Playlist referenced by that Master
//! Playlist. A tag that appears in both must have the same value;
//! otherwise, clients should ignore the value in the Media Playlist(s).
//!
//! These tags must not appear more than once in a Playlist. If a tag
//! appears more than once, clients must fail to parse the Playlist.
mod independent_segments;
mod start;

View file

@ -18,7 +18,9 @@ use crate::{Error, RequiredVersion};
#[builder(setter(into), build_fn(validate = "Self::validate"))]
#[shorthand(enable(must_use, into))]
pub struct DecryptionKey {
/// The [`EncryptionMethod`].
/// HLS supports multiple encryption methods for a segment.
///
/// For example `AES-128`.
///
/// # Example
///
@ -41,9 +43,10 @@ pub struct DecryptionKey {
/// This attribute is required.
#[shorthand(enable(copy))]
pub(crate) method: EncryptionMethod,
/// An `URI`, that specifies how to obtain the key.
/// An `URI` that specifies how to obtain the key.
///
/// # Example
///
/// ```
/// # use hls_m3u8::types::DecryptionKey;
/// use hls_m3u8::types::EncryptionMethod;
@ -63,8 +66,10 @@ pub struct DecryptionKey {
/// This attribute is required, if the [`EncryptionMethod`] is not `None`.
#[builder(setter(into, strip_option), default)]
pub(crate) uri: Option<String>,
/// The IV (Initialization Vector) for the key.
/// An IV (initialization vector) is used to prevent repetitions between
/// segments of encrypted data.
///
/// <https://en.wikipedia.org/wiki/Initialization_vector>
///
/// # Example
///
@ -90,7 +95,7 @@ pub struct DecryptionKey {
// TODO: workaround for https://github.com/Luro02/shorthand/issues/20
#[shorthand(enable(copy), disable(option_as_ref))]
pub(crate) iv: Option<[u8; 16]>,
/// [`KeyFormat`] specifies how the key is
/// The [`KeyFormat`] specifies how the key is
/// represented in the resource identified by the `URI`.
///
/// # Example
@ -157,23 +162,25 @@ impl DecryptionKey {
///
/// let key = DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/");
/// ```
pub fn new<T: ToString>(method: EncryptionMethod, uri: T) -> Self {
#[doc(hidden)]
pub fn new<T: Into<String>>(method: EncryptionMethod, uri: T) -> Self {
Self {
method,
uri: Some(uri.to_string()),
uri: Some(uri.into()),
iv: None,
key_format: None,
key_format_versions: None,
}
}
/// Returns a Builder to build a [DecryptionKey].
/// Returns a Builder to build a [`DecryptionKey`].
pub fn builder() -> DecryptionKeyBuilder { DecryptionKeyBuilder::default() }
}
/// This tag requires [`ProtocolVersion::V5`], if [`KeyFormat`] or
/// [`KeyFormatVersions`] is specified and [`ProtocolVersion::V2`] if an iv is
/// specified.
///
/// Otherwise [`ProtocolVersion::V1`] is required.
impl RequiredVersion for DecryptionKey {
fn required_version(&self) -> ProtocolVersion {
@ -187,6 +194,7 @@ impl RequiredVersion for DecryptionKey {
}
}
#[doc(hidden)]
impl FromStr for DecryptionKey {
type Err = Error;

View file

@ -5,15 +5,16 @@ use strum::{Display, EnumString};
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[strum(serialize_all = "SCREAMING-KEBAB-CASE")]
pub enum EncryptionMethod {
/// `None` means that the [`MediaSegment`]s are not encrypted.
///
/// [MediaSegment]: crate::MediaSegment
/// [`MediaSegment`]: crate::MediaSegment
None,
/// `Aes128` signals that the [MediaSegment]s are completely encrypted
/// `Aes128` signals that the [`MediaSegment`]s are completely encrypted
/// using the Advanced Encryption Standard ([AES-128]) with a 128-bit
/// key, Cipher Block Chaining (CBC), and
/// [Public-Key Cryptography Standards #7 (PKCS7)] padding.
@ -22,14 +23,14 @@ pub enum EncryptionMethod {
/// Initialization Vector (IV) attribute value or the Media Sequence
/// Number as the IV.
///
/// [MediaSegment]: crate::MediaSegment
/// [AES_128]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
/// [`MediaSegment`]: crate::MediaSegment
/// [AES-128]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
/// [Public-Key Cryptography Standards #7 (PKCS7)]: https://tools.ietf.org/html/rfc5652
#[strum(serialize = "AES-128")]
Aes128,
/// `SampleAes` means that the [MediaSegment]s
/// `SampleAes` means that the [`MediaSegment`]s
/// contain media samples, such as audio or video, that are encrypted
/// using the Advanced Encryption Standard ([AES_128]). How these media
/// using the Advanced Encryption Standard ([`AES-128`]). How these media
/// streams are encrypted and encapsulated in a segment depends on the
/// media encoding and the media format of the segment. fMP4 Media
/// Segments are encrypted using the 'cbcs' scheme of
@ -39,7 +40,7 @@ pub enum EncryptionMethod {
/// Live Streaming (HLS) [SampleEncryption specification].
///
/// [`MediaSegment`]: crate::MediaSegment
/// [AES-128]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
/// [`AES-128`]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
/// [Common Encryption]: https://tools.ietf.org/html/rfc8216#ref-COMMON_ENC
/// [H.264]: https://tools.ietf.org/html/rfc8216#ref-H_264
/// [AAC]: https://tools.ietf.org/html/rfc8216#ref-ISO_14496

View file

@ -5,6 +5,7 @@ use strum::{Display, EnumString};
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[strum(serialize_all = "SCREAMING-KEBAB-CASE")]

View file

@ -5,9 +5,11 @@ use strum::{Display, EnumString};
/// See: [4.3.4.1. EXT-X-MEDIA]
///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[strum(serialize_all = "UPPERCASE")]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[non_exhaustive]
pub enum InStreamId {
Cc1,
Cc2,

View file

@ -7,6 +7,7 @@ use crate::{Error, RequiredVersion};
/// [`KeyFormat`] specifies, how the key is represented in the
/// resource identified by the `URI`.
#[non_exhaustive]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum KeyFormat {
/// The key is a single packed array of 16 octets in binary format.
@ -28,7 +29,7 @@ impl FromStr for KeyFormat {
}
impl fmt::Display for KeyFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", quote("identity")) }
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", quote(&"identity")) }
}
/// This tag requires [`ProtocolVersion::V5`].

View file

@ -31,7 +31,10 @@ impl KeyFormatVersions {
/// Returns `true`, if [`KeyFormatVersions`] has the default value of
/// `vec![1]`.
pub fn is_default(&self) -> bool { self.0 == vec![1] && self.0.len() == 1 || self.0.is_empty() }
pub fn is_default(&self) -> bool {
//
self.0 == vec![1] && self.0.len() == 1 || self.0.is_empty()
}
}
impl Default for KeyFormatVersions {
@ -73,7 +76,7 @@ impl fmt::Display for KeyFormatVersions {
// vec![1, 2, 3] -> "1/2/3"
self.0
.iter()
.map(|v| v.to_string())
.map(ToString::to_string)
.collect::<Vec<String>>()
.join("/")
)

View file

@ -1,6 +1,7 @@
use strum::{Display, EnumString};
/// Specifies the media type.
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Ord, PartialOrd, Display, EnumString, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[strum(serialize_all = "SCREAMING-KEBAB-CASE")]

View file

@ -10,6 +10,7 @@ use crate::Error;
///
/// [7. Protocol Version Compatibility]:
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-7
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolVersion {

View file

@ -6,8 +6,9 @@ use hex;
use crate::utils::{quote, unquote};
use crate::Error;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// A [`Value`].
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
/// A [`String`].
String(String),

View file

@ -1,4 +1,5 @@
use crate::Error;
use core::iter;
macro_rules! required_version {
( $( $tag:expr ),* ) => {
@ -22,14 +23,7 @@ pub(crate) fn parse_iv_from_str(input: &str) -> crate::Result<[u8; 16]> {
let mut result = [0; 16];
// TODO:
// hex::decode_to_slice(value.as_bytes()[2..], &mut result)?;
for (i, c) in input.as_bytes().chunks(2).skip(1).enumerate() {
let d = core::str::from_utf8(c).map_err(Error::custom)?;
let b = u8::from_str_radix(d, 16).map_err(Error::custom)?;
result[i] = b;
}
hex::decode_to_slice(&input.as_bytes()[2..], &mut result).map_err(Error::hex)?;
Ok(result)
}
@ -50,19 +44,23 @@ pub(crate) fn parse_yes_or_no<T: AsRef<str>>(s: T) -> crate::Result<bool> {
///
/// Therefore it is safe to simply remove any occurence of those characters.
/// [rfc8216#section-4.2](https://tools.ietf.org/html/rfc8216#section-4.2)
pub(crate) fn unquote<T: ToString>(value: T) -> String {
pub(crate) fn unquote<T: AsRef<str>>(value: T) -> String {
value
.to_string()
.replace("\"", "")
.replace("\n", "")
.replace("\r", "")
.as_ref()
.chars()
.filter(|c| *c != '"' && *c != '\n' && *c != '\r')
.collect()
}
/// Puts a string inside quotes.
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn quote<T: ToString>(value: T) -> String {
// the replace is for the case, that quote is called on an already quoted
// string, which could cause problems!
format!("\"{}\"", value.to_string().replace("\"", ""))
iter::once('"')
.chain(value.to_string().chars().filter(|c| *c != '"'))
.chain(iter::once('"'))
.collect()
}
/// Checks, if the given tag is at the start of the input. If this is the case,