Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ serde_derive = "^1.0.77"
vk-parse = { path = "../vk-parse", features = ["serialize", "vkxml-convert"] }
vkxml = "^0.3"
xml-rs = "^0.8"

[features]
openxr = []
36 changes: 36 additions & 0 deletions ci/src/openxr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![cfg(feature = "openxr")]
use crate::{
TestApi,
parsing_test,
};

#[derive(Debug, Clone, Copy)]
pub struct OpenXR;

impl TestApi for OpenXR {
const MAIN_URL: Option<&'static str> = None;
const ALLOW_WARNINGS: bool = true;
const USE_VKXML: bool = false;
fn download_url(major: u32, minor: u32, patch: u32, _url_suffix: &str) -> String {
format!(
"https://github.com/KhronosGroup/OpenXR-SDK-Source/raw/release-{}.{}.{}/specification/registry/xr.xml",
major, minor, patch
)
}
}

macro_rules! test_xr_versions {
($($test_name:ident($major:expr, $minor:expr, $patch:expr)),+ $(,)?) => {
$(
#[test]
fn $test_name() {
parsing_test::<OpenXR>($major, $minor, $patch, "");
}
)+
};
}

test_xr_versions! {
xr_test_v1_0_33(1, 0, 33),
xr_test_v1_0_34(1, 0, 34),
}
55 changes: 40 additions & 15 deletions ci/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,35 @@ extern crate vk_parse;
extern crate vkxml;
extern crate xml;

const URL_REPO: &str = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs";
const URL_MAIN: &str = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml";
#[cfg(feature = "openxr")]
#[path = "../src/openxr.rs"]
mod openxr;

/// Trait representing a given API used for validation tests
pub(crate) trait TestApi {
const MAIN_URL: Option<&'static str>;
/// Fail tests on non-fatal parse warnings?
const ALLOW_WARNINGS: bool;
/// Also try to use [`vk_parse::parse_file_as_vkxml`] compatibility layer
const USE_VKXML: bool;
fn download_url(major: u32, minor: u32, patch: u32, url_suffix: &str) -> String;
}

#[derive(Debug, Clone, Copy)]
struct Vulkan;
impl TestApi for Vulkan {
const MAIN_URL: Option<&'static str> = Some("https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml");
const ALLOW_WARNINGS: bool = false;
const USE_VKXML: bool = true;
fn download_url(major: u32, minor: u32, patch: u32, url_suffix: &str) -> String {
format!(
"{}/v{}.{}.{}{}/vk.xml",
"https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs",
major, minor, patch,
url_suffix
)
}
}

fn download<T: std::io::Write>(dst: &mut T, url: &str) {
let resp = minreq::get(url)
Expand Down Expand Up @@ -120,37 +147,35 @@ fn write_code(path: &str, reg: &vk_parse::Registry) {
}
}

