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}