A SOAP client generator for Rust (forked from https://github.com/netwo-io/savon)
| .helix | add development stuff | |
| assets | add assets | |
| soafe-test | chore: rename savon to soafe | |
| src | chore: rename savon to soafe | |
| .envrc | add development stuff | |
| .gitignore | add development stuff | |
| Cargo.toml | chore: rename savon to soafe | |
| flake.lock | add development stuff | |
| flake.nix | add development stuff | |
| README.md | chore: rename savon to soafe | |
soafe, a SOAP client generator for Rust
This project is a fork of https://github.com/netwo-io/savon
soafe generates code from a WSDL file, that you can then include in your project. It will generate serialization and deserialization code, along with an async HTTP client API (based on reqwest).
Usage
in Cargo.toml:
[dependencies]
soafe = "0.2"
in build.rs:
fn main(){letout_dir=env::var("OUT_DIR").unwrap();lets=soafe::generate::gen_write("./assets/example.wsdl",&out_dir).unwrap();}Finally, in your code:
mod soap{include!(concat!(env!("OUT_DIR"),"/example.rs"));}You can then use it as follows:
letclient=soap::StockQuoteService::new("http://example.com".to_string());letres=client.get_last_trade_price(soap::GetLastTradePriceInput(TradePriceRequest{ticker_symbol: "SOAP".to_string()})).await?;Under the hood
If you use the following WSDL file as input:
<?xml version="1.0"?>
<definitions name="StockQuote"
targetNamespace="http://example.com/stockquote.wsdl"
xmlns:tns="http://example.com/stockquote.wsdl"
xmlns:xsd1="http://example.com/stockquote.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema targetNamespace="http://example.com/stockquote.xsd"
xmlns="http://www.w3.org/2000/10/XMLSchema">
<element name="TradePriceRequest">
<complexType>
<all>
<element name="tickerSymbol" type="string"/>
</all>
</complexType>
</element>
<element name="TradePrice">
<complexType>
<all>
<element name="price" type="float"/>
</all>
</complexType>
</element>
</schema>
</types>
<message name="GetLastTradePriceInput">
<part name="body" element="xsd1:TradePriceRequest"/>
</message>
<message name="GetLastTradePriceOutput">
<part name="body" element="xsd1:TradePrice"/>
</message>
<portType name="StockQuotePortType">
<operation name="GetLastTradePrice">
<input message="tns:GetLastTradePriceInput"/>
<output message="tns:GetLastTradePriceOutput"/>
</operation>
</portType>
<binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="GetLastTradePrice">
<soap:operation soapAction="http://example.com/GetLastTradePrice"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="StockQuoteService">
<documentation>My first service</documentation>
<port name="StockQuotePort" binding="tns:StockQuoteSoapBinding">
<soap:address location="http://example.com/stockquote"/>
</port>
</service>
</definitions>
It will generate this code:
usesoafe::internal::xmltree;usesoafe::rpser::xml::*;#[derive(Clone, Debug, Default)]pubstruct TradePriceRequest{pubticker_symbol: String,}implsoafe::generate::ToElementsforTradePriceRequest{fn to_elements(&self)-> Vec<xmltree::Element>{vec![vec![xmltree::Element::node("tickerSymbol").with_text(self.ticker_symbol.to_string())]].drain(..).flatten().collect()}}implsoafe::generate::FromElementforTradePriceRequest{fn from_element(element: &xmltree::Element)-> Result<Self,soafe::Error>{Ok(TradePriceRequest{ticker_symbol: element.get_at_path(&["tickerSymbol"]).and_then(|e|{e.get_text().map(|s|s.to_string()).ok_or(soafe::rpser::xml::Error::Empty)})?,})}}#[derive(Clone, Debug, Default)]pubstruct TradePrice{pubprice: f64,}implsoafe::generate::ToElementsforTradePrice{fn to_elements(&self)-> Vec<xmltree::Element>{vec![vec![xmltree::Element::node("price").with_text(self.price.to_string())]].drain(..).flatten().collect()}}implsoafe::generate::FromElementforTradePrice{fn from_element(element: &xmltree::Element)-> Result<Self,soafe::Error>{Ok(TradePrice{price: element.get_at_path(&["price"]).map_err(soafe::Error::from).and_then(|e|{e.get_text().ok_or(soafe::rpser::xml::Error::Empty).map_err(soafe::Error::from).and_then(|s|s.parse().map_err(soafe::Error::from))})?,})}}pubstruct StockQuoteService{pubbase_url: String,pubclient: soafe::internal::reqwest::Client,}pubmod messages{usesuper::*;#[derive(Clone, Debug, Default)]pubstruct GetLastTradePriceOutput(pubTradePrice);implsoafe::generate::ToElementsforGetLastTradePriceOutput{fn to_elements(&self)-> Vec<xmltree::Element>{self.0.to_elements()}}implsoafe::generate::FromElementforGetLastTradePriceOutput{fn from_element(element: &xmltree::Element)-> Result<Self,soafe::Error>{TradePrice::from_element(element).map(GetLastTradePriceOutput)}}#[derive(Clone, Debug, Default)]pubstruct GetLastTradePriceInput(pubTradePriceRequest);implsoafe::generate::ToElementsforGetLastTradePriceInput{fn to_elements(&self)-> Vec<xmltree::Element>{self.0.to_elements()}}implsoafe::generate::FromElementforGetLastTradePriceInput{fn from_element(element: &xmltree::Element)-> Result<Self,soafe::Error>{TradePriceRequest::from_element(element).map(GetLastTradePriceInput)}}}#[allow(dead_code)]implStockQuoteService{pubfn new(base_url: String)-> Self{Self::with_client(base_url,soafe::internal::reqwest::Client::new())}pubfn with_client(base_url: String,client: soafe::internal::reqwest::Client)-> Self{StockQuoteService{base_url,client}}pubasyncfn get_last_trade_price(&self,get_last_trade_price_input: messages::GetLastTradePriceInput,)-> Result<Result<messages::GetLastTradePriceOutput,()>,soafe::Error>{soafe::http::request_response(&self.client,&self.base_url,"http://example.com/stockquote.wsdl","GetLastTradePrice",&get_last_trade_price_input,).await}}