Skip to content

Commit

Permalink
pmrmodel_base: a bounded version of exposure types
Browse files Browse the repository at this point in the history
- The bounded version will own an inner copy and a reference to the
  origin backend, this will allow additional querying of data.
- However, there were some lifetime difficulties with traits and impls
  when trying to convert the existing impls fns from returning the
  current unbound version to return the bounded ref version.
- Reference: rust-lang/rust#105572
  • Loading branch information
metatoaster committed Jul 14, 2023
1 parent 400bfa8 commit bc68507
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 16 deletions.
1 change: 0 additions & 1 deletion pmrmodel/src/model/db/sqlite/exposure_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ WHERE exposure_id = ?1
Ok(rec.into())
}


async fn set_default_view_sqlite(
sqlite: &SqliteBackend,
id: i64,
Expand Down
11 changes: 10 additions & 1 deletion pmrmodel_base/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ pub mod task;
pub enum BackendError {
#[cfg(feature = "sqlx")]
#[error(transparent)]
SqlxError(#[from] sqlx::Error),
Sqlx(#[from] sqlx::Error),
#[error("unknown error")]
Unknown,
}

#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ValueError {
#[error(transparent)]
Backend(#[from] BackendError),
#[error("uninitialized value")]
Uninitialized,
}
10 changes: 10 additions & 0 deletions pmrmodel_base/src/exposure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,14 @@ pub struct ExposureFileViewTask {
#[cfg(feature = "display")]
mod display;
mod impls;
mod refs;
pub mod traits;

pub use refs::{
ExposureRef,
ExposureRefs,
ExposureFileRef,
ExposureFileRefs,
ExposureFileViewRef,
ExposureFileViewRefs,
};
118 changes: 118 additions & 0 deletions pmrmodel_base/src/exposure/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ impl DerefMut for ExposureFiles {
}
}

impl<'a> From<Vec<ExposureFileRef<'a>>> for ExposureFileRefs<'a> {
fn from(args: Vec<ExposureFileRef<'a>>) -> Self {
Self(args)
}
}

impl<'a, const N: usize> From<[ExposureFileRef<'a>; N]> for ExposureFileRefs<'a> {
fn from(args: [ExposureFileRef<'a>; N]) -> Self {
Self(args.into())
}
}

impl<'a> Deref for ExposureFileRefs<'a> {
type Target = Vec<ExposureFileRef<'a>>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Vec<ExposureFileView>> for ExposureFileViews {
fn from(args: Vec<ExposureFileView>) -> Self {
Self(args)
Expand All @@ -78,3 +98,101 @@ impl DerefMut for ExposureFileViews {
&mut self.0
}
}

impl traits::Exposure for Exposure {
fn id(&self) -> i64 {
self.id
}
fn workspace_id(&self) -> i64 {
self.workspace_id
}
fn workspace_tag_id(&self) -> Option<i64> {
self.workspace_tag_id
}
fn commit_id(&self) -> &str {
self.commit_id.as_ref()
}
fn created_ts(&self) -> i64 {
self.created_ts
}
fn default_file_id(&self) -> Option<i64> {
self.default_file_id
}
}

impl traits::Exposure for ExposureRef<'_> {
fn id(&self) -> i64 {
self.inner.id
}
fn workspace_id(&self) -> i64 {
self.inner.workspace_id
}
fn workspace_tag_id(&self) -> Option<i64> {
self.inner.workspace_tag_id
}
fn commit_id(&self) -> &str {
self.inner.commit_id.as_ref()
}
fn created_ts(&self) -> i64 {
self.inner.created_ts
}
fn default_file_id(&self) -> Option<i64> {
self.inner.default_file_id
}
}

impl traits::ExposureFile for ExposureFile {
fn id(&self) -> i64 {
self.id
}
fn exposure_id(&self) -> i64 {
self.exposure_id
}
fn workspace_file_path(&self) -> &str {
self.workspace_file_path.as_ref()
}
fn default_view_id(&self) -> Option<i64> {
self.default_view_id
}
// pub views: Option<ExposureFileViews>,
}

impl traits::ExposureFile for ExposureFileRef<'_> {
fn id(&self) -> i64 {
self.inner.id
}
fn exposure_id(&self) -> i64 {
self.inner.exposure_id
}
fn workspace_file_path(&self) -> &str {
self.inner.workspace_file_path.as_ref()
}
fn default_view_id(&self) -> Option<i64> {
self.inner.default_view_id
}
// pub views: Option<ExposureFileViews>,
}

