233 lines
5.6 KiB
Rust
233 lines
5.6 KiB
Rust
use core::fmt::{Debug, Display};
|
|
|
|
#[derive(PartialEq, Clone)]
|
|
pub struct Attribute {
|
|
name: String,
|
|
value: String,
|
|
}
|
|
|
|
impl Attribute {
|
|
fn to_string(&self) -> String {
|
|
format!("{:?}={:?}", self.name, self.value)
|
|
}
|
|
}
|
|
#[derive(Clone)]
|
|
// TODO Add an autotype attribute that defaults to true
|
|
pub struct XMLElement {
|
|
pub name: String,
|
|
pub text: String,
|
|
pub attributes: Vec<Attribute>,
|
|
pub children: Vec<XMLElement>,
|
|
}
|
|
impl XMLElement {
|
|
pub fn new<T: Display + Debug>(name: T) -> Self {
|
|
Self {
|
|
name: name.to_string(),
|
|
text: String::new(),
|
|
attributes: vec![],
|
|
children: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn to_string(self) -> String {
|
|
self.into()
|
|
}
|
|
|
|
pub fn set_child(&mut self, child: XMLElement) {
|
|
// TODO: handle replacing preexisting children
|
|
|
|
if child.name == "".to_string() {
|
|
panic!("empty element name")
|
|
}
|
|
self.children.push(child);
|
|
}
|
|
|
|
pub fn set_attribute<T1: Display + Debug, T2: Display + Debug>(&mut self, name: T1, value: T2) {
|
|
let new_attr = Attribute {
|
|
name: name.to_string(),
|
|
value: value.to_string(),
|
|
};
|
|
let mut duplicate_attribute = (false, 0);
|
|
for (index, attr) in self.attributes.clone().into_iter().enumerate() {
|
|
if attr.name == name.to_string() {
|
|
duplicate_attribute = (true, index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
match duplicate_attribute {
|
|
(false, _) => {
|
|
self.attributes.push(new_attr.clone());
|
|
}
|
|
(true, index) => {
|
|
self.attributes[index] = new_attr.clone();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Into<String> for XMLElement {
|
|
fn into(self) -> String {
|
|
let mut string = "".to_string();
|
|
let element_open: String = format!("<{}", self.name);
|
|
|
|
string.push_str(&element_open);
|
|
if self.attributes.len() > 0 {
|
|
string.push(' ');
|
|
}
|
|
let mut attr_count = 0;
|
|
for attr in &self.attributes {
|
|
string.push_str(&attr.to_string());
|
|
attr_count += 1;
|
|
|
|
if &attr_count != &self.attributes.len() {
|
|
string.push(' ');
|
|
}
|
|
}
|
|
string.push('>');
|
|
string.push_str(&self.text);
|
|
for children in &self.children.clone() {
|
|
let chl: String = (*children).clone().into();
|
|
string.push_str(&chl);
|
|
}
|
|
|
|
let element_close: String = format!("</{}>", self.name);
|
|
string.push_str(&element_close);
|
|
string
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn element_to_string_test() {
|
|
let mut xml_ele = XMLElement::new("mouse");
|
|
xml_ele.set_attribute("x", 0);
|
|
xml_ele.set_attribute("x", 1);
|
|
xml_ele.set_child(XMLElement::new("last_update"));
|
|
|
|
let xml_str: String = xml_ele.into();
|
|
let rhs = r##"<mouse "x"="1"><last_update></last_update></mouse>"##;
|
|
|
|
assert_eq!(xml_str, rhs.to_string());
|
|
}
|
|
#[repr(C)]
|
|
#[derive(Debug)]
|
|
pub enum Types {
|
|
// Used to represent an empty attribute
|
|
None,
|
|
Integer(Int),
|
|
UnsignedInteger(UInt),
|
|
Fixed(FixedPoint),
|
|
Float(FloatingPoint),
|
|
|
|
String(String),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum FixedPoint {
|
|
Fixed16(bool, u8, u8),
|
|
Fixed32(bool, u16, u16),
|
|
Fixed64(bool, u32, u32),
|
|
Fixed128(bool, u64, u64),
|
|
Fixed256(bool, u128, u128),
|
|
}
|
|
#[derive(Debug)]
|
|
pub enum FloatingPoint {
|
|
Float32(f32),
|
|
Float64(f64),
|
|
}
|
|
#[derive(Debug)]
|
|
pub enum Int {
|
|
I8(i8),
|
|
I16(i16),
|
|
I32(i32),
|
|
I64(i64),
|
|
I128(i128),
|
|
}
|
|
#[derive(Debug)]
|
|
pub enum UInt {
|
|
U8(u8),
|
|
U16(u16),
|
|
U32(u32),
|
|
U64(u64),
|
|
U128(u128),
|
|
}
|
|
|
|
impl From<u64> for UInt {
|
|
fn from(value: u64) -> Self {
|
|
Self::U64(value)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
pub fn test_string_to_num() {
|
|
println!("{:?}", string_to_num(256));
|
|
}
|
|
|
|
pub fn string_to_num<T: Debug + Display>(string: T) -> Result<Types, XMLError> {
|
|
// let string = "-27".to_string(); // `parse()` works with `&str` and `String`!
|
|
let string: String = string.to_string();
|
|
|
|
use FloatingPoint::*;
|
|
use Int::*;
|
|
use Types::{Fixed, Float, Integer, UnsignedInteger};
|
|
use UInt::*;
|
|
|
|
match string.parse::<u8>() {
|
|
Ok(num) => return Ok(UnsignedInteger(U8(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<u16>() {
|
|
Ok(num) => return Ok(UnsignedInteger(U16(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<u32>() {
|
|
Ok(num) => return Ok(UnsignedInteger(U32(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<u64>() {
|
|
Ok(num) => return Ok(UnsignedInteger(U64(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<u128>() {
|
|
Ok(num) => return Ok(UnsignedInteger(U128(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<i8>() {
|
|
Ok(num) => return Ok(Integer(I8(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<i16>() {
|
|
Ok(num) => return Ok(Integer(I16(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<i32>() {
|
|
Ok(num) => return Ok(Integer(I32(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<i64>() {
|
|
Ok(num) => return Ok(Integer(I64(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<i128>() {
|
|
Ok(num) => return Ok(Integer(I128(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<f32>() {
|
|
Ok(num) => return Ok(Float(Float32(num))),
|
|
Err(_) => {}
|
|
}
|
|
match string.parse::<f64>() {
|
|
Ok(num) => return Ok(Float(Float64(num))),
|
|
Err(_) => {}
|
|
}
|
|
|
|
// TODO: Decimal handler
|
|
{}
|
|
|
|
Err(XMLError::TypeNotANumber)
|
|
}
|
|
#[derive(Debug)]
|
|
pub enum XMLError {
|
|
TypeNotANumber,
|
|
}
|