Skip to main content

anitomy_ng/
element.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Port of `include/anitomy/element.hpp` and the `ElementKind` half of
6//! `include/anitomy/detail/format.hpp` (the `to_string`/`to_element_kind`
7//! tables). Keep the `as_str` mapping in sync with upstream if it changes —
8//! the conformance fixture suites (`tests/fixtures/*.json`) key on these
9//! exact strings.
10
11use std::fmt;
12use std::str::FromStr;
13
14/// Declares [`ElementKind`] and its string mapping from a single list, so
15/// `as_str` and `FromStr` are always exact inverses — adding a variant here
16/// updates both directions (and `Display`) at once, with no parallel tables to
17/// drift. The strings are the snake_case names upstream and the fixture suites
18/// key on; keep them in sync with upstream if it changes.
19macro_rules! element_kinds {
20    ($($variant:ident => $name:literal),+ $(,)?) => {
21        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22        pub enum ElementKind {
23            $($variant),+
24        }
25
26        impl ElementKind {
27            pub fn as_str(self) -> &'static str {
28                match self {
29                    $(ElementKind::$variant => $name),+
30                }
31            }
32        }
33
34        impl FromStr for ElementKind {
35            type Err = ParseElementKindError;
36
37            fn from_str(s: &str) -> Result<Self, Self::Err> {
38                Ok(match s {
39                    $($name => ElementKind::$variant,)+
40                    _ => return Err(ParseElementKindError),
41                })
42            }
43        }
44    };
45}
46
47element_kinds! {
48    AudioTerm => "audio_term",
49    Device => "device",
50    Episode => "episode",
51    EpisodeTitle => "episode_title",
52    FileChecksum => "file_checksum",
53    FileExtension => "file_extension",
54    Language => "language",
55    Other => "other",
56    Part => "part",
57    ReleaseGroup => "release_group",
58    ReleaseInformation => "release_information",
59    ReleaseVersion => "release_version",
60    Season => "season",
61    Source => "source",
62    Subtitles => "subtitles",
63    Title => "title",
64    Type => "type",
65    VideoResolution => "video_resolution",
66    VideoTerm => "video_term",
67    Volume => "volume",
68    Year => "year",
69}
70
71impl fmt::Display for ElementKind {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        f.write_str(self.as_str())
74    }
75}
76
77/// No matching `ElementKind` for the given string.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct ParseElementKindError;
80
81impl fmt::Display for ParseElementKindError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        f.write_str("not a valid ElementKind")
84    }
85}
86
87impl std::error::Error for ParseElementKindError {}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct Element {
91    pub kind: ElementKind,
92    pub value: String,
93    /// Index (in UTF-32 codepoints, matching upstream) in the input string.
94    pub position: usize,
95}