Implement get_value for GitConfig
This commit is contained in:
parent
3e97f07b28
commit
16496d91a1
3 changed files with 242 additions and 36 deletions
168
src/config.rs
168
src/config.rs
|
@ -1,8 +1,11 @@
|
||||||
use crate::parser::{parse_from_bytes, Event, ParsedSectionHeader, Parser, ParserError};
|
use crate::parser::{parse_from_bytes, Event, ParsedSectionHeader, Parser, ParserError};
|
||||||
use bstr::BStr;
|
use bstr::BStr;
|
||||||
use std::collections::{HashMap, VecDeque};
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::{borrow::Cow, fmt::Display};
|
use std::{borrow::Cow, fmt::Display};
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
error::Error,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord, Debug)]
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord, Debug)]
|
||||||
pub enum GitConfigError<'a> {
|
pub enum GitConfigError<'a> {
|
||||||
|
@ -12,8 +15,23 @@ pub enum GitConfigError<'a> {
|
||||||
SubSectionDoesNotExist(Option<&'a BStr>),
|
SubSectionDoesNotExist(Option<&'a BStr>),
|
||||||
/// The key does not exist in the requested section.
|
/// The key does not exist in the requested section.
|
||||||
KeyDoesNotExist(&'a BStr),
|
KeyDoesNotExist(&'a BStr),
|
||||||
|
FailedConversion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for GitConfigError<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
// Todo, try parse as utf8 first for better looking errors
|
||||||
|
Self::SectionDoesNotExist(s) => write!(f, "Subsection '{}' does not exist.", s),
|
||||||
|
Self::SubSectionDoesNotExist(s) => write!(f, "Subsection '{:?}' does not exist.", s),
|
||||||
|
Self::KeyDoesNotExist(k) => write!(f, "Name '{}' does not exist.", k),
|
||||||
|
Self::FailedConversion => write!(f, "Failed to convert to specified type."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for GitConfigError<'_> {}
|
||||||
|
|
||||||
/// The section ID is a monotonically increasing ID used to refer to sections.
|
/// The section ID is a monotonically increasing ID used to refer to sections.
|
||||||
/// This value does not imply any ordering between sections, as new sections
|
/// This value does not imply any ordering between sections, as new sections
|
||||||
/// with higher section IDs may be in between lower ID sections.
|
/// with higher section IDs may be in between lower ID sections.
|
||||||
|
@ -101,8 +119,12 @@ impl<'a> GitConfig<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an uninterpreted value given a section, an optional subsection
|
/// Returns an interpreted value given a section, an optional subsection and
|
||||||
/// and key.
|
/// key.
|
||||||
|
///
|
||||||
|
/// It's recommended to use one of the values in the [`values`] module as
|
||||||
|
/// the conversion is already implemented, but this function is flexible and
|
||||||
|
/// will accept any type that implements [`TryFrom<&[u8]>`][`TryFrom`].
|
||||||
///
|
///
|
||||||
/// # Multivar behavior
|
/// # Multivar behavior
|
||||||
///
|
///
|
||||||
|
@ -131,13 +153,110 @@ impl<'a> GitConfig<'a> {
|
||||||
/// ```
|
/// ```
|
||||||
/// # use git_config::config::GitConfig;
|
/// # use git_config::config::GitConfig;
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// # let git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # let git_config = GitConfig::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
|
||||||
/// assert_eq!(git_config.get_raw_value("core", None, "a"), Ok(&Cow::Borrowed("d".into())));
|
/// assert_eq!(git_config.get_raw_value("core", None, "a"), Ok(&Cow::Borrowed("d".into())));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Consider [`Self::get_raw_multi_value`] if you want to get all values of
|
/// Consider [`Self::get_raw_multi_value`] if you want to get all values of
|
||||||
/// a multivar instead.
|
/// a multivar instead.
|
||||||
///
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Fetching a config value
|
||||||
|
/// ```
|
||||||
|
/// # use git_config::config::{GitConfig, GitConfigError};
|
||||||
|
/// # use git_config::values::{Integer, Value, Boolean};
|
||||||
|
/// # use std::borrow::Cow;
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
|
/// let config = r#"
|
||||||
|
/// [core]
|
||||||
|
/// a = 10k
|
||||||
|
/// c
|
||||||
|
/// "#;
|
||||||
|
/// let git_config = GitConfig::try_from(config).unwrap();
|
||||||
|
/// // You can either use the turbofish to determine the type...
|
||||||
|
/// let a_value = git_config.get_value::<Integer, _>("core", None, "a")?;
|
||||||
|
/// // ... or explicitly declare the type to avoid the turbofish
|
||||||
|
/// let c_value: Boolean = git_config.get_value("core", None, "c")?;
|
||||||
|
/// # Ok::<(), GitConfigError>(())
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error if the key is not in the requested
|
||||||
|
/// section and subsection, if the section and subsection do not exist, or
|
||||||
|
/// if there was an issue converting the type into the requested variant.
|
||||||
|
///
|
||||||
|
/// [`values`]: crate::values
|
||||||
|
/// [`TryFrom`]: std::convert::TryFrom
|
||||||
|
pub fn get_value<'b, 'c, T: TryFrom<&'c [u8]>, S: Into<&'b BStr>>(
|
||||||
|
&'c self,
|
||||||
|
section_name: S,
|
||||||
|
subsection_name: Option<S>,
|
||||||
|
key: S,
|
||||||
|
) -> Result<T, GitConfigError<'b>> {
|
||||||
|
T::try_from(self.get_raw_value(section_name, subsection_name, key)?)
|
||||||
|
.map_err(|_| GitConfigError::FailedConversion)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_section_id_by_name_and_subname<'b>(
|
||||||
|
&'a self,
|
||||||
|
section_name: &'b BStr,
|
||||||
|
subsection_name: Option<&'b BStr>,
|
||||||
|
) -> Result<SectionId, GitConfigError<'b>> {
|
||||||
|
self.get_section_ids_by_name_and_subname(section_name, subsection_name)
|
||||||
|
.map(|vec| {
|
||||||
|
// get_section_ids_by_name_and_subname is guaranteed to return
|
||||||
|
// a non-empty vec, so max can never return empty.
|
||||||
|
*vec.iter().max().unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an uninterpreted value given a section, an optional subsection
|
||||||
|
/// and key.
|
||||||
|
///
|
||||||
|
/// # Multivar behavior
|
||||||
|
///
|
||||||
|
/// `git` is flexible enough to allow users to set a key multiple times in
|
||||||
|
/// any number of identically named sections. When this is the case, the key
|
||||||
|
/// is known as a "multivar". In this case, `get_raw_value` follows the
|
||||||
|
/// "last one wins" approach that `git-config` internally uses for multivar
|
||||||
|
/// resolution.
|
||||||
|
///
|
||||||
|
/// Concretely, the following config has a multivar, `a`, with the values
|
||||||
|
/// of `b`, `c`, and `d`, while `e` is a single variable with the value
|
||||||
|
/// `f g h`.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// [core]
|
||||||
|
/// a = b
|
||||||
|
/// a = c
|
||||||
|
/// [core]
|
||||||
|
/// a = d
|
||||||
|
/// e = f g h
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Calling this function to fetch `a` with the above config will return
|
||||||
|
/// `d`, since the last valid config value is `a = d`:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use git_config::config::{GitConfig, GitConfigError};
|
||||||
|
/// # use git_config::values::Value;
|
||||||
|
/// # use std::borrow::Cow;
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # let git_config = GitConfig::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// git_config.get_value::<Value, _>("core", None, "a")?,
|
||||||
|
/// Value::Other(Cow::Borrowed("d".into()))
|
||||||
|
/// );
|
||||||
|
/// # Ok::<(), GitConfigError>(())
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Consider [`Self::get_raw_multi_value`] if you want to get all values of
|
||||||
|
/// a multivar instead.
|
||||||
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This function will return an error if the key is not in the requested
|
/// This function will return an error if the key is not in the requested
|
||||||
|
@ -207,7 +326,8 @@ impl<'a> GitConfig<'a> {
|
||||||
/// # use git_config::config::{GitConfig, GitConfigError};
|
/// # use git_config::config::{GitConfig, GitConfigError};
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// # use bstr::BStr;
|
/// # use bstr::BStr;
|
||||||
/// # let mut git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # let mut git_config = GitConfig::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
|
||||||
/// let mut mut_value = git_config.get_raw_value_mut("core", None, "a")?;
|
/// let mut mut_value = git_config.get_raw_value_mut("core", None, "a")?;
|
||||||
/// assert_eq!(mut_value, &mut Cow::<BStr>::Borrowed("d".into()));
|
/// assert_eq!(mut_value, &mut Cow::<BStr>::Borrowed("d".into()));
|
||||||
/// *mut_value = Cow::Borrowed("hello".into());
|
/// *mut_value = Cow::Borrowed("hello".into());
|
||||||
|
@ -256,19 +376,6 @@ impl<'a> GitConfig<'a> {
|
||||||
latest_value.ok_or(GitConfigError::KeyDoesNotExist(key))
|
latest_value.ok_or(GitConfigError::KeyDoesNotExist(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_section_id_by_name_and_subname<'b>(
|
|
||||||
&'a self,
|
|
||||||
section_name: &'b BStr,
|
|
||||||
subsection_name: Option<&'b BStr>,
|
|
||||||
) -> Result<SectionId, GitConfigError<'b>> {
|
|
||||||
self.get_section_ids_by_name_and_subname(section_name, subsection_name)
|
|
||||||
.map(|vec| {
|
|
||||||
// get_section_ids_by_name_and_subname is guaranteed to return
|
|
||||||
// a non-empty vec, so max can never return empty.
|
|
||||||
*vec.iter().max().unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all uninterpreted values given a section, an optional subsection
|
/// Returns all uninterpreted values given a section, an optional subsection
|
||||||
/// and key. If you have the following config:
|
/// and key. If you have the following config:
|
||||||
///
|
///
|
||||||
|
@ -285,7 +392,8 @@ impl<'a> GitConfig<'a> {
|
||||||
/// ```
|
/// ```
|
||||||
/// # use git_config::config::GitConfig;
|
/// # use git_config::config::GitConfig;
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// # let git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # let git_config = GitConfig::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// git_config.get_raw_multi_value("core", None, "a"),
|
/// git_config.get_raw_multi_value("core", None, "a"),
|
||||||
/// Ok(vec![&Cow::Borrowed("b".into()), &Cow::Borrowed("c".into()), &Cow::Borrowed("d".into())]),
|
/// Ok(vec![&Cow::Borrowed("b".into()), &Cow::Borrowed("c".into()), &Cow::Borrowed("d".into())]),
|
||||||
|
@ -351,7 +459,8 @@ impl<'a> GitConfig<'a> {
|
||||||
/// # use git_config::config::{GitConfig, GitConfigError};
|
/// # use git_config::config::{GitConfig, GitConfigError};
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// # use bstr::BStr;
|
/// # use bstr::BStr;
|
||||||
/// # let mut git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # let mut git_config = GitConfig::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// git_config.get_raw_multi_value("core", None, "a")?,
|
/// git_config.get_raw_multi_value("core", None, "a")?,
|
||||||
/// vec![
|
/// vec![
|
||||||
|
@ -888,6 +997,25 @@ mod get_raw_value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod get_value {
|
||||||
|
use super::*;
|
||||||
|
use crate::values::{Boolean, TrueVariant, Value};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_section() -> Result<(), Box<dyn Error>> {
|
||||||
|
let config = GitConfig::try_from("[core]\na=b\nc").unwrap();
|
||||||
|
let first_value: Value = config.get_value("core", None, "a")?;
|
||||||
|
let second_value: Boolean = config.get_value("core", None, "c")?;
|
||||||
|
|
||||||
|
assert_eq!(first_value, Value::Other(Cow::Borrowed("b".into())));
|
||||||
|
assert_eq!(second_value, Boolean::True(TrueVariant::Implicit));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod get_raw_multi_value {
|
mod get_raw_multi_value {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -285,7 +285,7 @@ impl Display for ParserNode {
|
||||||
///
|
///
|
||||||
/// - This struct does _not_ implement [`FromStr`] due to lifetime
|
/// - This struct does _not_ implement [`FromStr`] due to lifetime
|
||||||
/// constraints implied on the required `from_str` method. Instead, it provides
|
/// constraints implied on the required `from_str` method. Instead, it provides
|
||||||
/// [`Parser::from_str`].
|
/// [`From<&'_ str>`].
|
||||||
///
|
///
|
||||||
/// # Idioms
|
/// # Idioms
|
||||||
///
|
///
|
||||||
|
@ -439,6 +439,7 @@ impl Display for ParserNode {
|
||||||
/// [`.ini` file format]: https://en.wikipedia.org/wiki/INI_file
|
/// [`.ini` file format]: https://en.wikipedia.org/wiki/INI_file
|
||||||
/// [`git`'s documentation]: https://git-scm.com/docs/git-config#_configuration_file
|
/// [`git`'s documentation]: https://git-scm.com/docs/git-config#_configuration_file
|
||||||
/// [`FromStr`]: std::str::FromStr
|
/// [`FromStr`]: std::str::FromStr
|
||||||
|
/// [`From<&'_ str>`]: std::convert::From
|
||||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
frontmatter: Vec<Event<'a>>,
|
frontmatter: Vec<Event<'a>>,
|
||||||
|
|
107
src/values.rs
107
src/values.rs
|
@ -1,10 +1,9 @@
|
||||||
//! Rust containers for valid `git-config` types.
|
//! Rust containers for valid `git-config` types.
|
||||||
|
|
||||||
use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr};
|
use bstr::{BStr, ByteSlice};
|
||||||
|
|
||||||
use bstr::BStr;
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
|
use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr};
|
||||||
|
|
||||||
/// Fully enumerated valid types that a `git-config` value can be.
|
/// Fully enumerated valid types that a `git-config` value can be.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -43,6 +42,27 @@ impl<'a> From<&'a str> for Value<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a [u8]> for Value<'a> {
|
||||||
|
fn from(s: &'a [u8]) -> Self {
|
||||||
|
// All parsable values must be utf-8 valid
|
||||||
|
if let Ok(s) = std::str::from_utf8(s) {
|
||||||
|
if let Ok(bool) = Boolean::try_from(s) {
|
||||||
|
return Self::Boolean(bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(int) = Integer::from_str(s) {
|
||||||
|
return Self::Integer(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(color) = Color::from_str(s) {
|
||||||
|
return Self::Color(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Other(Cow::Borrowed(s.as_bstr()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// todo display for value
|
// todo display for value
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -70,17 +90,25 @@ impl<'a> TryFrom<&'a str> for Boolean<'a> {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||||
|
Self::try_from(value.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a [u8]> for Boolean<'a> {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
|
||||||
if let Ok(v) = TrueVariant::try_from(value) {
|
if let Ok(v) = TrueVariant::try_from(value) {
|
||||||
return Ok(Self::True(v));
|
return Ok(Self::True(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
if value.eq_ignore_ascii_case("no")
|
if value.eq_ignore_ascii_case(b"no")
|
||||||
|| value.eq_ignore_ascii_case("off")
|
|| value.eq_ignore_ascii_case(b"off")
|
||||||
|| value.eq_ignore_ascii_case("false")
|
|| value.eq_ignore_ascii_case(b"false")
|
||||||
|| value.eq_ignore_ascii_case("zero")
|
|| value.eq_ignore_ascii_case(b"zero")
|
||||||
|| value == "\"\""
|
|| value == b"\"\""
|
||||||
{
|
{
|
||||||
return Ok(Self::False(value));
|
return Ok(Self::False(std::str::from_utf8(value).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(())
|
Err(())
|
||||||
|
@ -132,12 +160,22 @@ impl<'a> TryFrom<&'a str> for TrueVariant<'a> {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||||
if value.eq_ignore_ascii_case("yes")
|
Self::try_from(value.as_bytes())
|
||||||
|| value.eq_ignore_ascii_case("on")
|
}
|
||||||
|| value.eq_ignore_ascii_case("true")
|
}
|
||||||
|| value.eq_ignore_ascii_case("one")
|
|
||||||
|
impl<'a> TryFrom<&'a [u8]> for TrueVariant<'a> {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
|
||||||
|
if value.eq_ignore_ascii_case(b"yes")
|
||||||
|
|| value.eq_ignore_ascii_case(b"on")
|
||||||
|
|| value.eq_ignore_ascii_case(b"true")
|
||||||
|
|| value.eq_ignore_ascii_case(b"one")
|
||||||
{
|
{
|
||||||
Ok(Self::Explicit(value))
|
Ok(Self::Explicit(std::str::from_utf8(value).unwrap()))
|
||||||
|
} else if value.is_empty() {
|
||||||
|
Ok(Self::Implicit)
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
@ -224,6 +262,14 @@ impl FromStr for Integer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for Integer {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_str(std::str::from_utf8(s).map_err(|_| ())?).map_err(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
pub enum IntegerSuffix {
|
pub enum IntegerSuffix {
|
||||||
Kilo,
|
Kilo,
|
||||||
|
@ -279,6 +325,14 @@ impl FromStr for IntegerSuffix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for IntegerSuffix {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_str(std::str::from_utf8(s).map_err(|_| ())?).map_err(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
foreground: Option<ColorValue>,
|
foreground: Option<ColorValue>,
|
||||||
|
@ -364,6 +418,14 @@ impl FromStr for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for Color {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_str(std::str::from_utf8(s).map_err(|_| ())?).map_err(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
enum ColorValue {
|
enum ColorValue {
|
||||||
Normal,
|
Normal,
|
||||||
|
@ -479,6 +541,14 @@ impl FromStr for ColorValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for ColorValue {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_str(std::str::from_utf8(s).map_err(|_| ())?).map_err(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
pub enum ColorAttribute {
|
pub enum ColorAttribute {
|
||||||
Bold,
|
Bold,
|
||||||
|
@ -578,6 +648,14 @@ impl FromStr for ColorAttribute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for ColorAttribute {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_str(std::str::from_utf8(s).map_err(|_| ())?).map_err(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod boolean {
|
mod boolean {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -625,7 +703,6 @@ mod boolean {
|
||||||
fn from_str_err() {
|
fn from_str_err() {
|
||||||
assert!(Boolean::try_from("yesn't").is_err());
|
assert!(Boolean::try_from("yesn't").is_err());
|
||||||
assert!(Boolean::try_from("yesno").is_err());
|
assert!(Boolean::try_from("yesno").is_err());
|
||||||
assert!(Boolean::try_from("").is_err());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue