valico/json_schema/
helpers.rs1use serde_json::Value;
2use serde_json::Value::Number;
3use url::Url;
4use uuid::Uuid;
5
6use super::schema;
7
8pub fn generate_id() -> Url {
9 let uuid = Uuid::new_v4();
10 Url::parse(&format!("json-schema://{uuid}")).unwrap()
11}
12
13pub fn encode(string: &str) -> String {
15 const QUERY_SET: percent_encoding::AsciiSet = percent_encoding::CONTROLS
16 .add(b' ')
17 .add(b'"')
18 .add(b'#')
19 .add(b'<')
20 .add(b'>')
21 .add(b'%');
22 percent_encoding::percent_encode(
23 string.replace('~', "~0").replace('/', "~1").as_bytes(),
24 &QUERY_SET,
25 )
26 .to_string()
27}
28
29pub fn connect(strings: &[&str]) -> String {
31 strings
32 .iter()
33 .map(|s| encode(s))
34 .collect::<Vec<String>>()
35 .join("/")
36}
37
38pub fn parse_url_key(key: &str, obj: &Value) -> Result<Option<Url>, schema::SchemaError> {
39 match obj.get(key) {
40 Some(value) => match value.as_str() {
41 Some(string) => Url::parse(string)
42 .map(Some)
43 .map_err(schema::SchemaError::UrlParseError),
44 None => Ok(None),
45 },
46 None => Ok(None),
47 }
48}
49
50pub fn parse_url_key_with_base(
51 key: &str,
52 obj: &Value,
53 base: &Url,
54) -> Result<Option<Url>, schema::SchemaError> {
55 match obj.get(key) {
56 Some(value) => match value.as_str() {
57 Some(string) => Url::options()
58 .base_url(Some(base))
59 .parse(string)
60 .map(Some)
61 .map_err(schema::SchemaError::UrlParseError),
62 None => Ok(None),
63 },
64 None => Ok(None),
65 }
66}
67
68pub fn alter_fragment_path(mut url: Url, new_fragment: String) -> Url {
69 let normalized_fragment = if let Some(prefix) = new_fragment.strip_prefix('/') {
70 prefix
71 } else {
72 new_fragment.as_ref()
73 };
74
75 let result_fragment = match url.fragment() {
76 Some(fragment) if !fragment.is_empty() => {
77 if !fragment.starts_with('/') {
78 let mut result_fragment = "".to_string();
79 let mut fragment_parts = fragment.split('/').map(|s| s.to_string());
80 result_fragment.push('#');
81 result_fragment.push_str(fragment_parts.next().unwrap().as_ref());
82 result_fragment.push('/');
83 result_fragment.push_str(normalized_fragment.as_ref());
84 result_fragment
85 } else {
86 "/".to_string() + normalized_fragment
87 }
88 }
89 _ => "/".to_string() + normalized_fragment,
90 };
91
92 url.set_fragment(Some(&result_fragment));
93 url
94}
95
96pub fn serialize_schema_path(url: &Url) -> (String, Option<String>) {
97 let mut url_without_fragment = url.clone();
98 url_without_fragment.set_fragment(None);
99 let mut url_str: String = url_without_fragment.into();
100
101 match url.fragment().as_ref() {
102 Some(fragment) if !fragment.is_empty() => {
103 if !fragment.starts_with('/') {
104 let fragment_parts = fragment
105 .split('/')
106 .map(|s| s.to_string())
107 .collect::<Vec<String>>();
108 url_str.push('#');
109 url_str.push_str(fragment_parts[0].as_ref());
110 let fragment = if fragment_parts.len() > 1 {
111 Some("/".to_string() + fragment_parts[1..].join("/").as_ref())
112 } else {
113 None
114 };
115 (url_str, fragment)
116 } else {
117 (url_str, Some((*fragment).to_string()))
118 }
119 }
120 _ => (url_str, None),
121 }
122}
123
124pub fn convert_boolean_schema(val: Value) -> Value {
125 match val.as_bool() {
126 Some(b) => {
127 if b {
128 json!({})
129 } else {
130 json!({"not": {}})
131 }
132 }
133 None => val,
134 }
135}
136
137pub fn is_matching(va: &Value, vb: &Value) -> bool {
138 match va {
139 Number(a) => match vb {
140 Number(b) => a.as_f64().unwrap() == b.as_f64().unwrap(),
141 _ => false,
142 },
143 _ => *va == *vb,
144 }
145}