ui_cli/shared/
rust_ui_client.rs

1use crate::shared::cli_error::{CliError, CliResult};
2use crate::shared::markdown_utils::extract_rust_code_from_markdown;
3
4pub struct RustUIClient;
5
6impl RustUIClient {
7    const BASE_URL: &str = "https://www.rust-ui.com/registry";
8    const SITE_URL: &str = "https://www.rust-ui.com";
9
10    // URL builders - centralized URL construction
11    fn tree_url() -> String {
12        format!("{}/tree.md", Self::BASE_URL)
13    }
14
15    fn component_url(component_name: &str) -> String {
16        format!("{}/styles/default/{component_name}.md", Self::BASE_URL)
17    }
18
19    pub fn styles_index_url() -> String {
20        format!("{}/styles/index.json", Self::BASE_URL)
21    }
22
23    fn js_file_url(path: &str) -> String {
24        format!("{}{path}", Self::SITE_URL)
25    }
26
27    // Consolidated HTTP fetch method
28    async fn fetch_response(url: &str) -> CliResult<reqwest::Response> {
29        let response = reqwest::get(url).await.map_err(|_| CliError::registry_request_failed())?;
30
31        if !response.status().is_success() {
32            return Err(CliError::registry_request_failed());
33        }
34
35        Ok(response)
36    }
37
38    // Public API methods
39    pub async fn fetch_tree_md() -> CliResult<String> {
40        let response = Self::fetch_response(&Self::tree_url()).await?;
41        let content = response.text().await.map_err(|_| CliError::registry_request_failed())?;
42
43        if content.is_empty() {
44            return Err(CliError::registry_request_failed());
45        }
46
47        Ok(content)
48    }
49
50    pub async fn fetch_styles_default(component_name: &str) -> CliResult<String> {
51        let response = Self::fetch_response(&Self::component_url(component_name)).await?;
52        let markdown_content = response.text().await.map_err(|_| CliError::registry_request_failed())?;
53
54        extract_rust_code_from_markdown(&markdown_content).ok_or_else(CliError::registry_component_missing)
55    }
56
57    pub async fn fetch_styles_index() -> CliResult<String> {
58        let response = Self::fetch_response(&Self::styles_index_url()).await?;
59        let json =
60            response.json::<serde_json::Value>().await.map_err(|_| CliError::registry_invalid_format())?;
61
62        serde_json::to_string_pretty(&json)
63            .map_err(|err| CliError::malformed_registry(&format!("Failed to convert to pretty JSON: {err}")))
64    }
65
66    /// Fetch a JS file from the site (e.g., /hooks/lock_scroll.js)
67    pub async fn fetch_js_file(path: &str) -> CliResult<String> {
68        let response = Self::fetch_response(&Self::js_file_url(path)).await?;
69        let content = response.text().await.map_err(|_| CliError::registry_request_failed())?;
70
71        if content.is_empty() {
72            return Err(CliError::registry_request_failed());
73        }
74
75        Ok(content)
76    }
77}