| 14530604 Andy McCall ↶ |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
use super::{RepoError, Repos}; |
| 7 |
use crate::repos::RepoName; |
| 8 |
use oxy_core::hash::{ChangeHash, KeyId}; |
| 9 |
use oxy_core::identity::{Domain, Identity, SecretKey}; |
| 10 |
use oxy_core::repo::{FileEdit, PushReport, Repo}; |
| 11 |
|
| 12 |
type Result<T> = std::result::Result<T, RepoError>; |
| 13 |
|
| 14 |
fn fail(msg: impl Into<String>) -> RepoError { |
| 15 |
RepoError::Oxy(oxy_core::repo::RepoError::Msg(msg.into())) |
| 16 |
} |
| 17 |
|
| 18 |
|
| 19 |
pub struct Signer { |
| 20 |
|
| 21 |
pub author: Identity, |
| 22 |
pub hub_key: KeyId, |
| 23 |
pub hub_secret: SecretKey, |
| 24 |
pub hub_identity: Identity, |
| 25 |
} |
| 26 |
|
| 27 |
|
| 28 |
pub struct Committed { |
| 29 |
pub change: ChangeHash, |
| 30 |
pub bookmark: String, |
| 31 |
pub old_state: String, |
| 32 |
pub new_state: String, |
| 33 |
pub heads: Vec<ChangeHash>, |
| 34 |
pub report: PushReport, |
| 35 |
} |
| 36 |
|
| 37 |
|
| 38 |
|
| 39 |
pub async fn commit( |
| 40 |
repos: &Repos, |
| 41 |
repo: &RepoName, |
| 42 |
base_bookmark: String, |
| 43 |
bookmark: String, |
| 44 |
edits: Vec<FileEdit>, |
| 45 |
message: String, |
| 46 |
signer: Signer, |
| 47 |
) -> Result<Committed> { |
| 48 |
repos |
| 49 |
.write(repo, move |r: &Repo, _| { |
| 50 |
let view = r.view()?; |
| 51 |
let base_heads = match view.bookmarks.get(&base_bookmark) { |
| 52 |
Some(st) => r.store().get_state(st)?.heads, |
| 53 |
None => return Err(fail(format!("no bookmark {base_bookmark}"))), |
| 54 |
}; |
| 55 |
if bookmark != base_bookmark && view.bookmarks.contains_key(&bookmark) { |
| 56 |
return Err(fail(format!("bookmark {bookmark} exists; pick another name"))); |
| 57 |
} |
| 58 |
let author = signer.author.hash(); |
| 59 |
let change = r.build_change(&base_heads, &edits, &message, author)?; |
| 60 |
let hash = change.hash(); |
| 61 |
let sig = signer.hub_secret.sign(Domain::Change, &signer.hub_key, &hash); |
| 62 |
let mut state = r.load_state(&base_heads)?; |
| 63 |
state.add(change.clone()).map_err(|e| fail(e.to_string()))?; |
| 64 |
let mut candidates = base_heads.clone(); |
| 65 |
candidates.push(hash); |
| 66 |
let heads = state.maximal(&candidates); |
| 67 |
let update = r.host_ref_update(&bookmark, &heads, Some((&signer.hub_key, &signer.hub_secret)))?; |
| 68 |
let (old_state, new_state) = (update.old_state.to_hex(), update.new_state.to_hex()); |
| 69 |
let report = r.apply_hosted(Some((change, vec![sig])), &[signer.hub_identity.clone(), signer.author.clone()], update)?; |
| 70 |
if let Some(refused) = report.results.iter().find(|x| !x.ok) { |
| 71 |
return Err(fail(format!("the repository refused the change: {}", refused.reason))); |
| 72 |
} |
| 73 |
Ok(Committed { change: hash, bookmark, old_state, new_state, heads, report }) |
| 74 |
}) |
| 75 |
.await |
| 76 |
} |
| 77 |
|
| 78 |
|
| 79 |
|
| 80 |
pub async fn move_bookmark(repos: &Repos, repo: &RepoName, bookmark: String, heads: Vec<ChangeHash>, signer: Signer) -> Result<PushReport> { |
| 81 |
repos |
| 82 |
.write(repo, move |r: &Repo, _| { |
| 83 |
let update = r.host_ref_update(&bookmark, &heads, Some((&signer.hub_key, &signer.hub_secret)))?; |
| 84 |
let report = r.apply_hosted(None, &[signer.hub_identity.clone(), signer.author.clone()], update)?; |
| 85 |
if let Some(refused) = report.results.iter().find(|x| !x.ok) { |
| 86 |
return Err(fail(format!("the repository refused the update: {}", refused.reason))); |
| 87 |
} |
| 88 |
Ok(report) |
| 89 |
}) |
| 90 |
.await |
| 91 |
} |