use crate::parser::lifetimes::Lifetimes;
use std::collections::HashMap;
use syn::{parse, spanned::Spanned, Type};
pub type DataTypes = HashMap<String, Type>;
#[derive(Debug)]
pub struct DataDefinitions {
pub data_types: DataTypes,
pub all_lifetimes: Lifetimes,
pub lifetimes: HashMap<String, Lifetimes>,
}
impl DataDefinitions {
pub fn new() -> Self {
Self {
data_types: DataTypes::new(),
all_lifetimes: Lifetimes::new(),
lifetimes: HashMap::new(),
}
}
fn add(&mut self, key: String, data_type: Type) -> Result<(), parse::Error> {
let lifetimes = Lifetimes::from_type(&data_type)?;
self.data_types.insert(key.clone(), data_type);
if !lifetimes.is_empty() {
self.all_lifetimes.extend(&lifetimes);
self.lifetimes.insert(key, lifetimes);
}
Ok(())
}
pub fn collect(&mut self, key: String, data_type: Option<Type>) -> Result<(), parse::Error> {
let prev = self.data_types.get(&key);
if let Some(prev) = prev {
if let Some(ref data_type) = data_type {
if prev != &data_type.clone() {
return Err(parse::Error::new(
data_type.span(),
format!(
"This event's type {} does not match its previous definition",
key
),
));
}
} else {
return Err(parse::Error::new(
data_type.span(),
format!(
"This event's type {} does not match its previous definition",
key
),
));
}
}
if let Some(data_type) = data_type {
self.add(key, data_type)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ty(source: &str) -> Type {
syn::parse_str(source).unwrap()
}
#[test]
fn collects_consistent_types_and_lifetimes() {
let mut definitions = DataDefinitions::new();
definitions
.collect("event".to_owned(), Some(ty("&'a str")))
.unwrap();
definitions
.collect("event".to_owned(), Some(ty("&'a str")))
.unwrap();
assert_eq!(definitions.data_types.len(), 1);
assert_eq!(definitions.all_lifetimes.as_slice().len(), 1);
assert_eq!(definitions.lifetimes["event"].as_slice().len(), 1);
}
#[test]
fn rejects_conflicting_or_missing_redefinitions() {
let mut definitions = DataDefinitions::new();
definitions
.collect("event".to_owned(), Some(ty("u8")))
.unwrap();
assert!(definitions
.collect("event".to_owned(), Some(ty("u16")))
.is_err());
assert!(definitions.collect("event".to_owned(), None).is_err());
}
#[test]
fn ignores_absent_first_definition() {
let mut definitions = DataDefinitions::new();
definitions.collect("event".to_owned(), None).unwrap();
assert!(definitions.data_types.is_empty());
}
}