pub mod class;
#[cfg(feature = "debug_flow")]
mod debug_types;
pub mod document;
pub mod error;
pub mod item;
pub mod property_value;
pub mod relation;
pub mod temporal;
pub mod traits;
pub use class::{Class, KnownClass};
pub use document::Document;
pub use error::Error;
pub use item::{Item, Items, ValueKind};
pub use property_value::{
Fragment, Image, NodeList, Properties, PropertyValue, PropertyWithMetadata,
};
pub use relation::{Relation, Relations};
pub use traits::{FindItemById, FindItemByProperty, FindItemByUrl, LanguageFilter};
#[cfg(feature = "debug_flow")]
pub use debug_types::*;
pub type Microformat = Item;
pub use url::Url;
#[cfg(test)]
mod test;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TextValue {
value: String,
#[cfg(feature = "per_element_lang")]
lang: Option<String>,
}
impl TextValue {
pub fn new(value: String) -> Self {
Self {
value,
#[cfg(feature = "per_element_lang")]
lang: None,
}
}
#[cfg(feature = "per_element_lang")]
pub fn with_lang(value: String, lang: impl Into<String>) -> Self {
Self {
value,
lang: Some(lang.into()),
}
}
#[cfg(feature = "per_element_lang")]
pub fn lang(&self) -> Option<&str> {
self.lang.as_deref()
}
}
impl std::ops::Deref for TextValue {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl From<String> for TextValue {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for TextValue {
fn from(value: &str) -> Self {
Self::new(value.to_string())
}
}
impl std::fmt::Display for TextValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
#[cfg(not(feature = "per_element_lang"))]
impl serde::Serialize for TextValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.value)
}
}
#[cfg(feature = "per_element_lang")]
impl serde::Serialize for TextValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if let Some(ref lang) = self.lang {
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("TextValue", 2)?;
s.serialize_field("value", &self.value)?;
s.serialize_field("lang", lang)?;
s.end()
} else {
serializer.serialize_str(&self.value)
}
}
}
#[cfg(not(feature = "per_element_lang"))]
impl<'de> serde::Deserialize<'de> for TextValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(TextValue::new(s))
}
}
#[cfg(feature = "per_element_lang")]
impl<'de> serde::Deserialize<'de> for TextValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, MapAccess, Visitor};
use std::fmt;
struct TextValueVisitor;
impl<'de> Visitor<'de> for TextValueVisitor {
type Value = TextValue;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.write_str("a string or an object with \"value\" and optional \"lang\" fields")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(TextValue::new(value.to_string()))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(TextValue::new(value))
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut value: Option<String> = None;
let mut lang: Option<String> = None;
while let Some(key) = map.next_key()? {
match key {
"value" => {
if value.is_some() {
return Err(de::Error::duplicate_field("value"));
}
value = Some(map.next_value()?);
}
"lang" => {
if lang.is_some() {
return Err(de::Error::duplicate_field("lang"));
}
lang = Some(map.next_value()?);
}
_ => {
return Err(de::Error::unknown_field(key, &["value", "lang"]));
}
}
}
let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
Ok(TextValue { value, lang })
}
}
deserializer.deserialize_any(TextValueVisitor)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct UrlValue {
value: url::Url,
#[cfg(feature = "per_element_lang")]
lang: Option<String>,
}
impl UrlValue {
pub fn new(value: url::Url) -> Self {
Self {
value,
#[cfg(feature = "per_element_lang")]
lang: None,
}
}
#[cfg(feature = "per_element_lang")]
pub fn with_lang(value: url::Url, lang: impl Into<String>) -> Self {
Self {
value,
lang: Some(lang.into()),
}
}
#[cfg(feature = "per_element_lang")]
pub fn lang(&self) -> Option<&str> {
self.lang.as_deref()
}
}
impl std::ops::Deref for UrlValue {
type Target = url::Url;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl From<url::Url> for UrlValue {
fn from(value: url::Url) -> Self {
Self::new(value)
}
}
impl std::fmt::Display for UrlValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
#[cfg(not(feature = "per_element_lang"))]
impl serde::Serialize for UrlValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.value.as_str())
}
}
#[cfg(feature = "per_element_lang")]
impl serde::Serialize for UrlValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if let Some(ref lang) = self.lang {
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("UrlValue", 2)?;
s.serialize_field("value", self.value.as_str())?;
s.serialize_field("lang", lang)?;
s.end()
} else {
serializer.serialize_str(self.value.as_str())
}
}
}
#[cfg(not(feature = "per_element_lang"))]
impl<'de> serde::Deserialize<'de> for UrlValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let url = url::Url::parse(&s).map_err(serde::de::Error::custom)?;
Ok(UrlValue::new(url))
}
}
#[cfg(feature = "per_element_lang")]
impl<'de> serde::Deserialize<'de> for UrlValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, MapAccess, Visitor};
use std::fmt;
struct UrlValueVisitor;
impl<'de> Visitor<'de> for UrlValueVisitor {
type Value = UrlValue;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(
"a URL string or an object with \"value\" and optional \"lang\" fields",
)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let url = url::Url::parse(value).map_err(de::Error::custom)?;
Ok(UrlValue::new(url))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: de::Error,
{
let url = url::Url::parse(&value).map_err(de::Error::custom)?;
Ok(UrlValue::new(url))
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut value: Option<String> = None;
let mut lang: Option<String> = None;
while let Some(key) = map.next_key()? {
match key {
"value" => {
if value.is_some() {
return Err(de::Error::duplicate_field("value"));
}
value = Some(map.next_value()?);
}
"lang" => {
if lang.is_some() {
return Err(de::Error::duplicate_field("lang"));
}
lang = Some(map.next_value()?);
}
_ => {
return Err(de::Error::unknown_field(key, &["value", "lang"]));
}
}
}
let value_str = value.ok_or_else(|| de::Error::missing_field("value"))?;
let url = url::Url::parse(&value_str).map_err(de::Error::custom)?;
Ok(UrlValue { value: url, lang })
}
}
deserializer.deserialize_any(UrlValueVisitor)
}
}
#[cfg(test)]
mod test_per_element_lang {
use super::*;
#[test]
#[cfg(feature = "per_element_lang")]
fn text_value_without_lang_serializes_as_string() {
let tv = TextValue::new("hello".to_string());
assert_eq!(serde_json::to_string(&tv).unwrap(), r#""hello""#);
}
#[test]
#[cfg(feature = "per_element_lang")]
fn text_value_with_lang_serializes_as_object() {
let tv = TextValue::with_lang("hello".to_string(), "en");
let json = serde_json::to_string(&tv).unwrap();
assert!(json.contains(r#""value":"hello""#));
assert!(json.contains(r#""lang":"en""#));
}
#[test]
#[cfg(feature = "per_element_lang")]
fn text_value_deserializes_string() {
let tv: TextValue = serde_json::from_str("\"hello\"").unwrap();
assert_eq!(&*tv, "hello");
assert_eq!(tv.lang(), None);
}
#[test]
#[cfg(feature = "per_element_lang")]
fn text_value_deserializes_object() {
let tv: TextValue = serde_json::from_str("{\"value\":\"hello\",\"lang\":\"en\"}").unwrap();
assert_eq!(&*tv, "hello");
assert_eq!(tv.lang(), Some("en"));
}
#[test]
#[cfg(feature = "per_element_lang")]
fn url_value_without_lang_serializes_as_string() {
let uv = UrlValue::new("https://example.com".parse().unwrap());
assert_eq!(
serde_json::to_string(&uv).unwrap(),
r#""https://example.com/""#
);
}
#[test]
#[cfg(feature = "per_element_lang")]
fn url_value_with_lang_serializes_as_object() {
let uv = UrlValue::with_lang("https://example.com".parse().unwrap(), "en");
let json = serde_json::to_string(&uv).unwrap();
assert!(json.contains(r#""value":"https://example.com/""#));
assert!(json.contains(r#""lang":"en""#));
}
#[test]
#[cfg(feature = "per_element_lang")]
fn text_value_deref_works() {
let tv = TextValue::new("hello".to_string());
assert_eq!(tv.len(), 5); assert_eq!(&tv[..], "hello");
}
#[test]
#[cfg(feature = "per_element_lang")]
fn url_value_deref_works() {
let uv = UrlValue::new("https://example.com/path".parse().unwrap());
assert_eq!(uv.path(), "/path"); }
}