adobe_swatch_exchange/
lib.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2#![deny(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5pub use error::{ASEError, ConformationError};
6use types::{BlockType, GroupHold};
7pub use types::{ColorBlock, ColorType, ColorValue, Group};
8
9mod buffer;
10mod error;
11mod types;
12
13/// Creates an Adobe Swatch Exchange (ASE) file.
14///
15/// # Examples
16/// ```rust
17/// # use adobe_swatch_exchange::{ColorBlock, ColorValue, ColorType, create_ase};
18/// let color = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
19/// let ase = create_ase(vec![], vec![color]);
20/// # assert_eq!( ase, vec![65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2]);
21/// ```
22pub fn create_ase(groups: Vec<Group>, colors: Vec<ColorBlock>) -> Vec<u8> {
23    let group_size: u32 = groups.iter().map(Group::calculate_length).sum();
24    let color_size: u32 = colors.iter().map(ColorBlock::calculate_length).sum();
25    // we slightly over-estimate the required amount of space here, to avoid a costly resizing
26    let mut buf = buffer::Buffer::with_capacity((8 + group_size * 2 + color_size) as usize);
27
28    // file metadata
29    buf.write_slice(types::FILE_SIGNATURE);
30    buf.write_u32(types::VERSION);
31    // number of blocks
32    buf.write_u32((groups.len() + colors.len()) as u32);
33
34    // write groups
35    for group in groups {
36        group.write(&mut buf);
37    }
38
39    // write single colors
40    for block in colors {
41        block.write(&mut buf);
42    }
43
44    buf.into_vec()
45}
46
47/// Read groups and single colors from the `.ase` file.
48///
49/// # Errors
50///
51/// This function will return an error if either a read to the given data fails,
52/// or the ASE file is invalid.
53///
54/// # Examples
55/// ```rust
56/// # use adobe_swatch_exchange::read_ase;
57/// // any source
58/// let source = vec![65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 0];
59/// let (groups, colors) = read_ase(&*source).unwrap();
60/// # assert_eq!((groups, colors), (vec![], vec![]));
61/// ```
62pub fn read_ase<T: std::io::Read>(mut ase: T) -> Result<(Vec<Group>, Vec<ColorBlock>), ASEError> {
63    let mut buf_u32 = [0; 4];
64
65    // read magic bytes
66    ase.read_exact(&mut buf_u32)?;
67    if &buf_u32 != types::FILE_SIGNATURE {
68        return Err(ASEError::Invalid(error::ConformationError::FileSignature));
69    }
70
71    // read version, should be 1.0
72    ase.read_exact(&mut buf_u32)?;
73    if buf_u32 != types::VERSION.to_be_bytes() {
74        return Err(ASEError::Invalid(error::ConformationError::FileVersion));
75    }
76
77    ase.read_exact(&mut buf_u32)?;
78    let number_of_blocks = u32::from_be_bytes(buf_u32);
79
80    let mut groups = Vec::new();
81    let mut color_blocks = Vec::new();
82    let mut buf_u16 = [0; 2];
83
84    // temporary group to handle nonconforming group blocks
85    let mut group_hold = GroupHold::Empty;
86    let mut group_hold_value = Group::default();
87
88    let mut blocks_to_read = number_of_blocks;
89
90    // allow skipping of empty blocks when a group-end block has a size field
91    let mut skipped = 0;
92    let mut safe_to_skip = false;
93
94    while blocks_to_read > 0 {
95        ase.read_exact(&mut buf_u16)?;
96
97        // only skip if the next two bytes were zero and we haven't skipped two already.
98        if buf_u16 == [0, 0] && skipped < 2 && safe_to_skip {
99            skipped += 1;
100            continue;
101        }
102
103        let block_type = BlockType::try_from(u16::from_be_bytes(buf_u16))?;
104
105        if block_type != BlockType::GroupEnd && group_hold == GroupHold::HoldingBuilt {
106            return Err(ASEError::Invalid(error::ConformationError::GroupEnd));
107        }
108
109        // block length for GroupEnd blocks should always be zero, the `skipped`
110        // variable above is intended to help us avoid the issue where the size
111        // is specified.
112        let block_length = if block_type == BlockType::GroupEnd {
113            safe_to_skip = true;
114            skipped = 0;
115            0
116        } else {
117            ase.read_exact(&mut buf_u32)?;
118            let block_length = u32::from_be_bytes(buf_u32);
119            safe_to_skip = false;
120            block_length
121        };
122
123        let mut block = vec![0; block_length as usize];
124        ase.read_exact(&mut block)?;
125
126        // parse block data and add it appropriate vec
127        match block_type {
128            BlockType::GroupStart => {
129                let block = Group::parse(&block)?;
130                if group_hold != GroupHold::Empty {
131                    return Err(ASEError::Invalid(error::ConformationError::GroupEnd));
132                }
133                // if the parsed block has any sub-blocks then it has already been built
134                // and only a group-end block may follow it. Otherwise we are free to
135                // add colors as they appear until a group-end block is encountered.
136                group_hold = if block.blocks.is_empty() {
137                    GroupHold::HoldingBuilding
138                } else {
139                    blocks_to_read += 1;
140                    GroupHold::HoldingBuilt
141                };
142                group_hold_value = block;
143            }
144            // read by the group end
145            BlockType::GroupEnd => match group_hold {
146                GroupHold::HoldingBuilding | GroupHold::HoldingBuilt => {
147                    groups.push(group_hold_value.clone());
148                    group_hold = GroupHold::Empty;
149                }
150                GroupHold::Empty => {
151                    return Err(ASEError::Invalid(error::ConformationError::GroupEnd))
152                }
153            },
154            BlockType::ColorEntry => {
155                let block = ColorBlock::parse(&block)?;
156                match group_hold {
157                    GroupHold::HoldingBuilding => group_hold_value.blocks.push(block),
158                    GroupHold::Empty => color_blocks.push(block),
159                    GroupHold::HoldingBuilt => {
160                        return Err(ASEError::Invalid(error::ConformationError::GroupEnd))
161                    }
162                }
163            }
164        }
165
166        blocks_to_read -= 1;
167    }
168
169    // if we haven't saved the last group, even if no end was found, go ahead and add it.
170    if group_hold == GroupHold::HoldingBuilding {
171        groups.push(group_hold_value);
172    }
173
174    // if we received a built group, but it was terminated, it is an error.
175    if group_hold == GroupHold::HoldingBuilt {
176        return Err(ASEError::Invalid(error::ConformationError::GroupEnd));
177    }
178
179    Ok((groups, color_blocks))
180}
181
182#[cfg(test)]
183mod tests {
184    use crate::error::ConformationError;
185
186    use super::*;
187
188    #[test]
189    fn it_writes_empty_args() {
190        assert_eq!(
191            create_ase(vec![], vec![]),
192            vec![65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 0]
193        )
194    }
195
196    #[test]
197    fn it_writes_single_color() {
198        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
199        assert_eq!(
200            create_ase(vec![], vec![block]),
201            vec![
202                65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0,
203                109, 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2
204            ]
205        )
206    }
207
208    #[test]
209    fn it_writes_group_color() {
210        let group = Group::new(
211            "group name".to_owned(),
212            vec![
213                ColorBlock::new(
214                    "light grey".to_owned(),
215                    ColorValue::Gray(0.5),
216                    ColorType::Normal,
217                ),
218                ColorBlock::new(
219                    "dark red".to_owned(),
220                    ColorValue::Rgb(0.5, 0.3, 0.1),
221                    ColorType::Normal,
222                ),
223            ],
224        );
225        assert_eq!(
226            create_ase(vec![group], vec![]),
227            vec![
228                65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 192, 1, 0, 0, 0, 108, 0, 11, 0, 103, 0,
229                114, 0, 111, 0, 117, 0, 112, 0, 32, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 0, 1, 0,
230                0, 0, 34, 0, 11, 0, 108, 0, 105, 0, 103, 0, 104, 0, 116, 0, 32, 0, 103, 0, 114, 0,
231                101, 0, 121, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 38, 0, 9, 0,
232                100, 0, 97, 0, 114, 0, 107, 0, 32, 0, 114, 0, 101, 0, 100, 0, 0, 82, 71, 66, 32,
233                63, 0, 0, 0, 62, 153, 153, 154, 61, 204, 204, 205, 0, 2, 192, 2
234            ]
235        )
236    }
237
238    #[test]
239    fn it_writes_group_and_single_color() {
240        let group = Group::new(
241            "group name".to_owned(),
242            vec![
243                ColorBlock::new(
244                    "light grey".to_owned(),
245                    ColorValue::Gray(0.5),
246                    ColorType::Normal,
247                ),
248                ColorBlock::new(
249                    "dark red".to_owned(),
250                    ColorValue::Rgb(0.5, 0.3, 0.1),
251                    ColorType::Normal,
252                ),
253            ],
254        );
255        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
256        assert_eq!(
257            create_ase(vec![group], vec![block]),
258            vec![
259                65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 2, 192, 1, 0, 0, 0, 108, 0, 11, 0, 103, 0,
260                114, 0, 111, 0, 117, 0, 112, 0, 32, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 0, 1, 0,
261                0, 0, 34, 0, 11, 0, 108, 0, 105, 0, 103, 0, 104, 0, 116, 0, 32, 0, 103, 0, 114, 0,
262                101, 0, 121, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 38, 0, 9, 0,
263                100, 0, 97, 0, 114, 0, 107, 0, 32, 0, 114, 0, 101, 0, 100, 0, 0, 82, 71, 66, 32,
264                63, 0, 0, 0, 62, 153, 153, 154, 61, 204, 204, 205, 0, 2, 192, 2, 0, 1, 0, 0, 0, 22,
265                0, 5, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2
266            ]
267        )
268    }
269
270    #[test]
271    fn it_reads_empty() {
272        let res = read_ase(&*vec![65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 0]);
273        assert!(res.is_ok());
274        assert_eq!(res.unwrap(), (vec![], vec![]));
275    }
276
277    #[test]
278    fn it_reads_single_color() {
279        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
280        let res = read_ase(&*create_ase(vec![], vec![block.clone()]));
281        assert!(res.is_ok());
282        let res = res.unwrap();
283        assert_eq!(res, (vec![], vec![block]));
284        assert_eq!(res.1.first().unwrap().name, "name".to_owned());
285    }
286
287    #[test]
288    fn it_reads_group() {
289        let group = Group::new(
290            "group name".to_owned(),
291            vec![
292                ColorBlock::new(
293                    "light grey".to_owned(),
294                    ColorValue::Gray(0.5),
295                    ColorType::Normal,
296                ),
297                ColorBlock::new(
298                    "dark red".to_owned(),
299                    ColorValue::Rgb(0.5, 0.3, 0.1),
300                    ColorType::Normal,
301                ),
302            ],
303        );
304        let res = read_ase(&*create_ase(vec![group.clone()], vec![]));
305        assert!(res.is_ok());
306        let res = res.unwrap();
307        assert_eq!(res, (vec![group], vec![]));
308        assert_eq!(res.0.first().unwrap().name, "group name".to_owned());
309    }
310
311    #[test]
312    fn it_reads_group_and_single_color() {
313        let group = Group::new(
314            "group name".to_owned(),
315            vec![
316                ColorBlock::new(
317                    "light grey".to_owned(),
318                    ColorValue::Gray(0.5),
319                    ColorType::Normal,
320                ),
321                ColorBlock::new(
322                    "dark red".to_owned(),
323                    ColorValue::Rgb(0.5, 0.3, 0.1),
324                    ColorType::Normal,
325                ),
326            ],
327        );
328        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
329        let res = read_ase(&*create_ase(vec![group.clone()], vec![block.clone()]));
330        assert!(res.is_ok());
331        let res = res.unwrap();
332        assert_eq!(res, (vec![group], vec![block]));
333        assert_eq!(res.0.first().unwrap().name, "group name".to_owned());
334        assert_eq!(res.1.first().unwrap().name, "name".to_owned());
335    }
336
337    #[test]
338    fn it_reads_group_and_single_color_with_explicit_group_end_size() {
339        let group = Group::new(
340            "group name".to_owned(),
341            vec![
342                ColorBlock::new(
343                    "light grey".to_owned(),
344                    ColorValue::Gray(0.5),
345                    ColorType::Normal,
346                ),
347                ColorBlock::new(
348                    "dark red".to_owned(),
349                    ColorValue::Rgb(0.5, 0.3, 0.1),
350                    ColorType::Normal,
351                ),
352            ],
353        );
354        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
355        let input_ase_bytes = create_ase(vec![group.clone()], vec![block.clone()]);
356        let mut modified_ase_bytes = vec![0; 0];
357
358        // The following code modifies the generated ASE bytes so that
359        // the GroupEnd block is followed by a four byte zero length
360        // specifier. The slice lengths presented here are specific
361        // to the test and not general numbers.
362        // The layout, in this case, is:
363        //      bytes 0   - 127       GroupStart, ColorBlocks, GroupEnd
364        //      bytes 128 - end       Global Colors
365        // The modified data has the layout:
366        //      bytes 0   - 127       GroupStart, ColorBlocks, GroupEnd
367        //      bytes 128 - 131       u32(0)
368        //      bytes 132 - end       Global Colors
369        modified_ase_bytes.extend_from_slice(&input_ase_bytes[..128]);
370        modified_ase_bytes.extend_from_slice(&[0; 4]);
371        modified_ase_bytes.extend_from_slice(&input_ase_bytes[128..]);
372        let res = read_ase(&*input_ase_bytes);
373        assert!(res.is_ok());
374        let res = res.unwrap();
375        assert_eq!(res, (vec![group], vec![block]));
376    }
377
378    #[test]
379    fn it_reads_group_and_single_color_with_group_block_name_only_size() {
380        let group = Group::new(
381            "group name".to_owned(),
382            vec![
383                ColorBlock::new(
384                    "light grey".to_owned(),
385                    ColorValue::Gray(0.5),
386                    ColorType::Normal,
387                ),
388                ColorBlock::new(
389                    "dark red".to_owned(),
390                    ColorValue::Rgb(0.5, 0.3, 0.1),
391                    ColorType::Normal,
392                ),
393            ],
394        );
395        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
396        let input_ase_bytes = create_ase(vec![group.clone()], vec![block.clone()]);
397        let mut modified_ase_bytes = vec![0; 0];
398
399        // The following code modifies the generated ASE bytes so that
400        // the GroupStart block is sized to contain only the name
401        // of the group. This requires that the total number of blocks
402        // be updated to include the colors present in the group.
403        // The slice lengths presented here are specific
404        // to the test and not general numbers.
405        // The layout, in this case, is:
406        //      bytes 0   - 7         Header
407        //      bytes 8   - 11        2 expected blocks
408        //      bytes 12  - 13        GroupStart
409        //      bytes 14  - 17        Group block size 108
410        //      bytes 18  - end       Groups and Colors
411        // The modified data has the layout:
412        //      bytes 0   - 7         Header
413        //      bytes 8   - 11        5 expected blocks
414        //      bytes 12  - 13        GroupStart
415        //      bytes 14  - 17        Group block size 24
416        //      bytes 18  - end       Groups and Colors
417        modified_ase_bytes.extend_from_slice(&input_ase_bytes[..8]);
418        modified_ase_bytes.extend_from_slice(&(5_u32.to_be_bytes()));
419        modified_ase_bytes.extend_from_slice(&input_ase_bytes[12..14]);
420        modified_ase_bytes.extend_from_slice(&(24_u32.to_be_bytes()));
421        modified_ase_bytes.extend_from_slice(&input_ase_bytes[18..]);
422        let res = read_ase(&*modified_ase_bytes);
423        assert!(res.is_ok());
424        let res = res.unwrap();
425        assert_eq!(res, (vec![group], vec![block]));
426    }
427
428    #[test]
429    fn it_reads_group_and_single_color_with_group_block_name_only_size_and_explicit_group_end_size()
430    {
431        let group = Group::new(
432            "group name".to_owned(),
433            vec![
434                ColorBlock::new(
435                    "light grey".to_owned(),
436                    ColorValue::Gray(0.5),
437                    ColorType::Normal,
438                ),
439                ColorBlock::new(
440                    "dark red".to_owned(),
441                    ColorValue::Rgb(0.5, 0.3, 0.1),
442                    ColorType::Normal,
443                ),
444            ],
445        );
446        let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
447        let input_ase_bytes = create_ase(vec![group.clone()], vec![block.clone()]);
448        let mut modified_ase_bytes = vec![0; 0];
449
450        // The following code modifies the generated ASE bytes so that
451        // the GroupStart block is sized to contain only the name
452        // of the group. This requires that the total number of blocks
453        // be updated to include the colors present in the group.
454        // The slice lengths presented here are specific
455        // to the test and not general numbers.
456        // The layout, in this case, is:
457        //      bytes 0   - 7         Header
458        //      bytes 8   - 11        2 expected blocks
459        //      bytes 12  - 13        GroupStart
460        //      bytes 14  - 17        Group block size 108
461        //      bytes 18  - end       Groups and Colors
462        // The modified data has the layout:
463        //      bytes 0   - 7         Header
464        //      bytes 8   - 11        5 expected blocks
465        //      bytes 12  - 13        GroupStart
466        //      bytes 14  - 17        Group block size 24
467        //      bytes 18  - 127       Groups and Sub Colors
468        //      bytes 128 - 131       u32(0)
469        //      bytes 132 - end       Global colors
470        modified_ase_bytes.extend_from_slice(&input_ase_bytes[..8]);
471        modified_ase_bytes.extend_from_slice(&(5_u32.to_be_bytes()));
472        modified_ase_bytes.extend_from_slice(&input_ase_bytes[12..14]);
473        modified_ase_bytes.extend_from_slice(&(24_u32.to_be_bytes()));
474        modified_ase_bytes.extend_from_slice(&input_ase_bytes[18..128]);
475        modified_ase_bytes.extend_from_slice(&[0; 4]);
476        modified_ase_bytes.extend_from_slice(&input_ase_bytes[128..]);
477        let res = read_ase(&*modified_ase_bytes);
478        assert!(res.is_ok());
479        let res = res.unwrap();
480        assert_eq!(res, (vec![group], vec![block]));
481    }
482
483    #[test]
484    fn it_returns_incorrect_block_type_error() {
485        let input_bad_block_type = vec![
486            65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109,
487            0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2,
488        ];
489        let parser_result = read_ase(&*input_bad_block_type);
490        assert!(
491            parser_result.is_err(),
492            "Parser result must be an error with an invalid block type."
493        );
494        assert!(
495            matches!(parser_result.err(), Some(ASEError::BlockTypeError)),
496            "Expected bad block type error"
497        );
498    }
499
500    #[test]
501    fn it_returns_incorrect_color_type_error() {
502        let input_bad_color_type = vec![
503            65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109,
504            0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 3,
505        ];
506        let parser_result = read_ase(&*input_bad_color_type);
507        assert!(
508            parser_result.is_err(),
509            "Parser result must be an error with an invalid color type."
510        );
511        assert!(
512            matches!(parser_result.err(), Some(ASEError::ColorTypeError)),
513            "Expected bad color type error"
514        );
515    }
516
517    #[test]
518    fn it_returns_incorrect_color_format_error() {
519        let input_bad_color_format = vec![
520            65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109,
521            0, 101, 0, 0, 72, 114, 97, 121, 63, 0, 0, 0, 0, 3,
522        ];
523        let parser_result: Result<(Vec<Group>, Vec<ColorBlock>), ASEError> =
524            read_ase(&*input_bad_color_format);
525        assert!(
526            parser_result.is_err(),
527            "Parser result must be an error with an invalid color format."
528        );
529        assert!(
530            matches!(parser_result.err(), Some(ASEError::ColorFormat)),
531            "Expected bad color format error"
532        );
533    }
534
535    #[test]
536    fn it_returns_incorrect_signature_error() {
537        let input_bad_signature = vec![65, 80, 69, 70, 1, 1, 0, 0, 0, 0, 0, 0];
538        let parser_result = read_ase(&*input_bad_signature);
539        assert!(
540            parser_result.is_err(),
541            "Parser result must be an error with an invalid signature."
542        );
543        assert!(
544            matches!(
545                parser_result.err(),
546                Some(ASEError::Invalid(ConformationError::FileSignature))
547            ),
548            "Only ASEError::Invalid(error::ConformationError::FileSignature) should be returned"
549        );
550    }
551
552    #[test]
553    fn it_returns_incorrect_version_error() {
554        let input_bad_file_version = vec![65, 83, 69, 70, 1, 1, 0, 0, 0, 0, 0, 0];
555        let parser_result = read_ase(&*input_bad_file_version);
556        assert!(
557            parser_result.is_err(),
558            "Parser result must be an error with an invalid file version."
559        );
560        assert!(
561            matches!(
562                parser_result.err(),
563                Some(ASEError::Invalid(ConformationError::FileVersion))
564            ),
565            "Only ASEError::Invalid(error::ConformationError::FileVersion) should be returned"
566        );
567    }
568
569    #[test]
570    fn it_returns_incorrect_block_end_error() {
571        let input_bad_group_end = vec![
572            65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 2, 192, 1, 0, 0, 0, 108, 0, 11, 0, 103, 0, 114, 0,
573            111, 0, 117, 0, 112, 0, 32, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 0, 1, 0, 0, 0, 34, 0,
574            11, 0, 108, 0, 105, 0, 103, 0, 104, 0, 116, 0, 32, 0, 103, 0, 114, 0, 101, 0, 121, 0,
575            0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 38, 0, 9, 0, 100, 0, 97, 0, 114,
576            0, 107, 0, 32, 0, 114, 0, 101, 0, 100, 0, 0, 82, 71, 66, 32, 63, 0, 0, 0, 62, 153, 153,
577            154, 61, 204, 204, 205, 0, 2, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109, 0,
578            101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2,
579        ];
580        let parser_result = read_ase(&*input_bad_group_end);
581        assert!(
582            parser_result.is_err(),
583            "Parser result must be an error with an invalid group end."
584        );
585        assert!(
586            matches!(
587                parser_result.err(),
588                Some(ASEError::Invalid(ConformationError::GroupEnd))
589            ),
590            "Only ASEError::Invalid(error::ConformationError::GroupEnd) should be returned"
591        );
592    }
593}