Skip to main content

egui_charts/ui/dialogs/indicator_dialog/
data.rs

1//! Study browser entries and saved favorites.
2//!
3//! Use [`IndicatorPreferences`] to retain favorite factory names and
4//! [`IndicatorInfo`] to describe studies in a picker.
5
6use crate::studies::IndicatorFactory;
7
8/// Host-owned indicator browser settings. Save these alongside the user's other settings
9/// after [`IndicatorDialogAction::ToggleFavorite`](super::IndicatorDialogAction::ToggleFavorite),
10/// and restore them with [`IndicatorDialog::set_preferences`](super::IndicatorDialog::set_preferences).
11/// Unknown study names are retained so temporarily unavailable host studies keep their stars.
12#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
13pub struct IndicatorPreferences {
14    /// Favourite factory names, sorted for stable saved documents.
15    #[serde(default)]
16    pub favorites: std::collections::BTreeSet<String>,
17}
18
19/// Indicator information
20#[derive(Debug, Clone)]
21pub struct IndicatorInfo {
22    /// The name the dialog's [`IndicatorFactory`] creates the study under
23    pub id: String,
24    /// Display name.
25    pub name: String,
26    /// Description displayed to the user.
27    pub desc: String,
28    /// Category label used to group the entry in the browser.
29    pub category: String,
30    /// Whether the study draws on the price pane by default.
31    pub is_overlay: bool,
32    /// Display flag for a premium entry; access control belongs to the host.
33    pub is_premium: bool,
34}
35
36impl IndicatorInfo {
37    /// Create a new indicator info entry with the given ID, name, and description
38    pub fn new(id: impl Into<String>, name: impl Into<String>, desc: impl Into<String>) -> Self {
39        Self {
40            id: id.into(),
41            name: name.into(),
42            desc: desc.into(),
43            category: "Technical Analysis".to_string(),
44            is_overlay: false,
45            is_premium: false,
46        }
47    }
48
49    /// Mark this indicator as an overlay (renders on the price chart)
50    pub fn overlay(mut self) -> Self {
51        self.is_overlay = true;
52        self
53    }
54
55    /// Mark this entry as premium for display. Access control belongs to the host.
56    pub fn premium(mut self) -> Self {
57        self.is_premium = true;
58        self
59    }
60
61    /// Every study `factory` creates, sorted by display name, with its description, category
62    /// and overlay flag
63    pub(super) fn from_factory(factory: &IndicatorFactory) -> Vec<IndicatorInfo> {
64        let mut infos: Vec<IndicatorInfo> = factory
65            .list()
66            .into_iter()
67            .filter_map(|id| {
68                let study = factory.create(&id)?;
69                // A description reads "Full name - what it measures", as `Indicator::desc`
70                // shows; one without a full name keeps the registered name.
71                let (name, desc) = study
72                    .desc()
73                    .split_once(" - ")
74                    .unwrap_or((id.as_str(), study.desc()));
75                let mut info = IndicatorInfo::new(id.clone(), name, desc);
76                info.category = factory.category(&id).unwrap_or_default().to_string();
77                info.is_overlay = study.is_overlay();
78                Some(info)
79            })
80            .collect();
81        infos.sort_by_key(|info| info.name.to_lowercase());
82        infos
83    }
84}