facebook_graph_api_explorer_kit/
path_and_query.rs

1use url::{ParseError as UrlParseError, Url};
2
3const PREFIX: &str = "https://graph.facebook.com/v11.0";
4
5pub fn parse<'a>(path_and_query: &str, node_type: Option<&'a str>) -> Result<Root<'a>, ParseError> {
6    let url = if path_and_query.is_empty() {
7        return Err(ParseError::PathInvalid("IsEmpty"));
8    } else if path_and_query.starts_with('/') {
9        return Err(ParseError::PathInvalid("IsStartsWithSlash"));
10    } else {
11        format!("{}/{}", PREFIX, path_and_query)
12    };
13    let mut url = Url::parse(&url)?;
14    let path = url.path().to_owned();
15    if path.ends_with('/') {
16        url.set_path(&path[..path.len() - 1]);
17    }
18    let mut path_segments = url.path_segments().expect("");
19    debug_assert_eq!(path_segments.next(), Some("v11.0"));
20    let mut _query_pairs = url.query_pairs();
21
22    let root = path_segments.next().ok_or(ParseError::RootMissing)?;
23    let mut root = if let Ok(_node_id) = root.parse::<u64>() {
24        let node_type = node_type.ok_or(ParseError::NodeTypeMissing)?;
25        Root::Node(node_type, None)
26    } else {
27        match root {
28            "me" => Root::Node("User", None),
29            "ig_hashtag_search" => Root::Edge(root.to_owned()),
30            _ => return Err(ParseError::RootIsUnknown),
31        }
32    };
33    match root {
34        Root::Node(node_type, _) => {
35            if let Some(edge) = path_segments.next() {
36                root = Root::Node(node_type, Some(edge.to_owned()))
37            }
38            if path_segments.next().is_some() {
39                return Err(ParseError::PathInvalid("TODO"));
40            }
41        }
42        Root::Edge(_) => {
43            if path_segments.next().is_some() {
44                return Err(ParseError::PathInvalid("TODO"));
45            }
46        }
47    }
48
49    Ok(root)
50}
51
52#[derive(PartialEq, Eq, Debug, Clone)]
53pub enum Root<'a> {
54    Node(&'a str, Option<String>),
55    Edge(String),
56}
57
58#[derive(thiserror::Error, Debug)]
59pub enum ParseError {
60    #[error("PathInvalid {0}")]
61    PathInvalid(&'static str),
62    #[error("PathOrQueryInvalid {0}")]
63    PathOrQueryInvalid(#[from] UrlParseError),
64    #[error("RootMissing")]
65    RootMissing,
66    #[error("RootIsUnknown")]
67    RootIsUnknown,
68    #[error("NodeTypeMissing")]
69    NodeTypeMissing,
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_parse() {
78        match parse("", None) {
79            Err(ParseError::PathInvalid(_)) => {}
80            err => panic!("{:?}", err),
81        }
82        match parse("/", None) {
83            Err(ParseError::PathInvalid(_)) => {}
84            err => panic!("{:?}", err),
85        }
86
87        match parse("foo", None) {
88            Err(ParseError::RootIsUnknown) => {}
89            err => panic!("{:?}", err),
90        }
91
92        match parse("me", None) {
93            Ok(root) => assert_eq!(root, Root::Node("User", None)),
94            err => panic!("{:?}", err),
95        }
96        match parse("780170842505209", Some("User")) {
97            Ok(root) => assert_eq!(root, Root::Node("User", None)),
98            err => panic!("{:?}", err),
99        }
100        match parse("me?fields=id,name", None) {
101            Ok(root) => assert_eq!(root, Root::Node("User", None)),
102            err => panic!("{:?}", err),
103        }
104        match parse("me/accounts", None) {
105            Ok(root) => assert_eq!(root, Root::Node("User", Some("accounts".to_owned()))),
106            err => panic!("{:?}", err),
107        }
108        match parse("me/accounts?fields=id,name", None) {
109            Ok(root) => assert_eq!(root, Root::Node("User", Some("accounts".to_owned()))),
110            err => panic!("{:?}", err),
111        }
112        match parse("me/accounts/foo", None) {
113            Err(ParseError::PathInvalid(_)) => {}
114            err => panic!("{:?}", err),
115        }
116
117        match parse(
118            "ig_hashtag_search?user_id=17841406427775093&q=bluebottle",
119            None,
120        ) {
121            Ok(root) => assert_eq!(root, Root::Edge("ig_hashtag_search".to_owned())),
122            err => panic!("{:?}", err),
123        }
124        match parse("ig_hashtag_search/foo", None) {
125            Err(ParseError::PathInvalid(_)) => {}
126            err => panic!("{:?}", err),
127        }
128    }
129}