From 46722d792d2bb389767cd026a903ed41afbabda9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 19 Jun 2023 22:06:50 +1000 Subject: [PATCH] Use a separate service definition for backend-only services Realised this is clearer than tagging each method individually. The enum has been retained for the case where we want to implement the backend method separately from the collection one. --- proto/anki/ankidroid.proto | 22 ++++++++------------- proto/anki/card_rendering.proto | 2 +- proto/anki/codegen.proto | 13 +++++------- proto/anki/collection.proto | 20 +++++++------------ proto/anki/i18n.proto | 6 +++--- proto/anki/import_export.proto | 15 +++++++------- proto/anki/sync.proto | 35 +++++++++------------------------ rslib/rust_interface.rs | 17 ++++++++++------ 8 files changed, 51 insertions(+), 79 deletions(-) diff --git a/proto/anki/ankidroid.proto b/proto/anki/ankidroid.proto index 38c2b354b..cb9db1ecd 100644 --- a/proto/anki/ankidroid.proto +++ b/proto/anki/ankidroid.proto @@ -4,18 +4,10 @@ option java_multiple_files = true; import "anki/generic.proto"; import "anki/scheduler.proto"; -import "anki/codegen.proto"; package anki.ankidroid; service AnkidroidService { - rpc SchedTimingTodayLegacy(SchedTimingTodayLegacyRequest) - returns (scheduler.SchedTimingTodayResponse) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc LocalMinutesWestLegacy(generic.Int64) returns (generic.Int32) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } rpc RunDbCommand(generic.Json) returns (generic.Json); rpc RunDbCommandProto(generic.Json) returns (DbResponse); rpc InsertForId(generic.Json) returns (generic.Int64); @@ -23,15 +15,17 @@ service AnkidroidService { rpc FlushAllQueries(generic.Empty) returns (generic.Empty); rpc FlushQuery(generic.Int32) returns (generic.Empty); rpc GetNextResultPage(GetNextResultPageRequest) returns (DbResponse); - rpc SetPageSize(generic.Int64) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } rpc GetColumnNamesFromQuery(generic.String) returns (generic.StringList); rpc GetActiveSequenceNumbers(generic.Empty) returns (GetActiveSequenceNumbersResponse); - rpc DebugProduceError(generic.String) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } +} + +service BackendAnkidroidService { + rpc SchedTimingTodayLegacy(SchedTimingTodayLegacyRequest) + returns (scheduler.SchedTimingTodayResponse); + rpc LocalMinutesWestLegacy(generic.Int64) returns (generic.Int32); + rpc SetPageSize(generic.Int64) returns (generic.Empty); + rpc DebugProduceError(generic.String) returns (generic.Empty); } message DebugActiveDatabaseSequenceNumbersResponse { diff --git a/proto/anki/card_rendering.proto b/proto/anki/card_rendering.proto index ae5886c46..866e2e9ae 100644 --- a/proto/anki/card_rendering.proto +++ b/proto/anki/card_rendering.proto @@ -28,7 +28,7 @@ service CardRenderingService { rpc DecodeIriPaths(generic.String) returns (generic.String); rpc StripHtml(StripHtmlRequest) returns (generic.String) { // a bunch of our unit tests access this without a collection - option (codegen.rust_methods) = RUST_METHODS_COLLECTION_AND_MANUAL_BACKEND; + option (codegen.backend_method) = BACKEND_METHOD_IMPLEMENT; } rpc CompareAnswer(CompareAnswerRequest) returns (generic.String); rpc ExtractClozeForTyping(ExtractClozeForTypingRequest) diff --git a/proto/anki/codegen.proto b/proto/anki/codegen.proto index a25591d3c..51feed224 100644 --- a/proto/anki/codegen.proto +++ b/proto/anki/codegen.proto @@ -8,21 +8,18 @@ package anki.codegen; import "google/protobuf/descriptor.proto"; extend google.protobuf.MethodOptions { - RustMethods rust_methods = 50000; + BackendMethod backend_method = 50000; } message MethodOptions { - RustMethods rust_methods = 50000; + BackendMethod backend_method = 50000; } -enum RustMethods { +enum BackendMethod { /// Used for typical collection-based operations. We must implement the // method on Collection. The same method is automatically implemented on // Backend, which forwards to Collection. - RUST_METHODS_COLLECTION_AND_AUTO_BACKEND = 0; - /// Method only makes sense on the backend (eg one that closes and reopens - /// the collection). Backend method needs to be implemented. - RUST_METHODS_BACKEND_ONLY = 1; + BACKEND_METHOD_DELEGATE = 0; /// Both the backend and collection need to implement the method; there /// is no auto-delegation. Can be used to provide a method on both, but /// skip the Collection mutex lock when a backend handle is available. @@ -30,5 +27,5 @@ enum RustMethods { /// method in other services that doesn't happen to need the collection, /// we just delegate to the collection method for convenience, and to make /// sure it's available even if the consumer is not using Backend. - RUST_METHODS_COLLECTION_AND_MANUAL_BACKEND = 2; + BACKEND_METHOD_IMPLEMENT = 1; } diff --git a/proto/anki/collection.proto b/proto/anki/collection.proto index 475be93af..08c618223 100644 --- a/proto/anki/collection.proto +++ b/proto/anki/collection.proto @@ -8,15 +8,8 @@ option java_multiple_files = true; package anki.collection; import "anki/generic.proto"; -import "anki/codegen.proto"; service CollectionService { - rpc OpenCollection(OpenCollectionRequest) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc CloseCollection(CloseCollectionRequest) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } rpc CheckDatabase(generic.Empty) returns (CheckDatabaseResponse); rpc GetUndoStatus(generic.Empty) returns (UndoStatus); rpc Undo(generic.Empty) returns (OpChangesAfterUndo); @@ -25,18 +18,19 @@ service CollectionService { rpc MergeUndoEntries(generic.UInt32) returns (OpChanges); rpc LatestProgress(generic.Empty) returns (Progress); rpc SetWantsAbort(generic.Empty) returns (generic.Empty); +} + +service BackendCollectionService { + rpc OpenCollection(OpenCollectionRequest) returns (generic.Empty); + rpc CloseCollection(CloseCollectionRequest) returns (generic.Empty); // Create a no-media backup. Caller must ensure there is no active // transaction. Unlike a collection export, does not require reopening the DB, // as there is no downgrade step. // Returns false if it's not time to make a backup yet. - rpc CreateBackup(CreateBackupRequest) returns (generic.Bool) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } + rpc CreateBackup(CreateBackupRequest) returns (generic.Bool); // If a backup is running, wait for it to complete. Will return an error // if the backup encountered an error. - rpc AwaitBackupCompletion(generic.Empty) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } + rpc AwaitBackupCompletion(generic.Empty) returns (generic.Empty); } message OpenCollectionRequest { diff --git a/proto/anki/i18n.proto b/proto/anki/i18n.proto index 3330b190d..c00898eb9 100644 --- a/proto/anki/i18n.proto +++ b/proto/anki/i18n.proto @@ -12,13 +12,13 @@ import "anki/codegen.proto"; service I18nService { rpc TranslateString(TranslateStringRequest) returns (generic.String) { - option (codegen.rust_methods) = RUST_METHODS_COLLECTION_AND_MANUAL_BACKEND; + option (codegen.backend_method) = BACKEND_METHOD_IMPLEMENT; } rpc FormatTimespan(FormatTimespanRequest) returns (generic.String) { - option (codegen.rust_methods) = RUST_METHODS_COLLECTION_AND_MANUAL_BACKEND; + option (codegen.backend_method) = BACKEND_METHOD_IMPLEMENT; } rpc I18nResources(I18nResourcesRequest) returns (generic.Json) { - option (codegen.rust_methods) = RUST_METHODS_COLLECTION_AND_MANUAL_BACKEND; + option (codegen.backend_method) = BACKEND_METHOD_IMPLEMENT; } } diff --git a/proto/anki/import_export.proto b/proto/anki/import_export.proto index 4d1d1d1a6..f490aee3a 100644 --- a/proto/anki/import_export.proto +++ b/proto/anki/import_export.proto @@ -14,14 +14,6 @@ import "anki/generic.proto"; import "anki/codegen.proto"; service ImportExportService { - rpc ImportCollectionPackage(ImportCollectionPackageRequest) - returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc ExportCollectionPackage(ExportCollectionPackageRequest) - returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } rpc ImportAnkiPackage(ImportAnkiPackageRequest) returns (ImportResponse); rpc ExportAnkiPackage(ExportAnkiPackageRequest) returns (generic.UInt32); rpc GetCsvMetadata(CsvMetadataRequest) returns (CsvMetadata); @@ -32,6 +24,13 @@ service ImportExportService { rpc ImportJsonString(generic.String) returns (ImportResponse); } +service BackendImportExportService { + rpc ImportCollectionPackage(ImportCollectionPackageRequest) + returns (generic.Empty); + rpc ExportCollectionPackage(ExportCollectionPackageRequest) + returns (generic.Empty); +} + message ImportCollectionPackageRequest { string col_path = 1; string backup_path = 2; diff --git a/proto/anki/sync.proto b/proto/anki/sync.proto index 546496480..43e3ac198 100644 --- a/proto/anki/sync.proto +++ b/proto/anki/sync.proto @@ -8,33 +8,16 @@ option java_multiple_files = true; package anki.sync; import "anki/generic.proto"; -import "anki/codegen.proto"; -service SyncService { - rpc SyncMedia(SyncAuth) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc AbortMediaSync(generic.Empty) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc SyncLogin(SyncLoginRequest) returns (SyncAuth) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc SyncStatus(SyncAuth) returns (SyncStatusResponse) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc SyncCollection(SyncAuth) returns (SyncCollectionResponse) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc FullUpload(SyncAuth) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc FullDownload(SyncAuth) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } - rpc AbortSync(generic.Empty) returns (generic.Empty) { - option (codegen.rust_methods) = RUST_METHODS_BACKEND_ONLY; - } +service BackendSyncService { + rpc SyncMedia(SyncAuth) returns (generic.Empty); + rpc AbortMediaSync(generic.Empty) returns (generic.Empty); + rpc SyncLogin(SyncLoginRequest) returns (SyncAuth); + rpc SyncStatus(SyncAuth) returns (SyncStatusResponse); + rpc SyncCollection(SyncAuth) returns (SyncCollectionResponse); + rpc FullUpload(SyncAuth) returns (generic.Empty); + rpc FullDownload(SyncAuth) returns (generic.Empty); + rpc AbortSync(generic.Empty) returns (generic.Empty); } message SyncAuth { diff --git a/rslib/rust_interface.rs b/rslib/rust_interface.rs index 4c4988af6..287c20a70 100644 --- a/rslib/rust_interface.rs +++ b/rslib/rust_interface.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use std::path::PathBuf; use anki_io::write_file_if_changed; -use anki_proto::codegen::RustMethods; +use anki_proto::codegen::BackendMethod; use anyhow::Context; use anyhow::Result; use inflections::Inflect; @@ -48,6 +48,7 @@ struct RustMethod { input_type: Option, output_type: Option, options: anki_proto::codegen::MethodOptions, + service_name: String, } impl RustMethod { @@ -74,21 +75,21 @@ impl RustMethod { } fn wants_abstract_backend_method(&self) -> bool { - self.options.rust_methods() != RustMethods::CollectionAndAutoBackend + self.service_name.starts_with("Backend") + || self.options.backend_method() == BackendMethod::Implement } fn wants_abstract_collection_method(&self) -> bool { - self.options.rust_methods() != RustMethods::BackendOnly + !self.service_name.starts_with("Backend") } -} -impl RustMethod { fn from_proto(method: prost_reflect::MethodDescriptor) -> Self { RustMethod { name: method.name().to_snake_case(), input_type: rust_type(method.input().full_name()), output_type: rust_type(method.output().full_name()), options: method.options().transcode_to().unwrap(), + service_name: method.parent_service().name().to_string(), } } } @@ -189,7 +190,11 @@ fn render_collection_trait(service: &RustService, buf: &mut String) { } fn render_backend_trait(service: &RustService, buf: &mut String) { - let name = format!("Backend{}", service.name); + let name = if !service.name.starts_with("Backend") { + format!("Backend{}", service.name) + } else { + service.name.clone() + }; writeln!(buf, "pub trait {name} {{").unwrap(); for method in &service.methods { if method.wants_abstract_backend_method() {