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}