use super::super::range::{Position, Range};
use super::super::traits::{AstNode, Container, Visitor, VisualStructure};
use super::container::GeneralContainer;
use super::content_item::ContentItem;
use super::data::Data;
use super::label::Label;
use super::parameter::Parameter;
use super::typed_content::ContentElement;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct Annotation {
pub data: Data,
pub children: GeneralContainer,
pub location: Range,
}
impl Annotation {
fn default_location() -> Range {
Range::new(0..0, Position::new(0, 0), Position::new(0, 0))
}
pub fn new(label: Label, parameters: Vec<Parameter>, children: Vec<ContentElement>) -> Self {
let data = Data::new(label, parameters);
Self::from_data(data, children)
}
pub fn marker(label: Label) -> Self {
Self::from_data(Data::new(label, Vec::new()), Vec::new())
}
pub fn with_parameters(label: Label, parameters: Vec<Parameter>) -> Self {
Self::from_data(Data::new(label, parameters), Vec::new())
}
pub fn from_data(data: Data, children: Vec<ContentElement>) -> Self {
Self {
data,
children: GeneralContainer::from_typed(children),
location: Self::default_location(),
}
}
pub fn at(mut self, location: Range) -> Self {
self.location = location;
self
}
pub fn header_location(&self) -> &Range {
&self.data.location
}
pub fn body_location(&self) -> Option<Range> {
Range::bounding_box(self.children.iter().map(|item| item.range()))
}
}
impl AstNode for Annotation {
fn node_type(&self) -> &'static str {
"Annotation"
}
fn display_label(&self) -> String {
if self.data.parameters.is_empty() {
self.data.label.value.clone()
} else {
format!(
"{} ({} params)",
self.data.label.value,
self.data.parameters.len()
)
}
}
fn range(&self) -> &Range {
&self.location
}
fn accept(&self, visitor: &mut dyn Visitor) {
visitor.visit_annotation(self);
super::super::traits::visit_children(visitor, &self.children);
visitor.leave_annotation(self);
}
}
impl VisualStructure for Annotation {
fn is_source_line_node(&self) -> bool {
true
}
fn has_visual_header(&self) -> bool {
true
}
}
impl Container for Annotation {
fn label(&self) -> &str {
&self.data.label.value
}
fn children(&self) -> &[ContentItem] {
&self.children
}
fn children_mut(&mut self) -> &mut Vec<ContentItem> {
self.children.as_mut_vec()
}
}
impl fmt::Display for Annotation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Annotation('{}', {} params, {} items)",
self.data.label.value,
self.data.parameters.len(),
self.children.len()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lex::ast::elements::paragraph::Paragraph;
use crate::lex::ast::elements::typed_content::ContentElement;
#[test]
fn test_annotation_header_and_body_locations() {
let header_range = Range::new(0..4, Position::new(0, 0), Position::new(0, 4));
let child_range = Range::new(10..20, Position::new(1, 0), Position::new(2, 0));
let label = Label::new("note".to_string()).at(header_range.clone());
let data = Data::new(label, Vec::new()).at(header_range.clone());
let child = ContentElement::Paragraph(
Paragraph::from_line("body".to_string()).at(child_range.clone()),
);
let annotation = Annotation::from_data(data, vec![child]).at(Range::new(
0..25,
Position::new(0, 0),
Position::new(2, 0),
));
assert_eq!(annotation.header_location().span, header_range.span);
assert_eq!(annotation.body_location().unwrap().span, child_range.span);
}
}