fn parsing_test(major: u32, minor: u32, patch: u32, url_suffix: &str) {
let src = format!(
"{}/v{}.{}.{}{}/vk.xml",
URL_REPO, major, minor, patch, url_suffix
);
pub(crate) fn parsing_test<A: TestApi>(major: u32, minor: u32, patch: u32, url_suffix: &str) {
let src = A::download_url(major, minor, patch, url_suffix);
use std::io::Cursor;
let mut buf = Cursor::new(vec![0; 15]);
download(&mut buf, &src);
buf.set_position(0);

match vk_parse::parse_stream(buf.clone()) {
Ok((_reg, errors)) => {
// write_code(&format!("v{}.{}.{}.c", major, minor, patch), &_reg);
if !errors.is_empty() {
Ok((_reg, errors)) if !errors.is_empty() => {
if !A::ALLOW_WARNINGS {
panic!("{:?}", errors);
}
}
Err(fatal_error) => panic!("{:?}", fatal_error),
Ok(..) => {},
}

match vk_parse::parse_stream_as_vkxml(buf) {
Ok(_) => (),
Err(fatal_error) => panic!("{:?}", fatal_error),
if A::USE_VKXML {
if let Err(e) = vk_parse::parse_stream_as_vkxml(buf) {
panic!("{:?}", e);
}
}
}

macro_rules! test_version {
($test_name:ident, $major:expr, $minor:expr, $patch:expr, $url_suffix:expr) => {
#[test]
fn $test_name() {
parsing_test($major, $minor, $patch, $url_suffix);
parsing_test::<Vulkan>($major, $minor, $patch, $url_suffix);
}
};
}
Expand All @@ -159,7 +184,7 @@ macro_rules! test_version {
fn test_main() {
use std::io::Cursor;
let mut buf = Cursor::new(vec![0; 15]);
download(&mut buf, URL_MAIN);
download(&mut buf, Vulkan::MAIN_URL.unwrap());
buf.set_position(0);

match vk_parse::parse_stream(buf.clone()) {
Expand Down
34 changes: 34 additions & 0 deletions vk-parse/examples/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::{
error::Error,
path::PathBuf,
fmt,
};

fn main() -> Result<(), Box<dyn Error>> {
let path = std::env::args()
.skip(1)
.map(PathBuf::from)
.next()
.ok_or(MissingArgumentError("XML_PATH"))?;
let registry = vk_parse::parse_file(path.as_ref())
.map(|(ret, errors)| {
errors.into_iter().for_each(|e| {
eprintln!("non-fatal error while parsing registry: {:?}", &e);
});
ret
});
println!("{:#?}", &registry);
Ok(())
}

#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
struct MissingArgumentError<'a>(&'a str);

impl<'a> fmt::Display for MissingArgumentError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "missing required argument: {}", self.0)
}
}

impl<'a> Error for MissingArgumentError<'a> {}
114 changes: 51 additions & 63 deletions vk-parse/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ macro_rules! unwrap_attribute (
};
);

macro_rules! parse_attributes {
($ctx:expr, $($attribute:ident),+ => $val:expr) => {
{
let parse_xpath = &$ctx.xpath;
let parse_attribs = move || -> Result<_, Error> {
$(
let $attribute = $attribute.ok_or_else(|| {
Error::MissingAttribute {
xpath: parse_xpath.clone(),
name: stringify!($attribute).to_string(),
}
})?;
)+
Ok($val)
};
match parse_attribs() {
Ok(v) => Some(v),
Err(e) => {
$ctx.errors.push(e);
None
}
}
}
};
}

macro_rules! match_attributes {
($ctx:expr, $a:ident in $attributes:expr, $($p:pat => $e:expr),+ $(,)?) => {
for $a in $attributes {
Expand All @@ -76,7 +102,7 @@ macro_rules! match_attributes {
}

macro_rules! match_elements {
($ctx:expr, $($p:pat => $e:expr),+) => {
($ctx:expr, $($p:pat => $e:expr),+ $(,)?) => {
while let Some(Ok(e)) = $ctx.events.next() {
match e {
XmlEvent::StartElement { name, .. } => {
Expand All @@ -87,11 +113,11 @@ macro_rules! match_elements {
$p => $e,
)+
_ => {
consume_current_element($ctx);
$ctx.errors.push(Error::UnexpectedElement {
xpath: $ctx.xpath.clone(),
name: String::from(name),
});
consume_current_element($ctx);
}
}
}
Expand All @@ -115,11 +141,11 @@ macro_rules! match_elements {
$p => $e,
)+
_ => {
consume_current_element($ctx);
$ctx.errors.push(Error::UnexpectedElement {
xpath: $ctx.xpath.clone(),
name: String::from(name),
});
consume_current_element($ctx);
}
}
}
Expand Down Expand Up @@ -317,9 +343,7 @@ fn parse_registry<R: Read>(ctx: &mut ParseCtx<R>) -> Result<Registry, FatalError

registry.0.push(RegistryChild::Commands(Commands{comment, children}));
},
"feature" => if let Some(v) = parse_feature(ctx, attributes) {
registry.0.push(v);
},
"feature" => registry.0.extend(parse_feature(ctx, attributes)),
"extensions" => registry.0.push(parse_extensions(ctx, attributes)),
"formats" => registry.0.push(parse_formats(ctx)),
"spirvextensions" => registry.0.push(parse_spirvextensions(ctx, attributes)),
Expand Down Expand Up @@ -1100,7 +1124,6 @@ fn parse_extension_item_require<R: Read>(
let mut feature = None;
let mut comment = None;
let mut depends = None;
let mut items = Vec::new();

match_attributes! {ctx, a in attributes,
"api" => api = Some(a.value),
Expand All @@ -1111,24 +1134,7 @@ fn parse_extension_item_require<R: Read>(
"depends" => depends = Some(a.value),
}

while let Some(Ok(e)) = ctx.events.next() {
match e {
XmlEvent::StartElement {
name, attributes, ..
} => {
let name = name.local_name.as_str();
ctx.push_element(name);
if let Some(v) = parse_interface_item(ctx, name, attributes) {
items.push(v);
}
}
XmlEvent::EndElement { .. } => {
ctx.pop_element();
break;
}
_ => {}
}
}
let items = parse_interface_items(ctx);

ExtensionChild::Require {
api,
Expand All @@ -1148,32 +1154,14 @@ fn parse_extension_item_remove<R: Read>(
let mut api = None;
let mut profile = None;
let mut comment = None;
let mut items = Vec::new();

match_attributes! {ctx, a in attributes,
"api" => api = Some(a.value),
"profile" => profile = Some(a.value),
"comment" => comment = Some(a.value)
}

while let Some(Ok(e)) = ctx.events.next() {
match e {
XmlEvent::StartElement {
name, attributes, ..
} => {
let name = name.local_name.as_str();
ctx.push_element(name);
if let Some(v) = parse_interface_item(ctx, name, attributes) {
items.push(v);
}
}
XmlEvent::EndElement { .. } => {
ctx.pop_element();
break;
}
_ => {}
}
}
let items = parse_interface_items(ctx);

ExtensionChild::Remove {
api,
Expand All @@ -1183,44 +1171,44 @@ fn parse_extension_item_remove<R: Read>(
}
}

fn parse_interface_item<R: Read>(
fn parse_interface_items<R: Read>(
ctx: &mut ParseCtx<R>,
name: &str,
attributes: Vec<XmlAttribute>,
) -> Option<InterfaceItem> {
match name {
"comment" => Some(InterfaceItem::Comment(parse_text_element(ctx))),
) -> Vec<InterfaceItem> {
let mut items = Vec::new();
match_elements! {ctx, attributes,
"comment" => items.push(InterfaceItem::Comment(parse_text_element(ctx))),
"type" => {
let mut name = None;
let mut comment = None;
match_attributes! {ctx, a in attributes,
"name" => name = Some(a.value),
"comment" => comment = Some(a.value)
}
unwrap_attribute!(ctx, type, name);
let item = parse_attributes!(ctx, name => InterfaceItem::Type {
name, comment,
});
consume_current_element(ctx);
Some(InterfaceItem::Type { name, comment })
}
"enum" => parse_enum(ctx, attributes).map(|v| InterfaceItem::Enum(v)),
items.extend(item);
},
"enum" => {
let ret = parse_enum(ctx, attributes).map(|v| InterfaceItem::Enum(v));
items.extend(ret);
},
"command" => {
let mut name = None;
let mut comment = None;
match_attributes! {ctx, a in attributes,
"name" => name = Some(a.value),
"comment" => comment = Some(a.value)
}
unwrap_attribute!(ctx, type, name);
consume_current_element(ctx);
Some(InterfaceItem::Command { name, comment })
}
_ => {
ctx.errors.push(Error::UnexpectedElement {
xpath: ctx.xpath.clone(),
name: String::from(name),
let item = parse_attributes!(ctx, name => InterfaceItem::Command {
name, comment,
});
return None;
consume_current_element(ctx);
items.extend(item);
}
}
items
}

fn parse_formats<R: Read>(ctx: &mut ParseCtx<R>) -> RegistryChild {
Expand Down