use serde::Deserialize;
use serde_yaml::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum Extends {
One(String),
Many(Vec<String>),
}
#[derive(Debug, Clone, Deserialize)]
pub struct SyntaxDefinition {
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub file_extensions: Vec<String>,
#[serde(default)]
pub hidden_file_extensions: Vec<String>,
#[serde(default)]
pub first_line_match: Option<String>,
pub scope: String,
#[serde(default = "default_version")]
pub version: u32,
#[serde(default)]
pub extends: Option<Extends>,
#[serde(default)]
pub hidden: bool,
#[serde(default)]
pub variables: HashMap<String, String>,
pub contexts: HashMap<String, Vec<RawContextPattern>>,
}
fn default_version() -> u32 {
2
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
pub enum RawContextPattern {
Meta(MetaPattern),
Match(MatchPattern),
Include(IncludePattern),
}
impl<'de> Deserialize<'de> for RawContextPattern {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
let Value::Mapping(map) = &value else {
return Err(serde::de::Error::custom(
"context patterns must be YAML mappings",
));
};
let has_key = |k: &str| map.contains_key(Value::String(k.to_string()));
if has_key("match") {
let pattern: MatchPattern =
serde_yaml::from_value(value).map_err(serde::de::Error::custom)?;
return Ok(Self::Match(pattern));
}
if has_key("include") {
let pattern: IncludePattern =
serde_yaml::from_value(value).map_err(serde::de::Error::custom)?;
return Ok(Self::Include(pattern));
}
let meta: MetaPattern = serde_yaml::from_value(Value::Mapping(map.clone()))
.map_err(serde::de::Error::custom)?;
Ok(Self::Meta(meta))
}
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct MetaPattern {
#[serde(default)]
pub meta_scope: Option<String>,
#[serde(default)]
pub meta_content_scope: Option<String>,
#[serde(default)]
pub meta_include_prototype: Option<bool>,
#[serde(default)]
pub clear_scopes: Option<ClearScopes>,
#[serde(default)]
pub meta_prepend: Option<bool>,
#[serde(default)]
pub meta_append: Option<bool>,
#[serde(default)]
pub comment: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum ClearScopes {
Count(usize),
All(bool),
}
#[derive(Debug, Clone, Deserialize)]
pub struct IncludePattern {
pub include: String,
#[serde(default)]
pub apply_prototype: Option<bool>,
#[serde(default)]
pub comment: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MatchPattern {
#[serde(rename = "match")]
pub regex: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default)]
pub captures: HashMap<u32, CaptureSpec>,
#[serde(default)]
pub push: Option<ContextReference>,
#[serde(default)]
pub pop: Option<PopAction>,
#[serde(default)]
pub set: Option<ContextReference>,
#[serde(default)]
pub embed: Option<String>,
#[serde(default)]
pub embed_scope: Option<String>,
#[serde(default)]
pub escape: Option<String>,
#[serde(default)]
pub escape_captures: HashMap<u32, CaptureSpec>,
#[serde(default)]
pub with_prototype: Option<Vec<RawContextPattern>>,
#[serde(default)]
pub branch: Option<Vec<String>>,
#[serde(default)]
pub branch_point: Option<String>,
#[serde(default)]
pub fail: Option<String>,
#[serde(default)]
pub comment: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum CaptureSpec {
Scope(String),
Complex {
scope: String,
#[serde(default)]
captures: HashMap<u32, CaptureSpec>,
},
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum ContextReference {
Name(String),
Names(Vec<String>),
Inline(Vec<RawContextPattern>),
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum PopAction {
Bool(bool),
Count(usize),
}