1use crate::deserialise::error::Error as DeserialiseError;
2use crate::deserialise::parse::XmlExt;
3use crate::error::Error;
4
5use ahash::AHashMap as Map;
6
7use std::fs::File;
8use std::io::Read;
9use std::path::Path;
10
11#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
12pub struct Specification {
13 program_counter: String,
14 context_set: Map<String, u32>,
15 tracked_set: Map<String, u32>,
16}
17
18impl Specification {
19 pub fn program_counter(&self) -> &str {
20 self.program_counter.as_ref()
21 }
22
23 pub fn context_set(&self) -> impl Iterator<Item = (&str, u32)> {
24 self.context_set.iter().map(|(n, v)| (n.as_ref(), *v))
25 }
26
27 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
28 if input.tag_name().name() != "processor_spec" {
53 return Err(DeserialiseError::TagUnexpected(
54 input.tag_name().name().to_owned(),
55 ));
56 }
57
58 let mut program_counter = None;
59 let mut context_set = Map::default();
60 let mut tracked_set = Map::default();
61
62 for child in input.children().filter(xml::Node::is_element) {
63 match child.tag_name().name() {
64 "programcounter" => {
65 program_counter = Some(child.attribute_string("register")?);
66 }
67 "context_data" => {
68 for cchild in child.children().filter(xml::Node::is_element) {
69 match cchild.tag_name().name() {
70 "context_set" => {
71 for ct in cchild.children().filter(xml::Node::is_element) {
72 context_set.insert(
73 ct.attribute_string("name")?,
74 ct.attribute_int::<u32>("val")?,
75 );
76 }
77 }
78 "tracked_set" => {
79 for ct in cchild.children().filter(xml::Node::is_element) {
80 tracked_set.insert(
81 ct.attribute_string("name")?,
82 ct.attribute_int::<u32>("val")?,
83 );
84 }
85 }
86 _ => (),
87 }
88 }
89 }
90 _ => (),
91 }
92 }
93
94 if let Some(program_counter) = program_counter {
95 Ok(Self {
96 program_counter,
97 context_set,
98 tracked_set,
99 })
100 } else {
101 Err(DeserialiseError::Invariant(
102 "processor specification must define a program counter",
103 ))
104 }
105 }
106
107 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
108 let path = path.as_ref();
109 let mut file = File::open(path).map_err(|error| Error::ParseFile {
110 path: path.to_owned(),
111 error,
112 })?;
113
114 let mut input = String::new();
115 file.read_to_string(&mut input)
116 .map_err(|error| Error::ParseFile {
117 path: path.to_owned(),
118 error,
119 })?;
120
121 Self::from_str(&input).map_err(|error| Error::DeserialiseFile {
122 path: path.to_owned(),
123 error,
124 })
125 }
126
127 pub fn from_str<S: AsRef<str>>(input: S) -> Result<Self, DeserialiseError> {
128 let document = xml::Document::parse(input.as_ref()).map_err(DeserialiseError::Xml)?;
129
130 Self::from_xml(document.root_element())
131 }
132}