use ruprizzle_core::span::Span;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Ast {
pub decls: Vec<Decl>,
}
impl Ast {
pub fn datasources(&self) -> impl Iterator<Item = &Block> {
self.decls.iter().filter_map(|d| match d {
Decl::Datasource(b) => Some(b),
_ => None,
})
}
pub fn generators(&self) -> impl Iterator<Item = &Block> {
self.decls.iter().filter_map(|d| match d {
Decl::Generator(b) => Some(b),
_ => None,
})
}
pub fn enums(&self) -> impl Iterator<Item = &EnumDecl> {
self.decls.iter().filter_map(|d| match d {
Decl::Enum(e) => Some(e),
_ => None,
})
}
pub fn models(&self) -> impl Iterator<Item = &ModelDecl> {
self.decls.iter().filter_map(|d| match d {
Decl::Model(m) => Some(m),
_ => None,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Decl {
Datasource(Block),
Generator(Block),
Enum(EnumDecl),
Model(ModelDecl),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub name: String,
pub entries: Vec<ConfigEntry>,
pub span: Span,
}
impl Block {
#[must_use]
pub fn get(&self, key: &str) -> Option<&ConfigEntry> {
self.entries.iter().find(|e| e.key == key)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConfigEntry {
pub key: String,
pub value: Value,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumDecl {
pub name: String,
pub name_span: Span,
pub variants: Vec<VariantDecl>,
pub docs: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct VariantDecl {
pub name: String,
pub map: Option<String>,
pub docs: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ModelDecl {
pub name: String,
pub name_span: Span,
pub fields: Vec<FieldDecl>,
pub block_attrs: Vec<Attr>,
pub docs: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldDecl {
pub name: String,
pub name_span: Span,
pub type_name: String,
pub type_span: Span,
pub arity: Arity,
pub attrs: Vec<Attr>,
pub docs: Option<String>,
pub span: Span,
}
impl FieldDecl {
#[must_use]
pub fn attr(&self, path: &str) -> Option<&Attr> {
self.attrs.iter().find(|a| a.path == path)
}
#[must_use]
pub fn has_attr(&self, path: &str) -> bool {
self.attr(path).is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arity {
Required,
Optional,
List,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attr {
pub path: String,
pub args: Vec<Arg>,
pub span: Span,
}
impl Attr {
#[must_use]
pub fn named(&self, name: &str) -> Option<&Value> {
self.args.iter().find_map(|a| match a {
Arg::Named { name: n, value, .. } if n == name => Some(value),
_ => None,
})
}
pub fn positional(&self) -> impl Iterator<Item = &Value> {
self.args.iter().filter_map(|a| match a {
Arg::Positional(v) => Some(v),
Arg::Named { .. } => None,
})
}
#[must_use]
pub fn first_positional(&self) -> Option<&Value> {
self.positional().next()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Arg {
Named {
name: String,
value: Value,
span: Span,
},
Positional(Value),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Str(String, Span),
Num(String, Span),
Bool(bool, Span),
Ident(String, Span),
Env(String, Span),
Func {
name: String,
args: Vec<Value>,
span: Span,
},
Array(Vec<Value>, Span),
}
impl Value {
#[must_use]
pub fn span(&self) -> Span {
match self {
Value::Str(_, s)
| Value::Num(_, s)
| Value::Bool(_, s)
| Value::Ident(_, s)
| Value::Env(_, s)
| Value::Func { span: s, .. }
| Value::Array(_, s) => *s,
}
}
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match self {
Value::Str(s, _) => Some(s),
_ => None,
}
}
#[must_use]
pub fn as_ident(&self) -> Option<&str> {
match self {
Value::Ident(s, _) => Some(s),
_ => None,
}
}
#[must_use]
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Value::Array(v, _) => Some(v),
_ => None,
}
}
#[must_use]
pub fn describe(&self) -> String {
match self {
Value::Str(s, _) => format!("\"{s}\""),
Value::Num(n, _) => n.clone(),
Value::Bool(b, _) => b.to_string(),
Value::Ident(i, _) => i.clone(),
Value::Env(v, _) => format!("env(\"{v}\")"),
Value::Func { name, .. } => format!("{name}(…)"),
Value::Array(..) => "[…]".to_owned(),
}
}
}