1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2020-2021 Ian Jackson and contributors to Otter
// SPDX-License-Identifier: AGPL-3.0-or-later
// There is NO WARRANTY.

use crate::prelude::*;

// At the implementation level, each loaded item contains an
// `Arc<GroupDetails>`, which is simply stored directly.  The
// `GroupDefn` is processed.
#[derive(Debug,Deserialize)]
pub struct GroupDefn {
  pub files: FileList,
  #[serde(default)] pub item_prefix: String,
  #[serde(default)] pub item_suffix: String,
  #[serde(default)] pub sort: String,
  #[serde(flatten)] pub d: GroupDetails,
}

#[derive(Debug,Deserialize)]
pub struct GroupDetails {
  #[cfg(doc)] inherit: String, // handled specially
  #[serde(default)] pub size: Vec<f64>, // scaled in GroupData in mf1
  #[serde(default)] pub orig_size: Vec<f64>,
  #[serde(default)] pub centre: Option<[f64; 2]>,
  #[serde(default)] pub flip: bool,
  #[serde(default)] pub back: Option<Box <dyn PieceSpec>>,
  #[serde(default)] pub scale: Option<ScaleDetails>,
  #[serde(default)] pub colours: HashMap<String, RecolourData>,
  pub desc_template: Option<String>,
  pub occulted: Option<OccultationMethod>,
  pub outline: OutlineDetails,
  #[serde(default)] pub magic: Option<MagicDetails>,
}

#[derive(Debug,Deserialize)]
pub struct MagicDetails {
  #[serde(default)] pub item_prefix: String,
  #[serde(default)] pub item_suffix: String,
  pub template: String,
  #[serde(default)] pub substs: HashMap<String,String>,
}

#[derive(Debug,Deserialize,Copy,Clone)]
#[serde(untagged)]
pub enum ScaleDetails {
  Fit(ScaleFitDetails),
  Scale(f64),
  Stretch([f64;2]),
}

#[derive(Debug,Deserialize,Copy,Clone)]
pub enum ScaleFitDetails { Fit, Cover, Stretch }

#[derive(Debug,Deserialize)]
#[serde(untagged)]
pub enum OutlineDetails {
  Full(FullOutlineDetails), // introduced with mformat=2
  Shape(Shape),
}

#[derive(Debug,Deserialize)]
pub struct FullOutlineDetails {
  shape: Shape,
  #[serde(default)] size: Vec<f64>,
  #[serde(default)] scale: Option<f64>,
}

impl OutlineDetails {
  // enum_access could perhaps do this but controlling the serde
  // would become confusing
  pub fn shape(&self) -> Shape { match self {
    OutlineDetails::Full(full) => full.shape,
    OutlineDetails::Shape(shape) => *shape,
  }}
  pub fn size_scale(&self) -> (&[f64], Option<&f64>) { match self {
    OutlineDetails::Full(full) => (&full.size, full.scale.as_ref()),
    OutlineDetails::Shape(_) => default(),
  }}
}

#[derive(Deserialize,Clone,Debug)]
#[serde(tag="method")]
pub enum OccultationMethod {
  ByColour {
    colour: ColourSpec,
  },
  ByBack {
    ilk: OccultIlkName,
  },
}

#[derive(Debug,Deserialize)]
pub struct RecolourData {
  pub abbrev: String,
  #[cfg(doc)] pub map: HashMap<String, String>,
  #[serde(default)] pub substs: HashMap<String,String>,
}

#[doc(hidden)]
#[derive(Deserialize,Debug)]
#[serde(try_from="String")]
pub struct FileList(pub Vec<FileData>);

#[derive(Deserialize,Debug)]
pub struct FileData {
  pub item_spec: String,
  pub src_file_spec: String,
  pub extra_fields: HashMap<String, String>,
  pub desc: String,
}

#[cfg(doc)]
/// `scraper`, specifying where and how to get updated piece SVGs.
pub struct Scraper {
  /// Determines which scraper is run and the rest of the table
  /// `scraper` is interpreted.
  ///
  /// There are two methods available:
  ///
  ///  * `"none"`: Do not scrape anything.  The SVGs in the Otter
  ///  source tree are hand-edited.  The 2nd field in each
  ///  [`files`](GroupDefn::files) line
  ///  ([`src_file_spec`](FileData::src_file_spec)) is ignored.
  ///
  ///  * `"wikimedia"`: Scrape a site that uses Mediawiki the way that
  ///  Wikimedia does.  In this case
  ///  `scraper` is a table containing the
  ///  fields of [`WikimediaScraper`], not just `method`.
  ///
  ///  * `"cards-oxymoron`": Special for that subdirectory.
  pub method: String,
}

#[cfg(doc)]
/// Settings to go alongside `scraper = "wikimedia"`
///
/// TODO: Most fields here yet to be documented!
pub struct WikimediaScraper {
  /// See [`method` in `Scraper`](Scraper::method).  `"wikimedia"`
  pub method: String,

}