#[cfg(feature = "introspection")]
use crate::introspect::Type;
use alloc::{borrow::Cow, vec::Vec};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "introspection", derive(Type))]
#[cfg_attr(feature = "introspection", zlink(crate = "crate"))]
pub struct Info<'a> {
#[serde(borrow)]
pub vendor: Cow<'a, str>,
#[serde(borrow)]
pub product: Cow<'a, str>,
#[serde(borrow)]
pub version: Cow<'a, str>,
#[serde(borrow)]
pub url: Cow<'a, str>,
#[serde(borrow)]
pub interfaces: Vec<Cow<'a, str>>,
}
impl<'a> Info<'a> {
pub fn new(
vendor: impl Into<Cow<'a, str>>,
product: impl Into<Cow<'a, str>>,
version: impl Into<Cow<'a, str>>,
url: impl Into<Cow<'a, str>>,
interfaces: impl IntoIterator<Item = impl Into<Cow<'a, str>>>,
) -> Self {
Self {
vendor: vendor.into(),
product: product.into(),
version: version.into(),
url: url.into(),
interfaces: interfaces.into_iter().map(Into::into).collect(),
}
}
pub fn into_owned(self) -> Info<'static> {
Info {
vendor: Cow::Owned(self.vendor.into_owned()),
product: Cow::Owned(self.product.into_owned()),
version: Cow::Owned(self.version.into_owned()),
url: Cow::Owned(self.url.into_owned()),
interfaces: self
.interfaces
.into_iter()
.map(|s| Cow::Owned(s.into_owned()))
.collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct OwnedInfo(Info<'static>);
impl OwnedInfo {
pub fn new(
vendor: impl Into<Cow<'static, str>>,
product: impl Into<Cow<'static, str>>,
version: impl Into<Cow<'static, str>>,
url: impl Into<Cow<'static, str>>,
interfaces: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> Self {
Self(Info::new(vendor, product, version, url, interfaces))
}
pub fn inner(&self) -> &Info<'static> {
&self.0
}
pub fn into_inner(self) -> Info<'static> {
self.0
}
}
impl core::ops::Deref for OwnedInfo {
type Target = Info<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for OwnedInfo {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> From<Info<'a>> for OwnedInfo {
fn from(info: Info<'a>) -> Self {
Self(info.into_owned())
}
}
impl<'de> Deserialize<'de> for OwnedInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use alloc::string::String;
#[derive(Deserialize)]
struct InfoOwned {
vendor: String,
product: String,
version: String,
url: String,
interfaces: Vec<String>,
}
let info = InfoOwned::deserialize(deserializer)?;
Ok(Self(Info {
vendor: Cow::Owned(info.vendor),
product: Cow::Owned(info.product),
version: Cow::Owned(info.version),
url: Cow::Owned(info.url),
interfaces: info.interfaces.into_iter().map(Cow::Owned).collect(),
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialization() {
let mut interfaces = Vec::new();
interfaces.push("com.example.test");
let info = Info::new(
"Test Vendor",
"Test Product",
"1.0.0",
"https://example.com",
interfaces,
);
let json = serde_json::to_string(&info).unwrap();
assert!(json.contains("Test Vendor"));
assert!(json.contains("com.example.test"));
}
#[test]
fn deserialization() {
let json = r#"{
"vendor": "Test Vendor",
"product": "Test Product",
"version": "1.0.0",
"url": "https://example.com",
"interfaces": ["com.example.test", "com.example.other"]
}"#;
let info: Info<'_> = serde_json::from_str(json).unwrap();
assert_eq!(info.vendor, "Test Vendor");
assert_eq!(info.product, "Test Product");
assert_eq!(info.version, "1.0.0");
assert_eq!(info.url, "https://example.com");
assert_eq!(info.interfaces.len(), 2);
assert_eq!(info.interfaces[0], "com.example.test");
assert_eq!(info.interfaces[1], "com.example.other");
}
#[test]
fn round_trip_serialization() {
let mut interfaces = Vec::new();
interfaces.push("com.example.test");
interfaces.push("com.example.other");
let original = Info::new(
"Test Vendor",
"Test Product",
"1.0.0",
"https://example.com",
interfaces,
);
let json = serde_json::to_string(&original).unwrap();
let deserialized: Info<'_> = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
}