Skip to main content

data_gov/
config.rs

1use crate::ui::StatusReporter;
2use data_gov_catalog::Configuration as CatalogConfiguration;
3use std::fmt;
4use std::path::PathBuf;
5use std::sync::Arc;
6
7/// Operating mode for the client
8#[derive(Debug, Clone, PartialEq)]
9pub enum OperatingMode {
10    /// Interactive REPL mode - downloads to system Downloads directory
11    Interactive,
12    /// Command-line mode - downloads to current directory
13    CommandLine,
14}
15
16/// Configuration for the Data.gov client
17#[derive(Clone)]
18pub struct DataGovConfig {
19    /// Catalog API client configuration
20    pub catalog_config: Arc<CatalogConfiguration>,
21    /// Operating mode (affects base download directory)
22    pub mode: OperatingMode,
23    /// Base download directory for files (before dataset subdirectory)
24    pub base_download_dir: PathBuf,
25    /// User agent for HTTP requests
26    pub user_agent: String,
27    /// Maximum concurrent downloads
28    pub max_concurrent_downloads: usize,
29    /// Timeout for downloads in seconds
30    pub download_timeout_secs: u64,
31    /// Optional status reporter for UI callbacks
32    pub status_reporter: Option<Arc<dyn StatusReporter + Send + Sync>>,
33}
34
35impl fmt::Debug for DataGovConfig {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.debug_struct("DataGovConfig")
38            .field("catalog_config", &self.catalog_config)
39            .field("mode", &self.mode)
40            .field("base_download_dir", &self.base_download_dir)
41            .field("user_agent", &self.user_agent)
42            .field("max_concurrent_downloads", &self.max_concurrent_downloads)
43            .field("download_timeout_secs", &self.download_timeout_secs)
44            .field(
45                "status_reporter",
46                &self
47                    .status_reporter
48                    .as_ref()
49                    .map(|_| "Some(StatusReporter)"),
50            )
51            .finish()
52    }
53}
54
55impl Default for DataGovConfig {
56    fn default() -> Self {
57        Self {
58            catalog_config: Arc::new(CatalogConfiguration::default()),
59            mode: OperatingMode::Interactive,
60            base_download_dir: Self::get_default_download_dir(),
61            user_agent: concat!("data-gov-rs/", env!("CARGO_PKG_VERSION")).to_string(),
62            max_concurrent_downloads: 3,
63            download_timeout_secs: 300,
64            status_reporter: None,
65        }
66    }
67}
68
69impl DataGovConfig {
70    /// Get the default download directory (system Downloads folder).
71    fn get_default_download_dir() -> PathBuf {
72        if let Some(download_dir) = dirs::download_dir() {
73            download_dir
74        } else {
75            let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
76            home.join("Downloads")
77        }
78    }
79
80    /// Create a new configuration for data.gov.
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// Create configuration with a custom base download directory.
86    pub fn with_download_dir<P: Into<PathBuf>>(mut self, dir: P) -> Self {
87        self.base_download_dir = dir.into();
88        self
89    }
90
91    /// Set the operating mode.
92    pub fn with_mode(mut self, mode: OperatingMode) -> Self {
93        self.mode = mode;
94        self
95    }
96
97    /// Get the base download directory based on operating mode.
98    pub fn get_base_download_dir(&self) -> PathBuf {
99        match self.mode {
100            OperatingMode::Interactive => self.base_download_dir.clone(),
101            OperatingMode::CommandLine => {
102                std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
103            }
104        }
105    }
106
107    /// Get the full download directory for a specific dataset.
108    pub fn get_dataset_download_dir(&self, dataset_name: &str) -> PathBuf {
109        self.get_base_download_dir().join(dataset_name)
110    }
111
112    /// Override the Catalog API base URL (e.g., for testing with a mock server).
113    pub fn with_base_url<S: Into<String>>(mut self, base_url: S) -> Self {
114        let mut catalog_config = (*self.catalog_config).clone();
115        catalog_config.base_path = base_url.into();
116        self.catalog_config = Arc::new(catalog_config);
117        self
118    }
119
120    /// Set a custom user agent.
121    pub fn with_user_agent<S: Into<String>>(mut self, user_agent: S) -> Self {
122        self.user_agent = user_agent.into();
123        let mut catalog_config = (*self.catalog_config).clone();
124        catalog_config.user_agent = Some(self.user_agent.clone());
125        self.catalog_config = Arc::new(catalog_config);
126        self
127    }
128
129    /// Set the maximum concurrent downloads.
130    pub fn with_max_concurrent_downloads(mut self, max: usize) -> Self {
131        self.max_concurrent_downloads = max.max(1);
132        self
133    }
134
135    /// Set the download timeout.
136    pub fn with_download_timeout(mut self, timeout_secs: u64) -> Self {
137        self.download_timeout_secs = timeout_secs;
138        self
139    }
140
141    /// Attach a status reporter for UI callbacks.
142    pub fn with_status_reporter<R>(mut self, reporter: Arc<R>) -> Self
143    where
144        R: StatusReporter + Send + Sync + 'static,
145    {
146        self.status_reporter = Some(reporter);
147        self
148    }
149
150    /// Remove any configured status reporter.
151    pub fn without_status_reporter(mut self) -> Self {
152        self.status_reporter = None;
153        self
154    }
155
156    /// Borrow the configured status reporter.
157    pub fn status_reporter(&self) -> Option<&Arc<dyn StatusReporter + Send + Sync>> {
158        self.status_reporter.as_ref()
159    }
160}