1use crate::symbology::MarketdataVenue;
4use anyhow::{anyhow, Context, Result};
5use serde::{Deserialize, Serialize};
6use std::{
7 collections::BTreeMap,
8 path::{Path, PathBuf},
9};
10use url::Url;
11
12#[derive(Debug, Default, Clone, Serialize, Deserialize)]
13pub struct Config {
14 pub license: Option<PathBuf>,
17 pub license_key: Option<PathBuf>,
21 #[serde(default)]
23 pub secrets: Option<PathBuf>,
24 #[serde(default)]
25 pub userdb: Option<Url>,
26 #[serde(default)]
27 pub symbology: Option<Url>,
28 #[serde(default)]
29 pub marketdata: BTreeMap<MarketdataVenue, Url>,
30 #[serde(default)]
32 pub core: Option<Url>,
33}
34
35impl Config {
36 pub fn license(&self) -> Result<Option<PathBuf>> {
37 canonicalize(&self.license)
38 }
39
40 pub fn license_key(&self) -> Result<Option<PathBuf>> {
41 canonicalize(&self.license_key)
42 }
43
44 pub fn secrets(&self) -> Result<Option<PathBuf>> {
45 canonicalize(&self.secrets)
46 }
47}
48
49fn canonicalize(path: &Option<impl AsRef<Path>>) -> Result<Option<PathBuf>> {
50 if let Some(path) = path {
51 let expanded = expand_tilde(path).ok_or_else(|| {
52 anyhow!("while expanding tilde: {}", path.as_ref().display())
53 })?;
54 let canonicalized = match std::fs::canonicalize(&expanded)
55 .with_context(|| format!("while resolving path: {}", expanded.display()))
56 {
57 Ok(canon) => canon,
58 Err(_e) => {
59 expanded
62 }
63 };
64 Ok(Some(canonicalized))
65 } else {
66 Ok(None)
67 }
68}
69
70fn expand_tilde<P: AsRef<Path>>(path_user_input: P) -> Option<PathBuf> {
71 let p = path_user_input.as_ref();
72 if !p.starts_with("~") {
73 return Some(p.to_path_buf());
74 }
75 if p == Path::new("~") {
76 return dirs::home_dir();
77 }
78 dirs::home_dir().map(|mut h| {
79 if h == Path::new("/") {
80 p.strip_prefix("~").unwrap().to_path_buf()
83 } else {
84 h.push(p.strip_prefix("~/").unwrap());
85 h
86 }
87 })
88}