From 9b28ed87f25b7934a9ff7c36bfd23e9faa12dbea Mon Sep 17 00:00:00 2001 From: Abdo Date: Wed, 15 Oct 2025 00:45:52 +0300 Subject: [PATCH] Fix undo of methods returning OpChanges directly not being handled correctly --- qt/aqt/mediasrv.py | 9 +++++++-- rslib/proto/typescript.rs | 33 +++++++++++++++++++++++---------- ts/lib/generated/post.ts | 12 ++++-------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index b03925ce7..550a47ad3 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -999,8 +999,13 @@ def raw_backend_request(endpoint: str) -> Callable[[], bytes]: def wrapped() -> bytes: output = getattr(aqt.mw.col._backend, f"{endpoint}_raw")(request.data) - if "Has-Op-Changes" in request.headers: - response = OpChangesOnly() + op_changes_type = int(request.headers.get("Anki-Op-Changes", "0")) + if op_changes_type: + response: OpChanges | OpChangesOnly + if op_changes_type == 1: + response = OpChanges() + else: + response = OpChangesOnly() response.ParseFromString(output) def handle_on_main() -> None: diff --git a/rslib/proto/typescript.rs b/rslib/proto/typescript.rs index 61ba27bb8..6572eb949 100644 --- a/rslib/proto/typescript.rs +++ b/rslib/proto/typescript.rs @@ -74,15 +74,16 @@ fn write_ts_method( input_type, output_type, comments, - has_op_changes, + op_changes_type, }: &MethodDetails, out: &mut String, ) { + let op_changes_type = *op_changes_type as u8; let comments = format_comments(comments); writeln!( out, r#"{comments}export async function {method_name}(input: PlainMessage<{input_type}>, options?: PostProtoOptions): Promise<{output_type}> {{ - return await postProto("{method_name}", new {input_type}(input), {output_type}, options, {has_op_changes}); + return await postProto("{method_name}", new {input_type}(input), {output_type}, options, {op_changes_type}); }}"# ).unwrap() } @@ -94,12 +95,20 @@ fn format_comments(comments: &Option) -> String { .unwrap_or_default() } +#[derive(Clone, Copy)] +#[repr(u8)] +enum OpChangesType { + None = 0, + OpChanges = 1, + OpChangesOnly = 2, +} + struct MethodDetails { method_name: String, input_type: String, output_type: String, comments: Option, - has_op_changes: bool, + op_changes_type: OpChangesType, } impl MethodDetails { @@ -108,28 +117,32 @@ impl MethodDetails { let input_type = full_name_to_imported_reference(method.proto.input().full_name()); let output_type = full_name_to_imported_reference(method.proto.output().full_name()); let comments = method.comments.clone(); - let has_op_changes = has_op_changes(&method.proto.output()); + let op_changes_type = get_op_changes_type(&method.proto.output(), true); Self { method_name: name, input_type, output_type, comments, - has_op_changes, + op_changes_type, } } } -fn has_op_changes(message: &MessageDescriptor) -> bool { +fn get_op_changes_type(message: &MessageDescriptor, root: bool) -> OpChangesType { if message.full_name() == "anki.collection.OpChanges" { - true + if root { + OpChangesType::OpChanges + } else { + OpChangesType::OpChangesOnly + } } else if let Some(field) = message.get_field(1) { if let Some(field_message) = field.kind().as_message() { - has_op_changes(field_message) + get_op_changes_type(field_message, false) } else { - false + OpChangesType::None } } else { - false + OpChangesType::None } } diff --git a/ts/lib/generated/post.ts b/ts/lib/generated/post.ts index 1c9f23f3a..8730f631e 100644 --- a/ts/lib/generated/post.ts +++ b/ts/lib/generated/post.ts @@ -11,12 +11,12 @@ export async function postProto( input: { toBinary(): Uint8Array; getType(): { typeName: string } }, outputType: { fromBinary(arr: Uint8Array): T }, options: PostProtoOptions = {}, - hasOpChanges = false, + opChangesType = 0, ): Promise { try { const inputBytes = input.toBinary(); const path = `/_anki/${method}`; - const outputBytes = await postProtoInner(path, inputBytes, hasOpChanges); + const outputBytes = await postProtoInner(path, inputBytes, opChangesType); return outputType.fromBinary(outputBytes); } catch (err) { const { alertOnError = true } = options; @@ -27,14 +27,10 @@ export async function postProto( } } -async function postProtoInner(url: string, body: Uint8Array, hasOpChanges: boolean): Promise { - const headers = { "Content-Type": "application/binary" }; - if (hasOpChanges) { - headers["Has-Op-Changes"] = "1"; - } +async function postProtoInner(url: string, body: Uint8Array, opChangesType: number): Promise { const result = await fetch(url, { method: "POST", - headers, + headers: { "Content-Type": "application/binary", "Anki-Op-Changes": opChangesType.toString() }, body, }); if (!result.ok) {