diff --git a/build/configure/src/pylib.rs b/build/configure/src/pylib.rs index 9b1822be2..2e01d1a23 100644 --- a/build/configure/src/pylib.rs +++ b/build/configure/src/pylib.rs @@ -26,6 +26,7 @@ pub fn build_pylib(build: &mut Build) -> Result<()> { proto_files: inputs![glob!["proto/anki/*.proto"]], }, )?; + build.add_dependency("pylib:anki:proto", ":rslib:proto:py".into()); build.add_action( "pylib:anki:_fluent.py", diff --git a/build/configure/src/python.rs b/build/configure/src/python.rs index 68dae1a76..1d1a1bc7b 100644 --- a/build/configure/src/python.rs +++ b/build/configure/src/python.rs @@ -109,10 +109,6 @@ impl BuildAction for GenPythonProto { build.add_inputs("protoc", inputs![":protoc_binary"]); build.add_inputs("protoc-gen-mypy", inputs![":pyenv:protoc-gen-mypy"]); build.add_outputs("", python_outputs); - // not a direct dependency, but we include the output interface in our declared - // outputs - build.add_inputs("", inputs![":rslib:proto"]); - build.add_outputs("", vec!["pylib/anki/_backend_generated.py"]); } } diff --git a/build/configure/src/rust.rs b/build/configure/src/rust.rs index 3765e3003..32d9795a7 100644 --- a/build/configure/src/rust.rs +++ b/build/configure/src/rust.rs @@ -21,7 +21,7 @@ use crate::platform::overriden_rust_target_triple; pub fn build_rust(build: &mut Build) -> Result<()> { prepare_translations(build)?; - prepare_proto_descriptors(build)?; + build_proto_descriptors_and_interfaces(build)?; build_rsbridge(build) } @@ -81,16 +81,18 @@ fn prepare_translations(build: &mut Build) -> Result<()> { Ok(()) } -fn prepare_proto_descriptors(build: &mut Build) -> Result<()> { - // build anki_proto and spit out descriptors/Python interface +fn build_proto_descriptors_and_interfaces(build: &mut Build) -> Result<()> { + let outputs = vec![ + RustOutput::Data("descriptors.bin", "rslib/proto/descriptors.bin"), + RustOutput::Data("py", "pylib/anki/_backend_generated.py"), + RustOutput::Data("ts", "ts/lib/backend.d.ts"), + RustOutput::Data("ts", "ts/lib/backend.js"), + ]; build.add_action( "rslib:proto", CargoBuild { inputs: inputs![glob!["{proto,rslib/proto}/**"], ":protoc_binary",], - outputs: &[RustOutput::Data( - "descriptors.bin", - "$builddir/rslib/proto/descriptors.bin", - )], + outputs: &outputs, target: None, extra_args: "-p anki_proto", release_override: None, @@ -110,8 +112,8 @@ fn build_rsbridge(build: &mut Build) -> Result<()> { CargoBuild { inputs: inputs![ glob!["{pylib/rsbridge/**,rslib/**}"], - // declare a dependency on i18n/proto so it gets built first, allowing - // things depending on strings.json to build faster, and ensuring + // declare a dependency on i18n/proto so they get built first, allowing + // things depending on them to build faster, and ensuring // changes to the ftl files trigger a rebuild ":rslib:i18n", ":rslib:proto", diff --git a/build/configure/src/web.rs b/build/configure/src/web.rs index dc033c769..4bec355e1 100644 --- a/build/configure/src/web.rs +++ b/build/configure/src/web.rs @@ -149,7 +149,7 @@ fn build_and_check_tslib(build: &mut Build) -> Result<()> { }, )?; // ensure _service files are generated by rslib - build.add_dependency("ts:lib:proto", inputs![":rslib:proto"]); + build.add_dependency("ts:lib:proto", inputs![":rslib:proto:ts"]); // the generated _service.js files import @tslib/post, and esbuild won't be able // to import the .ts file, so we need to generate a .js file for it build.add_action( diff --git a/rslib/proto/ts.rs b/rslib/proto/ts.rs index cb79e6f45..d241bcd53 100644 --- a/rslib/proto/ts.rs +++ b/rslib/proto/ts.rs @@ -3,81 +3,72 @@ use std::collections::HashSet; use std::fmt::Write as WriteFmt; -use std::io::BufWriter; -use std::io::Write; use std::path::Path; use anki_io::create_dir_all; -use anki_io::create_file; +use anki_io::write_file_if_changed; use anki_proto_gen::BackendService; use anki_proto_gen::Method; use anyhow::Result; use inflections::Inflect; pub(crate) fn write_ts_interface(services: &[BackendService]) -> Result<()> { - let root = Path::new("../../out/ts/lib/anki"); + let root = Path::new("../../out/ts/lib"); create_dir_all(root)?; + let mut dts_out = String::new(); + let mut js_out = String::new(); + let mut referenced_packages = HashSet::new(); + for service in services { if service.name == "BackendAnkidroidService" { continue; } - let service_name = service - .name - .replace("Service", "") - .replace("Backend", "") - .to_snake_case(); - - write_dts_file(root, &service_name, service)?; - write_js_file(root, &service_name, service)?; + for method in service.all_methods() { + let method = MethodDetails::from_method(method); + record_referenced_type(&mut referenced_packages, &method.input_type); + record_referenced_type(&mut referenced_packages, &method.output_type); + write_dts_method(&method, &mut dts_out); + write_js_method(&method, &mut js_out); + } } + let imports = imports(referenced_packages); + write_file_if_changed( + root.join("backend.d.ts"), + format!("{}{}{}", dts_header(), imports, dts_out), + )?; + write_file_if_changed( + root.join("backend.js"), + format!("{}{}{}", js_header(), imports, js_out), + )?; + Ok(()) } -fn write_dts_file(root: &Path, service_name: &str, service: &BackendService) -> Result<()> { - let output_path = root.join(format!("{service_name}_service.d.ts")); - let mut out = BufWriter::new(create_file(output_path)?); - write_dts_header(&mut out)?; - - let mut referenced_packages = HashSet::new(); - let mut method_text = String::new(); - - for method in service.all_methods() { - let method = MethodDetails::from_method(method); - record_referenced_type(&mut referenced_packages, &method.input_type)?; - record_referenced_type(&mut referenced_packages, &method.output_type)?; - write_dts_method(&method, &mut method_text)?; - } - - write_imports(referenced_packages, &mut out)?; - write!(out, "{}", method_text)?; - Ok(()) -} - -fn write_dts_header(out: &mut impl std::io::Write) -> Result<()> { - out.write_all( - br#"// Copyright: Ankitects Pty Ltd and contributors +fn dts_header() -> String { + r#"// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; https://www.gnu.org/licenses/agpl.html import type { PlainMessage } from "@bufbuild/protobuf"; -import type { PostProtoOptions } from "../post"; -"#, - )?; - Ok(()) +import type { PostProtoOptions } from "./post"; +"# + .into() } -fn write_imports(referenced_packages: HashSet, out: &mut impl Write) -> Result<()> { +fn imports(referenced_packages: HashSet) -> String { + let mut out = String::new(); for package in referenced_packages { writeln!( - out, - "import * as {} from \"./{}_pb\";", + &mut out, + "import * as {} from \"./anki/{}_pb\";", package, package.to_snake_case() - )?; + ) + .unwrap(); } - Ok(()) + out } fn write_dts_method( @@ -88,43 +79,21 @@ fn write_dts_method( comments, }: &MethodDetails, out: &mut String, -) -> Result<()> { +) { let comments = format_comments(comments); writeln!( out, r#"{comments}export declare function {method_name}(input: PlainMessage<{input_type}>, options?: PostProtoOptions): Promise<{output_type}>;"# - )?; - Ok(()) + ).unwrap() } -fn write_js_file(root: &Path, service_name: &str, service: &BackendService) -> Result<()> { - let output_path = root.join(format!("{service_name}_service.js")); - let mut out = BufWriter::new(create_file(output_path)?); - write_js_header(&mut out)?; - - let mut referenced_packages = HashSet::new(); - let mut method_text = String::new(); - for method in service.all_methods() { - let method = MethodDetails::from_method(method); - record_referenced_type(&mut referenced_packages, &method.input_type)?; - record_referenced_type(&mut referenced_packages, &method.output_type)?; - write_js_method(&method, &mut method_text)?; - } - - write_imports(referenced_packages, &mut out)?; - write!(out, "{}", method_text)?; - Ok(()) -} - -fn write_js_header(out: &mut impl std::io::Write) -> Result<()> { - out.write_all( - br#"// Copyright: Ankitects Pty Ltd and contributors +fn js_header() -> String { + r#"// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; https://www.gnu.org/licenses/agpl.html -import { postProto } from "../post"; -"#, - )?; - Ok(()) +import { postProto } from "./post"; +"# + .into() } fn write_js_method( @@ -135,15 +104,15 @@ fn write_js_method( .. }: &MethodDetails, out: &mut String, -) -> Result<()> { +) { write!( out, r#"export async function {method_name}(input, options = {{}}) {{ return await postProto("{method_name}", new {input_type}(input), {output_type}, options); }} "# - )?; - Ok(()) + ) + .unwrap(); } fn format_comments(comments: &Option) -> String { @@ -175,12 +144,8 @@ impl MethodDetails { } } -fn record_referenced_type( - referenced_packages: &mut HashSet, - type_name: &str, -) -> Result<()> { +fn record_referenced_type(referenced_packages: &mut HashSet, type_name: &str) { referenced_packages.insert(type_name.split('.').next().unwrap().to_string()); - Ok(()) } // e.g. anki.import_export.ImportResponse -> diff --git a/ts/card-info/CardInfo.svelte b/ts/card-info/CardInfo.svelte index 4ac7e5f44..e47f78cd7 100644 --- a/ts/card-info/CardInfo.svelte +++ b/ts/card-info/CardInfo.svelte @@ -7,7 +7,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html CardStatsResponse, CardStatsResponse_StatsRevlogEntry, } from "@tslib/anki/stats_pb"; - import { cardStats } from "@tslib/anki/stats_service"; + import { cardStats } from "@tslib/backend"; import Container from "../components/Container.svelte"; import Row from "../components/Row.svelte"; diff --git a/ts/change-notetype/index.ts b/ts/change-notetype/index.ts index b2426cf10..dbbe00686 100644 --- a/ts/change-notetype/index.ts +++ b/ts/change-notetype/index.ts @@ -3,7 +3,7 @@ import "./change-notetype-base.scss"; -import { getChangeNotetypeInfo, getNotetypeNames } from "@tslib/anki/notetypes_service"; +import { getChangeNotetypeInfo, getNotetypeNames } from "@tslib/backend"; import { ModuleName, setupI18n } from "@tslib/i18n"; import { checkNightMode } from "@tslib/nightmode"; diff --git a/ts/change-notetype/lib.ts b/ts/change-notetype/lib.ts index 5c6d4878b..662f7fe70 100644 --- a/ts/change-notetype/lib.ts +++ b/ts/change-notetype/lib.ts @@ -2,7 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { ChangeNotetypeInfo, ChangeNotetypeRequest, NotetypeNames } from "@tslib/anki/notetypes_pb"; -import { changeNotetype, getChangeNotetypeInfo } from "@tslib/anki/notetypes_service"; +import { changeNotetype, getChangeNotetypeInfo } from "@tslib/backend"; import * as tr from "@tslib/ftl"; import { isEqual } from "lodash-es"; import type { Readable } from "svelte/store"; diff --git a/ts/congrats/index.ts b/ts/congrats/index.ts index 13fc8d3b6..a87f1acef 100644 --- a/ts/congrats/index.ts +++ b/ts/congrats/index.ts @@ -3,7 +3,7 @@ import "./congrats-base.scss"; -import { congratsInfo } from "@tslib/anki/scheduler_service"; +import { congratsInfo } from "@tslib/backend"; import { ModuleName, setupI18n } from "@tslib/i18n"; import { checkNightMode } from "@tslib/nightmode"; diff --git a/ts/deck-options/index.ts b/ts/deck-options/index.ts index 21b2417c5..7d8551295 100644 --- a/ts/deck-options/index.ts +++ b/ts/deck-options/index.ts @@ -46,7 +46,7 @@ export async function setupDeckOptions(did_: number): Promise { }); } -import { getDeckConfigsForUpdate } from "@tslib/anki/deck_config_service"; +import { getDeckConfigsForUpdate } from "@tslib/backend"; import TitledContainer from "../components/TitledContainer.svelte"; import EnumSelectorRow from "./EnumSelectorRow.svelte"; diff --git a/ts/deck-options/lib.ts b/ts/deck-options/lib.ts index 8141121bd..16a89faf3 100644 --- a/ts/deck-options/lib.ts +++ b/ts/deck-options/lib.ts @@ -8,7 +8,7 @@ import type { UpdateDeckConfigsRequest, } from "@tslib/anki/deck_config_pb"; import { DeckConfig, DeckConfig_Config, DeckConfigsForUpdate_CurrentDeck_Limits } from "@tslib/anki/deck_config_pb"; -import { updateDeckConfigs } from "@tslib/anki/deck_config_service"; +import { updateDeckConfigs } from "@tslib/backend"; import { localeCompare } from "@tslib/i18n"; import { cloneDeep, isEqual } from "lodash-es"; import type { Readable, Writable } from "svelte/store"; diff --git a/ts/graphs/WithGraphData.svelte b/ts/graphs/WithGraphData.svelte index 0ae211ca3..191470d1a 100644 --- a/ts/graphs/WithGraphData.svelte +++ b/ts/graphs/WithGraphData.svelte @@ -4,11 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -->