use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PresetName {
Google,
Standard,
Airbnb,
}
impl PresetName {
pub fn parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"google" => Some(PresetName::Google),
"standard" => Some(PresetName::Standard),
"airbnb" => Some(PresetName::Airbnb),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
PresetName::Google => "google",
PresetName::Standard => "standard",
PresetName::Airbnb => "airbnb",
}
}
pub fn all() -> &'static [PresetName] {
&[PresetName::Google, PresetName::Standard, PresetName::Airbnb]
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LanguageRules {
#[serde(default)]
pub indent: Option<IndentStyle>,
#[serde(default)]
pub max_line_length: Option<usize>,
#[serde(default)]
pub semicolons: Option<bool>,
#[serde(default)]
pub quotes: Option<QuoteStyle>,
#[serde(default)]
pub trailing_commas: Option<TrailingCommaStyle>,
#[serde(default)]
pub enable_rules: Vec<String>,
#[serde(default)]
pub disable_rules: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum IndentStyle {
Tabs,
Spaces(u8),
}
impl Default for IndentStyle {
fn default() -> Self {
IndentStyle::Spaces(4)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum QuoteStyle {
Single,
#[default]
Double,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TrailingCommaStyle {
#[default]
None,
Es5,
All,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Preset {
pub name: String,
pub description: String,
#[serde(default)]
pub languages: HashMap<String, LanguageRules>,
}
impl Preset {
pub fn google() -> Self {
let mut languages = HashMap::new();
languages.insert(
"javascript".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: Some(80),
semicolons: Some(true),
quotes: Some(QuoteStyle::Single),
trailing_commas: Some(TrailingCommaStyle::Es5),
..Default::default()
},
);
languages.insert(
"typescript".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: Some(80),
semicolons: Some(true),
quotes: Some(QuoteStyle::Single),
trailing_commas: Some(TrailingCommaStyle::Es5),
..Default::default()
},
);
languages.insert(
"python".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(80),
..Default::default()
},
);
languages.insert(
"cpp".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: Some(80),
..Default::default()
},
);
languages.insert(
"java".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: Some(100),
..Default::default()
},
);
languages.insert(
"go".to_string(),
LanguageRules {
indent: Some(IndentStyle::Tabs),
max_line_length: None, ..Default::default()
},
);
languages.insert(
"rust".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(100),
..Default::default()
},
);
Preset {
name: "google".to_string(),
description: "Google's coding standards".to_string(),
languages,
}
}
pub fn standard() -> Self {
let mut languages = HashMap::new();
languages.insert(
"javascript".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: None,
semicolons: Some(false), quotes: Some(QuoteStyle::Single),
trailing_commas: Some(TrailingCommaStyle::None),
..Default::default()
},
);
languages.insert(
"typescript".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: None,
semicolons: Some(false),
quotes: Some(QuoteStyle::Single),
trailing_commas: Some(TrailingCommaStyle::None),
..Default::default()
},
);
languages.insert(
"python".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(79),
..Default::default()
},
);
languages.insert(
"cpp".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(120),
..Default::default()
},
);
languages.insert(
"go".to_string(),
LanguageRules {
indent: Some(IndentStyle::Tabs),
max_line_length: None,
..Default::default()
},
);
languages.insert(
"rust".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(100),
..Default::default()
},
);
Preset {
name: "standard".to_string(),
description: "Community standard styles (PEP 8, StandardJS, etc.)".to_string(),
languages,
}
}
pub fn airbnb() -> Self {
let mut languages = HashMap::new();
languages.insert(
"javascript".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: Some(100),
semicolons: Some(true),
quotes: Some(QuoteStyle::Single),
trailing_commas: Some(TrailingCommaStyle::All),
..Default::default()
},
);
languages.insert(
"typescript".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(2)),
max_line_length: Some(100),
semicolons: Some(true),
quotes: Some(QuoteStyle::Single),
trailing_commas: Some(TrailingCommaStyle::All),
..Default::default()
},
);
languages.insert(
"python".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(100),
..Default::default()
},
);
languages.insert(
"go".to_string(),
LanguageRules {
indent: Some(IndentStyle::Tabs),
max_line_length: Some(120),
..Default::default()
},
);
languages.insert(
"rust".to_string(),
LanguageRules {
indent: Some(IndentStyle::Spaces(4)),
max_line_length: Some(100),
..Default::default()
},
);
Preset {
name: "airbnb".to_string(),
description: "Airbnb's style guide".to_string(),
languages,
}
}
pub fn load(name: PresetName) -> Self {
match name {
PresetName::Google => Self::google(),
PresetName::Standard => Self::standard(),
PresetName::Airbnb => Self::airbnb(),
}
}
pub fn get_language_rules(&self, language: &str) -> Option<&LanguageRules> {
self.languages.get(language)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preset_name_parse() {
assert_eq!(PresetName::parse("google"), Some(PresetName::Google));
assert_eq!(PresetName::parse("GOOGLE"), Some(PresetName::Google));
assert_eq!(PresetName::parse("standard"), Some(PresetName::Standard));
assert_eq!(PresetName::parse("airbnb"), Some(PresetName::Airbnb));
assert_eq!(PresetName::parse("unknown"), None);
}
#[test]
fn test_google_preset() {
let preset = Preset::google();
assert_eq!(preset.name, "google");
let js_rules = preset.get_language_rules("javascript").unwrap();
assert_eq!(js_rules.indent, Some(IndentStyle::Spaces(2)));
assert_eq!(js_rules.semicolons, Some(true));
}
#[test]
fn test_standard_preset() {
let preset = Preset::standard();
assert_eq!(preset.name, "standard");
let js_rules = preset.get_language_rules("javascript").unwrap();
assert_eq!(js_rules.semicolons, Some(false)); }
#[test]
fn test_airbnb_preset() {
let preset = Preset::airbnb();
assert_eq!(preset.name, "airbnb");
let js_rules = preset.get_language_rules("javascript").unwrap();
assert_eq!(js_rules.trailing_commas, Some(TrailingCommaStyle::All));
}
}