use std::collections::BTreeMap;
use std::str::FromStr;
use serde::{Deserialize, json as serde_json};
use crate::Error;
#[derive(Clone, Debug, Deserialize)]
pub struct RawGrammar {
#[serde(rename = "displayName")]
pub display_name: Option<String>,
pub name: String,
#[serde(rename = "scopeName")]
pub scope_name: String,
#[serde(rename = "fileTypes")]
pub file_types: Option<Vec<String>>,
#[serde(rename = "firstLineMatch")]
pub first_line_match: Option<serde_json::Value>,
pub patterns: Vec<RawPattern>,
pub repository: Option<BTreeMap<String, RawPattern>>,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct RawPattern {
pub name: Option<String>,
#[serde(rename = "match")]
pub match_rule: Option<String>,
pub begin: Option<String>,
pub end: Option<String>,
pub patterns: Option<Vec<RawPattern>>,
pub include: Option<String>,
pub captures: Option<BTreeMap<String, RawCapture>>,
#[serde(rename = "beginCaptures")]
pub begin_captures: Option<BTreeMap<String, RawCapture>>,
#[serde(rename = "endCaptures")]
pub end_captures: Option<BTreeMap<String, RawCapture>>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RawCapture {
pub name: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RawTheme {
pub name: String,
pub settings: Option<Vec<RawThemeRule>>,
#[serde(rename = "tokenColors")]
pub token_colors: Option<Vec<RawThemeRule>>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RawThemeRule {
pub scope: Option<serde_json::Value>,
pub settings: RawStyle,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct RawStyle {
pub foreground: Option<String>,
#[serde(rename = "fontStyle")]
pub font_style: Option<String>,
}
impl RawGrammar {
pub fn parse(input: &str) -> Result<Self, Error> {
input.parse()
}
}
impl FromStr for RawGrammar {
type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
serde_json::from_str(input).map_err(|_| Error::InvalidGrammar)
}
}
impl RawTheme {
pub fn parse(input: &str) -> Result<Self, Error> {
input.parse()
}
}
impl FromStr for RawTheme {
type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
serde_json::from_str(input).map_err(|_| Error::InvalidTheme)
}
}
impl RawThemeRule {
pub(crate) fn scope_selectors(&self) -> Vec<String> {
let Some(scope) = self.scope.as_ref() else {
return Vec::new();
};
let mut selectors = Vec::new();
match scope {
serde_json::Value::String(scope) => push_selectors(scope, &mut selectors),
serde_json::Value::Array(scopes) => {
for scope in scopes {
if let serde_json::Value::String(scope) = scope {
push_selectors(scope, &mut selectors);
}
}
}
_ => {}
}
selectors
}
}
pub(crate) fn first_line_patterns(value: Option<&serde_json::Value>) -> Vec<String> {
match value {
Some(serde_json::Value::String(pattern)) => vec![pattern.clone()],
Some(serde_json::Value::Array(values)) => values
.iter()
.filter_map(|value| {
if let serde_json::Value::String(pattern) = value {
Some(pattern.clone())
} else {
None
}
})
.collect(),
_ => Vec::new(),
}
}
fn push_selectors(input: &str, selectors: &mut Vec<String>) {
selectors.extend(
input
.split(',')
.map(str::trim)
.filter(|selector| !selector.is_empty())
.map(str::to_owned),
);
}