use std::borrow::Cow;
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::Arc;
use ktrs_editorconfig::{AnyPropertyType, Property, PropertyType, PropertyValue};
use crate::editorconfig::code_style::CodeStyleValue;
pub trait PropertyValueType: Clone + Debug + PartialEq + Send + Sync + 'static {
fn to_value_string(&self) -> String;
}
impl PropertyValueType for i32 {
fn to_value_string(&self) -> String {
self.to_string()
}
}
impl PropertyValueType for bool {
fn to_value_string(&self) -> String {
self.to_string()
}
}
impl PropertyValueType for String {
fn to_value_string(&self) -> String {
self.clone()
}
}
impl PropertyValueType for Vec<String> {
fn to_value_string(&self) -> String {
format!("[{}]", self.join(", "))
}
}
#[macro_export]
macro_rules! enum_property_value_type {
($($enum:ty),*) => {
$(impl $crate::editorconfig::PropertyValueType for $enum {
fn to_value_string(&self) -> String {
ktrs_editorconfig::EnumValue::name(*self).to_owned()
}
})*
};
}
enum_property_value_type!(
ktrs_editorconfig::EndOfLineValue,
ktrs_editorconfig::IndentStyleValue
);
pub type PropertyMapper<T> = fn(Option<&Property>, CodeStyleValue) -> Option<T>;
#[derive(Clone, Debug)]
pub struct EditorConfigProperty<T: PropertyValueType> {
pub type_: &'static PropertyType<T>,
pub default_value: T,
pub ktlint_official_code_style_default_value: T,
pub intellij_idea_code_style_default_value: T,
pub android_studio_code_style_default_value: T,
pub property_mapper: Option<PropertyMapper<T>>,
pub property_writer: fn(&T) -> String,
pub deprecation_warning: Option<&'static str>,
pub deprecation_error: Option<&'static str>,
pub name: Cow<'static, str>,
}
impl<T: PropertyValueType> EditorConfigProperty<T> {
pub fn new(type_: &'static PropertyType<T>, default_value: T) -> EditorConfigProperty<T> {
EditorConfigProperty {
type_,
ktlint_official_code_style_default_value: default_value.clone(),
intellij_idea_code_style_default_value: default_value.clone(),
android_studio_code_style_default_value: default_value.clone(),
default_value,
property_mapper: None,
property_writer: |value| value.to_value_string(),
deprecation_warning: None,
deprecation_error: None,
name: Cow::Borrowed(type_.name),
}
}
pub fn default_value_for(&self, code_style: CodeStyleValue) -> &T {
match code_style {
CodeStyleValue::AndroidStudio => &self.android_studio_code_style_default_value,
CodeStyleValue::IntellijIdea => &self.intellij_idea_code_style_default_value,
CodeStyleValue::KtlintOfficial => &self.ktlint_official_code_style_default_value,
}
}
}
pub trait AnyEditorConfigProperty: Debug + Send + Sync {
fn name(&self) -> &str;
fn property_type(&self) -> &'static dyn AnyPropertyType;
fn write_default_value(&self, code_style: CodeStyleValue) -> String;
fn parse(&self, value: Option<&str>) -> PropertyValue<()>;
fn identity(&self) -> String;
}
impl<T: PropertyValueType> AnyEditorConfigProperty for EditorConfigProperty<T> {
fn name(&self) -> &str {
&self.name
}
fn property_type(&self) -> &'static dyn AnyPropertyType {
self.type_
}
fn write_default_value(&self, code_style: CodeStyleValue) -> String {
(self.property_writer)(self.default_value_for(code_style))
}
fn parse(&self, value: Option<&str>) -> PropertyValue<()> {
self.type_.parse(value).erase()
}
fn identity(&self) -> String {
format!(
"{}:{}:{:?}:{:?}:{:?}:{:?}:{:?}:{:?}",
self.name,
self.type_.name,
self.default_value,
self.ktlint_official_code_style_default_value,
self.intellij_idea_code_style_default_value,
self.android_studio_code_style_default_value,
self.deprecation_warning,
self.deprecation_error
)
}
}
#[derive(Clone, Debug)]
pub enum PropertyRef {
Static(&'static dyn AnyEditorConfigProperty),
Shared(Arc<dyn AnyEditorConfigProperty>),
}
impl Deref for PropertyRef {
type Target = dyn AnyEditorConfigProperty;
fn deref(&self) -> &Self::Target {
match self {
PropertyRef::Static(p) => *p,
PropertyRef::Shared(p) => p.as_ref(),
}
}
}
impl<T: PropertyValueType> From<&'static EditorConfigProperty<T>> for PropertyRef {
fn from(p: &'static EditorConfigProperty<T>) -> PropertyRef {
PropertyRef::Static(p)
}
}
impl<T: PropertyValueType> From<EditorConfigProperty<T>> for PropertyRef {
fn from(p: EditorConfigProperty<T>) -> PropertyRef {
PropertyRef::Shared(Arc::new(p))
}
}
pub fn to_property_with_value(property: &dyn AnyEditorConfigProperty, value: &str) -> Property {
Property::new(property.name(), Some(property.property_type()), Some(value))
}
pub fn to_property_with_parsed_value(
property: &dyn AnyEditorConfigProperty,
value: PropertyValue<()>,
) -> Property {
Property::with_value(property.name(), Some(property.property_type()), value)
}