1use bellframe::{method::LABEL_LEAD_END, PlaceNot, Stage};
2use itertools::Itertools;
3use monument::parameters::{
4 default_calling_positions, BaseCallType, CallId, DEFAULT_MISC_CALL_WEIGHT,
5};
6use serde::Deserialize;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum BaseCalls {
12 None,
13 Near,
14 Far,
15}
16
17impl BaseCalls {
18 pub fn as_monument_type(self) -> Option<BaseCallType> {
19 match self {
20 Self::Near => Some(BaseCallType::Near),
21 Self::Far => Some(BaseCallType::Far),
22 Self::None => None,
23 }
24 }
25}
26
27impl Default for BaseCalls {
28 fn default() -> Self {
29 Self::Near
30 }
31}
32
33#[derive(Debug, Clone, Deserialize)]
35#[serde(deny_unknown_fields)]
36pub struct CustomCall {
37 place_notation: String,
38 symbol: String,
39 debug_symbol: Option<String>, #[serde(default = "lead_end")]
41 label: CallLabel,
42 lead_location: Option<CallLabel>,
44 calling_positions: Option<CallingPositions>,
46 #[serde(default = "default_misc_call_score")]
47 weight: f32,
48}
49
50#[derive(Debug, Clone, Deserialize)]
51#[serde(untagged)]
52pub enum CallLabel {
53 Same(String),
55 Different { from: String, to: String },
58}
59
60impl CustomCall {
61 pub(super) fn as_monument_call(
62 &self,
63 id: CallId,
64 stage: Stage,
65 ) -> anyhow::Result<monument::parameters::Call> {
66 let place_notation = PlaceNot::parse(&self.place_notation, stage).map_err(|e| {
67 anyhow::Error::msg(format!(
68 "Can't parse place notation {:?} for call {:?}: {}",
69 self.place_notation, &self.symbol, e
70 ))
71 })?;
72 if self.lead_location.is_some() {
73 return Err(anyhow::Error::msg(
74 "`calls.lead_location` has been renamed to `label`",
75 ));
76 }
77 if self.debug_symbol.is_some() {
78 return Err(anyhow::Error::msg(
79 "`debug_symbol` is now calculated automatically. Use `symbol = \"-\" for bobs.`",
80 ));
81 }
82 let (label_from, label_to) = match self.label.clone() {
83 CallLabel::Same(loc) => (loc.clone(), loc),
84 CallLabel::Different { from, to } => (from, to),
85 };
86 let calling_positions = match &self.calling_positions {
87 Some(c) => c.as_vec(),
88 None => default_calling_positions(&place_notation),
89 };
90
91 Ok(monument::parameters::Call {
92 id,
93 symbol: self.symbol.to_owned(),
94 calling_positions,
95 label_from,
96 label_to,
97 place_notation,
98 weight: self.weight,
99 })
100 }
101}
102
103#[derive(Debug, Clone, Deserialize)]
105#[serde(untagged, deny_unknown_fields)]
106enum CallingPositions {
107 Str(String),
109 List(Vec<String>),
111}
112
113impl CallingPositions {
114 fn as_vec(&self) -> Vec<String> {
117 match self {
118 CallingPositions::Str(s) => s.chars().map(|c| c.to_string()).collect_vec(),
119 CallingPositions::List(positions) => positions.clone(),
120 }
121 }
122}
123
124fn lead_end() -> CallLabel {
125 CallLabel::Same(LABEL_LEAD_END.to_owned())
126}
127
128fn default_misc_call_score() -> f32 {
129 DEFAULT_MISC_CALL_WEIGHT
130}