diff --git a/.gitignore b/.gitignore index 9872b4fd2..d82845757 100644 --- a/.gitignore +++ b/.gitignore @@ -3,15 +3,18 @@ *\# *~ .*.swp -.DS_Store .build .coverage +.DS_Store .mypy_cache .pytype __pycache__ anki/buildhash.py +anki/proto aqt/forms locale +rs/ankirs/src/proto.rs +rs/target tools/runanki.system ts/node_modules web/deckbrowser.js @@ -20,4 +23,3 @@ web/overview.js web/reviewer-bottom.js web/reviewer.js web/webview.js -rs/target diff --git a/Makefile b/Makefile index 4870cc295..ed9edf687 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,11 @@ RUST_TOOLCHAIN := $(shell cat rs/rust-toolchain) rustup component add clippy-preview --toolchain $(RUST_TOOLCHAIN) @touch $@ +# Protobuf +###################### + +PROTODEPS := $(wildcard proto/*.proto) + # Typescript source ###################### @@ -105,12 +110,12 @@ JSDEPS := $(patsubst ts/src/%.ts, web/%.js, $(TSDEPS)) # Rust source ###################### -RSDEPS := $(wildcard rs/*/src/*.rs) +RSDEPS := $(shell find rs -type f | grep -v target) # Building ###################### -BUILDDEPS := .build/ui .build/js .build/rs +BUILDDEPS := .build/ui .build/js .build/rs .build/py-proto .build/ui: $(RUNREQS) $(shell find designer -type f) ./tools/build_ui.sh @@ -120,10 +125,14 @@ BUILDDEPS := .build/ui .build/js .build/rs (cd ts && npm run build) @touch $@ -.build/rs: .build/rust-deps $(RUNREQS) $(RSDEPS) +.build/rs: .build/rust-deps $(RUNREQS) $(RSDEPS) $(PROTODEPS) (cd rs/pybridge && maturin develop $(RUSTARGS)) @touch $@ +.build/py-proto: $(RUNREQS) $(PROTODEPS) + protoc -I proto --python_betterproto_out=anki/proto proto/bridge.proto + @touch $@ + .PHONY: build clean build: $(BUILDDEPS) @@ -147,6 +156,9 @@ run: build .PHONY: check check: rs-test rs-fmt rs-clippy py-mypy py-test py-fmt py-imports py-lint ts-fmt +.PHONY: fix +fix: fix-py-fmt fix-py-imports fix-rs-fmt fix-ts-fmt + # Checking python ###################### @@ -156,7 +168,7 @@ PYCHECKDEPS := $(BUILDDEPS) .build/py-check-reqs $(shell find anki aqt -name '*. mypy anki aqt @touch $@ -.build/pytest: $(PYCHECKDEPS) $(wildcard tests/*.py) +.build/py-test: $(PYCHECKDEPS) $(wildcard tests/*.py) ./tools/tests.sh @touch $@ diff --git a/anki/collection.py b/anki/collection.py index 8eb74b15e..9c36362a8 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -29,6 +29,7 @@ from anki.lang import _, ngettext from anki.media import MediaManager from anki.models import ModelManager from anki.notes import Note +from anki.rsbridge import RSBridge from anki.sched import Scheduler as V1Scheduler from anki.schedv2 import Scheduler as V2Scheduler from anki.sound import stripSounds @@ -84,8 +85,12 @@ class _Collection: ls: int conf: Dict[str, Any] _undo: List[Any] + rust: RSBridge - def __init__(self, db: DB, server: bool = False, log: bool = False) -> None: + def __init__( + self, db: DB, server: bool = False, log: bool = False, rust: RSBridge = None + ) -> None: + self.rust = rust self._debugLog = log self.db = db self.path = db._path diff --git a/anki/rsbridge.py b/anki/rsbridge.py index 9f0ac5e25..17132d528 100644 --- a/anki/rsbridge.py +++ b/anki/rsbridge.py @@ -1,10 +1,37 @@ import _ankirs # pytype: disable=import-error +import betterproto + +from anki.proto import proto as pb + + +class BridgeException(Exception): + def __str__(self) -> str: + err: pb.BridgeError = self.args[0] # pylint: disable=unsubscriptable-object + (kind, obj) = betterproto.which_one_of(err, "value") + if kind == "invalid_input": + return f"invalid input: {obj.info}" + else: + return f"unhandled error: {err} {obj}" class RSBridge: def __init__(self): self._bridge = _ankirs.Bridge() - assert self._bridge.cmd("") == "test" + + def _run_command(self, input: pb.BridgeInput) -> pb.BridgeOutput: + input_bytes = bytes(input) + output_bytes = self._bridge.command(input_bytes) + output = pb.BridgeOutput().parse(output_bytes) + (kind, obj) = betterproto.which_one_of(output, "value") + if kind == "error": + raise BridgeException(obj) + else: + return output + + def plus_one(self, num: int) -> int: + input = pb.BridgeInput(plus_one=pb.PlusOneIn(num=num)) + output = self._run_command(input) + return output.plus_one.num bridge = RSBridge() diff --git a/anki/storage.py b/anki/storage.py index 5855a1f95..b75a54827 100644 --- a/anki/storage.py +++ b/anki/storage.py @@ -12,6 +12,7 @@ from anki.collection import _Collection from anki.consts import * from anki.db import DB from anki.lang import _ +from anki.rsbridge import RSBridge from anki.stdmodels import ( addBasicModel, addBasicTypingModel, @@ -26,6 +27,8 @@ def Collection( path: str, lock: bool = True, server: bool = False, log: bool = False ) -> _Collection: "Open a new or existing collection. Path must be unicode." + bridge = RSBridge() + assert bridge.plus_one(5) == 6 assert path.endswith(".anki2") path = os.path.abspath(path) create = not os.path.exists(path) @@ -46,7 +49,7 @@ def Collection( db.execute("pragma journal_mode = wal") db.setAutocommit(False) # add db to col and do any remaining upgrades - col = _Collection(db, server, log) + col = _Collection(db, server, log, rust=bridge) if ver < SCHEMA_VERSION: _upgrade(col, ver) elif ver > SCHEMA_VERSION: diff --git a/proto/bridge.proto b/proto/bridge.proto new file mode 100644 index 000000000..a132e308b --- /dev/null +++ b/proto/bridge.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package proto; + +message Empty {} + +message BridgeInput { + oneof value { + PlusOneIn plus_one = 2; + } +} + +message BridgeOutput { + oneof value { + BridgeError error = 1; + PlusOneOut plus_one = 2; + } +} + +message BridgeError { + oneof value { + InvalidInputError invalid_input = 1; + } +} + +message InvalidInputError { + string info = 1; +} + +message PlusOneIn { + int32 num = 1; +} + +message PlusOneOut { + int32 num = 1; +} diff --git a/requirements.txt b/requirements.txt index 3dd8678ba..4788d0bb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ jsonschema psutil; sys_platform == "win32" distro; sys_platform != "win32" and sys_platform != "darwin" typing +betterproto[compiler] diff --git a/rs/Cargo.lock b/rs/Cargo.lock index a8ec493ff..d63b0c82b 100644 --- a/rs/Cargo.lock +++ b/rs/Cargo.lock @@ -12,6 +12,12 @@ dependencies = [ [[package]] name = "ankirs" version = "0.1.0" +dependencies = [ + "bytes", + "failure", + "prost", + "prost-build", +] [[package]] name = "autocfg" @@ -19,14 +25,118 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +dependencies = [ + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +dependencies = [ + "ppv-lite86", +] + +[[package]] +name = "cc" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "ctor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" dependencies = [ - "quote", - "syn", + "quote 1.0.2", + "syn 1.0.11", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +dependencies = [ + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", + "synstructure", +] + +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" + +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +dependencies = [ + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -35,9 +145,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", ] [[package]] @@ -57,9 +176,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b714fc08d0961716390977cdff1536234415ac37b509e34e5a983def8340fb75" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", "unindent", ] @@ -80,9 +199,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", ] [[package]] @@ -103,12 +240,27 @@ version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +[[package]] +name = "multimap" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04b9f127583ed176e163fb9ec6f3e793b87e21deedd5734a69386a18a0151" + [[package]] name = "num-traits" version = "0.2.10" @@ -135,20 +287,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", ] +[[package]] +name = "petgraph" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +dependencies = [ + "fixedbitset", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + [[package]] name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", ] [[package]] @@ -157,13 +333,66 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.0", +] + +[[package]] +name = "prost" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d14b1c185652833d24aaad41c5832b0be5616a590227c1fbff57c616754b23" +dependencies = [ + "byteorder", + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb788126ea840817128183f8f603dce02cb7aea25c2a0b764359d8e20010702e" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e7dc378b94ac374644181a2247cebf59a6ec1c88b49ac77f3a94b86b79d0e11" +dependencies = [ + "failure", + "itertools", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "prost-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de482a366941c8d56d19b650fac09ca08508f2a696119ee7513ad590c8bac6f" +dependencies = [ + "bytes", + "prost", ] [[package]] name = "pybridge" version = "0.1.0" dependencies = [ + "ankirs", "pyo3", ] @@ -194,9 +423,9 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f6e56fb3e97b344a8f87d036f94578399402c6b75949de6270cd07928f790b1" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", ] [[package]] @@ -205,10 +434,19 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97452dcdf5941627ebc5c06664a07821fc7fc88d7515f02178193a8ebe316468" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.6", "pyo3-derive-backend", - "quote", - "syn", + "quote 1.0.2", + "syn 1.0.11", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", ] [[package]] @@ -217,9 +455,56 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.6", ] +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +dependencies = [ + "c2-chacha", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + [[package]] name = "regex" version = "1.3.1" @@ -238,6 +523,21 @@ version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + [[package]] name = "ryu" version = "1.0.2" @@ -259,9 +559,9 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", ] [[package]] @@ -281,15 +581,52 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.6", + "quote 1.0.2", + "unicode-xid 0.2.0", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", + "unicode-xid 0.2.0", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", ] [[package]] @@ -301,6 +638,18 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.0" @@ -318,3 +667,41 @@ name = "version_check" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" + +[[package]] +name = "which" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" +dependencies = [ + "failure", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/rs/ankirs/Cargo.toml b/rs/ankirs/Cargo.toml index 1981374cc..a9c34c962 100644 --- a/rs/ankirs/Cargo.toml +++ b/rs/ankirs/Cargo.toml @@ -3,3 +3,11 @@ name = "ankirs" version = "0.1.0" edition = "2018" authors = ["Ankitects Pty Ltd and contributors"] + +[dependencies] +failure = "0.1.6" +prost = "0.5.0" +bytes = "0.4" + +[build-dependencies] +prost-build = "0.5.0" diff --git a/rs/ankirs/build.rs b/rs/ankirs/build.rs new file mode 100644 index 000000000..c72a77327 --- /dev/null +++ b/rs/ankirs/build.rs @@ -0,0 +1,7 @@ +use prost_build; + +fn main() { + // avoid default OUT_DIR for now, for code completion + std::env::set_var("OUT_DIR", "src"); + prost_build::compile_protos(&["../../proto/bridge.proto"], &["../../proto/"]).unwrap(); +} diff --git a/rs/ankirs/src/bridge.rs b/rs/ankirs/src/bridge.rs new file mode 100644 index 000000000..df5a8c5a3 --- /dev/null +++ b/rs/ankirs/src/bridge.rs @@ -0,0 +1,84 @@ +use crate::err::{AnkiError, Result}; +use crate::proto as pt; +use crate::proto::bridge_input::Value; +use prost::Message; + +pub struct Bridge {} + +impl Default for Bridge { + fn default() -> Self { + Bridge {} + } +} + +/// Convert an Anki error to a protobuf error. +impl std::convert::From for pt::BridgeError { + fn from(err: AnkiError) -> Self { + use pt::bridge_error::Value as V; + let value = match err { + AnkiError::InvalidInput { info } => V::InvalidInput(pt::InvalidInputError { info }), + }; + + pt::BridgeError { value: Some(value) } + } +} + +// Convert an Anki error to a protobuf output. +impl std::convert::From for pt::bridge_output::Value { + fn from(err: AnkiError) -> Self { + pt::bridge_output::Value::Error(err.into()) + } +} + +impl Bridge { + pub fn new() -> Bridge { + Bridge::default() + } + + /// Decode a request, process it, and return the encoded result. + pub fn run_command_bytes(&mut self, req: &[u8]) -> Vec { + let mut buf = vec![]; + + let req = match pt::BridgeInput::decode(req) { + Ok(req) => req, + Err(_e) => { + // unable to decode + let err = AnkiError::invalid_input("couldn't decode bridge request"); + let output = pt::BridgeOutput { + value: Some(err.into()), + }; + output.encode(&mut buf).expect("encode failed"); + return buf; + } + }; + + let resp = self.run_command(req); + resp.encode(&mut buf).expect("encode failed"); + buf + } + + fn run_command(&self, input: pt::BridgeInput) -> pt::BridgeOutput { + let oval = if let Some(ival) = input.value { + match self.run_command_inner(ival) { + Ok(output) => output, + Err(err) => err.into(), + } + } else { + AnkiError::invalid_input("unrecognized bridge input value").into() + }; + + pt::BridgeOutput { value: Some(oval) } + } + + fn run_command_inner(&self, ival: pt::bridge_input::Value) -> Result { + use pt::bridge_output::Value as OValue; + Ok(match ival { + Value::PlusOne(input) => OValue::PlusOne(self.plus_one(input)?), + }) + } + + fn plus_one(&self, input: pt::PlusOneIn) -> Result { + let num = input.num + 1; + Ok(pt::PlusOneOut { num }) + } +} diff --git a/rs/ankirs/src/err.rs b/rs/ankirs/src/err.rs new file mode 100644 index 000000000..48b16d39e --- /dev/null +++ b/rs/ankirs/src/err.rs @@ -0,0 +1,16 @@ +pub use failure::{Error, Fail}; + +pub type Result = std::result::Result; + +#[derive(Debug, Fail)] +pub enum AnkiError { + #[fail(display = "invalid input: {}", info)] + InvalidInput { info: String }, +} + +// error helpers +impl AnkiError { + pub(crate) fn invalid_input>(s: S) -> AnkiError { + AnkiError::InvalidInput { info: s.into() } + } +} diff --git a/rs/ankirs/src/lib.rs b/rs/ankirs/src/lib.rs index 8b1378917..eb1b6bf97 100644 --- a/rs/ankirs/src/lib.rs +++ b/rs/ankirs/src/lib.rs @@ -1 +1,4 @@ +mod proto; +pub mod bridge; +pub mod err; diff --git a/rs/pybridge/Cargo.toml b/rs/pybridge/Cargo.toml index 0af13556b..6b87a5c18 100644 --- a/rs/pybridge/Cargo.toml +++ b/rs/pybridge/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2018" authors = ["Ankitects Pty Ltd and contributors"] +[dependencies] +ankirs = { path = "../ankirs" } + [dependencies.pyo3] version = "0.8.0" features = ["extension-module"] diff --git a/rs/pybridge/src/lib.rs b/rs/pybridge/src/lib.rs index c56231b73..f48351bd0 100644 --- a/rs/pybridge/src/lib.rs +++ b/rs/pybridge/src/lib.rs @@ -1,19 +1,27 @@ -use pyo3::exceptions; +use ankirs::bridge::Bridge as RustBridge; use pyo3::prelude::*; +use pyo3::types::PyBytes; #[pyclass] -struct Bridge {} +struct Bridge { + bridge: RustBridge, +} #[pymethods] impl Bridge { #[new] fn init(obj: &PyRawObject) { - obj.init({ Bridge {} }); + obj.init({ + Bridge { + bridge: Default::default(), + } + }); } - fn cmd(&mut self, _request: String) -> PyResult { - Ok("test".to_string()) - .map_err(|e: std::io::Error| exceptions::Exception::py_err(format!("{:?}", e))) + fn command(&mut self, py: Python, input: &PyBytes) -> PyResult { + let out_bytes = self.bridge.run_command_bytes(input.as_bytes()); + let out_obj = PyBytes::new(py, &out_bytes); + Ok(out_obj.into()) } } diff --git a/rs/rustfmt.toml b/rs/rustfmt.toml new file mode 100644 index 000000000..c9dc0fc83 --- /dev/null +++ b/rs/rustfmt.toml @@ -0,0 +1 @@ +ignore = ["proto.rs"]