use txml::{Attrs, Event, Parser}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct Protocol { pub name: String, pub copyright: Option, pub description: Option, pub interfaces: Vec, } impl Protocol { fn parse(parser: &mut Parser, attrs: Attrs) -> Option { let name = attrs.get("name")?.to_string(); let mut copyright = None; let mut description = None; let mut interfaces = Vec::new(); while let Some(event) = parser.next() { match event { Event::Open("interface", attrs) => { interfaces.push(Interface::parse(parser, attrs)?); } Event::Open("copyright", attrs) => { copyright = Some(Copyright::parse(parser, attrs)?); } Event::Open("description", attrs) => { description = Some(Description::parse(parser, attrs)?); } Event::Close("protocol") => { return Some(Self { name, copyright, description, interfaces, }) } _ => {} } } None } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Copyright { pub body: String, } impl Copyright { fn parse(parser: &mut Parser, _attrs: Attrs) -> Option { let mut body = String::new(); while let Some(event) = parser.next() { match event { Event::Text(text) => { text.for_each(|c| body.push(c)); } Event::Close("copyright") => { return Some(Self { body }); } _ => {} } } None } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Interface { pub name: String, pub version: u32, pub description: Option, pub requests: Vec, pub events: Vec, pub enums: Vec, } impl Interface { fn parse(parser: &mut Parser, attrs: Attrs) -> Option { let name = attrs.get("name")?.to_string(); let version = attrs.get("version")?.to_string().parse().ok()?; let mut description = None; let mut requests = Vec::new(); let mut events = Vec::new(); let mut enums = Vec::new(); while let Some(event) = parser.next() { match event { Event::Open("description", attrs) => { description = Some(Description::parse(parser, attrs)?); } Event::Open("request", attrs) => { requests.push(Message::parse(parser, attrs)?); } Event::Open("event", attrs) => { events.push(Message::parse(parser, attrs)?); } Event::Open("enum", attrs) => { // enums.push(Enum::parse(parser, attrs)?); } Event::Close("interface") => { return Some(Self { name, version, description, requests, events, enums, }); } _ => {} } } None } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Message { pub name: String, pub is_destructor: bool, pub since: u32, pub description: Option, pub args: Vec, } impl Message { fn parse(parser: &mut Parser, attrs: Attrs) -> Option { let name = attrs.get("name")?.to_string(); let is_destructor = attrs .get("type") .map(|x| x == "destructor") .unwrap_or(false); let since = attrs .get("since") .map(|x| x.to_string().parse().ok()) .flatten() .unwrap_or(1); let mut description = None; let mut args = Vec::new(); while let Some(event) = parser.next() { match event { Event::Open("description", attrs) => { description = Some(Description::parse(parser, attrs)?); } Event::Open("arg", attrs) => { args.push(Arg::parse(parser, attrs)?); } Event::Close("request") | Event::Close("event") => { return Some(Self { name, is_destructor, since, description, args, }); } _ => {} } } None } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Arg { pub name: String, pub kind: ArgKind, pub summary: Option, pub interface: Option, pub allow_null: bool, pub enumeration: Option, pub description: Option, } impl Arg { fn parse(parser: &mut Parser, attrs: Attrs) -> Option { let name = attrs.get("name")?.to_string(); let kind = match &*attrs.get("type")?.to_string() { "new_id" => ArgKind::NewId, "int" => ArgKind::Int, "uint" => ArgKind::Uint, "fixed" => ArgKind::Fixed, "string" => ArgKind::String, "object" => ArgKind::Object, "array" => ArgKind::Array, "fd" => ArgKind::Fd, _ => return None, }; let summary = attrs.get("summary").map(|x| x.to_string()); let interface = attrs.get("interface").map(|x| x.to_string()); let allow_null = attrs .get("allow_null") .map(|x| x == "true") .unwrap_or(false); let enumeration = attrs.get("enum").map(|x| x.to_string()); let mut description = None; while let Some(event) = parser.next() { match event { Event::Open("description", attrs) => { description = Some(Description::parse(parser, attrs)?); } Event::Close("arg") => { return Some(Self { name, kind, summary, interface, allow_null, enumeration, description, }); } _ => {} } } None } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum ArgKind { NewId, Int, Uint, Fixed, String, Object, Array, Fd, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Enum { pub name: String, pub since: u32, pub bitfield: bool, pub description: Option, pub entries: Vec, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Entry { pub name: String, pub value: u32, pub summary: Option, pub since: u32, pub description: Option, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Description { pub summary: String, pub body: String, } impl Description { fn parse(parser: &mut Parser, attrs: Attrs) -> Option { let mut summary = None; let mut body = String::new(); for (name, value) in attrs { match name { "summary" => { summary = Some(value.to_string()); } _ => {} } } while let Some(event) = parser.next() { match event { Event::Text(text) => { text.for_each(|c| body.push(c)); } Event::Close("description") => { return Some(Self { summary: summary?, body, }); } _ => {} } } None } } #[test] fn parse_wayland_xml() { const XML: &'static str = include_str!( "/nix/store/9jj2f9hfj316mmzzyn7rv1649qy2lxlj-wayland-1.18.0/share/wayland/wayland.xml" ); let mut parser = Parser::new(XML); while let Some(event) = parser.next() { match event { Event::Open("protocol", attrs) => { let protocol = Protocol::parse(&mut parser, attrs).unwrap(); println!("{:#?}", protocol); } _ => {} } } }