1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use serde_json::{Value};
use url::{Url};
use url::percent_encoding;
use uuid::{Uuid};

use super::schema;

pub fn generate_id() -> Url {
    let uuid = Uuid::new_v4();
    Url::parse(&format!("json-schema://{}", uuid)).unwrap()
}

/// http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
pub fn encode(string: &str) -> String {
    percent_encoding::percent_encode(
        string
            .replace("~", "~0")
            .replace("/", "~1")
            .replace("%", "%25")
            .as_bytes(),
        percent_encoding::QUERY_ENCODE_SET
    ).to_string()
}

/// Encode and connect
pub fn connect(strings: &[&str]) -> String {
    strings.iter().map(|s| encode(s)).collect::<Vec<String>>().join("/")
}

pub fn parse_url_key(key: &str, obj: &Value) -> Result<Option<Url>, schema::SchemaError> {
    match obj.get(key) {
        Some(value) => {
            match value.as_str() {
                Some(string) => Url::parse(string)
                                .map(|url| Some(url))
                                .map_err(|err| schema::SchemaError::UrlParseError(err)),
                None => Ok(None)
            }
        },
        None => Ok(None)
    }
}

pub fn parse_url_key_with_base(key: &str, obj: &Value, base: &Url) -> Result<Option<Url>, schema::SchemaError> {
    match obj.get(key) {
        Some(value) => {
            match value.as_str() {
                Some(string) => Url::options()
                                .base_url(Some(base))
                                .parse(string)
                                .map(|url| Some(url))
                                .map_err(|err| schema::SchemaError::UrlParseError(err)),
                None => Ok(None)
            }
        },
        None => Ok(None)
    }
}

pub fn alter_fragment_path(mut url: Url, new_fragment: String) -> Url {

    let normalized_fragment = if new_fragment.starts_with("/") {
        &new_fragment[1..]
    } else {
        new_fragment.as_ref()
    };

    let result_fragment = match url.fragment() {
        Some(ref fragment) if fragment.len() > 0 => {
            if !fragment.starts_with("/") {
                let mut result_fragment = "".to_string();
                let mut fragment_parts = fragment.split("/").map(|s| s.to_string());
                result_fragment.push_str("#");
                result_fragment.push_str(fragment_parts.next().unwrap().as_ref());
                result_fragment.push_str("/");
                result_fragment.push_str(normalized_fragment.as_ref());
                result_fragment
            } else {
                "/".to_string() + normalized_fragment
            }
        },
        _ => "/".to_string() + normalized_fragment
    };

    url.set_fragment(Some(&result_fragment));
    url
}

pub fn serialize_schema_path(url: &Url) -> (String, Option<String>) {
    let mut url_without_fragment = url.clone();
    url_without_fragment.set_fragment(None);
    let mut url_str = url_without_fragment.into_string();

    match url.fragment().as_ref() {
        Some(fragment) if fragment.len() > 0 => {
            if !fragment.starts_with("/") {
                let fragment_parts = fragment.split("/").map(|s| s.to_string()).collect::<Vec<String>>();
                url_str.push_str("#");
                url_str.push_str(fragment_parts[0].as_ref());
                let fragment = if fragment_parts.len() > 1 {
                    Some("/".to_string() + fragment_parts[1..].join("/").as_ref())
                } else {
                    None
                };
                (url_str, fragment)
            } else {
                (url_str, Some(fragment.to_string()))
            }

        },
        _ => (url_str, None)
    }
}