1#![forbid(unsafe_code)]
15
16pub use hikari_core::{ByteSpan, HighlightSpan, HlClass};
17
18use serde::{Deserialize, Serialize};
19
20#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
26#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
27#[serde(rename_all = "snake_case")]
28pub enum Semantic {
29 Keyword,
30 Symbol,
31 KeywordArg,
32 String,
33 Number,
34 Literal,
35 Comment,
36 Accent,
37 Muted,
38 Error,
39 Warning,
40 Info,
41 Hint,
42 Added,
43 Removed,
44 Unchanged,
45}
46
47impl Semantic {
48 #[must_use]
50 pub const fn all() -> [Semantic; 16] {
51 use Semantic::{
52 Accent, Added, Comment, Error, Hint, Info, Keyword, KeywordArg, Literal, Muted, Number,
53 Removed, String, Symbol, Unchanged, Warning,
54 };
55 [
56 Keyword, Symbol, KeywordArg, String, Number, Literal, Comment, Accent, Muted, Error,
57 Warning, Info, Hint, Added, Removed, Unchanged,
58 ]
59 }
60}
61
62impl From<Semantic> for HlClass {
66 fn from(s: Semantic) -> Self {
67 match s {
68 Semantic::Keyword => HlClass::Keyword,
69 Semantic::Symbol => HlClass::Punctuation,
70 Semantic::KeywordArg => HlClass::KeywordArg,
71 Semantic::String => HlClass::Str,
72 Semantic::Number => HlClass::Numeric { float: false },
73 Semantic::Literal => HlClass::Constant,
74 Semantic::Comment => HlClass::Comment { multiline: false },
75 Semantic::Accent => HlClass::Special,
76 Semantic::Muted => HlClass::Plain,
77 Semantic::Error => HlClass::Error,
78 Semantic::Warning => HlClass::Warning,
79 Semantic::Info => HlClass::Info,
80 Semantic::Hint => HlClass::Hint,
81 Semantic::Added => HlClass::Added,
82 Semantic::Removed => HlClass::Removed,
83 Semantic::Unchanged => HlClass::Unchanged,
84 }
85 }
86}
87
88#[must_use]
93pub fn hlclass_to_semantic(c: HlClass) -> Semantic {
94 match c {
95 HlClass::Keyword => Semantic::Keyword,
96 HlClass::KeywordArg => Semantic::KeywordArg,
97 HlClass::Str => Semantic::String,
98 HlClass::Numeric { .. } => Semantic::Number,
99 HlClass::Boolean | HlClass::Constant => Semantic::Literal,
100 HlClass::Comment { .. } => Semantic::Comment,
101 HlClass::Type
103 | HlClass::Function
104 | HlClass::Namespace
105 | HlClass::Attribute
106 | HlClass::Escape
107 | HlClass::Special
108 | HlClass::Hyperlink => Semantic::Accent,
109 HlClass::Punctuation | HlClass::Operator => Semantic::Symbol,
111 HlClass::Error => Semantic::Error,
112 HlClass::Warning => Semantic::Warning,
113 HlClass::Info => Semantic::Info,
114 HlClass::Hint => Semantic::Hint,
115 HlClass::Added => Semantic::Added,
116 HlClass::Removed => Semantic::Removed,
117 HlClass::Plain => Semantic::Muted,
119 HlClass::Variable | HlClass::Whitespace | HlClass::Unchanged => Semantic::Unchanged,
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::{HlClass, Semantic, hlclass_to_semantic};
126
127 #[test]
128 fn semantic_hlclass_roundtrips_to_identity() {
129 for s in Semantic::all() {
130 let hl: HlClass = s.into();
131 assert_eq!(
132 hlclass_to_semantic(hl),
133 s,
134 "Semantic -> HlClass -> Semantic must be identity for {s:?}",
135 );
136 }
137 }
138
139 #[test]
140 fn serde_is_snake_case_stable() {
141 assert_eq!(
142 serde_json::to_string(&Semantic::KeywordArg).unwrap(),
143 "\"keyword_arg\"",
144 );
145 let back: Semantic = serde_json::from_str("\"unchanged\"").unwrap();
146 assert_eq!(back, Semantic::Unchanged);
147 }
148}