odata_client_codegen 0.1.0

Strongly-typed OData client code generation
Documentation
use std::{fmt::Debug, marker::PhantomData, panic::UnwindSafe, path::PathBuf};

use serde::de::{value::StrDeserializer, IntoDeserializer};
use serde::Deserialize;

use crate::{
    entity_data_model_parse::{edm, edmx, xs},
    input_file_test::{run_file_parse_tests, InputFileTest},
};

fn test_data_path(dir: &str) -> PathBuf {
    // `file!` returns source file path relative to the workspace root. However tests run in the
    // crate root. So we strip leading crate folder segment from the filepath.
    let prefix_strip_count = "odata_client_codegen/".len();

    // Strip extension from filename for 2018-style module root file
    let suffix_strip_count = ".rs".len();

    let test_module_path = &file!()[prefix_strip_count..(file!().len() - suffix_strip_count)];

    [test_module_path, dir].iter().collect()
}

/// Tests that input entity data model fragments produce the expected output model when
/// deserialized.
struct TestEntityDataModelParse<T>(PhantomData<T>);

impl<T> Clone for TestEntityDataModelParse<T> {
    fn clone(&self) -> Self {
        TestEntityDataModelParse(PhantomData)
    }
}

impl<T> UnwindSafe for TestEntityDataModelParse<T> {}

impl<'de, T: Deserialize<'de> + Debug> InputFileTest for TestEntityDataModelParse<T> {
    type Parsed = T;
    type ParseError = serde_xml_rs::Error;

    const INPUT_FILE_EXT: &'static str = ".xml";
    const OUTPUT_FILE_EXT: &'static str = ".result";

    fn parse_input_file(&mut self, file_contents: &str) -> Result<Self::Parsed, Self::ParseError> {
        serde_xml_rs::from_str(file_contents)
    }

    fn write_test_result(&mut self, result: Self::Parsed) -> String {
        format!("{:#?}", result)
    }
}

#[test]
/// Test parsing full documents
fn parse_full_document() {
    run_file_parse_tests(
        TestEntityDataModelParse::<edmx::TEdmx>(PhantomData),
        &test_data_path("t_edmx"),
    );
}

#[test]
fn parse_t_entity_type() {
    run_file_parse_tests(
        TestEntityDataModelParse::<edm::TEntityType>(PhantomData),
        &test_data_path("t_entity_type"),
    );
}

#[test]
fn parse_t_entity_set() {
    run_file_parse_tests(
        TestEntityDataModelParse::<edm::TEntitySet>(PhantomData),
        &test_data_path("t_entity_set"),
    );
}

#[test]
fn parse_annotation() {
    run_file_parse_tests(
        TestEntityDataModelParse::<edm::Annotation>(PhantomData),
        &test_data_path("annotation"),
    );
}

#[test]
fn parse_g_expression() {
    run_file_parse_tests(
        TestEntityDataModelParse::<edm::GExpression>(PhantomData),
        &test_data_path("g_expression"),
    );
}

#[test]
fn parse_xs_list() {
    let input = "1 2 3";
    let expected = xs::List(vec![1, 2, 3]);
    let actual = {
        let str_de: StrDeserializer<'_, serde::de::value::Error> = input.into_deserializer();
        xs::List::deserialize(str_de)
    };

    assert_eq!(Ok(expected), actual);
}

#[test]
fn parse_t_type_name_qualified() {
    let input = "_Some.Name12345";
    let expected = edm::TTypeName::Qualified(edm::TQualifiedName(xs::NCName(input.to_string())));
    let actual = {
        let str_de: StrDeserializer<'_, serde::de::value::Error> = input.into_deserializer();
        edm::TTypeName::deserialize(str_de)
    };

    assert_eq!(Ok(expected), actual);
}

#[test]
fn parse_t_type_name_collection_qualified() {
    let input = "Collection(_Some.Name12345)";
    let expected = edm::TTypeName::CollectionQualified(edm::CollectionQualified(
        edm::TQualifiedName(xs::NCName("_Some.Name12345".to_string())),
    ));
    let actual = {
        let str_de: StrDeserializer<'_, serde::de::value::Error> = input.into_deserializer();
        edm::TTypeName::deserialize(str_de)
    };

    assert_eq!(Ok(expected), actual);
}