parse-sap-odata 1.4.9

Generate a pair of Rust modules from SAP V2 OData metadata
Documentation
# Metadata Module

Most of the metadata captured from the OData `$metadata` HTTP request is output to a separate module.
That is, all `<EntityType>` and `<ComplexType>` entities are transformed to Rust `struct`s and `enum`s.

The metadata information for the following entities is parsed, but not yet written to the generated metadata module:

* `<FunctionImport>`
* `<NavigationProperty>`

## Metadata for Complex Type `struct`s

For each metadata `<ComplexType>` containing more than one `<Property>`, a corresponding metadata `struct` is created.

E.G. In the `GWSAMPLE_BASIC` service, the metadata XML for the complex type `CT_Address`is:

```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="Country"     Type="Edm.String" MaxLength="3"  sap:label="Country"     sap:semantics="country"/>
  <Property Name="Building"    Type="Edm.String" MaxLength="10" sap:label="Building"/>
  <Property Name="AddressType" Type="Edm.String" MaxLength="2"  sap:label="Address Type"/>
</ComplexType>
```

This is then translated to a Rust `struct` whose name ends with `Metadata`:
 
```rust   
pub struct CtAddressMetadata {
    pub address_type: Property,
    pub building: Property,
    pub city: Property,
    pub country: Property,
    pub postal_code: Property,
    pub street: Property,
}
```
    
All fields within a complex type metadata `struct` are of type `parse_sap_odata::property::Property`.

    
## Metadata for Entity Type `struct`s

One or more `struct`s are created for each `<EntityType>` listed in the metadata.

E.G. In the `GWSAMPLE_BASIC` service, the metadata XML for `BusinessPartner` is the following:

```xml
<EntityType Name="BusinessPartner" sap:content-version="1">
    <Key>
        <PropertyRef Name="BusinessPartnerID"/>
    </Key>
    <Property Name="Address" Type="GWSAMPLE_BASIC.CT_Address" Nullable="false"/>
    <Property Name="BusinessPartnerID" Type="Edm.String" Nullable="false" MaxLength="10" sap:unicode="false" sap:label="Bus. Part. ID" sap:creatable="false" sap:updatable="false"/>
    <Property Name="CompanyName" Type="Edm.String" Nullable="false" MaxLength="80" sap:unicode="false" sap:label="Company Name"/>
    <Property Name="WebAddress" Type="Edm.String" sap:unicode="false" sap:label="Web Address" sap:sortable="false" sap:filterable="false" sap:semantics="url"/>
    <Property Name="EmailAddress" Type="Edm.String" Nullable="false" MaxLength="255" sap:unicode="false" sap:label="E-Mail Address" sap:semantics="email"/>
    <Property Name="PhoneNumber" Type="Edm.String" MaxLength="30" sap:unicode="false" sap:label="Phone No." sap:semantics="tel"/>
    <Property Name="FaxNumber" Type="Edm.String" MaxLength="30" sap:unicode="false" sap:label="Fax Number"/>
    <Property Name="LegalForm" Type="Edm.String" MaxLength="10" sap:unicode="false" sap:label="Legal Form"/>
    <Property Name="CurrencyCode" Type="Edm.String" Nullable="false" MaxLength="5" sap:unicode="false" sap:label="Currency" sap:semantics="currency-code"/>
    <Property Name="BusinessPartnerRole" Type="Edm.String" Nullable="false" MaxLength="3" sap:unicode="false" sap:label="Bus. Part. Role"/>
    <Property Name="CreatedAt" Type="Edm.DateTime" Precision="7" sap:unicode="false" sap:label="Time Stamp" sap:creatable="false" sap:updatable="false"/>
    <Property Name="ChangedAt" Type="Edm.DateTime" Precision="7" ConcurrencyMode="Fixed" sap:unicode="false" sap:label="Time Stamp" sap:creatable="false" sap:updatable="false"/>
    <NavigationProperty Name="ToSalesOrders" Relationship="GWSAMPLE_BASIC.Assoc_BusinessPartner_SalesOrders" FromRole="FromRole_Assoc_BusinessPartner_SalesOrders" ToRole="ToRole_Assoc_BusinessPartner_SalesOrders"/>
    <NavigationProperty Name="ToContacts" Relationship="GWSAMPLE_BASIC.Assoc_BusinessPartner_Contacts" FromRole="FromRole_Assoc_BusinessPartner_Contacts" ToRole="ToRole_Assoc_BusinessPartner_Contacts"/>
    <NavigationProperty Name="ToProducts" Relationship="GWSAMPLE_BASIC.Assoc_BusinessPartner_Products" FromRole="FromRole_Assoc_BusinessPartner_Products" ToRole="ToRole_Assoc_BusinessPartner_Products"/>
</EntityType>
```

