Access and entity words are core constants: access::*, entity::*
"owner"/"edit"/"view" and the entity names "note"/"label"/"attachment"/"preview" were literals at about thirty sites across store, pull and push, including match arms whose spelling had to agree with the rows a different module wrote. models::access and models::entity now name them, and push names its two ops. SQL text keeps its literals; Rust-side comparisons and writes read the constants. DRY pass #2, batch 2, F7 (#5372). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ use super::blobs::BlobStore;
|
||||
use super::client;
|
||||
use super::state;
|
||||
use super::wire;
|
||||
use crate::local::models::{access, entity};
|
||||
use crate::local::{now, Db};
|
||||
|
||||
/// Backstop against a server that never stops saying `has_more`. At the server's
|
||||
@@ -248,9 +249,9 @@ fn upsert_note(conn: &Connection, note: &wire::Note) -> rusqlite::Result<()> {
|
||||
// never changes, and the server's copy is the same value anyway.
|
||||
// A server without `shares` sends no permission, and every note it sends is ours.
|
||||
let permission = match note.permission.as_deref() {
|
||||
Some("edit") => "edit",
|
||||
Some("view") => "view",
|
||||
_ => "owner",
|
||||
Some(access::EDIT) => access::EDIT,
|
||||
Some(access::VIEW) => access::VIEW,
|
||||
_ => access::OWNER,
|
||||
};
|
||||
let (shared_by_id, shared_by_name) = match ¬e.shared_by {
|
||||
Some(by) => (Some(by.id.as_str()), Some(by.display_name.as_str())),
|
||||
@@ -330,7 +331,7 @@ fn replace_attachments(conn: &Connection, note: &wire::Note) -> rusqlite::Result
|
||||
params![note.id],
|
||||
)?;
|
||||
for (index, att) in note.attachments.iter().enumerate() {
|
||||
if removed_here(conn, "attachment", &att.id)? {
|
||||
if removed_here(conn, entity::ATTACHMENT, &att.id)? {
|
||||
continue;
|
||||
}
|
||||
// The server listing an id this device is still waiting to upload means the
|
||||
|
||||
+23
-18
@@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize};
|
||||
use super::blobs::BlobStore;
|
||||
use super::client::{self, UploadError};
|
||||
use super::state;
|
||||
use crate::local::models::{access, entity};
|
||||
use crate::local::Db;
|
||||
|
||||
/// The server rejects a batch larger than this (`MAX_PUSH` in `sync.py`).
|
||||
@@ -61,6 +62,10 @@ impl PushSummary {
|
||||
|
||||
// --- outgoing shapes ---------------------------------------------------------
|
||||
|
||||
/// A change's operation on the wire.
|
||||
const UPSERT: &str = "upsert";
|
||||
const DELETE: &str = "delete";
|
||||
|
||||
/// One entry in the `changes` array. Notes and labels share the envelope; serde skips
|
||||
/// the fields that don't apply, so the server sees exactly the shape docs/sync.md
|
||||
/// describes for each entity.
|
||||
@@ -108,7 +113,7 @@ impl Change {
|
||||
Change {
|
||||
entity,
|
||||
id,
|
||||
op: "delete",
|
||||
op: DELETE,
|
||||
edited_at,
|
||||
..Default::default()
|
||||
}
|
||||
@@ -201,10 +206,10 @@ fn collect_deletes(
|
||||
// Only these exist on the wire; anything else is a bug in a writer, and
|
||||
// shipping it would earn a rejection that no retry could clear.
|
||||
let entity: &'static str = match entity.as_str() {
|
||||
"note" => "note",
|
||||
"label" => "label",
|
||||
"attachment" => "attachment",
|
||||
"preview" => "preview",
|
||||
entity::NOTE => entity::NOTE,
|
||||
entity::LABEL => entity::LABEL,
|
||||
entity::ATTACHMENT => entity::ATTACHMENT,
|
||||
entity::PREVIEW => entity::PREVIEW,
|
||||
_ => continue,
|
||||
};
|
||||
out.push(Change::delete(entity, id, deleted_at));
|
||||
@@ -220,9 +225,9 @@ fn collect_labels(conn: &Connection, out: &mut Vec<Change>, limit: usize) -> rus
|
||||
)?;
|
||||
let rows = stmt.query_map(params![remaining as i64], |r| {
|
||||
Ok(Change {
|
||||
entity: "label",
|
||||
entity: entity::LABEL,
|
||||
id: r.get(0)?,
|
||||
op: "upsert",
|
||||
op: UPSERT,
|
||||
name: Some(r.get(1)?),
|
||||
color: Some(r.get(2)?),
|
||||
edited_at: r.get(3)?,
|
||||
@@ -313,15 +318,15 @@ fn note_change(conn: &Connection, id: &str, shared_state: bool) -> rusqlite::Res
|
||||
// pin, archive and place once they have been changed here, to a server that keeps
|
||||
// them. Trash, reminders and labels are the owner's, and this account's labels
|
||||
// were never on it.
|
||||
if row.permission != "owner" {
|
||||
if row.permission != access::OWNER {
|
||||
let state_at = row.state_at.filter(|_| shared_state);
|
||||
let own = state_at.is_some();
|
||||
return Ok(Change {
|
||||
entity: "note",
|
||||
entity: entity::NOTE,
|
||||
id: id.to_string(),
|
||||
op: "upsert",
|
||||
op: UPSERT,
|
||||
edited_at: row.updated_at,
|
||||
body: (row.permission == "edit").then_some(row.body),
|
||||
body: (row.permission == access::EDIT).then_some(row.body),
|
||||
pinned: own.then_some(row.pinned),
|
||||
archived: own.then_some(row.archived),
|
||||
position: own.then_some(row.position),
|
||||
@@ -341,9 +346,9 @@ fn note_change(conn: &Connection, id: &str, shared_state: bool) -> rusqlite::Res
|
||||
};
|
||||
|
||||
Ok(Change {
|
||||
entity: "note",
|
||||
entity: entity::NOTE,
|
||||
id: id.to_string(),
|
||||
op: "upsert",
|
||||
op: UPSERT,
|
||||
// The local `updated_at` IS the client's edit time, which is what the
|
||||
// server's last-write-wins comparison runs against.
|
||||
edited_at: row.updated_at,
|
||||
@@ -387,7 +392,7 @@ pub fn apply_results(
|
||||
} else {
|
||||
summary.applied += 1;
|
||||
}
|
||||
if change.op == "delete" {
|
||||
if change.op == DELETE {
|
||||
forget_pending_delete(&tx, change)?;
|
||||
}
|
||||
}
|
||||
@@ -403,7 +408,7 @@ pub fn apply_results(
|
||||
// lose to the same comparison forever — and let the next pull bring
|
||||
// the server's copy down.
|
||||
clear_dirty(&tx, change, None)?;
|
||||
if change.op == "delete" {
|
||||
if change.op == DELETE {
|
||||
// Our delete lost to a newer server edit; the note lives on, and
|
||||
// the pull will restore it locally. Drop the tombstone so we
|
||||
// don't keep trying to delete a note the user has since edited.
|
||||
@@ -448,11 +453,11 @@ pub fn apply_results(
|
||||
|
||||
fn clear_dirty(conn: &Connection, change: &Change, revision: Option<i64>) -> rusqlite::Result<()> {
|
||||
// A delete has no local row left to update.
|
||||
if change.op == "delete" {
|
||||
if change.op == DELETE {
|
||||
return Ok(());
|
||||
}
|
||||
let table = match change.entity {
|
||||
"label" => "labels",
|
||||
entity::LABEL => "labels",
|
||||
_ => "notes",
|
||||
};
|
||||
match revision {
|
||||
@@ -469,7 +474,7 @@ fn clear_dirty(conn: &Connection, change: &Change, revision: Option<i64>) -> rus
|
||||
}
|
||||
|
||||
fn forget_pending_delete(conn: &Connection, change: &Change) -> rusqlite::Result<()> {
|
||||
if change.op != "delete" {
|
||||
if change.op != DELETE {
|
||||
return Ok(());
|
||||
}
|
||||
conn.execute(
|
||||
|
||||
Reference in New Issue
Block a user