#![allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OdataPropertyKind {
Primitive,
Navigation,
Complex,
}
#[derive(Debug, Clone)]
pub struct OdataProperty {
pub name: String,
pub edm_type: String,
pub nullable: bool,
pub kind: OdataPropertyKind,
}
#[derive(Debug, Clone)]
pub struct OdataEntityType {
pub name: String,
pub key: String,
pub properties: Vec<OdataProperty>,
}
#[derive(Debug, Clone)]
pub struct OdataEntitySet {
pub name: String,
pub entity_type: String,
}
#[derive(Debug, Default)]
pub struct OdataExport {
pub entity_types: Vec<OdataEntityType>,
pub entity_sets: Vec<OdataEntitySet>,
pub service_root: String,
pub namespace: String,
}
pub fn new_odata_export(service_root: &str, namespace: &str) -> OdataExport {
OdataExport {
entity_types: Vec::new(),
entity_sets: Vec::new(),
service_root: service_root.to_owned(),
namespace: namespace.to_owned(),
}
}
pub fn add_odata_entity_type(export: &mut OdataExport, name: &str, key: &str) {
export.entity_types.push(OdataEntityType {
name: name.to_owned(),
key: key.to_owned(),
properties: Vec::new(),
});
}
pub fn add_odata_property(
export: &mut OdataExport,
prop_name: &str,
edm_type: &str,
nullable: bool,
) {
if let Some(et) = export.entity_types.last_mut() {
et.properties.push(OdataProperty {
name: prop_name.to_owned(),
edm_type: edm_type.to_owned(),
nullable,
kind: OdataPropertyKind::Primitive,
});
}
}
pub fn add_odata_nav_property(export: &mut OdataExport, prop_name: &str, edm_type: &str) {
if let Some(et) = export.entity_types.last_mut() {
et.properties.push(OdataProperty {
name: prop_name.to_owned(),
edm_type: edm_type.to_owned(),
nullable: true,
kind: OdataPropertyKind::Navigation,
});
}
}
pub fn add_odata_entity_set(export: &mut OdataExport, set_name: &str, entity_type: &str) {
export.entity_sets.push(OdataEntitySet {
name: set_name.to_owned(),
entity_type: entity_type.to_owned(),
});
}
pub fn odata_entity_type_count(export: &OdataExport) -> usize {
export.entity_types.len()
}
pub fn odata_entity_set_count(export: &OdataExport) -> usize {
export.entity_sets.len()
}
pub fn total_odata_properties(export: &OdataExport) -> usize {
export
.entity_types
.iter()
.map(|et| et.properties.len())
.sum()
}
pub fn find_odata_entity_type<'a>(
export: &'a OdataExport,
name: &str,
) -> Option<&'a OdataEntityType> {
export.entity_types.iter().find(|et| et.name == name)
}
pub fn find_odata_entity_set<'a>(
export: &'a OdataExport,
name: &str,
) -> Option<&'a OdataEntitySet> {
export.entity_sets.iter().find(|es| es.name == name)
}
pub fn navigation_property_count(export: &OdataExport) -> usize {
export
.entity_types
.iter()
.flat_map(|et| et.properties.iter())
.filter(|p| p.kind == OdataPropertyKind::Navigation)
.count()
}
pub fn render_odata_metadata(export: &OdataExport) -> String {
let mut out = format!(
"<edmx:Edmx Version=\"4.0\">\n<edmx:DataServices>\n<Schema Namespace=\"{}\">\n",
export.namespace
);
for et in &export.entity_types {
out.push_str(&format!(" <EntityType Name=\"{}\">\n", et.name));
out.push_str(&format!(
" <Key><PropertyRef Name=\"{}\"/></Key>\n",
et.key
));
for p in &et.properties {
out.push_str(&format!(
" <Property Name=\"{}\" Type=\"{}\"/>\n",
p.name, p.edm_type
));
}
out.push_str(" </EntityType>\n");
}
out.push_str("</Schema>\n</edmx:DataServices>\n</edmx:Edmx>");
out
}
pub fn odata_export_to_json(export: &OdataExport) -> String {
format!(
r#"{{"service_root":"{}", "namespace":"{}", "entity_type_count":{}, "entity_set_count":{}}}"#,
export.service_root,
export.namespace,
odata_entity_type_count(export),
odata_entity_set_count(export)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_export_empty() {
let e = new_odata_export("https://api.example.com/odata", "Example.Model");
assert_eq!(odata_entity_type_count(&e), 0);
}
#[test]
fn add_entity_type_increments_count() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Mesh", "Id");
assert_eq!(odata_entity_type_count(&e), 1);
}
#[test]
fn add_entity_set_increments_count() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Mesh", "Id");
add_odata_entity_set(&mut e, "Meshes", "Mesh");
assert_eq!(odata_entity_set_count(&e), 1);
}
#[test]
fn add_property_stored() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Mesh", "Id");
add_odata_property(&mut e, "Id", "Edm.Int32", false);
assert_eq!(e.entity_types[0].properties.len(), 1);
}
#[test]
fn total_properties_correct() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Mesh", "Id");
add_odata_property(&mut e, "Id", "Edm.Int32", false);
add_odata_property(&mut e, "Name", "Edm.String", true);
assert_eq!(total_odata_properties(&e), 2);
}
#[test]
fn find_entity_type_success() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Avatar", "Id");
assert!(find_odata_entity_type(&e, "Avatar").is_some());
}
#[test]
fn find_entity_type_missing_none() {
let e = new_odata_export("https://api.example.com/odata", "Example.Model");
assert!(find_odata_entity_type(&e, "Ghost").is_none());
}
#[test]
fn navigation_property_counted() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Mesh", "Id");
add_odata_property(&mut e, "Id", "Edm.Int32", false);
add_odata_nav_property(&mut e, "Bones", "Collection(Example.Model.Bone)");
assert_eq!(navigation_property_count(&e), 1);
}
#[test]
fn render_metadata_contains_schema() {
let mut e = new_odata_export("https://api.example.com/odata", "Example.Model");
add_odata_entity_type(&mut e, "Mesh", "Id");
assert!(render_odata_metadata(&e).contains("Schema"));
}
#[test]
fn json_contains_service_root() {
let e = new_odata_export("https://mesh.api.com/odata", "Mesh.Model");
assert!(odata_export_to_json(&e).contains("mesh.api.com"));
}
}