use crate::qname::QName;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct WsdlDefinition {
pub target_namespace: String,
pub imports: Vec<WsdlImport>,
pub types: TypesSection,
pub messages: HashMap<String, Message>,
pub port_types: HashMap<String, PortType>,
pub bindings: HashMap<String, Binding>,
pub services: HashMap<String, Service>,
}
#[derive(Debug, Clone, Default)]
pub struct TypesSection {
pub schemas: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct Message {
pub name: String,
pub parts: Vec<MessagePart>,
}
#[derive(Debug, Clone)]
pub struct MessagePart {
pub name: String,
pub element: Option<QName>,
pub type_ref: Option<QName>,
}
#[derive(Debug, Clone)]
pub struct PortType {
pub name: String,
pub operations: Vec<Operation>,
}
#[derive(Debug, Clone)]
pub struct Operation {
pub name: String,
pub input: Option<OperationMessage>,
pub output: Option<OperationMessage>,
pub faults: Vec<OperationFault>,
pub style: OperationStyle,
}
#[derive(Debug, Clone, PartialEq)]
pub enum OperationStyle {
OneWay,
RequestResponse,
SolicitResponse,
Notification,
}
#[derive(Debug, Clone)]
pub struct OperationMessage {
pub name: Option<String>,
pub message: QName,
}
#[derive(Debug, Clone)]
pub struct OperationFault {
pub name: String,
pub message: QName,
}
#[derive(Debug, Clone)]
pub struct Binding {
pub name: String,
pub port_type: QName,
pub soap_binding: SoapBinding,
pub operations: Vec<BindingOperation>,
}
#[derive(Debug, Clone)]
pub struct SoapBinding {
pub style: BindingStyle,
pub transport: String,
pub soap_version: SoapVersion,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BindingStyle {
Document,
Rpc,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SoapVersion {
Soap11,
Soap12,
}
#[derive(Debug, Clone)]
pub struct BindingOperation {
pub name: String,
pub soap_action: String,
pub input: BindingMessage,
pub output: BindingMessage,
}
#[derive(Debug, Clone)]
pub struct BindingMessage {
pub body: SoapBody,
pub headers: Vec<SoapHeader>,
}
#[derive(Debug, Clone)]
pub struct SoapBody {
pub use_attr: UseStyle,
pub namespace: Option<String>,
pub encoding_style: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum UseStyle {
Literal,
Encoded,
}
#[derive(Debug, Clone)]
pub struct SoapHeader {
pub message: QName,
pub part: String,
}
#[derive(Debug, Clone)]
pub struct Service {
pub name: String,
pub ports: Vec<Port>,
}
#[derive(Debug, Clone)]
pub struct Port {
pub name: String,
pub binding: QName,
pub address: String,
}
#[derive(Debug, Clone)]
pub struct WsdlImport {
pub namespace: String,
pub location: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::qname::QName;
fn make_minimal_definition() -> WsdlDefinition {
WsdlDefinition {
target_namespace: "http://example.com/service".to_string(),
imports: vec![],
types: TypesSection::default(),
messages: HashMap::new(),
port_types: HashMap::new(),
bindings: HashMap::new(),
services: HashMap::new(),
}
}
#[test]
fn wsdl_definition_can_be_constructed() {
let def = make_minimal_definition();
assert_eq!(def.target_namespace, "http://example.com/service");
assert!(def.messages.is_empty());
}
#[test]
fn message_with_parts() {
let msg = Message {
name: "GetStatusRequest".to_string(),
parts: vec![MessagePart {
name: "parameters".to_string(),
element: Some(QName::new("http://example.com", "GetStatus")),
type_ref: None,
}],
};
assert_eq!(msg.parts.len(), 1);
assert!(msg.parts[0].element.is_some());
}
#[test]
fn port_type_with_request_response_operation() {
let pt = PortType {
name: "DeviceService".to_string(),
operations: vec![Operation {
name: "GetStatus".to_string(),
input: Some(OperationMessage {
name: None,
message: QName::local("GetStatusRequest"),
}),
output: Some(OperationMessage {
name: None,
message: QName::local("GetStatusResponse"),
}),
faults: vec![],
style: OperationStyle::RequestResponse,
}],
};
assert_eq!(pt.operations.len(), 1);
assert_eq!(pt.operations[0].style, OperationStyle::RequestResponse);
}
#[test]
fn one_way_operation_has_no_output() {
let op = Operation {
name: "Notify".to_string(),
input: Some(OperationMessage {
name: None,
message: QName::local("NotifyRequest"),
}),
output: None,
faults: vec![],
style: OperationStyle::OneWay,
};
assert!(op.output.is_none());
assert_eq!(op.style, OperationStyle::OneWay);
}
#[test]
fn binding_soap12_document_literal() {
let binding = Binding {
name: "DeviceServiceBinding".to_string(),
port_type: QName::local("DeviceService"),
soap_binding: SoapBinding {
style: BindingStyle::Document,
transport: "http://schemas.xmlsoap.org/soap/http".to_string(),
soap_version: SoapVersion::Soap12,
},
operations: vec![],
};
assert_eq!(binding.soap_binding.soap_version, SoapVersion::Soap12);
assert_eq!(binding.soap_binding.style, BindingStyle::Document);
}
#[test]
fn service_with_port() {
let svc = Service {
name: "DeviceService".to_string(),
ports: vec![Port {
name: "DeviceServicePort".to_string(),
binding: QName::local("DeviceServiceBinding"),
address: "http://192.168.1.1/onvif/device_service".to_string(),
}],
};
assert_eq!(svc.ports.len(), 1);
assert!(svc.ports[0].address.contains("onvif"));
}
#[test]
fn wsdl_import_with_optional_location() {
let imp_with_loc = WsdlImport {
namespace: "http://example.com/types".to_string(),
location: Some("types.xsd".to_string()),
};
let imp_no_loc = WsdlImport {
namespace: "http://example.com/types".to_string(),
location: None,
};
assert!(imp_with_loc.location.is_some());
assert!(imp_no_loc.location.is_none());
}
#[test]
fn soap_header_references_message_and_part() {
let header = SoapHeader {
message: QName::new("http://docs.oasis-open.org/wss/2004", "Security"),
part: "token".to_string(),
};
assert_eq!(header.part, "token");
assert!(header.message.namespace.is_some());
}
#[test]
fn operation_fault_holds_message_ref() {
let fault = OperationFault {
name: "UnauthorizedFault".to_string(),
message: QName::local("UnauthorizedFaultMessage"),
};
assert_eq!(fault.name, "UnauthorizedFault");
}
}