etwin_populate 0.9.1

Hammerfest store implementation
Documentation
use etwin_constants::dinoparc::LOCATIONS;
use etwin_core::dinoparc::{DinoparcLocationId, DinoparcServer};
use sqlx::postgres::PgQueryResult;
use sqlx::{Postgres, Transaction};
use std::collections::BTreeSet;
use std::error::Error;

fn box_sqlx_error(e: sqlx::Error) -> Box<dyn Error + Send> {
  Box::new(e)
}

pub async fn populate_dinoparc(tx: &mut Transaction<'_, Postgres>) -> Result<(), Box<dyn Error + Send>> {
  populate_dinoparc_servers(tx).await?;
  populate_dinoparc_locations(tx).await?;
  Ok(())
}

async fn populate_dinoparc_servers(tx: &mut Transaction<'_, Postgres>) -> Result<(), Box<dyn Error + Send>> {
  #[derive(Debug, sqlx::FromRow)]
  struct Row {
    dinoparc_server: DinoparcServer,
  }

  let rows: Vec<Row> = sqlx::query_as::<_, Row>(
    r"
      SELECT dinoparc_server
      FROM dinoparc_servers;
    ",
  )
  .fetch_all(&mut *tx)
  .await
  .map_err(box_sqlx_error)?;

  let actual: BTreeSet<_> = rows.iter().map(|r| r.dinoparc_server).collect();
  let expected: BTreeSet<_> = DinoparcServer::iter().collect();

  if actual == expected {
    return Ok(());
  }

  for extra in actual.difference(&expected) {
    let res: PgQueryResult = sqlx::query(
      r"
      DELETE
      FROM dinoparc_servers
      WHERE dinoparc_server = $1::dinoparc_server;
    ",
    )
    .bind(extra)
    .execute(&mut *tx)
    .await
    .map_err(box_sqlx_error)?;
    assert_eq!(res.rows_affected(), 1);
  }

  for value in expected {
    let res: PgQueryResult = sqlx::query(
      r"
      INSERT
      INTO dinoparc_servers(dinoparc_server)
      VALUES ($1::dinoparc_server)
      ON CONFLICT (dinoparc_server) DO NOTHING;
    ",
    )
    .bind(value)
    .execute(&mut *tx)
    .await
    .map_err(box_sqlx_error)?;
    assert!((0..=1u64).contains(&res.rows_affected()));
  }

  Ok(())
}

async fn populate_dinoparc_locations(tx: &mut Transaction<'_, Postgres>) -> Result<(), Box<dyn Error + Send>> {
  #[derive(Debug, sqlx::FromRow)]
  struct Row {
    dinoparc_location_id: DinoparcLocationId,
  }

  let rows: Vec<Row> = sqlx::query_as::<_, Row>(
    r"
      SELECT dinoparc_location_id
      FROM dinoparc_locations;
    ",
  )
  .fetch_all(&mut *tx)
  .await
  .map_err(box_sqlx_error)?;

  let actual: BTreeSet<DinoparcLocationId> = rows.iter().map(|r| r.dinoparc_location_id).collect();
  let expected: BTreeSet<DinoparcLocationId> = LOCATIONS.iter().map(|l| l.id).collect();

  for extra_lid in actual.difference(&expected) {
    let res: PgQueryResult = sqlx::query(
      r"
      DELETE
      FROM dinoparc_locations
      WHERE dinoparc_location_id = $1::DINOPARC_LOCATION_ID;
    ",
    )
    .bind(extra_lid)
    .execute(&mut *tx)
    .await
    .map_err(box_sqlx_error)?;
    assert_eq!(res.rows_affected(), 1);
  }

  for lid in expected {
    let res: PgQueryResult = sqlx::query(
      r"
      INSERT
      INTO dinoparc_locations(dinoparc_location_id)
      VALUES ($1::DINOPARC_LOCATION_ID)
      ON CONFLICT (dinoparc_location_id) DO NOTHING;
    ",
    )
    .bind(lid)
    .execute(&mut *tx)
    .await
    .map_err(box_sqlx_error)?;
    assert!((0..=1u64).contains(&res.rows_affected()));
  }

  Ok(())
}