gitlab-butler-lib 0.9.1

Support library for [`gitlab-butler`](https://crates.io/crates/gitlab-butler) Gitlab cli to automate the boring stuff.
Documentation
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug)]
pub struct User {
    pub id: usize,
    pub username: String,
}

#[derive(Deserialize, Debug)]
pub struct Pipeline {
    pub id: usize,
    pub sha: String,
    #[serde(rename = "ref")]
    pub branch: String,
    pub status: String,
    pub updated_at: DateTime<Utc>,
}

#[derive(Deserialize, Debug)]
pub struct Emoji {
    pub id: usize,
    pub name: String,
    pub user: User,
    pub awardable_id: usize,
    pub awardable_type: String,
}

/// Merge request message from GitLab
///
/// See [api/merge_requests](https://docs.gitlab.com/ee/api/merge_requests.html)
#[derive(Deserialize, Debug)]
pub struct MergeRequest {
    pub title: String,
    pub description: Option<String>,
    pub project_id: usize,
    pub iid: usize, // internal id
    pub work_in_progress: bool,
    pub author: User,
    pub merge_status: String,
    pub source_branch: String,
    pub target_branch: String,
    pub sha: String,
    pub references: Reference,
    pub web_url: String,
    pub main_issue: Option<Issue>,
}

