1use std::{
4 fmt::{Display, Formatter},
5 ops::RangeInclusive,
6};
7
8use bellframe::{PlaceNot, Stage};
9
10#[allow(unused_imports)] use crate::parameters::{Call, Method, MusicType};
12use crate::{parameters::OptionalRangeInclusive, utils::TotalLength};
13
14pub type Result<T> = std::result::Result<T, Error>;
16
17#[derive(Debug)]
19pub enum Error {
20 MethodNotFound {
25 title: String,
26 suggestions: Vec<(String, usize)>,
27 },
28 MethodPnParse {
30 name: String,
31 place_notation_string: String,
32 error: bellframe::place_not::PnBlockParseError,
33 },
34 CustomCourseMaskParse {
36 method_title: String,
37 mask_str: String,
38 error: bellframe::mask::ParseError,
39 },
40
41 DifferentStartEndRowInMultipart,
44 UndefinedLabel { call_name: String, label: String },
46 NoMethods,
48 DuplicateShorthand {
50 shorthand: String,
51 title1: String,
52 title2: String,
53 },
54 WrongCallingPositionsLength {
56 call_name: String,
57 calling_position_len: usize,
58 stage: Stage,
59 },
60 DuplicateCall {
62 symbol: String,
63 label: String,
64 pn1: PlaceNot,
65 pn2: PlaceNot,
66 },
67
68 SizeLimit(usize),
71 InconsistentStroke,
74
75 UnachievableLength {
78 requested_range: RangeInclusive<TotalLength>,
79 next_shorter_len: Option<usize>,
80 next_longer_len: Option<usize>,
81 },
82 UnachievableMethodCount {
84 method_name: String,
85 requested_range: OptionalRangeInclusive,
86 next_shorter_len: Option<usize>,
87 next_longer_len: Option<usize>,
88 },
89 TooMuchMethodCount {
91 min_total_method_count: usize,
92 max_length: usize,
93 },
94 TooLittleMethodCount {
95 max_total_method_count: usize,
96 min_length: usize,
97 },
98}
99
100impl Display for Error {
101 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
102 match self {
103 Error::MethodNotFound { title, suggestions } => write!(
105 f,
106 "No method called {:?} in the Central Council library. Do you mean {:?}?",
107 title, suggestions[0]
108 ),
109 Error::MethodPnParse {
110 name,
111 place_notation_string: _,
112 error,
113 } => write!(f, "Error parsing place notation for {:?}: {}", name, error),
114 Error::CustomCourseMaskParse {
115 method_title,
116 mask_str,
117 error,
118 } => write!(
119 f,
120 "Error parsing course mask {} for method {:?}: {}",
121 mask_str, method_title, error
122 ),
123
124 Error::DifferentStartEndRowInMultipart => {
126 write!(f, "Start/end rows must be the same for multipart comps")
127 }
128 Error::NoMethods => write!(f, "Can't have a composition with no methods"),
129 Error::WrongCallingPositionsLength {
130 call_name,
131 calling_position_len,
132 stage,
133 } => write!(
134 f,
135 "Call {:?} only specifies {} calling positions, but the stage has {} bells",
136 call_name,
137 calling_position_len,
138 stage.num_bells()
139 ),
140 Error::DuplicateShorthand {
141 shorthand,
142 title1,
143 title2,
144 } => write!(
145 f,
146 "Methods {:?} and {:?} share a shorthand ({})",
147 title1, title2, shorthand
148 ),
149 Error::UndefinedLabel { call_name, label } => write!(
150 f,
151 "Call {:?} refers to a label {:?}, which doesn't exist",
152 call_name, label
153 ), Error::DuplicateCall {
155 symbol,
156 label,
157 pn1,
158 pn2,
159 } => write!(
160 f,
161 "Call symbol {:?} (at {:?}) is used for both {} and {}",
162 symbol, label, pn1, pn2
163 ),
164
165 Error::SizeLimit(limit) => write!(
167 f,
168 "Graph size limit of {} chunks reached. You can set it \
169higher with `--graph-size-limit <n>`.",
170 limit
171 ),
172 Error::InconsistentStroke => write!(
173 f,
174 "The same chunk of ringing can be at multiple strokes, probably \
175because you're using a method with odd-length leads"
176 ),
177
178 Error::UnachievableLength {
180 requested_range,
181 next_shorter_len,
182 next_longer_len,
183 } => {
184 write!(f, "No compositions can fit the required length range (")?;
185 write_range(
186 f,
187 "length",
188 Some(*requested_range.start()),
189 Some(*requested_range.end()),
190 )?;
191 write!(f, "). ")?;
192 match (next_shorter_len, next_longer_len) {
194 (Some(l1), Some(l2)) => write!(f, "The nearest lengths are {l1} and {l2}."),
195 (Some(l), None) | (None, Some(l)) => write!(f, "The nearest length is {l}."),
196 (None, None) => write!(f, "No compositions are possible."),
198 }
199 }
200 Error::UnachievableMethodCount {
201 method_name,
202 requested_range,
203 next_shorter_len,
204 next_longer_len,
205 } => {
206 assert_ne!((requested_range.min, requested_range.max), (None, None));
207 write!(
208 f,
209 "No method counts for {:?} satisfy the requested range (",
210 method_name,
211 )?;
212 write_range(f, "count", requested_range.min, requested_range.max)?;
213 write!(f, "). ")?;
214 match (next_shorter_len, next_longer_len) {
216 (Some(l1), Some(l2)) => write!(f, "The nearest counts are {l1} and {l2}."),
217 (Some(l), None) | (None, Some(l)) => write!(f, "The nearest count is {l}."),
218 (None, None) => unreachable!(), }
220 }
221 Error::TooMuchMethodCount {
222 min_total_method_count,
223 max_length,
224 } => {
225 write!(f, "Too much method counts; the method counts need at least")?;
226 write!(
227 f,
228 " {min_total_method_count} rows, but at most {max_length} rows are available."
229 )
230 }
231 Error::TooLittleMethodCount {
232 max_total_method_count,
233 min_length,
234 } => {
235 write!(
236 f,
237 "Not enough method counts; the composition needs at least {min_length} rows"
238 )?;
239 write!(
240 f,
241 " but the methods can make at most {max_total_method_count}."
242 )
243 }
244 }
245 }
246}
247
248fn write_range<T: Ord + Display>(
250 f: &mut impl std::fmt::Write,
251 name: &str,
252 min: Option<T>,
253 max: Option<T>,
254) -> std::fmt::Result {
255 match (min, max) {
256 (Some(min), Some(max)) if min == max => write!(f, "{name} == {min}")?,
258 (min, max) => {
260 if let Some(min) = min {
261 write!(f, "{min} <= ")?;
262 }
263 write!(f, "{name}")?;
264 if let Some(max) = max {
265 write!(f, " <= {max}")?;
266 }
267 }
268 }
269 Ok(())
270}
271
272impl std::error::Error for Error {}