Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tamer/src/obj/xmle/writer/writer.rs')
-rw-r--r--tamer/src/obj/xmle/writer/writer.rs320
1 files changed, 320 insertions, 0 deletions
diff --git a/tamer/src/obj/xmle/writer/writer.rs b/tamer/src/obj/xmle/writer/writer.rs
new file mode 100644
index 0000000..05d89f9
--- /dev/null
+++ b/tamer/src/obj/xmle/writer/writer.rs
@@ -0,0 +1,320 @@
+// xmle object file writer
+//
+// Copyright (C) 2014-2019 Ryan Specialty Group, LLC.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use crate::ir::asg::Object;
+use crate::sym::Symbol;
+use quick_xml::Error as XmlError;
+use std::io::{Error as IoError, Write};
+use std::result;
+use std::str::Utf8Error;
+
+type ObjectRef<'a, 'i> = &'a Object<'i>;
+pub type Result<T = ()> = result::Result<T, WriterError>;
+pub type ObjectVec<'a, 'i> = Vec<ObjectRef<'a, 'i>>;
+
+/// A wrapper around a `Write` object
+///
+/// This is used to take the [`Sections`] and write out the xmle files.
+pub trait Writer<W: Write> {
+ fn write(
+ &mut self,
+ sections: &Sections,
+ name: Symbol,
+ relroot: &str,
+ ) -> Result<()>
+ where
+ Self: Sized;
+}
+
+/// A Section that needs to be written to the buffer
+///
+/// Most sections will only need a `body`, but some innlude `head` and `tail`
+/// information. Rather than dealing with those differently, each `Section`
+/// will have a `head` and `tail` that are empty by default.
+#[derive(Clone, Default)]
+pub struct Section<'a, 'i> {
+ head: ObjectVec<'a, 'i>,
+ body: ObjectVec<'a, 'i>,
+ tail: ObjectVec<'a, 'i>,
+}
+
+impl<'a, 'i> Section<'a, 'i> {
+ /// Constructor for Sections
+ ///
+ /// ```
+ /// use tamer::obj::xmle::writer::Section;
+ ///
+ /// let section = Section::new();
+ /// ```
+ pub fn new() -> Self {
+ Self {
+ head: Vec::new(),
+ body: Vec::new(),
+ tail: Vec::new(),
+ }
+ }
+
+ /// The length of the `Section`
+ pub fn len(&self) -> usize {
+ self.head.len() + self.body.len() + self.tail.len()
+ }
+
+ /// Check if the `Section` is empty
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Push an `Object` into a `Section`'s head
+ pub fn push_head(&mut self, obj: ObjectRef<'a, 'i>) {
+ self.head.push(&obj)
+ }
+
+ /// Push an `Object` into a `Section`'s body
+ pub fn push_body(&mut self, obj: ObjectRef<'a, 'i>) {
+ self.body.push(&obj)
+ }
+
+ /// Push an `Object` into a `Section`'s tail
+ pub fn push_tail(&mut self, obj: ObjectRef<'a, 'i>) {
+ self.tail.push(&obj)
+ }
+
+ /// Merge the parts of a `Section` into one [`SectionIterator`]
+ ///
+ /// The `Section` internals need to be iterated as a group so we needed to
+ /// create a custom iterator, [`SectionIterator`] to do this for us. This
+ /// method allows us to access the iterator.
+ ///
+ /// ```
+ /// use tamer::obj::xmle::writer::Section;
+ /// use tamer::ir::asg::Object;
+ ///
+ /// let mut section = Section::new();
+ /// let obj = Object::Empty;
+ /// let expect = vec![&obj, &obj, &obj];
+ ///
+ /// section.push_head(&obj);
+ /// section.push_body(&obj);
+ /// section.push_tail(&obj);
+ /// let section_iter = section.iter();
+ ///
+ /// for object in section_iter {
+ /// assert_eq!(&obj, object);
+ /// }
+ /// ```
+ pub fn iter(&self) -> SectionIterator {
+ SectionIterator {
+ inner: Box::new(
+ self.head
+ .iter()
+ .chain(self.body.iter())
+ .chain(self.tail.iter())
+ .copied(),
+ ),
+ }
+ }
+}
+
+/// Wrapper for an Iterator
+///
+/// This allows us to iterate over all parts of a [`Section`].
+pub struct SectionIterator<'a, 'i> {
+ inner: Box<dyn Iterator<Item = &'a Object<'i>> + 'a>,
+}
+
+impl<'a, 'i> Iterator for SectionIterator<'a, 'i> {
+ type Item = &'a Object<'i>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.inner.next()
+ }
+}
+
+/// Sections that need to be written to a buffer
+///
+/// All the properties are public [`Section`] objects and will be accessed
+/// directly by the [`Writer`].
+#[derive(Default)]
+pub struct Sections<'a, 'i> {
+ pub map: Section<'a, 'i>,
+ pub retmap: Section<'a, 'i>,
+ pub meta: Section<'a, 'i>,
+ pub worksheet: Section<'a, 'i>,
+ pub params: Section<'a, 'i>,
+ pub types: Section<'a, 'i>,
+ pub funcs: Section<'a, 'i>,
+ pub rater: Section<'a, 'i>,
+}
+
+impl<'a, 'i> Sections<'a, 'i> {
+ /// Constructor for Sections
+ ///
+ /// ```
+ /// use tamer::obj::xmle::writer::Sections;
+ ///
+ /// let sections = Sections::new();
+ /// ```
+ pub fn new() -> Self {
+ Self {
+ map: Section::new(),
+ retmap: Section::new(),
+ meta: Section::new(),
+ worksheet: Section::new(),
+ params: Section::new(),
+ types: Section::new(),
+ funcs: Section::new(),
+ rater: Section::new(),
+ }
+ }
+}
+
+/// Error implementations for the writer
+#[derive(Debug)]
+pub enum WriterError {
+ Io(IoError),
+ Utf8(Utf8Error),
+ XmlError(XmlError),
+ ExpectedFragment(String),
+}
+
+impl From<IoError> for WriterError {
+ fn from(err: IoError) -> Self {
+ WriterError::Io(err)
+ }
+}
+
+impl From<Utf8Error> for WriterError {
+ fn from(err: Utf8Error) -> Self {
+ WriterError::Utf8(err)
+ }
+}
+
+impl From<XmlError> for WriterError {
+ fn from(err: XmlError) -> Self {
+ WriterError::XmlError(err)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn section_empty() {
+ let section = Section::new();
+
+ assert!(section.head.is_empty());
+ assert!(section.body.is_empty());
+ assert!(section.tail.is_empty());
+ }
+
+ #[test]
+ fn section_head() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert!(section.head.is_empty());
+
+ section.push_head(&obj);
+
+ assert_eq!(Some(&&obj), section.head.get(0));
+ }
+
+ #[test]
+ fn section_body() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert!(section.body.is_empty());
+
+ section.push_body(&obj);
+
+ let body = section.body;
+ assert_eq!(Some(&&obj), body.get(0));
+ }
+
+ #[test]
+ fn section_tail() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert!(section.tail.is_empty());
+
+ section.push_tail(&obj);
+
+ assert_eq!(Some(&&obj), section.tail.get(0));
+ }
+
+ #[test]
+ fn section_len() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert_eq!(0, section.len());
+ section.push_head(&obj);
+ assert_eq!(1, section.len());
+ section.push_body(&obj);
+ assert_eq!(2, section.len());
+ section.push_tail(&obj);
+ assert_eq!(3, section.len());
+ }
+
+ #[test]
+ fn section_is_empty_head() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert!(section.is_empty());
+ section.push_head(&obj);
+ assert!(!section.is_empty());
+ }
+
+ #[test]
+ fn section_is_empty_body() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert!(section.is_empty());
+ section.push_body(&obj);
+ assert!(!section.is_empty());
+ }
+
+ #[test]
+ fn section_is_empty_tail() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+
+ assert!(section.is_empty());
+ section.push_tail(&obj);
+ assert!(!section.is_empty());
+ }
+
+ #[test]
+ fn section_iterator() {
+ let mut section = Section::new();
+ let obj = Object::Empty;
+ let expect = vec![&obj, &obj, &obj];
+
+ section.push_head(&obj);
+ section.push_body(&obj);
+ section.push_tail(&obj);
+
+ let collection: Vec<_> = section.iter().collect();
+
+ assert_eq!(expect, collection);
+ }
+}