1use roxmltree::{Document, ExpandedName, Node, NodeId};
2use thiserror::Error;
3
4type Result<'a, 'input, T> = std::result::Result<T, WsError>;
5
6#[derive(Error, Debug)]
7pub enum WsErrorMalformedType {
8 #[error("missing attribute \"{0}\"")]
9 MissingAttribute(String),
10 #[error("missing element \"{0}\"")]
11 MissingElement(String),
12}
13
14#[derive(Error, Debug)]
15pub enum WsErrorType {
16 #[error("The input WSDL document was malformed: {0}")]
17 MalformedWsdl(WsErrorMalformedType),
18 #[error("Attempt to refer to unknown element {0}")]
19 InvalidReference(String),
20 #[error("Node unexpectedly did not have a parent node")]
21 NoParentNode,
22}
23
24#[derive(Error, Debug)]
25pub struct WsError(pub NodeId, pub WsErrorType);
26
27impl WsError {
28 fn new(node: Node, typ: WsErrorType) -> Self {
29 Self(node.id(), typ)
30 }
31}
32
33impl std::fmt::Display for WsError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.write_fmt(format_args!("{}", self.1))
36 }
37}
38
39fn target_namespace<'a, 'input>(node: Node<'a, 'input>) -> Result<'a, 'input, &'a str> {
40 let mut nparent = node.parent();
42 while let Some(parent) = nparent {
43 if let Some(ns) = parent.attribute("targetNamespace") {
44 return Ok(ns);
45 }
46
47 nparent = parent.parent();
48 }
49
50 Err(WsError::new(
51 node,
52 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute(
53 "targetNamespace".to_string(),
54 )),
55 ))
56}
57
58fn resolve_qualified<'a, 'input: 'a>(
59 node: Node<'a, 'input>,
60 qualified_name: &'a str,
61) -> std::result::Result<ExpandedName<'a, 'a>, WsErrorType> {
62 if qualified_name.contains(":") {
63 let mut s = qualified_name.split(":");
64
65 let ns = s
66 .next()
67 .ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
68
69 let uri = node
70 .lookup_namespace_uri(Some(ns))
71 .ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
72
73 let name = s
74 .next()
75 .ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
76
77 Ok((uri, name).into())
78 } else {
79 Ok(qualified_name.into())
80 }
81}
82
83fn split_qualified(qualified_name: &str) -> std::result::Result<(Option<&str>, &str), WsErrorType> {
84 let (namespace, name) = {
85 if qualified_name.contains(":") {
86 let mut s = qualified_name.split(":");
87 let ns = s
88 .next()
89 .ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
90
91 let name = s
92 .next()
93 .ok_or(WsErrorType::InvalidReference(qualified_name.to_string()))?;
94
95 (Some(ns), name)
96 } else {
97 (None, qualified_name)
98 }
99 };
100
101 Ok((namespace, name))
102}
103
104#[derive(Debug, Clone)]
119pub struct WsMessage<'a, 'input>(Node<'a, 'input>);
120
121impl<'a, 'input> WsMessage<'a, 'input> {
122 pub fn name(&self) -> Result<&'a str> {
124 self.0.attribute("name").ok_or(WsError::new(
125 self.0,
126 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
127 ))
128 }
129
130 pub fn parts(&self) -> impl Iterator<Item = WsMessagePart> {
132 self.0
133 .children()
134 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "part")))
135 .map(|n| WsMessagePart(n))
136 }
137
138 pub fn node(&self) -> Node<'a, 'input> {
140 self.0
141 }
142}
143
144#[derive(Debug, Clone)]
147pub struct WsMessagePart<'a, 'input>(Node<'a, 'input>);
148
149impl<'a, 'input: 'a> WsMessagePart<'a, 'input> {
150 pub fn name(&self) -> Result<&'a str> {
152 self.0.attribute("name").ok_or(WsError::new(
153 self.0,
154 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
155 ))
156 }
157
158 pub fn typename(&self) -> Result<ExpandedName<'a, 'a>> {
161 let typename = self
162 .0
163 .attribute("element")
164 .or(self.0.attribute("type"))
165 .ok_or(WsError::new(
166 self.0,
167 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute(
168 "type".to_string(),
169 )),
170 ))?;
171
172 resolve_qualified(self.0, typename).map_err(|e| WsError::new(self.0, e))
173 }
174
175 pub fn node(&self) -> Node<'a, 'input> {
177 self.0
178 }
179}
180
181#[derive(Debug, Clone)]
183pub struct WsPortType<'a, 'input>(Node<'a, 'input>);
184
185impl<'a, 'input> WsPortType<'a, 'input> {
186 pub fn name(&self) -> Result<&'a str> {
188 self.0.attribute("name").ok_or(WsError::new(
189 self.0,
190 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
191 ))
192 }
193
194 pub fn target_namespace(&self) -> Result<&'a str> {
196 target_namespace(self.0)
197 }
198
199 pub fn operations(&self) -> Result<impl Iterator<Item = WsPortOperation<'a, 'input>>> {
201 Ok(self
202 .0
203 .children()
204 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "operation")))
205 .map(|n| WsPortOperation(n)))
206 }
207
208 pub fn node(&self) -> Node<'a, 'input> {
210 self.0
211 }
212}
213
214#[derive(Debug, Clone)]
217pub struct WsPortOperation<'a, 'input>(Node<'a, 'input>);
218
219impl<'a, 'input> WsPortOperation<'a, 'input> {
220 pub fn name(&self) -> Result<&'a str> {
222 self.0.attribute("name").ok_or(WsError::new(
223 self.0,
224 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
225 ))
226 }
227
228 pub fn input(&self) -> Result<Option<WsMessage<'a, 'input>>> {
230 let message_typename = match self
231 .0
232 .children()
233 .find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "input")))
234 .map(|n| n.attribute("message"))
235 .flatten()
236 {
237 Some(n) => n,
238 None => return Ok(None),
239 };
240
241 let (_message_namespace, message_name) =
242 split_qualified(message_typename).map_err(|e| WsError::new(self.0, e))?;
243
244 let def = WsDefinitions::find_parent(self.0)?;
245 Ok(Some(
246 def.messages()?
247 .find(|n| n.0.attribute("name") == Some(message_name))
248 .ok_or(WsError::new(
249 self.0,
250 WsErrorType::InvalidReference(message_name.to_string()),
251 ))?,
252 ))
253 }
254
255 pub fn output(&self) -> Result<Option<WsMessage<'a, 'input>>> {
257 let message_typename = match self
258 .0
259 .children()
260 .find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "output")))
261 .map(|n| n.attribute("message"))
262 .flatten()
263 {
264 Some(n) => n,
265 None => return Ok(None),
266 };
267
268 let (_message_namespace, message_name) =
269 split_qualified(message_typename).map_err(|e| WsError::new(self.0, e))?;
270
271 let def = WsDefinitions::find_parent(self.0)?;
272 Ok(Some(
273 def.messages()?
274 .find(|n| n.0.attribute("name") == Some(message_name))
275 .ok_or(WsError::new(
276 self.0,
277 WsErrorType::InvalidReference(message_name.to_string()),
278 ))?,
279 ))
280 }
281
282 pub fn fault(&self) -> Result<Option<WsMessage<'a, 'input>>> {
284 let message_typename = match self
285 .0
286 .children()
287 .find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "fault")))
288 .map(|n| n.attribute("message"))
289 .flatten()
290 {
291 Some(n) => n,
292 None => return Ok(None),
293 };
294
295 let (_message_namespace, message_name) =
296 split_qualified(message_typename).map_err(|e| WsError::new(self.0, e))?;
297
298 let def = WsDefinitions::find_parent(self.0)?;
299 Ok(Some(
300 def.messages()?
301 .find(|n| n.0.attribute("name") == Some(message_name))
302 .ok_or(WsError::new(
303 self.0,
304 WsErrorType::InvalidReference(message_name.to_string()),
305 ))?,
306 ))
307 }
308
309 pub fn node(&self) -> Node<'a, 'input> {
311 self.0
312 }
313}
314
315#[derive(Debug, Clone)]
317pub struct WsBindingOperation<'a, 'input>(Node<'a, 'input>);
318
319impl<'a, 'input> WsBindingOperation<'a, 'input> {
320 pub fn name(&self) -> Result<&'a str> {
322 self.0.attribute("name").ok_or(WsError::new(
323 self.0,
324 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
325 ))
326 }
327
328 pub fn port_operation(&self) -> Result<WsPortOperation<'a, 'input>> {
330 let name = self.name()?;
331 let binding = WsBinding(
332 self.0
333 .parent()
334 .ok_or(WsError::new(self.0, WsErrorType::NoParentNode))?,
335 );
336
337 let port_type: WsPortType<'a, 'input> = binding.port_type()?;
338 let mut operations = port_type.operations()?;
339
340 operations
341 .try_find(|o| Ok(o.name()? == name))?
342 .ok_or(WsError::new(
343 self.0,
344 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingElement(name.to_string())),
345 ))
346 }
347
348 pub fn node(&self) -> Node<'a, 'input> {
350 self.0
351 }
352}
353
354#[derive(Debug, Clone)]
357pub struct WsBinding<'a, 'input>(Node<'a, 'input>);
358
359impl<'a, 'input> WsBinding<'a, 'input> {
360 pub fn name(&self) -> Result<&'a str> {
362 self.0.attribute("name").ok_or(WsError::new(
363 self.0,
364 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
365 ))
366 }
367
368 pub fn port_type(&self) -> Result<WsPortType<'a, 'input>> {
369 let port_typename = self.0.attribute("type").ok_or(WsError::new(
370 self.0,
371 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("type".to_string())),
372 ))?;
373
374 let (_port_namespace, port_name) =
375 split_qualified(port_typename).map_err(|e| WsError::new(self.0, e))?;
376
377 let def = WsDefinitions::find_parent(self.0)?;
378 def.port_types()?
379 .find(|n| n.0.attribute("name") == Some(port_name))
380 .ok_or(WsError::new(
381 self.0,
382 WsErrorType::InvalidReference(port_name.to_string()),
383 ))
384 }
385
386 pub fn operations(&self) -> Result<impl Iterator<Item = WsBindingOperation>> {
387 Ok(self
388 .0
389 .children()
390 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "operation")))
391 .map(|n| WsBindingOperation(n)))
392 }
393
394 pub fn node(&self) -> Node<'a, 'input> {
396 self.0
397 }
398}
399
400#[derive(Debug, Clone)]
401pub struct WsServicePort<'a, 'input>(Node<'a, 'input>);
402
403impl<'a, 'input> WsServicePort<'a, 'input> {
404 pub fn name(&self) -> Result<&'a str> {
405 self.0.attribute("name").ok_or(WsError::new(
406 self.0,
407 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
408 ))
409 }
410
411 pub fn binding(&self) -> Result<WsBinding<'a, 'input>> {
413 let binding_typename = self.0.attribute("binding").ok_or(WsError::new(
414 self.0,
415 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute(
416 "binding".to_string(),
417 )),
418 ))?;
419
420 let (_binding_namespace, binding_name) =
421 split_qualified(binding_typename).map_err(|e| WsError::new(self.0, e))?;
422
423 let def = WsDefinitions::find_parent(self.0)?;
424 def.bindings()?
425 .find(|n| n.0.attribute("name") == Some(binding_name))
426 .ok_or(WsError::new(
427 self.0,
428 WsErrorType::InvalidReference(binding_name.to_string()),
429 ))
430 }
431
432 pub fn node(&self) -> Node<'a, 'input> {
434 self.0
435 }
436}
437
438#[derive(Debug, Clone)]
441pub struct WsService<'a, 'input>(Node<'a, 'input>);
442
443impl<'a, 'input> WsService<'a, 'input> {
444 pub fn name(&self) -> Result<&'a str> {
445 self.0.attribute("name").ok_or(WsError::new(
446 self.0,
447 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingAttribute("name".to_string())),
448 ))
449 }
450
451 pub fn ports(&self) -> Result<impl Iterator<Item = WsServicePort>> {
452 Ok(self
453 .0
454 .children()
455 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "port")))
456 .map(|n| WsServicePort(n)))
457 }
458
459 pub fn node(&self) -> Node<'a, 'input> {
461 self.0
462 }
463}
464
465#[derive(Debug, Clone)]
466pub struct WsTypes<'a, 'input>(Node<'a, 'input>);
467
468impl<'a, 'input> WsTypes<'a, 'input> {
469 pub fn schemas(&self) -> Result<impl Iterator<Item = Node<'a, 'input>>> {
472 Ok(self
473 .0
474 .children()
475 .filter(|n| n.has_tag_name(("http://www.w3.org/2001/XMLSchema", "schema"))))
476 }
477}
478
479#[derive(Debug, Clone)]
480pub struct WsDefinitions<'a, 'input>(Node<'a, 'input>);
481
482impl<'a, 'input> WsDefinitions<'a, 'input> {
483 fn find_parent(mut node: Node<'a, 'input>) -> Result<'a, 'input, Self> {
485 loop {
486 node = node
487 .parent()
488 .ok_or(WsError::new(node, WsErrorType::NoParentNode))?;
489
490 if let Ok(definitions) = Self::from_node(node) {
491 return Ok(definitions);
492 }
493 }
494 }
495
496 pub fn from_node(node: Node<'a, 'input>) -> Result<'a, 'input, Self> {
497 if node.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "definitions")) {
498 Ok(Self(node))
499 } else {
500 Err(WsError::new(
501 node,
502 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingElement(
503 "definitions".to_string(),
504 )),
505 ))
506 }
507 }
508
509 pub fn from_document(document: &'a Document<'input>) -> Result<'a, 'input, Self> {
510 document
511 .root()
512 .children()
513 .find(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "definitions")))
514 .ok_or(WsError::new(
515 document.root_element(),
516 WsErrorType::MalformedWsdl(WsErrorMalformedType::MissingElement(
517 "definitions".to_string(),
518 )),
519 ))
520 .map(|n| Self(n))
521 }
522
523 pub fn port_types(&self) -> Result<impl Iterator<Item = WsPortType<'a, 'input>>> {
524 Ok(self
525 .0
526 .children()
527 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "portType")))
528 .map(|n| WsPortType(n))
529 .into_iter())
530 }
531
532 pub fn messages(&self) -> Result<impl Iterator<Item = WsMessage<'a, 'input>>> {
533 Ok(self
534 .0
535 .children()
536 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "message")))
537 .map(|n| WsMessage(n))
538 .into_iter())
539 }
540
541 pub fn bindings(&self) -> Result<impl Iterator<Item = WsBinding<'a, 'input>>> {
542 Ok(self
543 .0
544 .children()
545 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "binding")))
546 .map(|n| WsBinding(n))
547 .into_iter())
548 }
549
550 pub fn services(&self) -> Result<impl Iterator<Item = WsService<'a, 'input>>> {
551 Ok(self
552 .0
553 .children()
554 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "service")))
555 .map(|n| WsService(n))
556 .into_iter())
557 }
558
559 pub fn types(&self) -> Result<impl Iterator<Item = Node<'a, 'input>>> {
560 Ok(self
562 .0
563 .children()
564 .filter(|n| n.has_tag_name(("http://schemas.xmlsoap.org/wsdl/", "types")))
565 .into_iter())
566 }
567}