//! The views answers share, as `dev.knotless.defs` defines them: of //! repositories, commits, checks and diffs. use super::*; /// `dev.knotless.defs#repoViewDetailed`: a repository, with what its pages /// show above themselves. #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct RepoViewDetailed { pub uri: String, pub owner: ActorView, pub name: String, /// Its key, which a rename keeps, as its record holds it. #[serde(skip_serializing_if = "Option::is_none")] pub key: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, /// The links its pages show, in order, each an http or https address. #[serde(skip_serializing_if = "Vec::is_empty")] pub links: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub fork_of: Option, /// The default branch: empty, for a repository never pushed. pub default_branch: String, pub open_pulls: usize, pub open_issues: usize, /// Whether its pages lead to its merge queue: a branch's rule takes /// pulls through it, or a pull waits in it still. #[serde(skip_serializing_if = "std::ops::Not::not")] pub merge_queue: bool, /// The path of the picture its pages unfurl into. pub card: String, #[serde(skip_serializing_if = "Option::is_none")] pub star_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub fork_count: Option, /// What it publishes, in brief: none, with no releases. #[serde(skip_serializing_if = "Option::is_none")] pub releases: Option, #[serde(skip_serializing_if = "Option::is_none")] pub viewer: Option, /// Whether it is private: in a space, shown only to its members. #[serde(skip_serializing_if = "std::ops::Not::not")] pub private: bool, /// When its owner archived it, making it read only. #[serde(skip_serializing_if = "Option::is_none")] pub archived_at: Option, /// What it is about, as its record names them. #[serde(skip_serializing_if = "Vec::is_empty")] pub topics: Vec, /// The first of those who may push to it besides its owner. #[serde(skip_serializing_if = "Vec::is_empty")] pub collaborators: Vec, /// How many may push to it besides its owner: none, absent. #[serde(skip_serializing_if = "Option::is_none")] pub collaborator_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub site: Option, } /// `dev.knotless.defs#repoViewerState`. /// A repository's website: where it is served, and what it was deployed /// from. #[derive(Serialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SiteView { pub url: String, #[serde(skip_serializing_if = "Option::is_none")] pub commit: Option, #[serde(skip_serializing_if = "Option::is_none")] pub check: Option, pub deployed_at: String, } #[derive(Serialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RepoViewerState { #[serde(skip_serializing_if = "Option::is_none")] pub star: Option, /// Where the owner offered to move it, when that is to the asker. #[serde(skip_serializing_if = "Option::is_none")] pub transfer: Option, /// The labels it defines that the asker may put on its pulls and issues. #[serde(skip_serializing_if = "Vec::is_empty")] pub labels: Vec, /// What the asker chose to hear of it: `all`, `releases` or `ignore`. #[serde(skip_serializing_if = "Option::is_none")] pub subscription: Option, /// Whether the asker may change its settings: its owner, or an admin /// who joined the organization that owns it. #[serde(skip_serializing_if = "std::ops::Not::not")] pub admin: bool, } /// `dev.knotless.defs#commitView`. #[derive(Serialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct CommitView { pub sha: String, pub parents: Vec, pub author: CommitAuthorView, /// Who its message's `Co-authored-by` trailers name. pub coauthors: Vec, /// Who committed it, when that is someone other than its author. #[serde(skip_serializing_if = "Option::is_none")] pub committer: Option, /// When its author wrote it, which a rebase or a squash keeps. pub date: String, /// When it was committed: what history runs in, and the pages go by. pub committed_at: String, /// Its message, less the line naming the pull it merged, if it did. pub message: String, /// The pull its message's last line names, as a squash merge records /// it, when that is a pull on this repository. #[serde(skip_serializing_if = "Option::is_none")] pub pull: Option, } /// `dev.knotless.defs#commitRefView`. #[derive(Serialize, Clone, Debug, PartialEq)] pub struct CommitRefView { pub sha: String, } /// `dev.knotless.defs#commitAuthorView`: who wrote a commit or helped to, /// by the name git gives, and by account when that is one of the /// repository's own people whose address it is (`knotless::emails`). #[derive(Serialize, Clone, Debug, PartialEq)] pub struct CommitAuthorView { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub actor: Option, /// The coding agent whose address it is, when the pages know its face. #[serde(skip_serializing_if = "Option::is_none")] pub agent: Option<&'static str>, } /// `dev.knotless.defs#itemViewBasic`: a pull or an issue, by its record /// and its title. #[derive(Serialize, Clone, Debug, PartialEq)] pub struct ItemViewBasic { pub uri: String, pub title: String, } /// `dev.knotless.defs#checkView`: the newest run of one workflow, as a /// runner the repository names in its record reported it. #[derive(Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct CheckView { pub uri: String, /// The repository it is of, whose pages show it. pub repo: RepoViewBasic, pub workflow: String, pub status: String, pub commit: String, pub started_at: String, #[serde(skip_serializing_if = "Option::is_none")] pub finished_at: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, /// For a `success` that ran nothing, as the change touched none of the /// paths its workflow names: why. #[serde(skip_serializing_if = "Option::is_none")] pub skipped: Option, /// The pictures it made to be looked at, in order. pub images: Vec, } /// `dev.knotless.defs#checkImageView`: a picture a check made, such as /// screenshots of the pages a change moved. #[derive(Clone, Serialize)] pub struct CheckImageView { /// What it shows. pub alt: String, /// Where this server serves it, as a path. pub url: String, } /// `dev.knotless.defs#fileDiffView`: one file of a diff, or why its rows /// are not shown. #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct FileDiffView { pub old: String, pub new: String, /// `added`, `deleted`, `renamed` or `modified`. pub status: String, pub added: usize, pub removed: usize, #[serde(skip_serializing_if = "Option::is_none")] pub note: Option, #[serde(skip_serializing_if = "Option::is_none")] pub ranges: Option, pub rows: Vec, /// Where its rows are left out but would be shown, how many there are, /// and of them how many are hunk headers: the room a page keeps for /// them until they come. #[serde(skip_serializing_if = "Option::is_none")] pub row_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub hunk_count: Option, } /// `dev.knotless.defs#diffRowView`: one row of a file's diff, a hunk /// header, a note, or a line with its old and new numbers. #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct DiffRowView { /// `h`, `x`, `a`, `d`, or empty for context. pub kind: String, pub line: bool, #[serde(skip_serializing_if = "Option::is_none")] pub old: Option, #[serde(skip_serializing_if = "Option::is_none")] pub new: Option, pub text: String, } /// `dev.knotless.defs#languageView`: one language a repository is written /// in, its share in tenths of a percent, as a lexicon has no fractions. #[derive(Serialize)] pub struct LanguageView { pub name: String, pub color: String, pub bytes: u64, pub permille: u32, } impl From for LanguageView { fn from(l: Language) -> LanguageView { LanguageView { name: l.name, color: l.color, bytes: l.bytes, permille: (l.percent * 10.0).round() as u32, } } } /// `dev.knotless.repo#label`: a label, as the repository defines it. #[derive(Serialize, Clone, Debug, PartialEq)] pub struct LabelView { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, #[serde(skip_serializing_if = "std::ops::Not::not")] pub exclusive: bool, } /// `label` as `dev.knotless.repo#label` shows it. pub fn label_view(l: &knotless::records::Label) -> LabelView { LabelView { name: l.name.clone(), description: l.description.clone(), color: l.color.clone(), exclusive: l.exclusive, } } /// `dev.knotless.defs#itemView`: a pull or an issue in a list of them. #[derive(Serialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ItemView { pub uri: String, /// `pull` or `issue`. pub kind: String, pub title: String, /// `open`, `closed` or `merged`. pub state: String, pub author: ActorView, pub created_at: String, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option, pub comments: usize, /// A pull its author has not marked ready. #[serde(skip_serializing_if = "std::ops::Not::not")] pub draft: bool, /// Those asked to review a pull: by its author, then by the owner or /// a collaborator, then the required reviewers its base's rule names. #[serde(skip_serializing_if = "Vec::is_empty")] pub reviewers: Vec, /// Of `reviewers`, those its base's rule requires: asked of every /// pull into it, and stopped only by changing the rule. #[serde(skip_serializing_if = "Vec::is_empty")] pub required_reviewers: Vec, /// Those the owner or a collaborator assigns it to. #[serde(skip_serializing_if = "Vec::is_empty")] pub assignees: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] pub labels: Vec, } /// `item`, by its author's account, carrying the labels `record` defines. pub async fn item_view(app: &App, record: &RepoRecord, item: &Item) -> ItemView { let labels = item.labels.iter().filter_map(|name| record.label(name)); let labels = labels.map(label_view); let base = item.base.as_deref().unwrap_or_default(); let required = record .required_reviewers(base) .filter(|r| item.reviewers.iter().any(|d| d == r)); ItemView { uri: item.uri.clone(), kind: item.kind.clone(), title: item.title.clone(), state: item.state.clone(), author: actor(app, &item.author).await, created_at: item.created_at.clone(), updated_at: item.updated_at.clone(), comments: item.comments, draft: item.draft, reviewers: futures::future::join_all(item.reviewers.iter().map(|r| actor(app, r))).await, required_reviewers: futures::future::join_all(required.map(|r| actor(app, r))).await, assignees: futures::future::join_all(item.assignees.iter().map(|a| actor(app, a))).await, labels: labels.collect(), } } /// `items`, each by `item_view`. pub async fn item_views<'a>( app: &App, record: &RepoRecord, items: impl IntoIterator, ) -> Vec { let views = items.into_iter().map(|i| item_view(app, record, i)); futures::future::join_all(views).await } /// `dev.knotless.defs#commentView`: something said on a pull or an issue. #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct CommentView { /// Its record, for its author to change or take back. pub uri: String, pub author: ActorView, pub created_at: String, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option, /// What it says, as its author wrote it, to change it from. #[serde(skip_serializing_if = "Option::is_none")] pub body: Option, pub html: String, /// `approve` or `changes`, for a review. #[serde(skip_serializing_if = "Option::is_none")] pub verdict: Option, /// The head a review looked at, or the commit a line comment's lines /// are in. #[serde(skip_serializing_if = "Option::is_none")] pub commit: Option, /// For a comment on a pull's lines, which, as they stand now. #[serde(skip_serializing_if = "Option::is_none")] pub line: Option, /// The comment starting the thread it answers. #[serde(skip_serializing_if = "Option::is_none")] pub reply: Option, /// The review it was sent with. #[serde(skip_serializing_if = "Option::is_none")] pub review: Option, /// The review it dismissed, from the owner or a collaborator. #[serde(skip_serializing_if = "Option::is_none")] pub dismisses: Option, /// Whether the thread it starts is resolved. #[serde(skip_serializing_if = "std::ops::Not::not")] pub resolved: bool, #[serde(skip_serializing_if = "Vec::is_empty")] pub reactions: Vec, } /// `dev.knotless.defs#lineView`: the lines of a pull's diff a comment is /// on. #[derive(Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct LineView { pub path: String, #[serde(skip_serializing_if = "Option::is_none")] pub side: Option, pub line: u32, #[serde(skip_serializing_if = "Option::is_none")] pub start_line: Option, /// Whether the file on that side has changed since. pub outdated: bool, /// The lines as they were, and a few before one alone. #[serde(skip_serializing_if = "Vec::is_empty")] pub lines: Vec, } /// `dev.knotless.defs#lineText`: one line of a file. #[derive(Serialize, Clone)] pub struct LineTextView { pub number: u32, pub text: String, } /// `dev.knotless.defs#reviewView`: where one reviewer stands on a pull. #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct ReviewView { pub reviewer: ActorView, pub verdict: String, /// The comment that reviewed it. pub uri: String, #[serde(skip_serializing_if = "Option::is_none")] pub commit: Option, /// Whether the pull has moved past the head it looked at. pub outdated: bool, /// Whether the reviewer is the repository's owner or a collaborator. pub maintainer: bool, /// Whether the owner or a collaborator dismissed it. #[serde(skip_serializing_if = "std::ops::Not::not")] pub dismissed: bool, /// Whether the rule protecting the pull's base names the reviewer as /// a gate. #[serde(skip_serializing_if = "std::ops::Not::not")] pub gate: bool, } /// `dev.knotless.defs#pullHeadView`: the branch a pull brings in. #[derive(Serialize)] pub struct PullHeadView { #[serde(skip_serializing_if = "Option::is_none")] pub repo: Option, pub branch: String, } /// `dev.knotless.defs#changesView`: what a pull brings in, its commits /// oldest first. #[derive(Serialize)] pub struct ChangesView { /// The repository whose commit pages show them. pub repo: RepoViewBasic, pub commits: Vec, pub files: Vec, /// The head an unmerged pull is at, whose checks alone it shows. #[serde(skip_serializing_if = "Option::is_none")] pub head: Option, }