This XML is transformed into the following Rust `struct`:

```rust
pub struct BusinessPartnerMetadata {
    pub key: Vec<PropertyRef>,
    pub address: CtAddressMetadata,
    pub business_partner_id: Property,
    pub business_partner_role: Property,
    pub changed_at: Property,
    pub company_name: Property,
    pub created_at: Property,
    pub currency_code: Property,
    pub email_address: Property,
    pub fax_number: Property,
    pub legal_form: Property,
    pub phone_number: Property,
    pub web_address: Property,
}
```

All `<EntityType>` metadata `struct`s have an additional `key` field of type `Vec<PropertyRef>`
    
All fields in a metadata `struct` will either be of type `Property` or of a previously declared complex type `struct`.

## Implementation of Metadata Entity Type `struct`s

Each metadata `struct` for an `<EntityType>` has an implementation containing a getter function for the `key` and a getter function for each `struct` field.

* The `get_key()` function returns a vector of `PropertyRef`
* The field getter functions return either an instance of a `Property` or an instance of `ComplexType`.

E.G. The implementation of the `BusinessPartnerMetadata` `struct` shown above starts as follows:

```rust
impl BusinessPartnerMetadata {
    pub fn get_key() -> Vec<PropertyRef> {
        vec![PropertyRef {
                name: "business_partner_id".to_owned(),
            }]
    }

    pub fn get_address() -> ComplexType {
        ComplexType {
            name: "CT_Address".to_owned(),
            properties: vec![
                Property {...}, // City
                Property {...}, // PostalCode
                Property {...}, // Street
                Property {...}, // Building
                Property {...}, // Country
                Property {...}, // AddressType
            ]
        }
    }
    
    pub fn get_business_partner_id() -> Property {
        Property {...}
    }

    // SNIP
}
```

## Metadata for Associations

An `Association` describes the relationship between two `EntityTypes`.

Using the `GWSAMPLE_BASIC` OData service as an example, there is an association called `Assoc_VH_Country_BusinessPartners` between the entity types `BusinessPartner` and `Country`.

All associations have exactly two `End` objects, each of which describes the "From" and "To" sides of the association.

In this case, one `Country` is related to many `BusinessPartners`

```rust
pub fn vh_country_business_partners() -> Association {
    Association {
        name: "Assoc_VH_Country_BusinessPartners".to_owned(),
        sap_content_version: "1".to_owned(),
        ends: [
            End {
                role: "FromRole_Assoc_VH_Country_BusinessPartners".to_owned(),
                entity_set: None,
                end_type: Some("VhCountry".to_owned()),
                multiplicity: Some("1".to_owned()),
            },
            End {
                role: "ToRole_Assoc_VH_Country_BusinessPartners".to_owned(),
                entity_set: None,
                end_type: Some("BusinessPartner".to_owned()),
                multiplicity: Some("*".to_owned()),
            },
        ],
        referential_constraint: None,
    }
}
```

Since this is a simple relationship, there is no need to define a referential constraint.

### Referential Constraints

A referential constraint describes a foreign key relationship.
This is the situation in which the set of permissible values for a non-key field in one `EntityType` is defined (or constrained) by the key values found in some other `EntityType`.

For example, the referential constraint in association `Assoc_VH_ProductTypeCode_Products` describes the fact that the non-key field `Product.type_code` may only use a value found in the `EntitySet` field `VhProductTypeCode.type_code`.

```rust
pub fn vh_product_type_code_products() -> Association {
    Association {
        // SNIP

        referential_constraint: Some(ReferentialConstraint {
            principal: Principal {
                role: "FromRole_Assoc_VH_ProductTypeCode_Products".to_owned(),
                property_refs: vec![PropertyRef {
                    name: "type_code".to_owned(),
                }],
            },
            dependent: Dependent {
                role: "ToRole_Assoc_VH_ProductTypeCode_Products".to_owned(),
                property_refs: vec![PropertyRef {
                    name: "type_code".to_owned(),
                }],
            },
        }),
    }
}
```

There is no requirement for the "From" and "To" field names to be the same.
This can be seen in cases where an `EntitySet` containing the German field name `waers` (for currency code) is linked to the field name `currency_code` in some other `EntitySet`.

## Metadata for AssociationSets

In the same way that an `Association` describes the relationship between two `EntityType`s, an `AssociationSet` describes the relationship between two `EntitySet`s.