The entire metadata document is parsed, but only the following entities are transformed into Rust source code and written to the generated metadata module:
<EntityType>
<ComplexType>
<Association>
<AssociationSet>
The following entities are not transformed into Rust source code:
<FunctionImport>
<NavigationProperty>
For each metadata <ComplexType>
containing more than one <Property>
, a corresponding metadata struct
is created.
E.G. In the demo service GWSAMPLE_BASIC
, the metadata XML for the complex type CT_Address
is:
<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 upper snake-case name used in the XML is translated to a Rust struct
name with the suffix Metadata
:
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 metadata complex type struct
are of type parse_sap_odata::property::Property
.
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:
<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
:
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
.
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 ofPropertyRef
- The field getter functions return either an instance of a
Property
or an instance of someComplexType
.
E.G. The implementation of the BusinessPartnerMetadata
struct
shown above starts as follows:
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
}
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
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.
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
.
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
.
In the same way that an Association
describes the relationship between two EntityType
s, an AssociationSet
describes the relationship between two EntitySet
s.