1use anyhow::{Result, anyhow};
2use ebi_arithmetic::{ConstFraction, Fraction};
3use std::{collections::HashMap, fmt::Debug, hash::Hash, io::BufRead};
4use strum_macros::Display;
5
6use crate::constants::ebi_object::EbiObject;
7
8pub trait Importable {
9 const IMPORTER_PARAMETERS: &[ImporterParameter];
11
12 const FILE_FORMAT_SPECIFICATION_LATEX: &str;
14
15 fn import_as_object(
16 reader: &mut dyn BufRead,
17 parameter_values: &ImporterParameterValues,
18 ) -> Result<EbiObject>;
19
20 fn import(reader: &mut dyn BufRead, parameter_values: &ImporterParameterValues) -> Result<Self>
23 where
24 Self: Sized;
25
26 fn default_importer_parameter_values() -> ImporterParameterValues {
27 let mut result = HashMap::new();
28 for parameter in Self::IMPORTER_PARAMETERS {
29 result.insert(*parameter, parameter.default());
30 }
31 result
32 }
33}
34
35macro_rules! from_string {
36 ($t:ident) => {
37 impl std::str::FromStr for $t {
38 type Err = anyhow::Error;
39
40 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
41 let mut reader = std::io::Cursor::new(s);
42 let default_parameter_values = $t::default_importer_parameter_values();
43 Self::import(&mut reader, &default_parameter_values)
44 }
45 }
46 };
47}
48pub(crate) use from_string;
49
50pub type ImporterParameterValues = HashMap<ImporterParameter, ImporterParameterValue>;
51
52#[derive(Copy, Clone, Display)]
56pub enum ImporterParameter {
57 Flag {
58 name: &'static str,
59 short_name: &'static str,
60 explanation: &'static str,
61 },
62 String {
63 name: &'static str,
64 short_name: &'static str,
65 explanation: &'static str,
66 allowed_values: Option<&'static [&'static str]>,
67 default_value: &'static str,
68 },
69 Usize {
70 name: &'static str,
71 short_name: &'static str,
72 explanation: &'static str,
73 minimum_value: Option<usize>,
74 maximum_value: Option<usize>,
75 default_value: usize,
76 },
77 Fraction {
78 name: &'static str,
79 short_name: &'static str,
80 explanation: &'static str,
81 minimum_value: Option<ConstFraction>,
82 maximum_value: Option<ConstFraction>,
83 default_value: ConstFraction,
84 },
85}
86
87impl ImporterParameter {
88 pub fn name(&self) -> &'static str {
89 match self {
90 ImporterParameter::Flag { name, .. }
91 | ImporterParameter::String { name, .. }
92 | ImporterParameter::Usize { name, .. }
93 | ImporterParameter::Fraction { name, .. } => name,
94 }
95 }
96
97 pub fn short_name(&self) -> &'static str {
98 match self {
99 ImporterParameter::Flag { short_name, .. }
100 | ImporterParameter::String { short_name, .. }
101 | ImporterParameter::Usize { short_name, .. }
102 | ImporterParameter::Fraction { short_name, .. } => short_name,
103 }
104 }
105
106 pub fn explanation(&self) -> &'static str {
107 match self {
108 ImporterParameter::Flag { explanation, .. }
109 | ImporterParameter::String { explanation, .. }
110 | ImporterParameter::Usize { explanation, .. }
111 | ImporterParameter::Fraction { explanation, .. } => explanation,
112 }
113 }
114
115 pub fn default(&self) -> ImporterParameterValue {
116 match self {
117 ImporterParameter::Flag { .. } => ImporterParameterValue::Boolean(false),
118 ImporterParameter::String { default_value, .. } => {
119 ImporterParameterValue::String(default_value.to_string())
120 }
121 ImporterParameter::Usize { default_value, .. } => {
122 ImporterParameterValue::Usize(*default_value)
123 }
124 ImporterParameter::Fraction { default_value, .. } => {
125 ImporterParameterValue::Fraction(default_value.to_fraction())
126 }
127 }
128 }
129}
130
131impl Eq for ImporterParameter {}
132
133impl PartialEq for ImporterParameter {
134 fn eq(&self, other: &Self) -> bool {
135 self.name() == other.name()
136 }
137}
138
139impl Hash for ImporterParameter {
140 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
141 self.name().hash(state)
142 }
143}
144
145impl Ord for ImporterParameter {
146 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
147 self.name().cmp(other.name())
148 }
149}
150
151impl PartialOrd for ImporterParameter {
152 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
153 self.name().partial_cmp(other.name())
154 }
155}
156
157impl Debug for ImporterParameter {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 match self {
160 Self::Flag { name, .. } => write!(f, "{} (flag)", name),
161 Self::String { name, .. } => write!(f, "{} (string)", name),
162 Self::Usize { name, .. } => write!(f, "{} (usize)", name),
163 Self::Fraction { name, .. } => write!(f, "{} (fraction)", name),
164 }
165 }
166}
167
168#[derive(Clone,Debug)]
169pub enum ImporterParameterValue {
170 Boolean(bool),
171 String(String),
172 Usize(usize),
173 Fraction(Fraction),
174}
175
176impl ImporterParameterValue {
177 pub fn as_string(&self) -> Result<String> {
178 match self {
179 ImporterParameterValue::String(s) => Ok(s.clone()),
180 _ => Err(anyhow!("cannot read importer parameter as string")),
181 }
182 }
183
184 pub fn as_bool(&self) -> Result<bool> {
185 match self {
186 ImporterParameterValue::Boolean(s) => Ok(*s),
187 _ => Err(anyhow!("cannot read importer parameter as bool")),
188 }
189 }
190}