| 1 | //! The repos service: repository metadata, contents, forks and git access. |
| 2 | //! |
| 3 | //! Each `*Args` struct is the argument of the method of the same name, |
| 4 | //! served at `POST /rpc/<method>`. |
| 5 | |
| 6 | use serde::{Deserialize, Serialize}; |
| 7 | |
| 8 | use crate::{User, Viewer}; |
| 9 | |
| 10 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 11 | #[serde(rename_all = "camelCase")] |
| 12 | pub struct Repo { |
| 13 | pub id: String, |
| 14 | /// The owning user's (later, workspace's) name: the first URL segment. |
| 15 | pub namespace: String, |
| 16 | pub name: String, |
| 17 | pub description: Option<String>, |
| 18 | pub is_private: bool, |
| 19 | pub owner_id: String, |
| 20 | pub default_branch: String, |
| 21 | /// Set when this repo is an attempt's working copy of another repo. |
| 22 | pub fork_of: Option<String>, |
| 23 | /// RFC 3339. |
| 24 | pub created_at: String, |
| 25 | } |
| 26 | |
| 27 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 28 | pub struct RepoPath { |
| 29 | pub namespace: String, |
| 30 | pub name: String, |
| 31 | } |
| 32 | |
| 33 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 34 | pub struct Signature { |
| 35 | pub name: String, |
| 36 | pub email: String, |
| 37 | } |
| 38 | |
| 39 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 40 | #[serde(rename_all = "camelCase")] |
| 41 | pub struct Commit { |
| 42 | pub hash: String, |
| 43 | pub tree_hash: String, |
| 44 | pub message: String, |
| 45 | pub author: Signature, |
| 46 | pub parents: Vec<String>, |
| 47 | /// RFC 3339. |
| 48 | pub authored_at: String, |
| 49 | } |
| 50 | |
| 51 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] |
| 52 | #[serde(rename_all = "lowercase")] |
| 53 | pub enum EntryKind { |
| 54 | Tree, |
| 55 | Blob, |
| 56 | Symlink, |
| 57 | Gitlink, |
| 58 | Exec, |
| 59 | } |
| 60 | |
| 61 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 62 | pub struct TreeEntry { |
| 63 | pub name: String, |
| 64 | pub hash: String, |
| 65 | pub kind: EntryKind, |
| 66 | } |
| 67 | |
| 68 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 69 | pub struct Readme { |
| 70 | pub name: String, |
| 71 | /// Null when the file is binary or too large to show. |
| 72 | pub text: Option<String>, |
| 73 | } |
| 74 | |
| 75 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 76 | pub struct TreeView { |
| 77 | pub repo: Repo, |
| 78 | #[serde(rename = "ref")] |
| 79 | pub git_ref: String, |
| 80 | pub path: String, |
| 81 | /// Null when the repo has no commits yet. |
| 82 | pub head: Option<Commit>, |
| 83 | pub entries: Vec<TreeEntry>, |
| 84 | pub readme: Option<Readme>, |
| 85 | } |
| 86 | |
| 87 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 88 | pub struct BlobView { |
| 89 | pub repo: Repo, |
| 90 | #[serde(rename = "ref")] |
| 91 | pub git_ref: String, |
| 92 | pub path: String, |
| 93 | pub size: u64, |
| 94 | /// Null when the file is binary or too large to show. |
| 95 | pub text: Option<String>, |
| 96 | } |
| 97 | |
| 98 | /// A git remote and a short-lived credential for it. |
| 99 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 100 | pub struct GitAccess { |
| 101 | pub remote: String, |
| 102 | pub token: String, |
| 103 | } |
| 104 | |
| 105 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] |
| 106 | pub enum GitService { |
| 107 | #[serde(rename = "git-upload-pack")] |
| 108 | UploadPack, |
| 109 | #[serde(rename = "git-receive-pack")] |
| 110 | ReceivePack, |
| 111 | } |
| 112 | |
| 113 | /// The result of landing an attempt. |
| 114 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 115 | pub struct Landed { |
| 116 | /// The commit the branch points to now. |
| 117 | pub commit: String, |
| 118 | /// The commit it pointed to before, if it had one. Comparing against |
| 119 | /// this shows what the attempt changed. |
| 120 | pub previous: Option<String>, |
| 121 | } |
| 122 | |
| 123 | /// `get`. Returns `Outcome<Repo>`. |
| 124 | #[derive(Debug, Serialize, Deserialize)] |
| 125 | pub struct GetArgs { |
| 126 | pub path: RepoPath, |
| 127 | pub viewer: Viewer, |
| 128 | } |
| 129 | |
| 130 | /// `get_by_id`. Returns `Outcome<Repo>`. |
| 131 | #[derive(Debug, Serialize, Deserialize)] |
| 132 | pub struct GetByIdArgs { |
| 133 | pub id: String, |
| 134 | pub viewer: Viewer, |
| 135 | } |
| 136 | |
| 137 | /// `list`: repos the viewer may see, newest first. Returns `Vec<Repo>`. |
| 138 | #[derive(Debug, Default, Serialize, Deserialize)] |
| 139 | pub struct ListArgs { |
| 140 | pub viewer: Viewer, |
| 141 | #[serde(default)] |
| 142 | pub query: Option<String>, |
| 143 | #[serde(default)] |
| 144 | pub namespace: Option<String>, |
| 145 | } |
| 146 | |
| 147 | /// `create`. Returns `Outcome<Repo>`. |
| 148 | #[derive(Debug, Serialize, Deserialize)] |
| 149 | #[serde(rename_all = "camelCase")] |
| 150 | pub struct CreateArgs { |
| 151 | pub owner: User, |
| 152 | pub name: String, |
| 153 | #[serde(default)] |
| 154 | pub description: Option<String>, |
| 155 | #[serde(default)] |
| 156 | pub is_private: bool, |
| 157 | } |
| 158 | |
| 159 | /// `tree`. Returns `Outcome<TreeView>`. |
| 160 | #[derive(Debug, Serialize, Deserialize)] |
| 161 | #[serde(rename_all = "camelCase")] |
| 162 | pub struct TreeArgs { |
| 163 | pub path: RepoPath, |
| 164 | pub viewer: Viewer, |
| 165 | /// The default branch when absent. |
| 166 | #[serde(default, rename = "ref")] |
| 167 | pub git_ref: Option<String>, |
| 168 | #[serde(default)] |
| 169 | pub tree_path: String, |
| 170 | } |
| 171 | |
| 172 | /// `blob`. Returns `Outcome<BlobView>`. |
| 173 | #[derive(Debug, Serialize, Deserialize)] |
| 174 | #[serde(rename_all = "camelCase")] |
| 175 | pub struct BlobArgs { |
| 176 | pub path: RepoPath, |
| 177 | pub viewer: Viewer, |
| 178 | #[serde(rename = "ref")] |
| 179 | pub git_ref: String, |
| 180 | pub file_path: String, |
| 181 | } |
| 182 | |
| 183 | /// `log`. Returns `Outcome<Vec<Commit>>`. |
| 184 | #[derive(Debug, Serialize, Deserialize)] |
| 185 | pub struct LogArgs { |
| 186 | pub path: RepoPath, |
| 187 | pub viewer: Viewer, |
| 188 | #[serde(default, rename = "ref")] |
| 189 | pub git_ref: Option<String>, |
| 190 | pub limit: u32, |
| 191 | } |
| 192 | |
| 193 | /// `fork_for_attempt`: a copy-on-write copy of the source repo, hidden from |
| 194 | /// listings, for one attempt to work in. Returns `Outcome<Repo>`. |
| 195 | #[derive(Debug, Serialize, Deserialize)] |
| 196 | #[serde(rename_all = "camelCase")] |
| 197 | pub struct ForkArgs { |
| 198 | pub source_id: String, |
| 199 | pub attempt_id: String, |
| 200 | pub actor: User, |
| 201 | } |
| 202 | |
| 203 | /// `git_access`: authorizes a git operation and says where to send it. |
| 204 | /// Pushing to a repo that does not exist creates it in the pusher's own |
| 205 | /// namespace. Returns `Outcome<GitAccess>`. |
| 206 | #[derive(Debug, Serialize, Deserialize)] |
| 207 | pub struct GitAccessArgs { |
| 208 | pub path: RepoPath, |
| 209 | pub viewer: Viewer, |
| 210 | pub service: GitService, |
| 211 | } |
| 212 | |
| 213 | /// `land`: moves the default branch of the repo a fork came from to the |
| 214 | /// fork's head. Refused with `conflict` when the fork is behind, since |
| 215 | /// that would discard commits. Returns `Outcome<Landed>`. |
| 216 | #[derive(Debug, Serialize, Deserialize)] |
| 217 | #[serde(rename_all = "camelCase")] |
| 218 | pub struct LandArgs { |
| 219 | pub fork_id: String, |
| 220 | pub actor: User, |
| 221 | } |
| 222 | |
| 223 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] |
| 224 | #[serde(rename_all = "lowercase")] |
| 225 | pub enum FileStatus { |
| 226 | Added, |
| 227 | Modified, |
| 228 | Deleted, |
| 229 | } |
| 230 | |
| 231 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] |
| 232 | #[serde(rename_all = "lowercase")] |
| 233 | pub enum LineKind { |
| 234 | /// Unchanged, shown for context. |
| 235 | Context, |
| 236 | Add, |
| 237 | Delete, |
| 238 | } |
| 239 | |
| 240 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 241 | pub struct DiffLine { |
| 242 | pub kind: LineKind, |
| 243 | /// Line number in the old file; absent for added lines. |
| 244 | pub old: Option<u32>, |
| 245 | /// Line number in the new file; absent for deleted lines. |
| 246 | pub new: Option<u32>, |
| 247 | pub text: String, |
| 248 | } |
| 249 | |
| 250 | /// A run of changed lines with their surrounding context. |
| 251 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 252 | pub struct Hunk { |
| 253 | pub lines: Vec<DiffLine>, |
| 254 | } |
| 255 | |
| 256 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 257 | pub struct FileDiff { |
| 258 | pub path: String, |
| 259 | pub status: FileStatus, |
| 260 | pub additions: u32, |
| 261 | pub deletions: u32, |
| 262 | /// True when the file is binary or too large, so no lines are shown. |
| 263 | pub binary: bool, |
| 264 | pub hunks: Vec<Hunk>, |
| 265 | } |
| 266 | |
| 267 | /// What changed between two commits. |
| 268 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 269 | pub struct Comparison { |
| 270 | /// Null when the head has no earlier commit to compare against. |
| 271 | pub base: Option<String>, |
| 272 | pub head: String, |
| 273 | pub files: Vec<FileDiff>, |
| 274 | /// True when the change was too large to return in full. |
| 275 | pub truncated: bool, |
| 276 | } |
| 277 | |
| 278 | /// `compare`: what a repository's head changes. |
| 279 | /// |
| 280 | /// With no `base`, an attempt's fork is compared against the point where it |
| 281 | /// and the repository it came from last agreed, and any other repository |
| 282 | /// against its head's parent. Returns `Outcome<Comparison>`. |
| 283 | #[derive(Debug, Serialize, Deserialize)] |
| 284 | #[serde(rename_all = "camelCase")] |
| 285 | pub struct CompareArgs { |
| 286 | pub repo_id: String, |
| 287 | pub viewer: Viewer, |
| 288 | #[serde(default)] |
| 289 | pub base: Option<String>, |
| 290 | } |