use std::str::FromStr;
use colorsys::ColorAlpha;
use colorsys::Hsl;
use colorsys::Rgb;
use indexmap::IndexMap;
use indexmap::IndexSet;
use lazy_static::lazy_static;
use regex::Regex;
use typed_builder::TypedBuilder;
use crate::constants::INDENTATION;
use crate::CssValue;
lazy_static! {
static ref ESCAPE_CSS_STRING_REGEX: Regex =
Regex::new(r#"(#|&|~|=|>|'|:|"|!|;|,|\.|\*|\+|\||\[|\]|\(|\)|/|\^|\$)"#).unwrap();
static ref CSS_VARIABLE_REGEX: Regex =
Regex::new(r#"(?m)(?i)var\((--[a-zA-Z0-9_\-]+?)(?:,.*?)?\)"#).unwrap();
}
pub fn get_css_variables_from_string(value: &str) -> IndexSet<String> {
CSS_VARIABLE_REGEX
.captures_iter(value)
.map(|capture| capture[1].to_owned())
.collect()
}
pub fn escape_css_string(value: &str) -> String {
ESCAPE_CSS_STRING_REGEX
.replace_all(value, "\\$1")
.to_string()
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct IndentProps {
#[builder(setter(into))]
pub content: String,
#[builder(
default,
setter(doc = "Set the indentation style for the indent function.")
)]
pub style: IndentStyle,
}
#[derive(Debug, Clone)]
pub enum IndentStyle {
Tab,
Spaces(u8),
}
impl Default for IndentStyle {
fn default() -> Self {
Self::Spaces(INDENTATION)
}
}
pub fn indent(props: IndentProps) -> String {
let IndentProps { content, style } = props;
let lines = content.split('\n');
let mut result = String::new();
let empty_line_regex = Regex::new(r"^\s*$").unwrap();
for line in lines {
if empty_line_regex.is_match(line) {
result.push('\n');
continue;
}
let indentation = match style {
IndentStyle::Tab => String::from("\t"),
IndentStyle::Spaces(spaces) => " ".repeat(spaces.into()),
};
result.push_str(&format!("{indentation}{line}\n"));
}
result.trim_end().to_string()
}
pub fn convert_css_value_to_color(
original: &CssValue,
palette: &IndexMap<String, String>,
opacity: &str,
) -> String {
let mut string_value = original.get_string();
if let Some(derived_value) = palette.get(&string_value) {
string_value = derived_value.clone();
}
get_rgba_color_from_string(&string_value, opacity)
}
pub fn get_rgba_color_from_string(value: &str, opacity: &str) -> String {
let mut string_value = value.to_owned();
let rgb = if let Some(stripped) = string_value.strip_prefix('#') {
Rgb::from_hex_str(stripped).ok()
} else if string_value.starts_with("rgb") {
Rgb::from_str(&string_value).ok()
} else if string_value.starts_with("hsl") {
if let Ok(hsl) = Hsl::from_str(&string_value) {
Some(Rgb::from(hsl))
} else {
None
}
} else {
None
};
let wrapped_opacity = wrap_css_variable(opacity);
if let Some(rgb) = rgb {
let alpha = if rgb.alpha() < 1.0 {
format!("calc({} * {})", rgb.alpha(), wrapped_opacity)
} else {
wrapped_opacity
};
string_value = format!(
"rgba({}, {}, {}, {})",
rgb.red(),
rgb.green(),
rgb.blue(),
alpha
);
}
string_value
}
pub fn wrap_css_variable(value: &str) -> String {
if value.starts_with("var(") && value.ends_with(')') {
value.to_owned()
} else {
format!("var({value})")
}
}