From 7733e03360c847dcaf113eece191f8bb2fc89ba2 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 22 Dec 2019 19:28:36 +1000 Subject: [PATCH] make it possible to implement functionality in Rust --- .gitignore | 1 + Makefile | 19 ++- README.development | 11 ++ anki/__init__.py | 3 + anki/rsbridge.py | 9 ++ mypy.ini | 2 + rs/Cargo.lock | 316 +++++++++++++++++++++++++++++++++++++++++++++ rs/Cargo.toml | 20 +++ rs/rust-toolchain | 1 + rs/src/lib.rs | 31 +++++ 10 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 anki/rsbridge.py create mode 100644 rs/Cargo.lock create mode 100644 rs/Cargo.toml create mode 100644 rs/rust-toolchain create mode 100644 rs/src/lib.rs diff --git a/.gitignore b/.gitignore index 16ba7b97c..9872b4fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ web/overview.js web/reviewer-bottom.js web/reviewer.js web/webview.js +rs/target diff --git a/Makefile b/Makefile index 07cf42554..4a1689a0e 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ MAKEFLAGS += --no-builtin-rules RUNARGS := .SUFFIXES: BLACKARGS := -t py36 anki aqt +RUSTARGS := --release --strip $(shell mkdir -p .build) @@ -66,6 +67,10 @@ RUNREQS := .build/pyrunreqs .build/jsreqs ./tools/typecheck-setup.sh touch $@ +.build/rustreqs: .build/pyrunreqs + pip install maturin + touch $@ + .build/jsreqs: ts/package.json (cd ts && npm i) touch $@ @@ -76,10 +81,15 @@ RUNREQS := .build/pyrunreqs .build/jsreqs TSDEPS := $(wildcard ts/src/*.ts) JSDEPS := $(patsubst ts/src/%.ts, web/%.js, $(TSDEPS)) +# Rust source +###################### + +RSDEPS := $(wildcard rs/src/*.rs) + # Building ###################### -BUILDDEPS := .build/ui .build/js +BUILDDEPS := .build/ui .build/js .build/rs .build/ui: $(RUNREQS) $(shell find designer -type f) ./tools/build_ui.sh @@ -89,6 +99,10 @@ BUILDDEPS := .build/ui .build/js (cd ts && npm run build) touch $@ +.build/rs: .build/rustreqs $(RUNREQS) $(RSDEPS) + (cd rs && maturin develop $(RUSTARGS)) + touch $@ + .PHONY: build clean build: $(BUILDDEPS) @@ -97,6 +111,7 @@ build: $(BUILDDEPS) clean: rm -rf .build rm -rf $(JSDEPS) + rm -rf rs/target # Running ###################### @@ -125,7 +140,7 @@ PYCHECKDEPS := $(BUILDDEPS) .build/pycheckreqs $(shell find anki aqt -name '*.py touch $@ .build/pylint: $(PYCHECKDEPS) - pylint -j 0 --rcfile=.pylintrc -f colorized --extension-pkg-whitelist=PyQt5 anki aqt + pylint -j 0 --rcfile=.pylintrc -f colorized --extension-pkg-whitelist=PyQt5,_ankirs anki aqt touch $@ .build/pyimports: $(PYCHECKDEPS) diff --git a/README.development b/README.development index 8f257c375..4c71b82fd 100644 --- a/README.development +++ b/README.development @@ -18,6 +18,17 @@ To start, make sure you have the following installed: - mpv - lame - npm + - your platform's C compiler, eg gcc, Xcode or Visual Studio 2017. + - GNU make + +Once the above are installed, install rustup from https://rustup.rs/. You can +save some time by choosing a custom installation, and selecting a toolkit of +'none' and 'minimal' tools. + +Next, build a Python virtual environment and activate it: + +$ python3 -m venv ~/pyenv +$ . ~/pyenv/bin/activate If the distro you are using has PyQt5 installed, make sure you have the PyQt5 WebEngine module and development tools (eg pyqt5-dev-tools) installed as well. diff --git a/anki/__init__.py b/anki/__init__.py index baea42e12..19468b6df 100644 --- a/anki/__init__.py +++ b/anki/__init__.py @@ -6,6 +6,9 @@ import sys from anki.storage import Collection +# temporary +from . import rsbridge + if sys.version_info[0] < 3 or sys.version_info[1] < 5: raise Exception("Anki requires Python 3.5+") diff --git a/anki/rsbridge.py b/anki/rsbridge.py new file mode 100644 index 000000000..cf05f5fef --- /dev/null +++ b/anki/rsbridge.py @@ -0,0 +1,9 @@ +import _ankirs # pytype: disable=import-error + + +class RSBridge: + def __init__(self): + self._bridge = _ankirs.Bridge() + assert self._bridge.cmd("") == "test" + +bridge = RSBridge() diff --git a/mypy.ini b/mypy.ini index 4f76feffc..91e7b46c6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -34,3 +34,5 @@ ignore_missing_imports = True ignore_missing_imports = True [mypy-jsonschema.*] ignore_missing_imports = True +[mypy-_ankirs] +ignore_missing_imports = True diff --git a/rs/Cargo.lock b/rs/Cargo.lock new file mode 100644 index 000000000..672910bf7 --- /dev/null +++ b/rs/Cargo.lock @@ -0,0 +1,316 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +dependencies = [ + "memchr", +] + +[[package]] +name = "ankirs" +version = "0.1.0" +dependencies = [ + "pyo3", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "ctor" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ghost" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indoc" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9553c1e16c114b8b77ebeb329e5f2876eed62a8d51178c8bc6bff0d65f98f8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b714fc08d0961716390977cdff1536234415ac37b509e34e5a983def8340fb75" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "inventory" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4cece20baea71d9f3435e7bbe9adf4765f091c5fe404975f844006964a71299" +dependencies = [ + "ctor", + "ghost", + "inventory-impl", +] + +[[package]] +name = "inventory-impl" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[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", +] + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pyo3" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9df1468dddf8a59ec799cf3b930bb75ec09deabe875ba953e06c51d1077136" +dependencies = [ + "indoc", + "inventory", + "lazy_static", + "libc", + "num-traits", + "paste", + "pyo3cls", + "regex", + "serde", + "serde_json", + "spin", + "unindent", + "version_check", +] + +[[package]] +name = "pyo3-derive-backend" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6e56fb3e97b344a8f87d036f94578399402c6b75949de6270cd07928f790b1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pyo3cls" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97452dcdf5941627ebc5c06664a07821fc7fc88d7515f02178193a8ebe316468" +dependencies = [ + "proc-macro2", + "pyo3-derive-backend", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[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", +] + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "unindent" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63f18aa3b0e35fed5a0048f029558b1518095ffe2a0a31fb87c93dece93a4993" + +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" diff --git a/rs/Cargo.toml b/rs/Cargo.toml new file mode 100644 index 000000000..6638703a4 --- /dev/null +++ b/rs/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ankirs" +version = "0.1.0" +edition = "2018" +authors = ["Ankitects Pty Ltd and contributors"] + +[dependencies] + + +[dependencies.pyo3] +version = "0.8.0" +features = ["extension-module"] + +[profile.release] +lto = true +codegen-units = 1 + +[lib] +name = "_ankirs" +crate-type = ["cdylib"] diff --git a/rs/rust-toolchain b/rs/rust-toolchain new file mode 100644 index 000000000..a9fe5202c --- /dev/null +++ b/rs/rust-toolchain @@ -0,0 +1 @@ +nightly-2019-12-15 diff --git a/rs/src/lib.rs b/rs/src/lib.rs new file mode 100644 index 000000000..b70f02282 --- /dev/null +++ b/rs/src/lib.rs @@ -0,0 +1,31 @@ +use pyo3::prelude::*; +use pyo3::exceptions; + +#[pyclass] +struct Bridge { +} + +#[pymethods] +impl Bridge { + + #[new] + fn new(obj: &PyRawObject) { + obj.init({ + Bridge { } + }); + } + + fn cmd(&mut self, request: String) -> PyResult { + Ok("test".to_string()) + .map_err(|e: std::io::Error| { + exceptions::Exception::py_err(format!("{:?}", e)) + }) + } +} + +#[pymodule] +fn _ankirs(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + + Ok(()) +}