linguist/
serde.rs

1use std::{collections::HashMap, ffi::OsString, path::Path};
2
3use serde::Deserialize;
4
5use crate::{
6    error::LinguistError,
7    resolver::{HeuristicRule, Language, Scope},
8};
9
10#[derive(Debug, Clone)]
11pub struct StaticLanguage<'src> {
12    pub name: &'src str,
13    pub scope: &'src str,
14    pub aliases: Option<&'src [&'src str]>,
15    pub extensions: Option<&'src [&'src str]>,
16    pub filenames: Option<&'src [&'src str]>,
17    pub interpreters: Option<&'src [&'src str]>,
18    pub color: Option<&'src str>,
19    pub parent: Option<&'src str>,
20}
21
22impl<'src> From<&'src StaticLanguage<'src>> for Language {
23    fn from(value: &'src StaticLanguage<'src>) -> Self {
24        let parent = value.parent.map(String::from);
25        let name = String::from(value.name);
26        let aliases = value
27            .aliases
28            .map(|aliases| aliases.iter().map(|alias| String::from(*alias)).collect());
29        let scope = Scope::from(value.scope);
30        let extensions = value
31            .extensions
32            .map(|extensions| extensions.iter().map(|ext| OsString::from(*ext)).collect());
33        let filenames = value.filenames.map(|filenames| {
34            filenames
35                .iter()
36                .map(|filename| OsString::from(*filename))
37                .collect()
38        });
39        let interpreters = value.interpreters.map(|interpreters| {
40            interpreters
41                .iter()
42                .map(|interp| String::from(*interp))
43                .collect()
44        });
45        let color = value.color.map(String::from);
46
47        Language {
48            parent,
49            name,
50            aliases: aliases.unwrap_or_default(),
51            scope,
52            extensions: extensions.unwrap_or_default(),
53            filenames: filenames.unwrap_or_default(),
54            interpreters: interpreters.unwrap_or_default(),
55            color,
56        }
57    }
58}
59
60#[derive(Debug, Clone)]
61pub struct StaticHeuristicRule<'a> {
62    pub language: &'a str,
63    pub extensions: &'a [&'a str],
64    pub patterns: &'a [&'a str],
65}
66
67impl<'a> From<&'a StaticHeuristicRule<'a>> for HeuristicRule {
68    fn from(value: &'a StaticHeuristicRule<'a>) -> Self {
69        Self {
70            language: String::from(value.language),
71            extensions: value
72                .extensions
73                .iter()
74                .map(|ext| OsString::from(*ext))
75                .collect(),
76            patterns: value.patterns.iter().map(|&s| String::from(s)).collect(),
77        }
78    }
79}
80/// Deserialize a YAML file into a vector of languages. This supports the deserialization of
81/// custom language definition types by taking a generic type parameter. The generic type must
82/// implement the `TryInto<Language>` and the `serde::Deserialize` trait. Furthermore, the path
83/// to the YAML file must be provided.
84pub fn deserialize_languages<T>(path: impl AsRef<Path>) -> Result<Vec<Language>, LinguistError>
85where
86    for<'de> T: Deserialize<'de>,
87    T: TryInto<Language>,
88{
89    let content = std::fs::read_to_string(path).unwrap_or_default();
90    let data: HashMap<String, T> = match serde_yaml::from_str(&content) {
91        Ok(result) => result,
92        Err(_) => {
93            return Err(LinguistError::DeserializationError);
94        }
95    };
96
97    let mut languages: Vec<Language> = Vec::new();
98    for (name, item) in data.into_iter() {
99        match item.try_into() {
100            Ok(mut lang) => {
101                lang.name = name;
102                languages.push(lang)
103            }
104            Err(_) => {
105                return Err(LinguistError::DeserializationError);
106            }
107        };
108    }
109
110    Ok(languages)
111}
112
113/// Deserialize a YAML file into a vector of strings.
114pub fn deserialize_strings(path: impl AsRef<Path>) -> Result<Vec<String>, LinguistError> {
115    let content = std::fs::read_to_string(path).unwrap_or_default();
116    let data: Vec<String> = match serde_yaml::from_str(&content) {
117        Ok(result) => result,
118        Err(_) => {
119            return Err(LinguistError::DeserializationError);
120        }
121    };
122
123    Ok(data)
124}