etf_matcher_vector_config_loader/
lib.rs

1#[cfg(doctest)]
2doc_comment::doctest!("../README.md");
3
4use reqwest;
5use serde::Deserialize;
6use std::collections::BTreeMap;
7use std::error::Error;
8
9static BASE_URL: &str = "https://etfmatcher.com/data/";
10
11/// Represents the configuration for a ticker vector file.
12/// This struct is deserialized from the TOML configuration file.
13#[derive(Debug, Deserialize, Clone)]
14pub struct TickerVectorConfig {
15    /// File path of the ticker vector.
16    pub path: String,
17    /// Description of the dataset.
18    pub description: Option<String>,
19    /// Notebook used to generate the dataset.
20    pub proto_noteboook: Option<String>,
21    /// Timestamp of last training.
22    pub last_training_time: Option<String>,
23    /// Number of features used in the dataset.
24    pub features: Option<u32>,
25    /// Dimensionality of the vector representation.
26    pub vector_dimensions: Option<u32>,
27    /// Sequence length used in training.
28    pub training_sequence_length: Option<u32>,
29    /// List of data sources used for training.
30    pub training_data_sources: Option<Vec<String>>,
31}
32
33pub type TickerVectorConfigMap = BTreeMap<String, TickerVectorConfig>;
34
35/// Represents the structure of the entire TOML configuration file.
36/// The configuration contains multiple named ticker vector configurations.
37#[derive(Debug, Deserialize)]
38pub struct Config {
39    #[serde(rename = "ticker_vector_config")]
40    pub ticker_vector_config: TickerVectorConfigMap,
41}
42
43/// Fetches all ETF Matcher ticker vector configurations.
44///
45/// # Returns
46/// * `Ok(TickerVectorConfigMap)` if the request succeeds.
47/// * `Err(Box<dyn std::error::Error>)` if the request fails.
48///
49/// # Example
50/// ```
51/// use etf_matcher_vector_config_loader::get_all_etf_matcher_configs;
52/// let configs = get_all_etf_matcher_configs().unwrap();
53/// println!("Loaded {} configurations", configs.len());
54/// ```
55pub fn get_all_etf_matcher_configs() -> Result<TickerVectorConfigMap, Box<dyn std::error::Error>> {
56    load_all_configs_from_url(&format!("{}ticker_vector_configs.toml", BASE_URL))
57}
58
59/// Fetches a specific ETF Matcher ticker vector configuration by key.
60///
61/// # Arguments
62/// * `key` - The name of the configuration to retrieve.
63///
64/// # Returns
65/// * `Ok(TickerVectorConfig)` if the key exists.
66/// * `Err(Box<dyn std::error::Error>)` if the key is not found.
67///
68/// # Example
69/// ```
70/// use etf_matcher_vector_config_loader::get_etf_matcher_config_by_key;
71/// let config = get_etf_matcher_config_by_key("default").unwrap();
72/// println!("Config path: {}", config.path);
73/// ```
74pub fn get_etf_matcher_config_by_key(
75    key: &str,
76) -> Result<TickerVectorConfig, Box<dyn std::error::Error>> {
77    let all_configs = get_all_etf_matcher_configs()?;
78
79    let selected_config = get_config_by_key(&all_configs, key)
80        .ok_or_else(|| format!("Config for key '{}' not found", key))?;
81
82    Ok(selected_config.clone())
83}
84
85/// Fetches the ticker vectors collection using a specific ETF Matcher configuration key.
86///
87/// # Arguments
88/// * `key` - The name of the configuration to retrieve.
89///
90/// # Returns
91/// * `Ok(Vec<u8>)` containing the binary data.
92/// * `Err(Box<dyn std::error::Error>)` if fetching fails.
93///
94/// # Example
95/// ```
96/// use etf_matcher_vector_config_loader::get_ticker_vectors_collection_by_key;
97/// let data = get_ticker_vectors_collection_by_key("v5-sma-lstm-stacks").unwrap();
98/// println!("Downloaded {} bytes of ticker vectors", data.len());
99/// ```
100pub fn get_ticker_vectors_collection_by_key(
101    key: &str,
102) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
103    // Fetch the configuration by key
104    let config = get_etf_matcher_config_by_key(key)?;
105
106    // Fetch the ticker vectors collection file using the config path
107    get_resource(&config.path)
108}
109
110/// Retrieves the fully qualified URL for the ticker symbol map file.
111///
112/// # Returns
113/// * A `String` containing the complete URL.
114///
115/// # Example
116/// ```
117/// use etf_matcher_vector_config_loader::get_symbol_map_url;
118/// let url = get_symbol_map_url();
119/// println!("Symbol map URL: {}", url);
120/// ```
121pub fn get_symbol_map_url() -> String {
122    get_resource_url("ticker_symbol_map.flatbuffers.bin")
123}
124
125/// Fetches the ETF Matcher ticker symbol map as raw bytes.
126///
127/// # Returns
128/// * `Ok(Vec<u8>)` containing the binary data.
129/// * `Err(Box<dyn std::error::Error>)` if the request fails.
130///
131/// # Example
132/// ```
133/// use etf_matcher_vector_config_loader::get_symbol_map;
134/// let data = get_symbol_map().unwrap();
135/// println!("Downloaded {} bytes", data.len());
136/// ```
137pub fn get_symbol_map() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
138    get_resource(&get_symbol_map_url())
139}
140
141/// Constructs a fully qualified URL for a given filename.
142///
143/// # Arguments
144/// * `filename` - The name of the file to create a full URL for.
145///
146/// # Returns
147/// * A `String` containing the full URL.
148///
149/// # Example
150/// ```
151/// use etf_matcher_vector_config_loader::get_resource_url;
152/// let url = get_resource_url("dataset.bin");
153/// assert_eq!(url, "https://etfmatcher.com/data/dataset.bin");
154/// ```
155pub fn get_resource_url(filename: &str) -> String {
156    format!("{}{}", BASE_URL, filename)
157}
158
159/// Fetches a resource, automatically determining if it's a filename or a full URL.
160///
161/// # Arguments
162/// * `path` - Either a filename (e.g., `"dataset.bin"`) or a full URL (`"https://example.com/data.bin"`).
163///
164/// # Returns
165/// * `Ok(Vec<u8>)` containing the binary data.
166/// * `Err(Box<dyn std::error::Error>)` if the request fails.
167///
168/// # Example
169/// ```
170/// use etf_matcher_vector_config_loader::get_resource;
171///
172/// // Fetch using a filename (automatically constructs full URL)
173/// let data = get_resource("sample.bin").unwrap();
174///
175/// // Fetch using a full URL
176/// let data = get_resource("https://example.com/data.bin").unwrap();
177/// ```
178pub fn get_resource(path: &str) -> Result<Vec<u8>, Box<dyn Error>> {
179    // Check if the input looks like a full URL
180    let url = if path.starts_with("http://") || path.starts_with("https://") {
181        path.to_string() // Already an FQDN, use as-is
182    } else {
183        get_resource_url(path) // It's a filename, construct full URL
184    };
185
186    let response = reqwest::blocking::get(url)?.bytes()?;
187    Ok(response.to_vec())
188}
189
190/// Fetches the ETF Matcher ticker vector configurations from a remote TOML file.
191///
192/// # Arguments
193/// * `url` - The URL of the TOML configuration file.
194///
195/// # Returns
196/// * `Ok(TickerVectorConfigMap)` on success.
197/// * `Err(Box<dyn std::error::Error>)` if the request fails or the TOML parsing fails.
198///
199/// # Example
200/// ```
201/// use etf_matcher_vector_config_loader::load_all_configs_from_url;
202/// let configs = load_all_configs_from_url("https://etfmatcher.com/data/ticker_vector_configs.toml").unwrap();
203/// println!("Loaded {} configurations", configs.len());
204/// ```
205pub fn load_all_configs_from_url(
206    url: &str,
207) -> Result<TickerVectorConfigMap, Box<dyn std::error::Error>> {
208    // Fetch the TOML file from the remote URL.
209    let response = reqwest::blocking::get(url)?.text()?;
210
211    // Parse the TOML content into a Config struct.
212    let config: Config = toml::from_str(&response)?;
213
214    // Return all configurations as a BTreeMap.
215    Ok(config.ticker_vector_config)
216}
217
218/// Retrieves a specific configuration from the loaded ETF Matcher configurations.
219///
220/// # Arguments
221/// * `configs` - A reference to the `BTreeMap` containing configurations.
222/// * `key` - The key name of the configuration to retrieve.
223///
224/// # Returns
225/// * `Some(&TickerVectorConfig)` if the key exists.
226/// * `None` if the key does not exist.
227///
228/// # Example
229/// ```
230/// use etf_matcher_vector_config_loader::{load_all_configs_from_url, get_config_by_key};
231/// let configs = load_all_configs_from_url("https://etfmatcher.com/data/ticker_vector_configs.toml").unwrap();
232/// let config = get_config_by_key(&configs, "default");
233/// assert!(config.is_some());
234/// ```
235pub fn get_config_by_key<'a>(
236    configs: &'a TickerVectorConfigMap,
237    key: &str,
238) -> Option<&'a TickerVectorConfig> {
239    configs.get(key)
240}