egui_charts/studies/
error.rs1use super::Indicator;
4#[cfg(feature = "studies")]
5use super::StudyInput;
6use std::fmt;
7use std::sync::{Mutex, PoisonError};
8
9#[derive(Debug)]
11#[non_exhaustive]
12pub enum StudyError {
13 UnknownStudy(String),
15 UnknownStudyId(usize),
17 UnknownInput(String),
19 InvalidInput {
21 id: String,
23 reason: String,
25 },
26 EmptyInputId {
28 index: usize,
30 },
31 NotConfigured,
33 InvalidPrecision(usize),
35 InvalidPlotWidth {
37 plot: usize,
39 width: f32,
41 },
42 InvalidLevelValue {
44 level: usize,
46 value: f64,
48 },
49 PlotCount {
51 expected: usize,
53 found: usize,
55 },
56 UnknownSourceStudy(usize),
58 UnknownSourcePlot {
60 id: usize,
62 plot: usize,
64 },
65 CyclicSource,
67 StudyLimitReached(RefusedStudy),
70 NotClipboard,
72 Clipboard(serde_json::Error),
74}
75
76impl StudyError {
77 pub(crate) fn invalid_input(id: &str) -> Self {
79 Self::InvalidInput {
80 id: id.to_owned(),
81 reason: "is outside its accepted values".into(),
82 }
83 }
84
85 #[cfg(feature = "studies")]
87 pub(crate) fn rejected(inputs: &[StudyInput], id: &str) -> Self {
88 if inputs.iter().any(|input| input.id == id) {
89 Self::invalid_input(id)
90 } else {
91 Self::UnknownInput(id.to_owned())
92 }
93 }
94}
95
96pub struct RefusedStudy(Mutex<Box<dyn Indicator>>);
100
101impl RefusedStudy {
102 #[cfg(feature = "widget")]
103 pub(crate) fn new(study: Box<dyn Indicator>) -> Self {
104 Self(Mutex::new(study))
105 }
106
107 pub fn into_inner(self) -> Box<dyn Indicator> {
109 self.0.into_inner().unwrap_or_else(PoisonError::into_inner)
110 }
111}
112
113impl fmt::Debug for RefusedStudy {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 let study = self.0.lock().unwrap_or_else(PoisonError::into_inner);
116 f.debug_tuple("RefusedStudy").field(&study.name()).finish()
117 }
118}
119
120impl fmt::Display for StudyError {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 Self::UnknownStudy(name) => write!(f, "unknown study {name:?}"),
124 Self::UnknownStudyId(id) => write!(f, "no study has id {id}"),
125 Self::UnknownInput(id) => write!(f, "unknown study input {id:?}"),
126 Self::InvalidInput { id, reason } => write!(f, "study input {id:?} {reason}"),
127 Self::EmptyInputId { index } => write!(f, "study input {index} has an empty id"),
128 Self::NotConfigured => {
129 f.write_str("wrap the study in ConfiguredStudy to edit its presentation")
130 }
131 Self::InvalidPrecision(p) => {
132 write!(f, "study precision must be at most 12 decimals, got {p}")
133 }
134 Self::InvalidPlotWidth { plot, width } => write!(
135 f,
136 "plot {plot} width must be between 0.5 and 10, got {width:?}"
137 ),
138 Self::InvalidLevelValue { level, value } => {
139 write!(f, "level {level} value must be finite, got {value:?}")
140 }
141 Self::PlotCount { expected, found } => write!(
142 f,
143 "the study draws {expected} plots, got {found} plot styles"
144 ),
145 Self::UnknownSourceStudy(id) => write!(f, "no study has id {id} to read as a source"),
146 Self::UnknownSourcePlot { id, plot } => write!(f, "study {id} has no plot {plot}"),
147 Self::CyclicSource => f.write_str("the study would read its own output"),
148 Self::StudyLimitReached(_) => f.write_str("study limit reached"),
149 Self::NotClipboard => f.write_str("not a study clipboard"),
150 Self::Clipboard(error) => write!(f, "cannot read the copied study: {error}"),
151 }
152 }
153}
154impl std::error::Error for StudyError {
155 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
156 match self {
157 Self::Clipboard(error) => Some(error),
158 _ => None,
159 }
160 }
161}