omnitrack 0.3.0

Universal issue-tracker provider contracts and clients (Linear, Jira, ...) for Rust, in one crate.
Documentation
use super::errors::error;
use super::models::{
    Comment, CommentId, Cycle, CycleId, Issue, IssueDraft, IssueFilter, IssueId, IssuePatch, Label,
    LabelId, Milestone, MilestoneId, Project, ProjectId, StatusCategory, Team, TeamId, User,
    UserId, issue_patch,
};
use super::pagination::{Page, PageRequest};
use super::{BoxFuture, IssueResult};

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum Capability {
    Issues,
    Projects,
    Milestones,
    Cycles,
    Teams,
    Users,
    Labels,
    Viewer,
    Comments,
}

macro_rules! capability {
    ($trait_name:ident, $entity:ty, $id:ty, $not_configured:ident) => {
        pub trait $trait_name: Send + Sync {
            fn get(&self, id: $id) -> BoxFuture<'_, IssueResult<$entity>>;

            fn list(&self, page: Option<PageRequest>) -> BoxFuture<'_, IssueResult<Page<$entity>>>;
        }

        #[derive(Clone, Copy, Debug, Default)]
        pub struct $not_configured;

        impl $trait_name for $not_configured {
            fn get(&self, _id: $id) -> BoxFuture<'_, IssueResult<$entity>> {
                Box::pin(async { Err(error().transport_not_configured()) })
            }

            fn list(
                &self,
                _page: Option<PageRequest>,
            ) -> BoxFuture<'_, IssueResult<Page<$entity>>> {
                Box::pin(async { Err(error().transport_not_configured()) })
            }
        }
    };
}

pub trait Issues: Send + Sync {
    fn get(&self, id: IssueId) -> BoxFuture<'_, IssueResult<Issue>>;

    fn list(
        &self,
        filter: IssueFilter,
        page: Option<PageRequest>,
    ) -> BoxFuture<'_, IssueResult<Page<Issue>>>;

    fn create(&self, draft: IssueDraft) -> BoxFuture<'_, IssueResult<Issue>>;

    fn update(&self, id: IssueId, patch: IssuePatch) -> BoxFuture<'_, IssueResult<Issue>>;

    fn delete(&self, id: IssueId) -> BoxFuture<'_, IssueResult<()>>;

    fn close(&self, id: IssueId) -> BoxFuture<'_, IssueResult<Issue>> {
        self.update(
            id,
            issue_patch().category(StatusCategory::Completed).build(),
        )
    }
}

#[derive(Clone, Copy, Debug, Default)]
pub struct TransportNotConfiguredIssues;

impl Issues for TransportNotConfiguredIssues {
    fn get(&self, _id: IssueId) -> BoxFuture<'_, IssueResult<Issue>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }

    fn list(
        &self,
        _filter: IssueFilter,
        _page: Option<PageRequest>,
    ) -> BoxFuture<'_, IssueResult<Page<Issue>>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }

    fn create(&self, _draft: IssueDraft) -> BoxFuture<'_, IssueResult<Issue>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }

    fn update(&self, _id: IssueId, _patch: IssuePatch) -> BoxFuture<'_, IssueResult<Issue>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }

    fn delete(&self, _id: IssueId) -> BoxFuture<'_, IssueResult<()>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }
}

pub trait Viewer: Send + Sync {
    fn current_user(&self) -> BoxFuture<'_, IssueResult<User>>;
}

#[derive(Clone, Copy, Debug, Default)]
pub struct TransportNotConfiguredViewer;

impl Viewer for TransportNotConfiguredViewer {
    fn current_user(&self) -> BoxFuture<'_, IssueResult<User>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }
}

pub trait Comments: Send + Sync {
    fn list_comments(
        &self,
        issue: IssueId,
        page: Option<PageRequest>,
    ) -> BoxFuture<'_, IssueResult<Page<Comment>>>;

    fn post_comment(&self, issue: IssueId, body: String) -> BoxFuture<'_, IssueResult<Comment>>;

    fn delete_comment(&self, id: CommentId) -> BoxFuture<'_, IssueResult<()>>;
}

#[derive(Clone, Copy, Debug, Default)]
pub struct TransportNotConfiguredComments;

impl Comments for TransportNotConfiguredComments {
    fn list_comments(
        &self,
        _issue: IssueId,
        _page: Option<PageRequest>,
    ) -> BoxFuture<'_, IssueResult<Page<Comment>>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }

    fn post_comment(&self, _issue: IssueId, _body: String) -> BoxFuture<'_, IssueResult<Comment>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }

    fn delete_comment(&self, _id: CommentId) -> BoxFuture<'_, IssueResult<()>> {
        Box::pin(async { Err(error().transport_not_configured()) })
    }
}

capability!(Projects, Project, ProjectId, TransportNotConfiguredProjects);
capability!(
    Milestones,
    Milestone,
    MilestoneId,
    TransportNotConfiguredMilestones
);
capability!(Cycles, Cycle, CycleId, TransportNotConfiguredCycles);
capability!(Teams, Team, TeamId, TransportNotConfiguredTeams);
capability!(Users, User, UserId, TransportNotConfiguredUsers);
capability!(Labels, Label, LabelId, TransportNotConfiguredLabels);