use zenith_core::{Diagnostic, Document, Token, TokenLiteral, TokenType, TokenValue};
use super::record_affected;
use super::structure::parse_dimension_str;
fn parse_token_literal(token_type: &TokenType, value: &str) -> Option<TokenLiteral> {
match token_type {
TokenType::Color | TokenType::FontFamily => Some(TokenLiteral::String(value.to_owned())),
TokenType::Dimension => {
let dim = parse_dimension_str(value)?;
Some(TokenLiteral::Dimension(dim))
}
TokenType::Number | TokenType::FontWeight => {
let n: f64 = value.trim().parse().ok()?;
if n.is_finite() {
Some(TokenLiteral::Number(n))
} else {
None
}
}
TokenType::Gradient
| TokenType::Shadow
| TokenType::Filter
| TokenType::Mask
| TokenType::Unknown(_) => None,
}
}
fn token_type_name(token_type: &TokenType) -> &str {
match token_type {
TokenType::Color => "color",
TokenType::Dimension => "dimension",
TokenType::Number => "number",
TokenType::FontFamily => "fontFamily",
TokenType::FontWeight => "fontWeight",
TokenType::Gradient => "gradient",
TokenType::Shadow => "shadow",
TokenType::Filter => "filter",
TokenType::Mask => "mask",
TokenType::Unknown(s) => s.as_str(),
}
}
pub(super) fn apply_create_token(
id: &str,
token_type: &str,
value: &str,
doc: &mut Document,
diagnostics: &mut Vec<Diagnostic>,
affected: &mut Vec<String>,
) {
if doc.tokens.tokens.iter().any(|t| t.id == id) {
diagnostics.push(Diagnostic::error(
"tx.duplicate_id",
format!("create_token: a token with id {:?} already exists", id),
None,
Some(id.to_owned()),
));
return;
}
let ty = TokenType::from_type_name(token_type);
match &ty {
TokenType::Gradient
| TokenType::Shadow
| TokenType::Filter
| TokenType::Mask
| TokenType::Unknown(_) => {
diagnostics.push(Diagnostic::error(
"tx.invalid_value",
format!(
"create_token: token type {:?} is not supported via this op \
(gradient/shadow/unknown tokens must be authored in source)",
token_type_name(&ty)
),
None,
Some(id.to_owned()),
));
return;
}
TokenType::Color
| TokenType::Dimension
| TokenType::Number
| TokenType::FontFamily
| TokenType::FontWeight => {}
}
let Some(lit) = parse_token_literal(&ty, value) else {
diagnostics.push(Diagnostic::error(
"tx.invalid_value",
format!(
"create_token: value {:?} is not valid for token type {:?}",
value,
token_type_name(&ty)
),
None,
Some(id.to_owned()),
));
return;
};
doc.tokens.tokens.push(Token {
id: id.to_owned(),
token_type: ty,
value: TokenValue::Literal(lit),
source_span: None,
});
record_affected(id, affected);
}
pub(super) fn apply_update_token_value(
id: &str,
value: &str,
doc: &mut Document,
diagnostics: &mut Vec<Diagnostic>,
affected: &mut Vec<String>,
) {
let Some(idx) = doc.tokens.tokens.iter().position(|t| t.id == id) else {
diagnostics.push(Diagnostic::error(
"tx.unknown_token",
format!("update_token_value: no token with id {:?} exists", id),
None,
Some(id.to_owned()),
));
return;
};
let Some(ty) = doc.tokens.tokens.get(idx).map(|t| t.token_type.clone()) else {
return; };
match &ty {
TokenType::Gradient
| TokenType::Shadow
| TokenType::Filter
| TokenType::Mask
| TokenType::Unknown(_) => {
diagnostics.push(Diagnostic::error(
"tx.invalid_value",
format!(
"update_token_value: token {:?} has type {:?} which cannot be \
updated via this op",
id,
token_type_name(&ty)
),
None,
Some(id.to_owned()),
));
return;
}
TokenType::Color
| TokenType::Dimension
| TokenType::Number
| TokenType::FontFamily
| TokenType::FontWeight => {}
}
let Some(lit) = parse_token_literal(&ty, value) else {
diagnostics.push(Diagnostic::error(
"tx.invalid_value",
format!(
"update_token_value: value {:?} is not valid for token type {:?}",
value,
token_type_name(&ty)
),
None,
Some(id.to_owned()),
));
return;
};
if let Some(t) = doc.tokens.tokens.get_mut(idx) {
t.value = TokenValue::Literal(lit);
}
record_affected(id, affected);
}