impl traits::ExposureFileView for ExposureFileView {
fn id(&self) -> i64 {
self.id
}
fn exposure_file_id(&self) -> i64 {
self.exposure_file_id
}
fn view_key(&self) -> &str {
self.view_key.as_ref()
}
}

impl traits::ExposureFileView for ExposureFileViewRef<'_> {
fn id(&self) -> i64 {
self.inner.id
}
fn exposure_file_id(&self) -> i64 {
self.inner.exposure_file_id
}
fn view_key(&self) -> &str {
self.inner.view_key.as_ref()
}
}
83 changes: 83 additions & 0 deletions pmrmodel_base/src/exposure/refs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use crate::exposure::{
Exposure,
ExposureFile,
ExposureFileView,
traits,
};

pub struct ExposureRef<'a> {
pub(super) inner: Exposure,
// pub(super) inner_files: ExposureFileRefs<'a>,
pub(super) backend: &'a dyn traits::Backend,
}

pub struct ExposureRefs<'a>(Vec<ExposureRef<'a>>);

pub struct ExposureFileRef<'a> {
pub(super) inner: ExposureFile,
// pub(super) inner_views: ExposureFileViewRefs<'a>,
pub(super) backend: &'a dyn traits::Backend,
}

pub struct ExposureFileRefs<'a>(pub(super) Vec<ExposureFileRef<'a>>);

pub struct ExposureFileViewRef<'a> {
pub(super) inner: ExposureFileView,
pub(super) backend: &'a dyn traits::Backend,
}

pub struct ExposureFileViewRefs<'a>(pub(super) Vec<ExposureFileViewRef<'a>>);

impl Exposure {
pub(super) fn bind<'a>(
self,
backend: &'a dyn traits::Backend,
) -> ExposureRef<'a> {
ExposureRef {
inner: self,
backend: backend,
}
}
}

impl ExposureRef<'_> {
pub fn into_inner(self) -> Exposure {
self.inner
}
}

impl ExposureFile {
pub(super) fn bind<'a>(
self,
backend: &'a dyn traits::Backend,
) -> ExposureFileRef<'a> {
ExposureFileRef {
inner: self,
backend: backend,
}
}
}

impl ExposureFileRef<'_> {
pub fn into_inner(self) -> ExposureFile {
self.inner
}
}

impl ExposureFileView {
pub(super) fn bind<'a>(
self,
backend: &'a dyn traits::Backend,
) -> ExposureFileViewRef<'a> {
ExposureFileViewRef {
inner: self,
backend: backend,
}
}
}

