use roxmltree::{Document, ExpandedName, Node, NodeId};
use thiserror::Error;
type Result<'a, 'input, T> = std::result::Result<T, WsError>;
#[derive(Error, Debug)]
pub enum WsErrorMalformedType {
#[error("missing attribute \"{0}\"")]
MissingAttribute(String),
#[error("missing element \"{0}\"")]
MissingElement(String),
}
#[derive(Error, Debug)]
pub enum WsErrorType {
#[error("The input WSDL document was malformed: {0}")]
MalformedWsdl(WsErrorMalformedType),
#[error("Attempt to refer to unknown element {0}")]
InvalidReference(String),
#[error("Node unexpectedly did not have a parent node")]
NoParentNode,
}
#[derive(Error, Debug)]
pub struct WsError(pub NodeId, pub WsErrorType);
impl WsError {
fn new(node: Node, typ: WsErrorType) -> Self {
Self(node.id(), typ)
}
}
impl std::fmt::Display for WsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.1))
}
}
fn target_namespace<'a, 'input>(node: Node<'a, 'input>) -> Result<'a, 'input, &'a str> {
let mut nparent = node.parent();
while let Some(parent) = nparent {
if let Some(ns) = parent.attribute("targetNamespace") {
return Ok(ns);
}
nparent = parent.parent();
}
Err(WsError::new(
node,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute(
"targetNamespace".to_string(),
)),
))
}
fn resolve_qualified<'a, 'input: 'a>(
node: Node<'a, 'input>,
qualified_name: &'a str,
) -> std::result::Result<ExpandedName<'a, 'a>, WsErrorType> {
if qualified_name.contains(":") {
let mut s = qualified_name.split(":");
let ns = s
.next()
.ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
let uri = node
.lookup_namespace_uri(Some(ns))
.ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
let name = s
.next()
.ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
Ok((uri, name).into())
} else {
Ok(qualified_name.into())
}
}
fn split_qualified(qualified_name: &str) -> std::result::Result<(Option<&str>, &str), WsErrorType> {
let (namespace, name) = {
if qualified_name.contains(":") {
let mut s = qualified_name.split(":");
let ns = s
.next()
.ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
let name = s
.next()
.ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
(Some(ns), name)
} else {
(None, qualified_name)
}
};
Ok((namespace, name))
}
#[derive(Debug, Clone)]
pub struct WsMessage<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsMessage<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn parts(&self) -> impl Iterator<Item = WsMessagePart> {
self.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "part")))
.map(|n| WsMessagePart(n))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsMessagePart<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input: 'a> WsMessagePart<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn typename(&self) -> Result<ExpandedName<'a, 'a>> {
let typename = self
.0
.attribute("element")
.or(self.0.attribute("type"))
.ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute(
"type".to_string(),
)),
))?;
resolve_qualified(self.0, typename).map_err(|e| WsError::new(self.0, e))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsPortType<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsPortType<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn target_namespace(&self) -> Result<&'a str> {
target_namespace(self.0)
}
pub fn operations(&self) -> Result<impl Iterator<Item = WsPortOperation<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "operation")))
.map(|n| WsPortOperation(n)))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsPortOperation<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsPortOperation<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn input(&self) -> Result<Option<WsMessage<'a, 'input>>> {
let message_typename = match self
.0
.children()
.find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "input")))
.map(|n| n.attribute("message"))
.flatten()
{
Some(n) => n,
None => return Ok(None),
};
let (_message_namespace, message_name) =
split_qualified(message_typename).map_err(|e| WsError::new(self.0, e))?;
let def = WsDefinitions::find_parent(self.0)?;
Ok(Some(
def.messages()?
.find(|n| n.0.attribute("name") == Some(message_name))
.ok_or(WsError::new(
self.0,
WsErrorType::InvalidReference(message_name.to_string()),
))?,
))
}
pub fn output(&self) -> Result<Option<WsMessage<'a, 'input>>> {
let message_typename = match self
.0
.children()
.find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "output")))
.map(|n| n.attribute("message"))
.flatten()
{
Some(n) => n,
None => return Ok(None),
};
let (_message_namespace, message_name) =
split_qualified(message_typename).map_err(|e| WsError::new(self.0, e))?;
let def = WsDefinitions::find_parent(self.0)?;
Ok(Some(
def.messages()?
.find(|n| n.0.attribute("name") == Some(message_name))
.ok_or(WsError::new(
self.0,
WsErrorType::InvalidReference(message_name.to_string()),
))?,
))
}
pub fn fault(&self) -> Result<Option<WsMessage<'a, 'input>>> {
let message_typename = match self
.0
.children()
.find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "fault")))
.map(|n| n.attribute("message"))
.flatten()
{
Some(n) => n,
None => return Ok(None),
};
let (_message_namespace, message_name) =
split_qualified(message_typename).map_err(|e| WsError::new(self.0, e))?;
let def = WsDefinitions::find_parent(self.0)?;
Ok(Some(
def.messages()?
.find(|n| n.0.attribute("name") == Some(message_name))
.ok_or(WsError::new(
self.0,
WsErrorType::InvalidReference(message_name.to_string()),
))?,
))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsBindingOperation<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsBindingOperation<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn port_operation(&self) -> Result<WsPortOperation<'a, 'input>> {
let name = self.name()?;
let binding = WsBinding(
self.0
.parent()
.ok_or(WsError::new(self.0, WsErrorType::NoParentNode))?,
);
let port_type: WsPortType<'a, 'input> = binding.port_type()?;
let mut operations = port_type.operations()?;
operations
.try_find(|o| Ok(o.name()? == name))?
.ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingElement(name.to_string())),
))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsBinding<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsBinding<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn port_type(&self) -> Result<WsPortType<'a, 'input>> {
let port_typename = self.0.attribute("type").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("type".to_string())),
))?;
let (_port_namespace, port_name) =
split_qualified(port_typename).map_err(|e| WsError::new(self.0, e))?;
let def = WsDefinitions::find_parent(self.0)?;
def.port_types()?
.find(|n| n.0.attribute("name") == Some(port_name))
.ok_or(WsError::new(
self.0,
WsErrorType::InvalidReference(port_name.to_string()),
))
}
pub fn operations(&self) -> Result<impl Iterator<Item = WsBindingOperation>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "operation")))
.map(|n| WsBindingOperation(n)))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsServicePort<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsServicePort<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn binding(&self) -> Result<WsBinding<'a, 'input>> {
let binding_typename = self.0.attribute("binding").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute(
"binding".to_string(),
)),
))?;
let (_binding_namespace, binding_name) =
split_qualified(binding_typename).map_err(|e| WsError::new(self.0, e))?;
let def = WsDefinitions::find_parent(self.0)?;
def.bindings()?
.find(|n| n.0.attribute("name") == Some(binding_name))
.ok_or(WsError::new(
self.0,
WsErrorType::InvalidReference(binding_name.to_string()),
))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsService<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsService<'a, 'input> {
pub fn name(&self) -> Result<&'a str> {
self.0.attribute("name").ok_or(WsError::new(
self.0,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
))
}
pub fn ports(&self) -> Result<impl Iterator<Item = WsServicePort>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "port")))
.map(|n| WsServicePort(n)))
}
pub fn node(&self) -> Node<'a, 'input> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct WsTypes<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsTypes<'a, 'input> {
pub fn schemas(&self) -> Result<impl Iterator<Item = Node<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://www.w3.org/2001/XMLSchema", "schema"))))
}
}
#[derive(Debug, Clone)]
pub struct WsDefinitions<'a, 'input>(Node<'a, 'input>);
impl<'a, 'input> WsDefinitions<'a, 'input> {
fn find_parent(mut node: Node<'a, 'input>) -> Result<'a, 'input, Self> {
loop {
node = node
.parent()
.ok_or(WsError::new(node, WsErrorType::NoParentNode))?;
if let Ok(definitions) = Self::from_node(node) {
return Ok(definitions);
}
}
}
pub fn from_node(node: Node<'a, 'input>) -> Result<'a, 'input, Self> {
if node.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "definitions")) {
Ok(Self(node))
} else {
Err(WsError::new(
node,
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingElement(
"definitions".to_string(),
)),
))
}
}
pub fn from_document(document: &'a Document<'input>) -> Result<'a, 'input, Self> {
document
.root()
.children()
.find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "definitions")))
.ok_or(WsError::new(
document.root_element(),
WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingElement(
"definitions".to_string(),
)),
))
.map(|n| Self(n))
}
pub fn port_types(&self) -> Result<impl Iterator<Item = WsPortType<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "portType")))
.map(|n| WsPortType(n))
.into_iter())
}
pub fn messages(&self) -> Result<impl Iterator<Item = WsMessage<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "message")))
.map(|n| WsMessage(n))
.into_iter())
}
pub fn bindings(&self) -> Result<impl Iterator<Item = WsBinding<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "binding")))
.map(|n| WsBinding(n))
.into_iter())
}
pub fn services(&self) -> Result<impl Iterator<Item = WsService<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "service")))
.map(|n| WsService(n))
.into_iter())
}
pub fn types(&self) -> Result<impl Iterator<Item = Node<'a, 'input>>> {
Ok(self
.0
.children()
.filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "types")))
.into_iter())
}
}