use core::{convert::TryFrom, fmt::Debug, str};
use serde::{
de::{self, Deserialize, Deserializer, Visitor},
ser::{Serialize, Serializer},
};
use static_assertions::assert_impl_all;
use std::borrow::Cow;
use crate::{Basic, EncodingFormat, Error, Result, Signature, Str, Type};
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct ObjectPath<'a>(Str<'a>);
assert_impl_all!(ObjectPath<'_>: Send, Sync, Unpin);
impl<'a> ObjectPath<'a> {
pub fn as_ref(&self) -> ObjectPath<'_> {
ObjectPath(self.0.as_ref())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
pub unsafe fn from_bytes_unchecked<'s: 'a>(bytes: &'s [u8]) -> Self {
Self(std::str::from_utf8_unchecked(bytes).into())
}
pub fn from_str_unchecked<'s: 'a>(path: &'s str) -> Self {
Self(path.into())
}
pub fn from_static_str(name: &'static str) -> Result<Self> {
ensure_correct_object_path_str(name.as_bytes())?;
Ok(Self::from_static_str_unchecked(name))
}
pub const fn from_static_str_unchecked(name: &'static str) -> Self {
Self(Str::from_static(name))
}
pub fn from_string_unchecked(path: String) -> Self {
Self(path.into())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn to_owned(&self) -> ObjectPath<'static> {
ObjectPath(self.0.to_owned())
}
pub fn into_owned(self) -> ObjectPath<'static> {
ObjectPath(self.0.into_owned())
}
}
impl std::default::Default for ObjectPath<'_> {
fn default() -> Self {
ObjectPath::from_str_unchecked("/")
}
}
impl<'a> Basic for ObjectPath<'a> {
const SIGNATURE_CHAR: char = 'o';
const SIGNATURE_STR: &'static str = "o";
fn alignment(format: EncodingFormat) -> usize {
match format {
EncodingFormat::DBus => <&str>::alignment(format),
#[cfg(feature = "gvariant")]
EncodingFormat::GVariant => 1,
}
}
}
impl<'a> Type for ObjectPath<'a> {
fn signature() -> Signature<'static> {
Signature::from_static_str_unchecked(Self::SIGNATURE_STR)
}
}
impl<'a> TryFrom<&'a [u8]> for ObjectPath<'a> {
type Error = Error;
fn try_from(value: &'a [u8]) -> Result<Self> {
ensure_correct_object_path_str(value)?;
unsafe { Ok(Self::from_bytes_unchecked(value)) }
}
}
impl<'a> TryFrom<&'a str> for ObjectPath<'a> {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self> {
Self::try_from(value.as_bytes())
}
}
impl<'a> TryFrom<String> for ObjectPath<'a> {
type Error = Error;
fn try_from(value: String) -> Result<Self> {
ensure_correct_object_path_str(value.as_bytes())?;
Ok(Self::from_string_unchecked(value))
}
}
impl<'a> TryFrom<Cow<'a, str>> for ObjectPath<'a> {
type Error = Error;
fn try_from(value: Cow<'a, str>) -> Result<Self> {
match value {
Cow::Borrowed(s) => Self::try_from(s),
Cow::Owned(s) => Self::try_from(s),
}
}
}
impl<'o> From<&ObjectPath<'o>> for ObjectPath<'o> {
fn from(o: &ObjectPath<'o>) -> Self {
o.clone()
}
}
impl<'a> std::ops::Deref for ObjectPath<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<'a> PartialEq<str> for ObjectPath<'a> {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl<'a> PartialEq<&str> for ObjectPath<'a> {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl<'a> Debug for ObjectPath<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ObjectPath").field(&self.as_str()).finish()
}
}
impl<'a> std::fmt::Display for ObjectPath<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.as_str(), f)
}
}
impl<'a> Serialize for ObjectPath<'a> {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de: 'a, 'a> Deserialize<'de> for ObjectPath<'a> {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let visitor = ObjectPathVisitor;
deserializer.deserialize_str(visitor)
}
}
struct ObjectPathVisitor;
impl<'de> Visitor<'de> for ObjectPathVisitor {
type Value = ObjectPath<'de>;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("an ObjectPath")
}
#[inline]
fn visit_borrowed_str<E>(self, value: &'de str) -> core::result::Result<ObjectPath<'de>, E>
where
E: serde::de::Error,
{
ObjectPath::try_from(value).map_err(serde::de::Error::custom)
}
}
fn ensure_correct_object_path_str(path: &[u8]) -> Result<()> {
let mut prev = b'\0';
if path.is_empty() {
return Err(serde::de::Error::invalid_length(0, &"> 0 character"));
}
for i in 0..path.len() {
let c = path[i];
if i == 0 && c != b'/' {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Char(c as char),
&"/",
));
} else if c == b'/' && prev == b'/' {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str("//"),
&"/",
));
} else if path.len() > 1 && i == (path.len() - 1) && c == b'/' {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Char('/'),
&"an alphanumeric character or `_`",
));
} else if !c.is_ascii_alphanumeric() && c != b'/' && c != b'_' {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Char(c as char),
&"an alphanumeric character, `_` or `/`",
));
}
prev = c;
}
Ok(())
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, Type)]
pub struct OwnedObjectPath(ObjectPath<'static>);
assert_impl_all!(OwnedObjectPath: Send, Sync, Unpin);
impl OwnedObjectPath {
pub fn into_inner(self) -> ObjectPath<'static> {
self.0
}
}
impl std::ops::Deref for OwnedObjectPath {
type Target = ObjectPath<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::convert::From<OwnedObjectPath> for ObjectPath<'static> {
fn from(o: OwnedObjectPath) -> Self {
o.into_inner()
}
}
impl std::convert::From<OwnedObjectPath> for crate::Value<'static> {
fn from(o: OwnedObjectPath) -> Self {
o.into_inner().into()
}
}
impl<'unowned, 'owned: 'unowned> From<&'owned OwnedObjectPath> for ObjectPath<'unowned> {
fn from(o: &'owned OwnedObjectPath) -> Self {
ObjectPath::from_str_unchecked(o.as_str())
}
}
impl<'a> std::convert::From<ObjectPath<'a>> for OwnedObjectPath {
fn from(o: ObjectPath<'a>) -> Self {
OwnedObjectPath(o.into_owned())
}
}
impl TryFrom<&'_ str> for OwnedObjectPath {
type Error = Error;
fn try_from(value: &str) -> Result<Self> {
Ok(Self::from(ObjectPath::try_from(value)?))
}
}
impl TryFrom<String> for OwnedObjectPath {
type Error = Error;
fn try_from(value: String) -> Result<Self> {
Ok(Self::from(ObjectPath::try_from(value)?))
}
}
impl<'de> Deserialize<'de> for OwnedObjectPath {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)
.and_then(|s| ObjectPath::try_from(s).map_err(|e| de::Error::custom(e.to_string())))
.map(Self)
}
}
impl std::fmt::Display for OwnedObjectPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.as_str(), f)
}
}
#[cfg(test)]
mod unit {
use super::*;
#[test]
fn owned_from_reader() {
let json_str = "\"/some/path\"";
serde_json::de::from_reader::<_, OwnedObjectPath>(json_str.as_bytes()).unwrap();
}
}