kdb-connection 0.1.0

kDB connection.
Documentation
use lazy_static::lazy_static;
use std::{sync::Mutex, thread::sleep, time::Duration};

use kdb_store::StoreFactory as _;

use crate::{Connection, Value, ValueHash};

lazy_static! {
    static ref TEST_MUTEX: Mutex<()> = Mutex::new(());
}

pub(crate) async fn wait_for_connection(c: &impl Connection)
{
    for _ in 0..30
    {
        if c.is_connected().await
        {
            break;
        }
        sleep(Duration::from_secs(1));
    }
}

pub(crate) fn create_store_configuration() -> kdb_store::StoreConfiguration
{
    kdb_store::StoreConfiguration::new().set_temporary_store()
}

pub(crate) struct StoreGuard<'a>
{
    #[allow(unused)]
    guard: std::sync::MutexGuard<'a, ()>,
    store: kdb_store::BoxedStore,
}

impl<'a> StoreGuard<'a>
{
    pub(crate) fn start(&mut self) -> kdb_store::Result<()>
    {
        self.store.start()
    }
}

impl<'a> Drop for StoreGuard<'a>
{
    fn drop(&mut self)
    {
        // Make sure the store is stopped before the guard is released.
        if let Err(e) = self.store.stop()
        {
            eprintln!("failed to stop store in Drop: {e}");
        }
    }
}

pub(crate) fn create_store<'a>(config: kdb_store::StoreConfiguration) -> StoreGuard<'a>
{
    let guard = crate::test::TEST_MUTEX.lock().unwrap();
    if pathsearch::find_executable_in_path("kdb").is_some()
    {
        StoreGuard {
            guard,
            store: Box::new(kdb_store::ProcessStore::new(config).unwrap()),
        }
    }
    else
    {
        StoreGuard {
            guard,
            store: Box::new(kdb_store::DockerStore::new(config).unwrap()),
        }
    }
}

pub(crate) fn validate_agent_0(agent_0: crate::ValueHash)
{
    assert_eq!(agent_0.len(), 3);

    // ---- object_uri ----
    let object_uri = agent_0.get("object_uri").unwrap();
    assert_eq!(
        object_uri,
        &Value::from_uri_value(
            "http://www.w3.org/2001/XMLSchema#anyURI",
            "http://example.org/agent/0"
        )
    );

    // ---- type_uri ----
    let type_uri = agent_0.get("type_uri").unwrap();
    assert_eq!(
        type_uri,
        &Value::from_uri_value(
            "http://www.w3.org/2001/XMLSchema#anyURI",
            "http://askco.re/agent#uav"
        )
    );

    // ---- properties ----
    let properties_value = agent_0.get("properties").unwrap();
    assert_eq!(
        properties_value.datatype_ref(),
        "http://askco.re/datatype#valuehash"
    );

    let properties_map: ValueHash = properties_value.clone().try_into().unwrap();

    // ---- individual property checks ----
    assert_eq!(
        properties_map
            .get("http://askco.re/agent#agent_type")
            .unwrap(),
        &Value::from_uri_value(
            "http://www.w3.org/2001/XMLSchema#anyURI",
            "http://askco.re/agent#uav"
        )
    );

    assert_eq!(
        properties_map.get("http://askco.re/agent#frame").unwrap(),
        &Value::from("agent0")
    );

    assert_eq!(
        properties_map
            .get("http://xmlns.com/foaf/0.1/name")
            .unwrap(),
        &Value::from("test agent")
    );
}

pub(crate) fn validate_dataset_0(dataset_0: crate::ValueHash)
{
    // ---- top-level length ----
    assert_eq!(dataset_0.len(), 3);

    // ---- object_uri ----
    let object_uri = dataset_0.get("object_uri").unwrap();
    assert_eq!(
        object_uri,
        &Value::from_uri_value(
            "http://www.w3.org/2001/XMLSchema#anyURI",
            "http://example.org/dataset/0"
        )
    );

    // ---- type_uri ----
    let type_uri = dataset_0.get("type_uri").unwrap();
    assert_eq!(
        type_uri,
        &Value::from_uri_value(
            "http://www.w3.org/2001/XMLSchema#anyURI",
            "http://askco.re/dataset#point_cloud_dataset"
        )
    );

    // ---- properties ----
    let properties_value = dataset_0.get("properties").unwrap();
    assert_eq!(
        properties_value.datatype_ref(),
        "http://askco.re/datatype#valuehash"
    );

    let properties_map: ValueHash = properties_value.clone().try_into().unwrap();

    // ---- content_type ----
    assert_eq!(
        properties_map
            .get("http://askco.re/dataset#content_type")
            .unwrap(),
        &Value::from_uri_value(
            "http://www.w3.org/2001/XMLSchema#anyURI",
            "http://askco.re/sensing#point_cloud"
        )
    );

    // ---- sensing: point_density ----
    let point_density = properties_map
        .get("http://askco.re/sensing#point_density")
        .unwrap();

    // cast the quantityDecimal value directly into a map
    let point_density_map: ValueHash = point_density.clone().try_into().unwrap();

    assert_eq!(
        point_density_map.get("unit").unwrap(),
        &Value::from("point/m^2")
    );

    assert_eq!(
        point_density_map.get("value").unwrap(),
        &Value::from(10_i64)
    );

    // ---- geometry ----
    let geometry = properties_map
        .get("http://www.opengis.net/ont/geosparql#hasGeometry")
        .unwrap();

    // datatype check
    assert_eq!(
        geometry.datatype_ref(),
        "http://www.opengis.net/ont/geosparql#Geometry"
    );

    let geometry_hash: ValueHash = geometry.clone().try_into().unwrap();

    // ---- geometry type ----
    assert_eq!(geometry_hash.get("type").unwrap(), &Value::from("Polygon"));

    // ---- coordinates ----
    let coordinates_value = geometry_hash.get("coordinates").unwrap();
    assert_eq!(
        coordinates_value.datatype_ref(),
        "http://askco.re/datatype#valuelist"
    );

    let rings: Vec<Value> = coordinates_value.clone().try_into().unwrap();
    assert_eq!(rings.len(), 1, "Expected exactly one polygon ring");

    let ring: Vec<Value> = rings[0].clone().try_into().unwrap();
    assert_eq!(ring.len(), 5, "Polygon must have 5 points (closed)");

    let expected_coords = vec![
        (30_i64, 10_i64),
        (40_i64, 40_i64),
        (20_i64, 40_i64),
        (10_i64, 20_i64),
        (30_i64, 10_i64), // closed polygon
    ];

    for (idx, ((exp_x, exp_y), coord)) in expected_coords.into_iter().zip(ring.iter()).enumerate()
    {
        let point: Vec<Value> = coord.clone().try_into().unwrap();
        assert_eq!(point.len(), 2, "Point {} must have 2 coordinates", idx);

        assert_eq!(point[0], Value::from(exp_x), "X mismatch at point {}", idx);
        assert_eq!(point[1], Value::from(exp_y), "Y mismatch at point {}", idx);
    }
}