#[derive(Serialize, Debug, Default)]
pub struct CreateMergeRequest {
    #[serde(rename = "id")]
    pub project_id: String,
    pub source_branch: String,
    pub target_branch: String,
    pub title: String,
    pub milestone_id: Option<usize>,
    pub description: Option<String>,
    pub assignee_ids: Vec<usize>,
    /// Comma separated
    pub labels: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct MergeRequestDetails {
    pub title: String,
    pub project_id: usize,
    pub iid: usize, // internal id
    pub work_in_progress: bool,
    pub author: User,
    pub merge_status: String,
    pub source_branch: String,
    pub target_branch: String,
    pub sha: Option<String>,
    // should be there with `include_rebase_in_progress=true`, but sometime is missing
    pub rebase_in_progress: Option<bool>,
    pub merge_error: Option<String>,
    pub pipeline: Option<Pipeline>,
    // should be there with `include_diverged_commits_count=true`, but perhaps 0 is not shown?
    pub diverged_commits_count: Option<i32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum MergeRequestState {
    Opened,
    Closed,
    Merged,
}

impl MergeRequestState {
    pub fn name(&self) -> &str {
        match self {
            MergeRequestState::Opened => "opened",
            MergeRequestState::Closed => "closed",
            MergeRequestState::Merged => "merged",
        }
    }
}

impl std::str::FromStr for MergeRequestState {
    type Err = String;
    fn from_str(state: &str) -> Result<Self, Self::Err> {
        match state {
            "opened" => Ok(MergeRequestState::Opened),
            "closed" => Ok(MergeRequestState::Closed),
            "merged" => Ok(MergeRequestState::Merged),
            other_state => Err(format!(
                "Invalid state, {} not in [opened, closed, merged]",
                other_state
            )
            .to_string()),
        }
    }
}

#[derive(Deserialize, Debug)]
pub struct ProjectMilestone {
    pub title: String,
    pub id: usize,
    pub iid: usize,
    pub project_id: Option<usize>,
    pub group_id: Option<usize>,
    pub description: String,
    pub due_date: Option<String>,
    pub start_date: Option<String>,
    pub state: String,
}

#[derive(Serialize, Debug)]
pub struct CreateProjectMilestone {
    #[serde(rename = "id")]
    pub project_id: usize,
    pub title: String,
    pub description: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Issue {
    pub title: String,
    pub iid: usize, // internal id
    pub state: IssueState,
    pub description: Option<String>,
    pub author: User,
    pub milestone: Option<ProjectMilestone>,
    pub project_id: usize,
    pub assignees: Vec<User>,
    pub web_url: String,
    pub weight: Option<usize>,
    pub labels: Vec<String>,
    pub references: Reference,
}

#[derive(Deserialize, Debug)]
pub struct Reference {
    pub short: String,
    pub relative: String,
    pub full: String,
}

#[derive(Serialize, Debug, Default)]
pub struct CreateIssue {
    #[serde(rename = "id")]
    pub project_id: String,
    pub title: String,
    pub description: Option<String>,
    pub milestone_id: Option<usize>,
    pub assignee_ids: Vec<usize>,
    pub weight: Option<usize>,
    /// Comma separated
    pub labels: Option<String>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum IssueState {
    All,
    Opened,
    Closed,
}

impl IssueState {
    pub fn name(&self) -> &str {
        match self {
            IssueState::All => "all",
            IssueState::Closed => "closed",
            IssueState::Opened => "opened",
        }
    }
}

impl std::str::FromStr for IssueState {
    type Err = String;
    fn from_str(state: &str) -> Result<Self, Self::Err> {
        match state {
            "all" => Ok(IssueState::All),
            "closed" => Ok(IssueState::Closed),
            "opened" => Ok(IssueState::Opened),
            other_state => Err(format!(
                "Invalid state, {} not in [all, opened, closed]",
                other_state
            )
            .to_string()),
        }
    }
}

#[derive(Serialize, Debug)]
pub struct RebaseBranch {
    pub id: usize,
    pub merge_request_iid: usize,
}

impl<'a> From<&'a MergeRequest> for RebaseBranch {
    fn from(mr: &'a MergeRequest) -> RebaseBranch {
        RebaseBranch {
            id: mr.project_id,
            merge_request_iid: mr.iid,
        }
    }
}

#[derive(Serialize, Debug)]
pub struct RemoveSourceBranch {
    pub id: usize,
    pub merge_request_iid: usize,
    pub remove_source_branch: bool,
}

impl<'a> From<&'a MergeRequest> for RemoveSourceBranch {
    fn from(mr: &'a MergeRequest) -> RemoveSourceBranch {
        RemoveSourceBranch {
            id: mr.project_id,
            merge_request_iid: mr.iid,
            remove_source_branch: true,
        }
    }
}

#[derive(Serialize, Debug)]
pub struct ListPipelines {
    pub id: usize,
    pub merge_request_iid: usize,
}

impl<'a> From<&'a MergeRequest> for ListPipelines {
    fn from(mr: &'a MergeRequest) -> ListPipelines {
        ListPipelines {
            id: mr.project_id,
            merge_request_iid: mr.iid,
        }
    }
}

#[derive(Serialize, Debug)]
pub struct AcceptMergeRequest {
    pub id: usize,
    pub merge_request_iid: usize,
    pub merge_when_pipeline_succeeds: bool,
    pub should_remove_source_branch: bool,
    pub sha: String,
}

impl<'a> From<&'a MergeRequest> for AcceptMergeRequest {
    fn from(mr: &'a MergeRequest) -> AcceptMergeRequest {
        AcceptMergeRequest {
            id: mr.project_id,
            merge_request_iid: mr.iid,
            merge_when_pipeline_succeeds: true,
            should_remove_source_branch: true,
            sha: mr.sha.clone(),
        }
    }
}

#[derive(Serialize, Debug)]
pub struct GetMergeRequestDetails {
    pub id: usize,
    pub merge_request_iid: usize,
    pub include_diverged_commits_count: bool,
    pub include_rebase_in_progress: bool,
}

impl GetMergeRequestDetails {
    pub fn new(project_id: usize, merge_request_iid: usize) -> Self {
        GetMergeRequestDetails {
            id: project_id,
            merge_request_iid,
            include_diverged_commits_count: true,
            include_rebase_in_progress: true,
        }
    }
}

impl<'a> From<&'a MergeRequest> for GetMergeRequestDetails {
    fn from(mr: &'a MergeRequest) -> GetMergeRequestDetails {
        GetMergeRequestDetails::new(mr.project_id, mr.iid)
    }
}

#[derive(Serialize, Debug)]
pub struct AwardEmoji {
    pub id: usize,
    pub awardable_id: usize,
}

impl<'a> From<&'a MergeRequest> for AwardEmoji {
    fn from(mr: &'a MergeRequest) -> AwardEmoji {
        AwardEmoji {
            id: mr.project_id,
            awardable_id: mr.iid,
        }
    }
}

#[derive(Deserialize, Debug)]
pub struct Project {
    pub id: usize,
    pub description: Option<String>,
    pub ssh_url_to_repo: String,
    pub http_url_to_repo: String,
    pub web_url: String,
    pub name: String,
    pub name_with_namespace: String,
    pub path: String,
    pub path_with_namespace: String,
    pub avatar_url: Option<String>,
}