1use json_pointer::JsonPointer;
4use serde::de::{self, Deserialize, Deserializer, Visitor};
5use serde::ser::{Serialize, Serializer};
6use url::Url;
7
8use std::fmt;
9use std::str::FromStr;
10
11use crate::error::{InvalidFragment, InvalidPath};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum SchemaId {
16 Url(Url),
17 Pointer(JsonPointer<String, Vec<String>>),
18 Fragment(Fragment),
19 Path(Path),
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Fragment(String);
24
25impl FromStr for Fragment {
26 type Err = InvalidFragment;
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 match s.chars().nth(0) {
30 Some('#') => Ok(Fragment(s[1..].to_owned())),
31 _ => Err(InvalidFragment),
32 }
33 }
34}
35
36impl ToString for Fragment {
37 fn to_string(&self) -> String {
38 format!("#{}", self.0)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Path(String);
44
45impl FromStr for Path {
46 type Err = InvalidPath;
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 if s.chars().any(char::is_whitespace) {
50 Err(InvalidPath)
51 } else {
52 Ok(Path(s.to_string()))
53 }
54 }
55}
56
57impl ToString for Path {
58 fn to_string(&self) -> String {
59 self.0.clone()
60 }
61}
62
63impl<'de> Deserialize<'de> for SchemaId {
64 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65 where
66 D: Deserializer<'de>,
67 {
68 deserializer.deserialize_str(SchemaIdVisitor)
69 }
70}
71
72struct SchemaIdVisitor;
73
74impl<'de> Visitor<'de> for SchemaIdVisitor {
75 type Value = SchemaId;
76
77 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
78 write!(
79 formatter,
80 "a string that is either a Url, JsonPointer or Path"
81 )
82 }
83
84 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
85 where
86 E: de::Error,
87 {
88 Fragment::from_str(s)
89 .map(SchemaId::Fragment)
90 .or_else(|_| Url::parse(s).map(SchemaId::Url))
91 .or_else(|_| JsonPointer::from_str(s).map(SchemaId::Pointer))
92 .or_else(|_| Path::from_str(s).map(SchemaId::Path))
93 .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &self))
94 }
95}
96
97impl ToString for SchemaId {
98 fn to_string(&self) -> String {
99 match self {
100 Self::Pointer(p) => p.to_string(),
101 Self::Url(u) => u.to_string(),
102 Self::Fragment(f) => f.to_string(),
103 Self::Path(p) => p.to_string(),
104 }
105 }
106}
107
108impl Serialize for SchemaId {
109 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110 where
111 S: Serializer,
112 {
113 serializer.serialize_str(&self.to_string())
114 }
115}