1use maddi_xml::{Content, Element, FromElement, FromValue, Parser, Result};
6use std::{net::Ipv4Addr, path::Path};
7
8struct IP(Ipv4Addr);
11
12impl<'a, 'b> FromValue<'a, 'b> for IP {
13 fn from_value(value: &'b str, position: &'b maddi_xml::Position<'a>) -> Result<'a, Self> {
14 let Ok(ip) = value.parse::<Ipv4Addr>() else {
15 return Err(position.error("expected an ip address".into()));
16 };
17 Ok(IP(ip))
18 }
19}
20
21#[derive(Debug)]
23struct Config {
24 servers: Vec<Server>,
25}
26
27impl<'a, 'b> FromElement<'a, 'b> for Config {
28 fn from_element(element: &'b Element<'a>) -> Result<'a, Self> {
29 Ok(Config {
30 servers: element.children("server").collect::<Result<_>>()?,
31 })
32 }
33}
34
35#[derive(Debug)]
38struct Server {
39 name: String,
40 ip: Ipv4Addr,
41 aliases: Vec<Alias>,
42}
43
44impl<'a, 'b> FromElement<'a, 'b> for Server {
45 fn from_element(element: &'b Element<'a>) -> Result<'a, Self> {
46 Ok(Server {
47 name: element.attribute("name")?,
48 ip: element.attribute::<IP>("ip")?.0,
49 aliases: element.children("alias").collect::<Result<Vec<_>>>()?,
50 })
51 }
52}
53
54#[derive(Debug)]
56struct Alias(String);
57
58impl<'a, 'b> FromElement<'a, 'b> for Alias {
59 fn from_element(element: &'b Element<'a>) -> Result<'a, Self> {
60 match element.contents.as_slice() {
61 [] => Err(element.position.error("alias cannot be empty".into())),
62 [Content::Text(alias)] => Ok(Alias(alias.clone())),
63 _ => Err(element
64 .position
65 .error("expected a single alias name".into())),
66 }
67 }
68}
69
70fn main() {
71 let config = r#"
73 <config>
74 <server name="localhost" ip="127.0.0.1">
75 <alias>local</alias>
76 <alias>me</alias>
77 </server>
78 <server name="example" ip="10.0.0.101">
79 <alias>example.com</alias>
80 </server>
81 </config>
82 "#;
83 let mut parser = Parser::new(Path::new("example.xml"), config);
85 match parser.parse::<Option<Result<Content>>>() {
88 Some(Ok(content)) => println!("{content:?}"),
89 Some(Err(e)) => println!("{e}"),
90 None => {
91 let error = parser
92 .position
93 .error("Could not find root config node".into());
94 println!("{error}");
95 }
96 }
97}