glycin_common/
operations.rs

1use std::io::Read;
2use std::str::FromStr;
3
4use gufo_common::orientation::{Orientation, Rotation};
5use serde::de::{value, IntoDeserializer};
6use serde::{Deserialize, Deserializer, Serialize};
7
8#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
9#[non_exhaustive]
10pub enum Operation {
11    Clip((u32, u32, u32, u32)),
12    MirrorHorizontally,
13    MirrorVertically,
14    /// Counter-clockwise rotation
15    Rotate(gufo_common::orientation::Rotation),
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
19#[non_exhaustive]
20pub enum OperationId {
21    Clip,
22    MirrorHorizontally,
23    MirrorVertically,
24    Rotate,
25}
26
27#[derive(Debug, PartialEq, Deserialize, Serialize)]
28#[serde(from = "OperationsIntermediate")]
29pub struct Operations {
30    operations: Vec<Operation>,
31    #[serde(skip)]
32    unknown_operations: Vec<String>,
33}
34
35impl Operations {
36    pub fn new(operations: Vec<Operation>) -> Operations {
37        Self {
38            operations,
39            unknown_operations: vec![],
40        }
41    }
42
43    /// Creates new operations that apply the specified orientation
44    pub fn new_orientation(orientation: Orientation) -> Operations {
45        let mut operations = Vec::new();
46
47        if orientation.mirror() {
48            operations.push(Operation::MirrorHorizontally);
49        }
50
51        let rotate = orientation.rotate();
52        if rotate != Rotation::_0 {
53            operations.push(Operation::Rotate(rotate));
54        }
55
56        Self {
57            operations,
58            unknown_operations: Vec::new(),
59        }
60    }
61
62    /// Prepend operations
63    ///
64    /// ```
65    /// # use glycin_common::{Operation, Operations};
66    /// # use gufo_common::orientation::{Orientation, Rotation};
67    /// let mut ops = Operations::new(vec![Operation::MirrorVertically]);
68    /// ops.prepend(Operations::new_orientation(Orientation::Rotation90));
69    ///
70    /// assert_eq!(
71    ///     ops.operations(),
72    ///     &[
73    ///         Operation::Rotate(Rotation::_90),
74    ///         Operation::MirrorVertically
75    ///     ]
76    /// );
77    /// ```
78    pub fn prepend(&mut self, mut operations: Operations) {
79        std::mem::swap(self, &mut operations);
80        self.operations.append(&mut operations.operations);
81    }
82
83    pub fn from_read(reader: impl Read) -> Result<Self, rmp_serde::decode::Error> {
84        rmp_serde::decode::from_read(reader)
85    }
86
87    pub fn from_slice(slice: impl AsRef<[u8]>) -> Result<Self, rmp_serde::decode::Error> {
88        rmp_serde::decode::from_slice(slice.as_ref())
89    }
90
91    pub fn to_message_pack(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
92        let mut buf = Vec::new();
93        self.serialize(&mut rmp_serde::Serializer::new(&mut buf).with_human_readable())?;
94
95        Ok(buf)
96    }
97
98    pub fn operations(&self) -> &[Operation] {
99        &self.operations
100    }
101
102    pub fn operation_ids(&self) -> Vec<OperationId> {
103        self.operations.iter().map(|x| x.id()).collect()
104    }
105
106    /// Returns information about all operations that were unknown when
107    /// deserializing
108    pub fn unknown_operations(&self) -> &[String] {
109        &self.unknown_operations
110    }
111
112    /// Returns an [`Orientation`] if all operations can be reduced to that
113    ///
114    /// ```
115    /// # use glycin_common::{Operations, Operation};
116    /// # use gufo_common::orientation::{Rotation, Orientation};
117    /// assert_eq!(
118    ///     Operations::new(vec![
119    ///         Operation::Rotate(Rotation::_180),
120    ///         Operation::Rotate(Rotation::_270)
121    ///     ])
122    ///     .orientation(),
123    ///     Some(Orientation::Rotation90)
124    /// );
125    ///
126    /// assert_eq!(
127    ///     Operations::new(vec![
128    ///         Operation::Rotate(Rotation::_90),
129    ///         Operation::MirrorHorizontally
130    ///     ])
131    ///     .orientation(),
132    ///     Some(Orientation::MirroredRotation270)
133    /// );
134    ///
135    /// assert_eq!(
136    ///     Operations::new(vec![
137    ///         Operation::MirrorHorizontally,
138    ///         Operation::MirrorVertically,
139    ///         Operation::Rotate(Rotation::_270),
140    ///         Operation::MirrorHorizontally,
141    ///     ])
142    ///     .orientation(),
143    ///     Some(Orientation::MirroredRotation270)
144    /// );
145    /// ```
146    pub fn orientation(&self) -> Option<Orientation> {
147        let mut orientation = Orientation::Id;
148
149        for operation in &self.operations {
150            match operation {
151                Operation::MirrorHorizontally => {
152                    orientation = orientation.add_mirror_horizontally();
153                }
154                Operation::MirrorVertically => {
155                    orientation = orientation.add_mirror_vertically();
156                }
157                Operation::Rotate(rotation) => {
158                    orientation = orientation.add_rotation(*rotation);
159                }
160                _ => return None,
161            }
162        }
163
164        Some(orientation)
165    }
166}
167
168impl From<OperationsIntermediate> for Operations {
169    fn from(operations: OperationsIntermediate) -> Operations {
170        Operations {
171            operations: operations
172                .operations
173                .iter()
174                .filter_map(|x| x.operation().cloned())
175                .collect(),
176
177            unknown_operations: operations
178                .operations
179                .iter()
180                .filter_map(|x| x.unknown())
181                .collect(),
182        }
183    }
184}
185
186/// Decoding format that allows to decode without failing for unknown operations
187#[derive(Debug, PartialEq, Deserialize)]
188struct OperationsIntermediate {
189    operations: Vec<MaybeOperation>,
190}
191
192#[derive(Debug, PartialEq)]
193enum MaybeOperation {
194    Operation(Operation),
195    Unknown(String),
196}
197
198impl MaybeOperation {
199    fn operation(&self) -> Option<&Operation> {
200        match self {
201            Self::Operation(operation) => Some(operation),
202            Self::Unknown(_) => None,
203        }
204    }
205
206    fn unknown(&self) -> Option<String> {
207        match self {
208            Self::Operation(_) => None,
209            Self::Unknown(s) => Some(s.to_string()),
210        }
211    }
212}
213
214impl<'de> Deserialize<'de> for MaybeOperation {
215    fn deserialize<D>(deserializer: D) -> Result<MaybeOperation, D::Error>
216    where
217        D: Deserializer<'de>,
218    {
219        match serde::Deserialize::deserialize(deserializer) {
220            Ok(val) => Ok(Self::Operation(val)),
221            Err(err) => Ok(Self::Unknown(err.to_string())),
222        }
223    }
224}
225
226impl Operation {
227    pub fn id(&self) -> OperationId {
228        match self {
229            Self::Clip(_) => OperationId::Clip,
230            Self::MirrorHorizontally => OperationId::MirrorHorizontally,
231            Self::MirrorVertically => OperationId::MirrorVertically,
232            Self::Rotate(_) => OperationId::Rotate,
233        }
234    }
235}
236
237impl FromStr for OperationId {
238    type Err = value::Error;
239
240    /// ```
241    /// # use glycin_common::OperationId;
242    /// # use std::str::FromStr;
243    /// let id = OperationId::from_str("Clip").unwrap();
244    /// assert_eq!(id, OperationId::Clip)
245    /// ```
246    fn from_str(slice: &str) -> Result<Self, value::Error> {
247        Self::deserialize(slice.into_deserializer())
248    }
249}