Files
pmomusic/pmoupnp/src/soap/fault.rs

174 lines
4.7 KiB
Rust

//! SOAP Faults pour UPnP
use xmltree::{Element, XMLNode};
/// Erreur SOAP (Fault)
#[derive(Debug, Clone)]
pub struct SoapFault {
/// Code d'erreur (ex: "s:Client", "401")
pub fault_code: String,
/// Description de l'erreur
pub fault_string: String,
/// Détails UPnP optionnels
pub upnp_error: Option<UpnpError>,
}
/// Erreur UPnP spécifique
#[derive(Debug, Clone)]
pub struct UpnpError {
/// Code d'erreur UPnP (ex: "401", "501")
pub error_code: String,
/// Description de l'erreur
pub error_description: String,
}
impl SoapFault {
/// Crée un fault SOAP simple
pub fn new(fault_code: String, fault_string: String) -> Self {
Self {
fault_code,
fault_string,
upnp_error: None,
}
}
/// Crée un fault SOAP avec erreur UPnP
pub fn with_upnp_error(
fault_code: String,
fault_string: String,
error_code: String,
error_description: String,
) -> Self {
Self {
fault_code,
fault_string,
upnp_error: Some(UpnpError {
error_code,
error_description,
}),
}
}
}
/// Construit un SOAP Fault XML
///
/// # Arguments
///
/// * `fault_code` - Code du fault (ex: "s:Client")
/// * `fault_string` - Message d'erreur
/// * `upnp_error_code` - Code d'erreur UPnP optionnel (ex: "401")
/// * `upnp_error_desc` - Description d'erreur UPnP optionnelle
///
/// # Returns
///
/// XML SOAP Fault formaté
pub fn build_soap_fault(
fault_code: &str,
fault_string: &str,
upnp_error_code: Option<&str>,
upnp_error_desc: Option<&str>,
) -> Result<String, xmltree::Error> {
// Construire l'élément Fault
let mut fault = Element::new("s:Fault");
// faultcode
let mut faultcode_elem = Element::new("faultcode");
faultcode_elem
.children
.push(XMLNode::Text(fault_code.to_string()));
fault.children.push(XMLNode::Element(faultcode_elem));
// faultstring
let mut faultstring_elem = Element::new("faultstring");
faultstring_elem
.children
.push(XMLNode::Text(fault_string.to_string()));
fault.children.push(XMLNode::Element(faultstring_elem));
// detail (si erreur UPnP)
if let (Some(code), Some(desc)) = (upnp_error_code, upnp_error_desc) {
let mut detail = Element::new("detail");
let mut upnp_error = Element::new("UPnPError");
upnp_error.attributes.insert(
"xmlns".to_string(),
"urn:schemas-upnp-org:control-1-0".to_string(),
);
let mut error_code_elem = Element::new("errorCode");
error_code_elem
.children
.push(XMLNode::Text(code.to_string()));
upnp_error
.children
.push(XMLNode::Element(error_code_elem));
let mut error_desc_elem = Element::new("errorDescription");
error_desc_elem
.children
.push(XMLNode::Text(desc.to_string()));
upnp_error
.children
.push(XMLNode::Element(error_desc_elem));
detail.children.push(XMLNode::Element(upnp_error));
fault.children.push(XMLNode::Element(detail));
}
// Construire le Body
let mut body = Element::new("s:Body");
body.children.push(XMLNode::Element(fault));
// Construire l'Envelope
let mut envelope = Element::new("s:Envelope");
envelope.attributes.insert(
"xmlns:s".to_string(),
"http://schemas.xmlsoap.org/soap/envelope/".to_string(),
);
envelope.children.push(XMLNode::Element(body));
// Sérialiser
let mut buf = Vec::new();
let config = xmltree::EmitterConfig::new()
.perform_indent(true)
.indent_string(" ");
envelope.write_with_config(&mut buf, config)?;
Ok(String::from_utf8(buf).unwrap())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_simple_fault() {
let xml = build_soap_fault("s:Client", "Invalid Action", None, None).unwrap();
assert!(xml.contains("<s:Fault>"));
assert!(xml.contains("<faultcode>s:Client</faultcode>"));
assert!(xml.contains("<faultstring>Invalid Action</faultstring>"));
assert!(!xml.contains("UPnPError"));
}
#[test]
fn test_build_upnp_fault() {
let xml = build_soap_fault(
"s:Client",
"UPnP Error",
Some("401"),
Some("Invalid Action"),
)
.unwrap();
assert!(xml.contains("<s:Fault>"));
assert!(xml.contains("<detail>"));
assert!(xml.contains("<UPnPError"));
assert!(xml.contains("<errorCode>401</errorCode>"));
assert!(xml.contains("<errorDescription>Invalid Action</errorDescription>"));
}
}