Skip to main content

github_mcp/core/
api_url_builder.rs

1// GitHub v3 REST API MCP server — generated by mcpify. Do not hand-edit.
2
3use url::Url;
4
5/// Joins a configured base URL with an operation path and query params,
6/// normalizing slashes so callers never have to think about
7/// trailing/leading separators.
8pub fn build_api_url(
9    base_url: &str,
10    path: &str,
11    query: &[(String, String)],
12) -> anyhow::Result<String> {
13    let base = if base_url.ends_with('/') {
14        base_url.to_string()
15    } else {
16        format!("{base_url}/")
17    };
18    let base = Url::parse(&base)?;
19    let mut url = base.join(path.trim_start_matches('/'))?;
20
21    // `query_pairs_mut()` unconditionally leaves a trailing `?` behind even
22    // when zero pairs are appended to it, so it's only touched when there's
23    // actually something to add.
24    if !query.is_empty() {
25        let mut pairs = url.query_pairs_mut();
26        for (key, value) in query {
27            pairs.append_pair(key, value);
28        }
29    }
30
31    Ok(url.to_string())
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn joins_a_base_url_and_path_without_double_slashes() {
40        let url = build_api_url("https://api.example.com", "/widgets", &[]).unwrap();
41        assert_eq!(url, "https://api.example.com/widgets");
42    }
43
44    #[test]
45    fn handles_a_base_url_without_a_trailing_slash() {
46        let url = build_api_url("https://api.example.com/v1", "widgets", &[]).unwrap();
47        assert_eq!(url, "https://api.example.com/v1/widgets");
48    }
49
50    #[test]
51    fn appends_query_parameters() {
52        let url = build_api_url(
53            "https://api.example.com",
54            "/widgets",
55            &[("limit".to_string(), "10".to_string())],
56        )
57        .unwrap();
58        assert_eq!(url, "https://api.example.com/widgets?limit=10");
59    }
60}