open_payments/client/
utils.rs

1//! # Open Payments Client Utilities
2//!
3//! This module provides utility functions for URL manipulation and resource server
4//! URL extraction that are commonly used throughout the Open Payments client.
5//!
6//! ## Functions
7//!
8//! - [`get_resource_server_url`] - Extract resource server URL from wallet address
9//! - [`join_url_paths`] - Safely join URL paths with proper handling
10//!
11use crate::OpClientError;
12use crate::Result;
13use url::Url;
14
15/// Extracts the resource server URL from a wallet address URL.
16///
17/// This function takes a wallet address URL
18/// and extracts the base resource server URL by removing the wallet address portion.
19///
20/// ## Arguments
21///
22/// * `wallet_address_url` - The complete wallet address URL path
23///
24/// ## Returns
25///
26/// Returns the resource server base URL as a string, or an error if the URL cannot be parsed.
27///
28/// ## Errors
29///
30/// - `OpClientError::Url` if the wallet address URL cannot be parsed
31pub fn get_resource_server_url(wallet_address_url: &str) -> Result<String> {
32    let url = Url::parse(wallet_address_url).map_err(OpClientError::Url)?;
33
34    let path_segments: Vec<&str> = url
35        .path_segments()
36        .map(|segments| segments.collect())
37        .unwrap_or_default();
38
39    // Remove the last segment (wallet address) to get the resource server URL
40    let resource_server_path = if path_segments.len() > 1 {
41        path_segments[..path_segments.len() - 1].join("/")
42    } else {
43        String::new()
44    };
45
46    let mut resource_url = url;
47
48    if resource_server_path.is_empty() {
49        resource_url.set_path("/");
50    } else {
51        resource_url.set_path(&format!("/{}", resource_server_path));
52    }
53
54    Ok(resource_url.to_string())
55}
56
57/// Safely joins a base URL with a path component.
58///
59/// This function ensures proper URL path joining by handling trailing slashes
60/// and ensuring the resulting URL is valid. It's useful for constructing
61/// API endpoint URLs from base URLs and relative paths.
62///
63/// ## Arguments
64///
65/// * `base_url` - The base URL to join with the path
66/// * `path` - The path component to append to the base URL
67///
68/// ## Returns
69///
70/// Returns the joined URL as a string, or an error if URL parsing fails.
71///
72/// ## Errors
73///
74/// - `OpClientError::Url` if the base URL cannot be parsed or the path cannot be joined
75pub fn join_url_paths(base_url: &str, path: &str) -> Result<String> {
76    let mut url = Url::parse(base_url).map_err(OpClientError::Url)?;
77
78    if path.is_empty() {
79        return Ok(url.to_string());
80    }
81
82    if !url.path().ends_with('/') {
83        url.set_path(&format!("{}/", url.path()));
84    }
85
86    let joined_url = url.join(path).map_err(OpClientError::Url)?;
87    Ok(joined_url.to_string())
88}