impl ExposureFileViewRef<'_> {
pub fn into_inner(self) -> ExposureFileView {
self.inner
}
}
101 changes: 87 additions & 14 deletions pmrmodel_base/src/exposure/traits.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
use async_trait::async_trait;
use std::ops::Deref;
use crate::{
error::BackendError,
exposure::{
Exposure,
Exposures,
ExposureFile,
ExposureFiles,
ExposureFileView,
ExposureFileViews,
error::{
BackendError,
ValueError,
},
exposure,
};

pub trait Exposure {
fn id(&self) -> i64;
fn workspace_id(&self) -> i64;
fn workspace_tag_id(&self) -> Option<i64>;
fn commit_id(&self) -> &str;
fn created_ts(&self) -> i64;
fn default_file_id(&self) -> Option<i64>;
// plural form is a bit tricky - what do traits of them look like?
// can we get them to behave similarly to a vec of the undelrying?
// fn files(&self) -> Result<ExposureFiles, ValueError>,
}

pub trait ExposureFile {
fn id(&self) -> i64;
fn exposure_id(&self) -> i64;
fn workspace_file_path(&self) -> &str;
fn default_view_id(&self) -> Option<i64>;
// fn views(&self) -> Result<ExposureFileViews, ValueError>,
}

pub trait ExposureFileView {
fn id(&self) -> i64;
fn exposure_file_id(&self) -> i64;
fn view_key(&self) -> &str;
}

#[async_trait]
pub trait ExposureBackend {
/// Inserts a new `Exposure` entry.
Expand All @@ -28,13 +51,13 @@ pub trait ExposureBackend {
async fn list_for_workspace(
&self,
workspace_id: i64,
) -> Result<Exposures, BackendError>;
) -> Result<exposure::Exposures, BackendError>;

/// Returns the `Exposure` for the given `id`.
async fn get_id(
&self,
id: i64,
) -> Result<Exposure, BackendError>;
) -> Result<exposure::Exposure, BackendError>;

/// For the given `Exposure` identified by its `id`, set the default
/// `ExposureFile` via its `id`.
Expand All @@ -61,13 +84,13 @@ pub trait ExposureFileBackend {
async fn list_for_exposure(
&self,
exposure_id: i64,
) -> Result<ExposureFiles, BackendError>;
) -> Result<exposure::ExposureFiles, BackendError>;

/// Returns the `ExposureFile` for the given `id`.
async fn get_id(
&self,
id: i64,
) -> Result<ExposureFile, BackendError>;
) -> Result<exposure::ExposureFile, BackendError>;

/// For the given `ExposureFile` identified by its `id`, set the
/// default `ExposureFileView` via its `id`.
Expand All @@ -93,11 +116,61 @@ pub trait ExposureFileViewBackend {
async fn list_for_exposure_file(
&self,
exposure_file_id: i64,
) -> Result<ExposureFileViews, BackendError>;
) -> Result<exposure::ExposureFileViews, BackendError>;

/// Returns the `ExposureFileView` for the given `id`.
async fn get_id(
&self,
id: i64,
) -> Result<ExposureFileView, BackendError>;
) -> Result<exposure::ExposureFileView, BackendError>;
}

// When trait aliases become stabilized (<https://github.com/rust-lang/rust/issues/41517>)
// pub trait Backend = ExposureBackend + ExposureFileBackend + ExposureViewBackend;

#[async_trait]
pub trait Backend: ExposureBackend + ExposureFileBackend + ExposureFileViewBackend {
async fn get_exposure<'a>(
&'a self,
id: i64,
) -> Result<exposure::ExposureRef<'a>, BackendError>
where Self: Sized
{
ExposureBackend::get_id(self, id).await.map(|v| v.bind(self))
}

async fn get_exposure_file<'a>(
&'a self,
id: i64,
) -> Result<exposure::ExposureFileRef<'a>, BackendError>
where Self: Sized
{
ExposureFileBackend::get_id(self, id).await.map(|v| v.bind(self))
}

async fn get_exposure_files<'a>(
&'a self,
exposure_id: i64,
) -> Result<exposure::ExposureFileRefs<'a>, BackendError>
where Self: Sized
{
let exposures = ExposureFileBackend::list_for_exposure(self, exposure_id).await?;
let result = exposures
.deref()
.iter()
// FIXME the clone is wasteful here, just a temporary workaround
.map(|v| v.clone().bind(self))
.collect::<Vec<_>>().into();
Ok(result)
}

async fn get_exposure_file_view<'a>(
&'a self,
id: i64,
) -> Result<exposure::ExposureFileViewRef<'a>, BackendError>
where Self: Sized
{
ExposureFileViewBackend::get_id(self, id).await.map(|v| v.bind(self))
}
}
impl<B: ExposureBackend + ExposureFileBackend + ExposureFileViewBackend> Backend for B {}

0 comments on commit bc68507

Please sign in to comment.