-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
1,383 additions
and
11 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
[workspace] | ||
resolver = "2" | ||
members = ["quickfix-ffi", "quickfix"] | ||
members = ["quickfix-ffi", "quickfix-spec-parser", "quickfix"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "quickfix-spec-parser" | ||
version = "0.1.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
bytes = "1.5.0" | ||
quick-xml = "0.31.0" | ||
thiserror = "1.0.56" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use std::fs; | ||
|
||
use quickfix_spec_parser::{parse_spec, write_spec}; | ||
|
||
fn main() { | ||
// Parse XML spec. | ||
let spec = parse_spec(include_bytes!( | ||
"../../quickfix-ffi/libquickfix/spec/FIXT11.xml" | ||
)) | ||
.unwrap(); | ||
|
||
// Print it. | ||
println!("spec: {spec:#?}"); | ||
|
||
// Rewrite it as XML and apply change to make it match with original spec format. | ||
let out = write_spec(&spec).unwrap(); | ||
let txt = String::from_utf8(out.to_vec()) | ||
.unwrap() | ||
.replace('\"', "'") | ||
.replace("/>", " />"); | ||
|
||
fs::write("out.xml", txt).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use std::{num::ParseIntError, string::FromUtf8Error}; | ||
|
||
use thiserror::Error; | ||
|
||
#[derive(Debug, Error, PartialEq, Eq)] | ||
pub enum FixSpecError { | ||
#[error("invalid document: {0}")] | ||
InvalidDocument(&'static str), | ||
|
||
#[error("invalid attribute: {0}")] | ||
InvalidAttribute(String), | ||
|
||
#[error("invalid content: {0}")] | ||
InvalidContent(String), | ||
|
||
#[error("xml error: {0}")] | ||
Xml(String), | ||
} | ||
|
||
impl From<FromUtf8Error> for FixSpecError { | ||
fn from(err: FromUtf8Error) -> Self { | ||
Self::InvalidContent(err.to_string()) | ||
} | ||
} | ||
|
||
impl From<ParseIntError> for FixSpecError { | ||
fn from(err: ParseIntError) -> Self { | ||
Self::InvalidContent(err.to_string()) | ||
} | ||
} | ||
|
||
impl From<quick_xml::Error> for FixSpecError { | ||
fn from(err: quick_xml::Error) -> Self { | ||
Self::Xml(err.to_string()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use bytes::Bytes; | ||
use quick_xml::{events::Event, Reader, Writer}; | ||
|
||
mod error; | ||
mod model { | ||
mod component; | ||
mod component_spec; | ||
mod field; | ||
mod field_allowed_value; | ||
mod field_spec; | ||
mod field_type; | ||
mod field_value; | ||
mod group; | ||
mod message; | ||
mod message_category; | ||
mod spec; | ||
|
||
pub use self::component::Component; | ||
pub use self::component_spec::ComponentSpec; | ||
pub use self::field::Field; | ||
pub use self::field_allowed_value::FieldAllowedValue; | ||
pub use self::field_spec::FieldSpec; | ||
pub use self::field_type::FieldType; | ||
pub use self::field_value::FieldValue; | ||
pub use self::group::Group; | ||
pub use self::message::Message; | ||
pub use self::message_category::MessageCategory; | ||
pub use self::spec::FixSpec; | ||
} | ||
mod xml_ext; | ||
|
||
pub use error::*; | ||
pub use model::*; | ||
pub use xml_ext::*; | ||
|
||
type XmlWriter = Writer<Vec<u8>>; | ||
type XmlReader<'a> = Reader<&'a [u8]>; | ||
|
||
pub fn parse_spec(input: &[u8]) -> Result<FixSpec, FixSpecError> { | ||
let mut reader = Reader::from_reader(input); | ||
reader.trim_text(true); | ||
|
||
match reader.read_event()? { | ||
// If we are at start of FIX spec. | ||
Event::Start(e) if e.name().as_ref() == FixSpec::TAG_NAME.as_bytes() => { | ||
FixSpec::parse_xml_tree(&e, &mut reader) | ||
} | ||
// Otherwise document is invalid | ||
_ => Err(FixSpecError::InvalidDocument("invalid root")), | ||
} | ||
} | ||
|
||
pub fn write_spec(spec: &FixSpec) -> Result<Bytes, FixSpecError> { | ||
let mut writer = Writer::new_with_indent(Vec::new(), b' ', 1); | ||
|
||
spec.write_xml(&mut writer)?; | ||
|
||
Ok(Bytes::from(writer.into_inner())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use quick_xml::events::BytesStart; | ||
|
||
use crate::{read_attribute, FixSpecError, XmlObject, XmlReadable, XmlWritable, XmlWriter}; | ||
|
||
#[derive(Debug)] | ||
pub struct Component { | ||
pub name: String, | ||
pub required: bool, | ||
} | ||
|
||
impl XmlObject for Component { | ||
const TAG_NAME: &'static str = "component"; | ||
} | ||
|
||
impl XmlReadable for Component { | ||
fn parse_xml_node(element: &BytesStart) -> Result<Self, FixSpecError> { | ||
let name = read_attribute(element, "name")?; | ||
let required = read_attribute(element, "required")? == "Y"; | ||
Ok(Self { name, required }) | ||
} | ||
} | ||
|
||
impl XmlWritable for Component { | ||
fn write_xml<'a>(&self, writer: &'a mut XmlWriter) -> quick_xml::Result<&'a mut XmlWriter> { | ||
writer | ||
.create_element(Self::TAG_NAME) | ||
.with_attribute(("name", self.name.as_str())) | ||
.with_attribute(("required", if self.required { "Y" } else { "N" })) | ||
.write_empty() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use quick_xml::events::BytesStart; | ||
|
||
use crate::{ | ||
read_attribute, write_xml_list, FieldValue, FixSpecError, XmlObject, XmlReadable, XmlReader, | ||
XmlWritable, XmlWriter, | ||
}; | ||
|
||
#[derive(Debug)] | ||
pub struct ComponentSpec { | ||
pub name: String, | ||
pub values: Vec<FieldValue>, | ||
} | ||
|
||
impl XmlObject for ComponentSpec { | ||
const TAG_NAME: &'static str = "component"; | ||
} | ||
|
||
impl XmlReadable for ComponentSpec { | ||
fn parse_xml_node(element: &BytesStart) -> Result<Self, FixSpecError> { | ||
let name = read_attribute(element, "name")?; | ||
Ok(Self { | ||
name, | ||
values: Vec::new(), | ||
}) | ||
} | ||
|
||
fn parse_xml_tree(element: &BytesStart, reader: &mut XmlReader) -> Result<Self, FixSpecError> { | ||
let mut output = Self::parse_xml_node(element)?; | ||
output.values = FieldValue::parse_xml_tree(reader, Self::TAG_NAME)?; | ||
Ok(output) | ||
} | ||
} | ||
|
||
impl XmlWritable for ComponentSpec { | ||
fn write_xml<'a>(&self, writer: &'a mut XmlWriter) -> quick_xml::Result<&'a mut XmlWriter> { | ||
let element = writer | ||
.create_element(Self::TAG_NAME) | ||
.with_attribute(("name", self.name.as_str())); | ||
|
||
if self.values.is_empty() { | ||
element.write_empty() | ||
} else { | ||
element.write_inner_content(|writer| write_xml_list(writer, &self.values)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use quick_xml::events::BytesStart; | ||
|
||
use crate::{read_attribute, FixSpecError, XmlObject, XmlReadable, XmlWritable, XmlWriter}; | ||
|
||
#[derive(Debug)] | ||
pub struct Field { | ||
pub name: String, | ||
pub required: bool, | ||
} | ||
|
||
impl XmlObject for Field { | ||
const TAG_NAME: &'static str = "field"; | ||
} | ||
|
||
impl XmlReadable for Field { | ||
fn parse_xml_node(element: &BytesStart) -> Result<Self, FixSpecError> { | ||
let name = read_attribute(element, "name")?; | ||
let required = read_attribute(element, "required")? == "Y"; | ||
Ok(Self { name, required }) | ||
} | ||
} | ||
|
||
impl XmlWritable for Field { | ||
fn write_xml<'a>(&self, writer: &'a mut XmlWriter) -> quick_xml::Result<&'a mut XmlWriter> { | ||
writer | ||
.create_element(Self::TAG_NAME) | ||
.with_attribute(("name", self.name.as_str())) | ||
.with_attribute(("required", if self.required { "Y" } else { "N" })) | ||
.write_empty() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use quick_xml::events::BytesStart; | ||
|
||
use crate::{read_attribute, FixSpecError, XmlObject, XmlReadable, XmlWritable, XmlWriter}; | ||
|
||
#[derive(Debug)] | ||
pub struct FieldAllowedValue { | ||
pub value: String, | ||
pub description: String, | ||
} | ||
|
||
impl XmlObject for FieldAllowedValue { | ||
const TAG_NAME: &'static str = "value"; | ||
} | ||
|
||
impl XmlReadable for FieldAllowedValue { | ||
fn parse_xml_node(element: &BytesStart) -> Result<Self, FixSpecError> { | ||
let value = read_attribute(element, "enum")?; | ||
let description = read_attribute(element, "description")?; | ||
Ok(Self { value, description }) | ||
} | ||
} | ||
|
||
impl XmlWritable for FieldAllowedValue { | ||
fn write_xml<'a>(&self, writer: &'a mut XmlWriter) -> quick_xml::Result<&'a mut XmlWriter> { | ||
writer | ||
.create_element(Self::TAG_NAME) | ||
.with_attribute(("enum", self.value.as_str())) | ||
.with_attribute(("description", self.description.as_str())) | ||
.write_empty() | ||
} | ||
} |
Oops, something went wrong.