Onset: version control where nobody waits and nothing gets lost. The on command, its core and onset-server.
| 6 |
| 7 | use serde_json::json; |
| 8 |
| 9 | use crate::app::{App, Session}; |
| 10 | use crate::ids; |
| 11 | use crate::out::{Fail, Out, Report}; |
| 12 |
| 13 | pub fn run(app: &App, target: &str, who: Vec<String>, edit: bool) -> Out { |
| 14 | let s = app.session()?; |
| 15 | let change = app.save(&s)?; |
| 16 | let _ = change; |
| 17 | let is_change = ids::looks_like_change(target) && !app.db.changes_by_prefix(&s.project.id, target)?.is_empty(); |
| 18 | if is_change { |
| 19 | return crate::remote::access_change(app, &s, target, who, edit); |
| 20 | } |
| 21 | let path = normalize(app, &s, target); |
| 22 | let mut patterns = personal(app, &s.project.id)?; |
| 23 | if who.is_empty() { |
| 24 | let mut r = Report::new(); |
| 25 | let mine = patterns.iter().any(|p| p == &path) || crate::ws::Mine::new(&crate::ws::config(&s.root), &patterns).matches(&path); |
| 26 | if mine { |
| 27 | r.line("access", format!("{path}: only you (one of your own files)")); |
| 28 | r.set("who", vec!["me"]); |
| 29 | } else { |
| 30 | let rules = crate::remote::path_rules(app, &s.project)?; |
| 31 | match rules.iter().find(|(p, _)| crate::path_matches(&path, p)) { |
| 32 | Some((p, who)) => { |
| 33 | r.line("access", format!("{path}: {} (rule on {p})", who.join(", "))); |
| 34 | r.set("who", who.clone()); |
| 35 | } |
| 36 | None => { |
| 37 | r.line("access", format!("{path}: everyone with access to the project")); |
| 38 | r.set("who", vec!["team"]); |
| 39 | } |
| 40 | } |
| 41 | } |
| 42 | r.set("path", path); |
| 43 | return Ok(r); |
| 44 | } |
| 45 | if who.len() == 1 && who[0] == "me" { |
| 46 | if patterns.contains(&path) { |
| 47 | let mut r = Report::new(); |
| 48 | r.line("access", format!("{path} is already only yours")); |
| 49 | return Ok(r); |
| 50 | } |
| 51 | let before = patterns.clone(); |
| 52 | patterns.push(path.clone()); |
| 53 | set_personal(app, &s.project.id, &patterns)?; |
| 54 | app.db.add_op(&s.ws.id, "access", &json!({ "target": path, "who": "me", "personal_before": before }))?; |
| 55 | let mut r = Report::new(); |
| 56 | r.line("access", format!("{path} is now only yours: saved and carried to your other workspaces, never landed")); |
| 57 | r.set("path", path).set("who", vec!["me"]); |
| 58 | if crate::app::index_files(&crate::ws::Index::load(&s.root)).contains_key(&r.json["path"].as_str().unwrap_or("").to_string()) { |
| 59 | r.next("main still has it; land a change that removes it from main if others shouldn't see it"); |
| 60 | } |
| 61 | return Ok(r); |
| 62 | } |
| 63 | if !s.project.hosted() { |
| 64 | return Err(Fail::new("bad_usage", "a project on this machine only has you; rules for other people need a hosted project") |
| 65 | .state("nothing changed") |
| 66 | .next("vcs publish <host/owner/name> to put it on a host")); |
| 67 | } |
| 68 | crate::remote::set_path_rule(app, &s, &path, who) |
| 69 | } |
| 70 |
| 71 | fn normalize(app: &App, s: &Session, target: &str) -> String { |
| 72 | let abs = app.cwd.join(target); |
| 73 | let rel = abs.strip_prefix(&s.root).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| target.to_string()); |
| 74 | rel.trim_matches('/').to_string() |
| 75 | } |
| 76 |
| 77 | pub fn personal(app: &App, project: &str) -> anyhow::Result<Vec<String>> { |
| 78 | let text: Option<String> = app |
| 79 | .db |
| 80 | .conn |
| 81 | .query_row("SELECT patterns FROM personal WHERE project = ?1", [project], |r| r.get(0)) |
| 82 | .ok(); |
| 83 | Ok(text.and_then(|t| serde_json::from_str(&t).ok()).unwrap_or_default()) |
| 84 | } |
| 85 |
| 86 | fn set_personal(app: &App, project: &str, patterns: &[String]) -> anyhow::Result<()> { |
| 87 | app.db.conn.execute( |
| 88 | "INSERT OR REPLACE INTO personal (project, patterns) VALUES (?1, ?2)", |
| 89 | rusqlite::params![project, serde_json::to_string(patterns)?], |
| 90 | )?; |
| 91 | Ok(()) |
| 92 | } |
| 93 |
| 94 | pub fn undo(app: &App, s: &Session, data: &serde_json::Value) -> Result<(), Fail> { |
| 95 | if let Some(before) = data.get("personal_before") { |
| 96 | let patterns: Vec<String> = serde_json::from_value(before.clone()).unwrap_or_default(); |
| 97 | set_personal(app, &s.project.id, &patterns)?; |
| 98 | return Ok(()); |
| 99 | } |
| 100 | if let Some(before) = data.get("rule_before") { |
| 101 | let path = data.get("target").and_then(|v| v.as_str()).unwrap_or(""); |
| 102 | let who: Vec<String> = serde_json::from_value(before.clone()).unwrap_or_default(); |
| 103 | crate::remote::set_path_rule(app, s, path, if who.is_empty() { vec!["team".into()] } else { who })?; |
| 104 | return Ok(()); |
| 105 | } |
| 106 | Err(Fail::new("failed", "that access change can't be undone here")) |
| 107 | } |
| 108 |