123 lines
3.6 KiB
Rust
123 lines
3.6 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
convert::{TryFrom, TryInto},
|
|
};
|
|
|
|
use simplexpr::{
|
|
dynval::{DynVal, FromDynVal},
|
|
eval::EvalError,
|
|
SimplExpr,
|
|
};
|
|
|
|
use crate::{
|
|
error::AstError,
|
|
parser::{ast::Ast, from_ast::FromAst},
|
|
};
|
|
use eww_shared_util::{AttrName, Span, VarName};
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum AttrError {
|
|
#[error("Missing required attribute {0}")]
|
|
MissingRequiredAttr(Span, AttrName),
|
|
|
|
#[error("{1}")]
|
|
EvaluationError(Span, EvalError),
|
|
|
|
#[error("{1}")]
|
|
Other(Span, Box<dyn std::error::Error + Sync + Send + 'static>),
|
|
}
|
|
|
|
impl AttrError {
|
|
pub fn span(&self) -> Span {
|
|
match self {
|
|
AttrError::MissingRequiredAttr(span, _) => *span,
|
|
AttrError::EvaluationError(span, _) => *span,
|
|
AttrError::Other(span, _) => *span,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct UnusedAttrs {
|
|
definition_span: Span,
|
|
attrs: Vec<(Span, AttrName)>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
|
|
pub struct AttrEntry {
|
|
pub key_span: Span,
|
|
pub value: Ast,
|
|
}
|
|
|
|
impl AttrEntry {
|
|
pub fn new(key_span: Span, value: Ast) -> AttrEntry {
|
|
AttrEntry { key_span, value }
|
|
}
|
|
}
|
|
|
|
// TODO maybe make this generic over the contained content
|
|
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)]
|
|
pub struct Attributes {
|
|
pub span: Span,
|
|
pub attrs: HashMap<AttrName, AttrEntry>,
|
|
}
|
|
|
|
impl Attributes {
|
|
pub fn new(span: Span, attrs: HashMap<AttrName, AttrEntry>) -> Self {
|
|
Attributes { span, attrs }
|
|
}
|
|
|
|
pub fn ast_required<T: FromAst>(&mut self, key: &str) -> Result<T, AstError> {
|
|
let key = AttrName(key.to_string());
|
|
match self.attrs.remove(&key) {
|
|
Some(AttrEntry { key_span, value }) => T::from_ast(value),
|
|
None => Err(AttrError::MissingRequiredAttr(self.span, key.clone()).into()),
|
|
}
|
|
}
|
|
|
|
pub fn ast_optional<T: FromAst>(&mut self, key: &str) -> Result<Option<T>, AstError> {
|
|
match self.attrs.remove(&AttrName(key.to_string())) {
|
|
Some(AttrEntry { key_span, value }) => T::from_ast(value).map(Some),
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
pub fn primitive_required<T, E>(&mut self, key: &str) -> Result<T, AstError>
|
|
where
|
|
E: std::error::Error + 'static + Sync + Send,
|
|
T: FromDynVal<Err = E>,
|
|
{
|
|
let ast: SimplExpr = self.ast_required(&key)?;
|
|
Ok(ast
|
|
.eval_no_vars()
|
|
.map_err(|err| AttrError::EvaluationError(ast.span().into(), err))?
|
|
.read_as()
|
|
.map_err(|e| AttrError::Other(ast.span().into(), Box::new(e)))?)
|
|
}
|
|
|
|
pub fn primitive_optional<T, E>(&mut self, key: &str) -> Result<Option<T>, AstError>
|
|
where
|
|
E: std::error::Error + 'static + Sync + Send,
|
|
T: FromDynVal<Err = E>,
|
|
{
|
|
let ast: SimplExpr = match self.ast_optional(key)? {
|
|
Some(ast) => ast,
|
|
None => return Ok(None),
|
|
};
|
|
Ok(Some(
|
|
ast.eval_no_vars()
|
|
.map_err(|err| AttrError::EvaluationError(ast.span().into(), err))?
|
|
.read_as()
|
|
.map_err(|e| AttrError::Other(ast.span().into(), Box::new(e)))?,
|
|
))
|
|
}
|
|
|
|
/// Consumes the attributes to return a list of unused attributes which may be used to emit a warning.
|
|
/// TODO actually use this and implement warnings,... lol
|
|
pub fn get_unused(self, definition_span: Span) -> UnusedAttrs {
|
|
UnusedAttrs {
|
|
definition_span,
|
|
attrs: self.attrs.into_iter().map(|(k, v)| (v.key_span.to(v.value.span().into()), k)).collect(),
|
|
}
|
|
}
|
|
}
|