From 3301430c676e4af6b95d96b6408a66f9d2768653 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Sun, 8 Sep 2019 16:15:46 +0200 Subject: First version --- src/doxygen.rs | 454 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 src/doxygen.rs (limited to 'src/doxygen.rs') diff --git a/src/doxygen.rs b/src/doxygen.rs new file mode 100644 index 0000000..8d2267d --- /dev/null +++ b/src/doxygen.rs @@ -0,0 +1,454 @@ +/// A 'recursive descent' parser for Doxygen XML files +mod builders; + +use xml::{ + attribute::OwnedAttribute, + reader::{EventReader, ParserConfig, XmlEvent}, +}; + +use std::collections::HashMap; +use std::io::Read; + +// === Types === + +#[derive(Debug, Clone)] +pub(crate) enum DoxygenType { + NameSpace(NameSpace), + Class(Class), + Unhandled, +} + +#[derive(Debug, Clone)] +pub(crate) struct NameSpace { + pub(crate) name: String, + pub(crate) members: Vec, + pub(crate) brief_description: Description, + pub(crate) detailed_description: Description, +} + +#[derive(Debug, Clone)] +pub(crate) struct FunctionArgument { + pub(crate) name: String, + pub(crate) r#type: String, + pub(crate) default_value: Option, +} + +#[derive(Debug, Clone)] +pub(crate) enum Member { + Variable { + name: String, + r#type: String, + }, + // What + /* + Enum { + + } + */ + Function { + name: String, + args: Vec, + return_type: String, + }, + Unhandled, +} + +#[derive(Debug, Clone)] +pub(crate) struct Class { + pub(crate) name: String, + pub(crate) inners: Vec<(String, String)>, + pub(crate) sections: HashMap>, + pub(crate) brief_description: Description, + pub(crate) detailed_description: Description, +} + +// === Description === + +#[derive(Debug, Clone)] +pub(crate) struct Description { + pub(crate) inner: Vec, +} + +impl Description { + fn new() -> Self { + Self { inner: Vec::new() } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum DescriptionNode { + Text(String), + Title(String), + Para(Paragraph), + Sect1(String), + Internal(String), +} + +#[derive(Debug, Clone)] +pub(crate) struct Paragraph { + pub(crate) inner: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) enum ParagraphNode { + Text(String), + Ref(Ref), +} + +#[derive(Debug, Clone)] +pub(crate) struct Ref { + pub(crate) id: String, + pub(crate) kind: RefKind, + pub(crate) text: String, +} + +#[derive(Debug, Clone)] +pub(crate) enum RefKind { + Compound, + Member, +} + +#[derive(Debug, Clone)] +enum DoxygenTypeKind { + NameSpace, + Unhandled, +} + +trait TypeBuilder { + //const KIND: DoxygenTypeKind; + + fn build(self: Box) -> DoxygenType; + + fn name(&mut self, value: String); + fn brief_description(&mut self, description: Description); + fn detailed_description(&mut self, description: Description); + + fn add_inner(&mut self, name: String, kind: String); + fn add_member(&mut self, section: String, member: Member); +} + +trait FromXML { + fn from_xml(attributes: Vec, event_reader: &mut EventReader) + -> Self; +} + +impl FromXML for Description { + fn from_xml( + _attributes: Vec, + mut event_reader: &mut EventReader, + ) -> Self { + let mut inner = Vec::new(); + + while let Ok(event) = event_reader.next() { + match event { + XmlEvent::Characters(text) => inner.push(DescriptionNode::Text(text)), + XmlEvent::StartElement { + name, attributes, .. + } => inner.push(match name.local_name.as_str() { + "para" => { + DescriptionNode::Para(Paragraph::from_xml(attributes, &mut event_reader)) + } + "title" => DescriptionNode::Title(event_simple(&mut event_reader)), + "sect1" => DescriptionNode::Sect1(event_simple(&mut event_reader)), + "internal" => DescriptionNode::Internal(event_simple(&mut event_reader)), + other => { + warn!("Description element not supported: {}", other); + continue; + } + }), + XmlEvent::EndElement { .. } => break, + other => { + warn!("Description event not supported: {:?}", other); + } + } + } + + Self { inner } + } +} + +impl FromXML for Paragraph { + fn from_xml( + _attributes: Vec, + mut event_reader: &mut EventReader, + ) -> Self { + let mut inner = Vec::new(); + + while let Ok(event) = event_reader.next() { + match event { + XmlEvent::Characters(text) => inner.push(ParagraphNode::Text(text)), + XmlEvent::StartElement { + name, attributes, .. + } => inner.push(match name.local_name.as_str() { + "ref" => ParagraphNode::Ref(Ref::from_xml(attributes, &mut event_reader)), + other => { + warn!("Paragraph element not supported: {}", other); + continue; + } + }), + XmlEvent::EndElement { .. } => break, + other => { + warn!("Paragraph event not supported: {:?}", other); + } + } + } + + Self { inner } + } +} + +impl FromXML for Ref { + fn from_xml( + attributes: Vec, + mut event_reader: &mut EventReader, + ) -> Self { + let mut id = None; + let mut kind = None; + + for attr in attributes.into_iter() { + match attr.name.local_name.as_str() { + "refid" => id = Some(attr.value), + "kindref" => { + kind = Some(match attr.value.as_str() { + "compound" => RefKind::Compound, + "member" => RefKind::Member, + other => { + warn!("Ref kind not supported: {}", other); + RefKind::Compound + } + }); + } + other => { + warn!("Ref element not supported: {}", other); + } + } + } + + let text = event_simple(&mut event_reader); + + Ref { + id: id.unwrap(), + kind: kind.unwrap(), + text, + } + } +} + +/// Parse a type from a XML Doxygen file +pub(crate) fn parse_type(reader: impl Read) -> Vec { + let mut event_reader = EventReader::new(reader); + + match event_reader.next().unwrap() { + XmlEvent::StartDocument { .. } => (), + _ => panic!("XML file does not begin with document"), + } + + match event_reader.next().unwrap() { + XmlEvent::StartElement { name, .. } => { + if name.local_name != "doxygen" { + panic!("XML file does not start with a 'doxygen' element"); + } + } + _ => panic!("XML file does not start with a 'doxygen' element"), + } + + let mut result = Vec::new(); + + while let Ok(event) = event_reader.next() { + match dbg!(event) { + XmlEvent::StartElement { + name, attributes, .. + } => { + if name.local_name != "compounddef" { + panic!("'doxygen' is not a sequence of 'compounddef' elements"); + } + + result.push(event_compounddef(attributes, &mut event_reader)); + } + XmlEvent::Whitespace(_) => (), + XmlEvent::EndElement { .. } => break, + _ => panic!("'doxygen' is not a sequence of 'compounddef' elements"), + } + } + + // Unnecessarily strict? + match event_reader.next().unwrap() { + XmlEvent::EndDocument => (), + _ => panic!("XML file does not end after 'doxygen' element"), + } + + dbg!(result) +} + +fn event_compounddef( + attributes: Vec, + mut event_reader: &mut EventReader, +) -> DoxygenType { + let kind = attributes + .into_iter() + .find_map(|attr| { + if attr.name.local_name == "kind" { + Some(attr.value) + } else { + None + } + }) + .unwrap(); + + let mut builder = builders::builder_for(kind); + + while let Ok(event) = event_reader.next() { + match dbg!(event) { + XmlEvent::StartElement { + name, attributes, .. + } => match name.local_name.as_str() { + "compoundname" => { + let name = event_simple(&mut event_reader); + builder.name(name); + } + "briefdescription" => { + let brief_description = Description::from_xml(attributes, &mut event_reader); + builder.brief_description(brief_description); + } + "detaileddescription" => { + let detailed_description = Description::from_xml(attributes, &mut event_reader); + builder.detailed_description(detailed_description); + } + "location" => { + event_simple(&mut event_reader); + debug!("Do something?"); + } + "sectiondef" => { + event_section(&mut builder, &mut event_reader); + } + other_name if is_inner_type(other_name) => { + let inner = event_simple(&mut event_reader); + builder.add_inner(inner, other_name.to_string()); + } + _other_name => { + event_ignore(&mut event_reader); + } + }, + XmlEvent::Whitespace(_) => (), + XmlEvent::EndElement { .. } => break, + _ => panic!("Unhandled XML event"), + } + } + + builder.build() +} + +/// Returns true if the given XML Element is a reference to another inner type. +/// +/// Corresponds to the "refType" XSD type in `compound.xsd` +fn is_inner_type(element_name: &str) -> bool { + match element_name { + "innerdir" | "innerfile" | "innerclass" | "innernamespace" | "innerpage" | "innergroup" => { + true + } + _ => false, + } +} + +/// Get the text inside a simple, non recursive XML tag +fn event_simple(event_reader: &mut EventReader) -> String { + let result; + + match dbg!(event_reader.next().unwrap()) { + XmlEvent::Characters(tmp_result) => { + result = tmp_result; + } + XmlEvent::EndElement { .. } => return "".to_string(), + _ => panic!("Simple XML event is not so simple"), + } + + match dbg!(event_reader.next().unwrap()) { + XmlEvent::EndElement { .. } => (), + _ => panic!("Simple XML event is not so simple"), + } + + result +} + +fn event_ignore(mut event_reader: &mut EventReader) { + loop { + let event = event_reader.next().unwrap(); + match dbg!(event) { + XmlEvent::StartElement { .. } => event_ignore(&mut event_reader), + XmlEvent::EndElement { .. } => break, + _ => (), + } + } +} + +fn event_section( + mut builder: &mut Box, + mut event_reader: &mut EventReader, +) { + loop { + let event = event_reader.next().unwrap(); + match dbg!(event) { + XmlEvent::StartElement { + name, attributes, .. + } => match name.local_name.as_str() { + "memberdef" => { + let member = event_member(attributes, &mut event_reader); + builder.add_member("bla".to_owned(), member); + } + _ => panic!("Unhandled thing"), + }, + + XmlEvent::Whitespace(_) => (), + XmlEvent::EndElement { .. } => break, + _ => panic!("Unknown section XML event"), + } + } +} + +fn event_member( + attributes: Vec, + mut event_reader: &mut EventReader, +) -> Member { + let kind = attributes + .into_iter() + .find_map(|attr| { + if attr.name.local_name == "kind" { + Some(attr.value) + } else { + None + } + }) + .unwrap(); + + match kind.as_str() { + "variable" => event_member_variable(&mut event_reader), + _ => { + event_ignore(&mut event_reader); + Member::Unhandled + } + } +} + +fn event_member_variable(mut event_reader: &mut EventReader) -> Member { + let mut member_name = None; + let mut r#type = None; + + loop { + let event = event_reader.next().unwrap(); + match dbg!(event) { + XmlEvent::StartElement { name, .. } => match name.local_name.as_str() { + "name" => member_name = Some(event_simple(&mut event_reader)), + "definition" => r#type = Some(event_simple(&mut event_reader)), + _ => event_ignore(&mut event_reader), + }, + XmlEvent::Whitespace(_) => (), + XmlEvent::EndElement { .. } => break, + _ => panic!("Unknown member XML event"), + } + } + + Member::Variable { + name: member_name.unwrap(), + r#type: r#type.unwrap(), + } +} -- cgit v1.2.3