This is a work in progress!
Parse the metadata XML describing an SAP OData service and generate Rust entities for each EDM type:
ComplexType
EntityType
FunctionImport
TODO
Currently when generating a Rust
struct
, only theName
andType
properties are extracted from the XML<EntityType>
declaration.
Consider how the other XML attribute values and SAP annotations could be made available within the Ruststruct
.
In the Cargo.toml
of your application, define an entry in [build-dependencies]
that points to the parse-sap-odata
crate:
toml
[build-dependencies]
parse-sap-odata = "^1.1.0"
Your app will also require these dependencies
toml
[dependencies]
rust_decimal = "^1.30"
uuid = "^1.4"
chrono = "^0.4"
In your app's build.rs
, run the generator for your desired OData service:
```rust use parsesapodata::utils::parseodata::gensrc;
fn main() {
// gensrc() requires two arguments
// 1) metadatafilename: String The name of the XML file in the ./odata directory
// Do not include the '.xml' suffix in the file name!
// 2) namespace: String The namespace defined in the
See the Rust documentation page for build scripts for more information.
All metadata XML for the OData services your app consumes must be located in the ./odata
directory immediately under your app's top level directory.
If cargo
detects a build.rs
file in your project/crate, then it automatically populates the environment variable OUT_DIR
.
This variable then points to the directory into which all build script output is written.
The default directory name will be target/debug/build/<your_package_name>/out
, and this is where you can find the generated struct
declarations for the OData service.
You can specify your own value for OUT_DIR
either by calling cargo
with the --out_dir
flag, or by creating your own config.toml
file in the ./cargo
directory.
See Cargo Configuration for more details.
In the source code of your application, the generated OData structs
can be referenced like this:
```rust use chrono::Utc;
include!(concat!(env!("OUTDIR"), "/gwsamplebasic.rs"));
fn main() { let now = Utc::now().naiveutc(); let bp = BusinessPartner { address: Address { addresstype: Some(String::from("Dummay addres type")), building: Some(String::from("Dummy building")), city: Some(String::from("Dummy city")), country: Some(String::from("Dummy country")), postalcode: Some(String::from("Dummy postal code")), street: Some(String::from("Dummy street")), }, businesspartnerid: String::from("Dummy business partner id"), businesspartnerrole: String::from("Dummy business partner role"), changedat: Some(now), companyname: String::from("Dummy company name"), createdat: Some(now), currencycode: String::from("GBP"), emailaddress: String::from("Dummy email address"), faxnumber: Some(String::from("Dummy fax number")), legalform: Some(String::from("Dummy legal form")), phonenumber: Some(String::from("0123456789")), webaddress: Some(String::from("Dummy website address")), };
println!("{:#?}", bp);
} ```
In the event an Entity Type definition uses a complex type, then the complex type is first created as a Rust struct
.
The field in Rust struct
that has this complex type is then defined using this struct
.
An example of this is the Address
property.
```xml
```
The Rust struct
name is generated by trimming the namespace qualifier and (if present) the CT_
prefix
xml
<ComplexType Name="CT_Address">
<Property Name="City" Type="Edm.String" MaxLength="40" sap:label="City" sap:semantics="city"/>
<Property Name="PostalCode" Type="Edm.String" MaxLength="10" sap:label="Postal Code" sap:semantics="zip"/>
<Property Name="Street" Type="Edm.String" MaxLength="60" sap:label="Street" sap:semantics="street"/>
<Property Name="Building" Type="Edm.String" MaxLength="10" sap:label="Building"/>
<Property Name="Country" Type="Edm.String" MaxLength="3" sap:label="Country" sap:semantics="country"/>
<Property Name="AddressType" Type="Edm.String" MaxLength="2" sap:label="Address Type"/>
</ComplexType>
So the above XML definition becomes:
```rust
pub struct Address {
pub addresstype: Option
The metadata for the GWSAMPLE_BASIC
OData service contains the following complex type:
xml
<ComplexType Name="CT_String">
<Property Name="String" Type="Edm.String" Nullable="false" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
</ComplexType>
Allowing for the current situation in which additional attribute values and SAP Annotations are not preserved, this particular type turns out not to be complex at all — its just a String
.
In such cases, fields declared to be of these "simple" complex types (such as CT_String
), are collapsed down to the Rust native type of the single inner property — which in this example is simply a String
.
build_test_crate
subdirectory.cargo build
./target/debug/build-test-crate
and you will see output similar to this:rust
BusinessPartner {
address: Address {
address_type: Some(
"Dummy address type",
),
building: Some(
"Dummy building",
),
city: Some(
"Dummy city",
),
country: Some(
"Dummy country",
),
postal_code: Some(
"Dummy postal code",
),
street: Some(
"Dummy street",
),
},
business_partner_id: "Dummy business partner id",
business_partner_role: "Dummy business partner role",
changed_at: Some(
2023-08-03T10:42:57.532857,
),
company_name: "Dummy company name",
created_at: Some(
2023-08-03T10:42:57.532857,
),
currency_code: "GBP",
email_address: "Dummy email address",
fax_number: Some(
"Dummy fax number",
),
legal_form: Some(
"Dummy legal form",
),
phone_number: Some(
"0123456789",
),
web_address: Some(
"Dummy website address",
),
}