diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..b4f6ef62 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,181 @@ +# This file is autogenerated by maturin v1.7.8 +# To update, run +# +# maturin generate-ci github +# +name: CI + +on: + push: + branches: + - main + - master + tags: + - '*' + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + linux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + - runner: ubuntu-22.04 + target: s390x + - runner: ubuntu-22.04 + target: ppc64le + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-linux-${{ matrix.platform.target }} + path: dist + + musllinux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + manylinux: musllinux_1_2 + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-musllinux-${{ matrix.platform.target }} + path: dist + + windows: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: windows-latest + target: x64 + - runner: windows-latest + target: x86 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + architecture: ${{ matrix.platform.target }} + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-windows-${{ matrix.platform.target }} + path: dist + + macos: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: macos-13 + target: x86_64 + - runner: macos-14 + target: aarch64 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-macos-${{ matrix.platform.target }} + path: dist + + sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: wheels-sdist + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} + needs: [linux, musllinux, windows, macos, sdist] + permissions: + # Use to sign the release artifacts + id-token: write + # Used to upload release artifacts + contents: write + # Used to generate artifact attestation + attestations: write + steps: + - uses: actions/download-artifact@v4 + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: 'wheels-*/*' + - name: Publish to PyPI + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: PyO3/maturin-action@v1 + env: + MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + with: + command: upload + args: --non-interactive --skip-existing wheels-*/* diff --git a/.github/workflows/cross_build.yml b/.github/workflows/cross_build.yml index c2103a1e..0863729d 100644 --- a/.github/workflows/cross_build.yml +++ b/.github/workflows/cross_build.yml @@ -3,7 +3,7 @@ name: cross-build on: push: branches: - - improve-and-fix-workflow + - embedded-lib-cross-build # pull_request: # types: [closed] # branches: @@ -18,7 +18,7 @@ jobs: run: "echo 'Waiting for other workflow to complete...'" build: - if: github.event.pull_request.merged == true +# if: github.event.pull_request.merged == true name: Build C release for ${{ matrix.os.name }} strategy: matrix: diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..3ad977bf --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rust_surrealdb" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "rust_surrealdb" +crate-type = ["cdylib"] + +[dependencies] +hex = "0.4.3" +pyo3 = "0.23.1" +surrealdb = { version = "2.1.4", features = ["kv-surrealkv", "kv-mem", "http"] } +thiserror = "2.0.9" +tokio = "1.42.0" +uuid = { version = "1.11.0", features = ["v4"] } diff --git a/MANIFEST.in b/MANIFEST.in index 3c666bc3..f5b79e7a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ recursive-include surrealdb *.so -recursive-include surrealdb *.txt \ No newline at end of file +recursive-include surrealdb *.txt diff --git a/libsrc/libsurrealdb_c.dylib b/libsrc/libsurrealdb_c.dylib deleted file mode 100755 index 73885b12..00000000 Binary files a/libsrc/libsurrealdb_c.dylib and /dev/null differ diff --git a/libsrc/surrealdb.h b/libsrc/surrealdb.h deleted file mode 100644 index f27490e2..00000000 --- a/libsrc/surrealdb.h +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include -#include -#include - -#define sr_SR_NONE 0 - -#define sr_SR_CLOSED -1 - -#define sr_SR_ERROR -2 - -#define sr_SR_FATAL -3 - -typedef enum sr_action { - SR_ACTION_CREATE, - SR_ACTION_UPDATE, - SR_ACTION_DELETE, -} sr_action; - -typedef struct sr_opaque_object_internal_t sr_opaque_object_internal_t; - -typedef struct sr_RpcStream sr_RpcStream; - -/** - * may be sent across threads, but must not be aliased - */ -typedef struct sr_stream_t sr_stream_t; - -/** - * The object representing a Surreal connection - * - * It is safe to be referenced from multiple threads - * If any operation, on any thread returns SR_FATAL then the connection is poisoned and must not be used again. - * (use will cause the program to abort) - * - * should be freed with sr_surreal_disconnect - */ -typedef struct sr_surreal_t sr_surreal_t; - -/** - * The object representing a Surreal connection - * - * It is safe to be referenced from multiple threads - * If any operation, on any thread returns SR_FATAL then the connection is poisoned and must not be used again. - * (use will cause the program to abort) - * - * should be freed with sr_surreal_disconnect - */ -typedef struct sr_surreal_rpc_t sr_surreal_rpc_t; - -typedef char *sr_string_t; - -typedef struct sr_object_t { - struct sr_opaque_object_internal_t *_0; -} sr_object_t; - -typedef enum sr_number_t_Tag { - SR_NUMBER_INT, - SR_NUMBER_FLOAT, -} sr_number_t_Tag; - -typedef struct sr_number_t { - sr_number_t_Tag tag; - union { - struct { - int64_t sr_number_int; - }; - struct { - double sr_number_float; - }; - }; -} sr_number_t; - -typedef struct sr_duration_t { - uint64_t secs; - uint32_t nanos; -} sr_duration_t; - -typedef struct sr_uuid_t { - uint8_t _0[16]; -} sr_uuid_t; - -typedef struct sr_bytes_t { - uint8_t *arr; - int len; -} sr_bytes_t; - -typedef enum sr_id_t_Tag { - SR_ID_NUMBER, - SR_ID_STRING, - SR_ID_ARRAY, - SR_ID_OBJECT, -} sr_id_t_Tag; - -typedef struct sr_id_t { - sr_id_t_Tag tag; - union { - struct { - int64_t sr_id_number; - }; - struct { - sr_string_t sr_id_string; - }; - struct { - struct sr_array_t *sr_id_array; - }; - struct { - struct sr_object_t sr_id_object; - }; - }; -} sr_id_t; - -typedef struct sr_thing_t { - sr_string_t table; - struct sr_id_t id; -} sr_thing_t; - -typedef enum sr_value_t_Tag { - SR_VALUE_NONE, - SR_VALUE_NULL, - SR_VALUE_BOOL, - SR_VALUE_NUMBER, - SR_VALUE_STRAND, - SR_VALUE_DURATION, - SR_VALUE_DATETIME, - SR_VALUE_UUID, - SR_VALUE_ARRAY, - SR_VALUE_OBJECT, - SR_VALUE_BYTES, - SR_VALUE_THING, -} sr_value_t_Tag; - -typedef struct sr_value_t { - sr_value_t_Tag tag; - union { - struct { - bool sr_value_bool; - }; - struct { - struct sr_number_t sr_value_number; - }; - struct { - sr_string_t sr_value_strand; - }; - struct { - struct sr_duration_t sr_value_duration; - }; - struct { - sr_string_t sr_value_datetime; - }; - struct { - struct sr_uuid_t sr_value_uuid; - }; - struct { - struct sr_array_t *sr_value_array; - }; - struct { - struct sr_object_t sr_value_object; - }; - struct { - struct sr_bytes_t sr_value_bytes; - }; - struct { - struct sr_thing_t sr_value_thing; - }; - }; -} sr_value_t; - -typedef struct sr_array_t { - struct sr_value_t *arr; - int len; -} sr_array_t; - -/** - * when code = 0 there is no error - */ -typedef struct sr_SurrealError { - int code; - sr_string_t msg; -} sr_SurrealError; - -typedef struct sr_arr_res_t { - struct sr_array_t ok; - struct sr_SurrealError err; -} sr_arr_res_t; - -typedef struct sr_option_t { - bool strict; - uint8_t query_timeout; - uint8_t transaction_timeout; -} sr_option_t; - -typedef struct sr_notification_t { - struct sr_uuid_t query_id; - enum sr_action action; - struct sr_value_t data; -} sr_notification_t; - -/** - * connects to a local, remote, or embedded database - * - * if any function returns SR_FATAL, this must not be used (except to drop) (TODO: check this is safe) doing so will cause the program to abort - * - * # Examples - * - * ```c - * sr_string_t err; - * sr_surreal_t *db; - * - * // connect to in-memory instance - * if (sr_connect(&err, &db, "mem://") < 0) { - * printf("error connecting to db: %s\n", err); - * return 1; - * } - * - * // connect to surrealkv file - * if (sr_connect(&err, &db, "surrealkv://test.skv") < 0) { - * printf("error connecting to db: %s\n", err); - * return 1; - * } - * - * // connect to surrealdb server - * if (sr_connect(&err, &db, "wss://localhost:8000") < 0) { - * printf("error connecting to db: %s\n", err); - * return 1; - * } - * - * sr_surreal_disconnect(db); - * ``` - */ -int sr_connect(sr_string_t *err_ptr, - struct sr_surreal_t **surreal_ptr, - const char *endpoint); - -/** - * disconnect a database connection - * note: the Surreal object must not be used after this function has been called - * any object allocations will still be valid, and should be freed, using the appropriate function - * TODO: check if Stream can be freed after disconnection because of rt - * - * # Examples - * - * ```c - * sr_surreal_t *db; - * // connect - * disconnect(db); - * ``` - */ -void sr_surreal_disconnect(struct sr_surreal_t *db); - -/** - * create a record - * - */ -int sr_create(const struct sr_surreal_t *db, - sr_string_t *err_ptr, - struct sr_object_t **res_ptr, - const char *resource, - const struct sr_object_t *content); - -/** - * make a live selection - * if successful sets *stream_ptr to be an exclusive reference to an opaque Stream object - * which can be moved accross threads but not aliased - * - * # Examples - * - * sr_stream_t *stream; - * if (sr_select_live(db, &err, &stream, "foo") < 0) - * { - * printf("%s", err); - * return 1; - * } - * - * sr_notification_t not ; - * if (sr_stream_next(stream, ¬ ) > 0) - * { - * sr_print_notification(¬ ); - * } - * sr_stream_kill(stream); - */ -int sr_select_live(const struct sr_surreal_t *db, - sr_string_t *err_ptr, - struct sr_stream_t **stream_ptr, - const char *resource); - -int sr_query(const struct sr_surreal_t *db, - sr_string_t *err_ptr, - struct sr_arr_res_t **res_ptr, - const char *query, - const struct sr_object_t *vars); - -/** - * select a resource - * - * can be used to select everything from a table or a single record - * writes values to *res_ptr, and returns number of values - * result values are allocated by Surreal and must be freed with sr_free_arr - * - * # Examples - * - * ```c - * sr_surreal_t *db; - * sr_string_t err; - * sr_value_t *foos; - * int len = sr_select(db, &err, &foos, "foo"); - * if (len < 0) { - * printf("%s", err); - * return 1; - * } - * ``` - * for (int i = 0; i < len; i++) - * { - * sr_value_print(&foos[i]); - * } - * sr_free_arr(foos, len); - */ -int sr_select(const struct sr_surreal_t *db, - sr_string_t *err_ptr, - struct sr_value_t **res_ptr, - const char *resource); - -/** - * select database - * NOTE: namespace must be selected first with sr_use_ns - * - * # Examples - * ```c - * sr_surreal_t *db; - * sr_string_t err; - * if (sr_use_db(db, &err, "test") < 0) - * { - * printf("%s", err); - * return 1; - * } - * ``` - */ -int sr_use_db(const struct sr_surreal_t *db, sr_string_t *err_ptr, const char *query); - -/** - * select namespace - * NOTE: database must be selected before use with sr_use_db - * - * # Examples - * ```c - * sr_surreal_t *db; - * sr_string_t err; - * if (sr_use_ns(db, &err, "test") < 0) - * { - * printf("%s", err); - * return 1; - * } - * ``` - */ -int sr_use_ns(const struct sr_surreal_t *db, sr_string_t *err_ptr, const char *query); - -/** - * returns the db version - * NOTE: version is allocated in Surreal and must be freed with sr_free_string - * # Examples - * ```c - * sr_surreal_t *db; - * sr_string_t err; - * sr_string_t ver; - * - * if (sr_version(db, &err, &ver) < 0) - * { - * printf("%s", err); - * return 1; - * } - * printf("%s", ver); - * sr_free_string(ver); - * ``` - */ -int sr_version(const struct sr_surreal_t *db, sr_string_t *err_ptr, sr_string_t *res_ptr); - -int sr_surreal_rpc_new(sr_string_t *err_ptr, - struct sr_surreal_rpc_t **surreal_ptr, - const char *endpoint, - struct sr_option_t options); - -/** - * execute rpc - * - * free result with sr_free_byte_arr - */ -int sr_surreal_rpc_execute(const struct sr_surreal_rpc_t *self, - sr_string_t *err_ptr, - uint8_t **res_ptr, - const uint8_t *ptr, - int len); - -int sr_surreal_rpc_notifications(const struct sr_surreal_rpc_t *self, - sr_string_t *err_ptr, - struct sr_RpcStream **stream_ptr); - -void sr_surreal_rpc_free(struct sr_surreal_rpc_t *ctx); - -void sr_free_arr(struct sr_value_t *ptr, int len); - -void sr_free_bytes(struct sr_bytes_t bytes); - -void sr_free_byte_arr(uint8_t *ptr, int len); - -void sr_print_notification(const struct sr_notification_t *notification); - -const struct sr_value_t *sr_object_get(const struct sr_object_t *obj, const char *key); - -struct sr_object_t sr_object_new(void); - -void sr_object_insert(struct sr_object_t *obj, const char *key, const struct sr_value_t *value); - -void sr_object_insert_str(struct sr_object_t *obj, const char *key, const char *value); - -void sr_object_insert_int(struct sr_object_t *obj, const char *key, int value); - -void sr_object_insert_float(struct sr_object_t *obj, const char *key, float value); - -void sr_object_insert_double(struct sr_object_t *obj, const char *key, double value); - -void sr_free_object(struct sr_object_t obj); - -void sr_free_arr_res(struct sr_arr_res_t res); - -void sr_free_arr_res_arr(struct sr_arr_res_t *ptr, int len); - -/** - * blocks until next item is recieved on stream - * will return 1 and write notification to notification_ptr is recieved - * will return SR_NONE if the stream is closed - */ -int sr_stream_next(struct sr_stream_t *self, struct sr_notification_t *notification_ptr); - -void sr_stream_kill(struct sr_stream_t *stream); - -void sr_free_string(sr_string_t string); - -void sr_value_print(const struct sr_value_t *val); - -bool sr_value_eq(const struct sr_value_t *lhs, const struct sr_value_t *rhs); diff --git a/pyproject.toml b/pyproject.toml index 142c8fb0..92e1f062 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,8 @@ classifiers = [ ] [build-system] -requires = ["setuptools", "wheel", "setuptools-rust"] +requires = ["setuptools", "wheel", "setuptools-rust", "maturin>=1.7,<2.0"] +build-backend = "maturin" [tool.poetry.dependencies] python = "^3.10" @@ -34,3 +35,6 @@ ruff = "^0.8.0" black = "^24.10.0" mypy = "^1.13.0" types-requests = "^2.32.0.20241016" + +[tool.maturin] +features = ["pyo3/extension-module"] diff --git a/setup.py b/setup.py index 53bde2d5..e31bc0aa 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,6 @@ import pathlib from setuptools import setup -from setuptools_rust import Binding, RustExtension with open("README.md", "r") as fh: @@ -14,18 +13,18 @@ setup( - name="surrealdb-beta", - author="Maxwell Flitton", + name="surrealdb", + author="SurrealDB", author_email="maxwell@gmail.com", description="SurrealDB python client.", long_description=long_description, long_description_content_type="text/markdown", version=version, - rust_extensions=[RustExtension("surrealdb.rust_surrealdb", binding=Binding.PyO3)], + # package_dir={"surrealdb": "surrealdb"}, packages=[ "surrealdb", - "surrealdb.execution_mixins", - "surrealdb.async_execution_mixins" + "surrealdb.data", + "surrealdb.data.types", ], package_data={ "surrealdb": ["binaries/*"], diff --git a/src/connection.rs b/src/connection.rs new file mode 100644 index 00000000..1a7f3895 --- /dev/null +++ b/src/connection.rs @@ -0,0 +1,165 @@ +use pyo3::pyclass; +use std::collections::BTreeMap; +use std::error; +use std::sync::RwLock; +use std::time::Duration; +use surrealdb; +use surrealdb::dbs::Session; +use surrealdb::kvs::Datastore; +use surrealdb::rpc::format::cbor; +use surrealdb::rpc::method::Method; +use surrealdb::rpc::{Data, RpcContext}; +use surrealdb::sql; + +#[derive(Debug)] +pub enum AdapterError { + RuntimeInitFailedErr, + ConnectionInitFailedErr, +} + +struct SurrealRpc { + kvs: Datastore, + sess: Session, + vars: BTreeMap, +} + +#[pyclass] +pub struct Adapter { + rpc: RwLock, +} + +pub struct AdapterConfig { + pub strict: bool, + pub query_timeout: Option, + pub transaction_timeout: Option, +} + +impl RpcContext for SurrealRpc { + fn kvs(&self) -> &Datastore { + &self.kvs + } + + fn session(&self) -> &Session { + &self.sess + } + + fn session_mut(&mut self) -> &mut Session { + &mut self.sess + } + + fn vars(&self) -> &std::collections::BTreeMap { + &self.vars + } + + fn vars_mut(&mut self) -> &mut std::collections::BTreeMap { + &mut self.vars + } + + fn version_data(&self) -> Data { + let ver_str = surrealdb::env::VERSION.to_string(); + ver_str.into() + } + + const LQ_SUPPORT: bool = true; + async fn handle_live(&self, _lqid: &uuid::Uuid) {} + async fn handle_kill(&self, _lqid: &uuid::Uuid) {} +} + +pub async fn make_connection( + address: &str, + adapter_config: AdapterConfig, +) -> Result> { + let kvs = match Datastore::new(address).await { + Ok(kvs) => { + if let Err(error) = kvs.check_version().await { + return Err(error.into()); + }; + if let Err(error) = kvs.bootstrap().await { + return Err(error.into()); + } + kvs + } + Err(error) => { + return Err(error.into()); + } + }; + + let kvs = kvs + .with_notifications() + .with_strict_mode(adapter_config.strict) + .with_query_timeout(adapter_config.query_timeout) + .with_transaction_timeout(adapter_config.transaction_timeout); + + let rpc = SurrealRpc { + kvs, + sess: Session::default().with_rt(true), + vars: BTreeMap::default(), + }; + + Ok(Adapter { + rpc: RwLock::new(rpc), + }) +} + +pub async fn execute( + adapter: &Adapter, + request_data: Vec, +) -> Result, Box> { + let in_data = cbor::req(request_data)?; + + let method = Method::parse(in_data.method); + let res = match method.needs_mutability() { + true => { + adapter + .rpc + .write() + .unwrap() + .execute_mutable(method, in_data.params) + .await + } + false => { + adapter + .rpc + .read() + .unwrap() + .execute_immutable(method, in_data.params) + .await + } + }?; + + let out = cbor::res(res)?; + Ok(out) +} + +#[cfg(test)] +mod tests { + use crate::connection::{execute, make_connection, AdapterConfig}; + use hex; + use std::time::Duration; + + #[test] + fn test_get_connection() { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + let con = make_connection( + "memory", + AdapterConfig { + strict: true, + query_timeout: Option::from(Duration::new(10, 0)), + transaction_timeout: Option::from(Duration::new(10, 0)), + }, + ) + .await + .unwrap(); + + let setup_ns_and_db_req = + hex::decode("a36269646a78744533413077566571666d6574686f646375736566706172616d738267746573745f6e7367746573745f6462") + .unwrap(); + + let res = execute(&con, setup_ns_and_db_req).await.unwrap(); + + // let request_data = cbor::res(request).unwrap(); + println!("{:?}", hex::encode(&res)); + }) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..8666cac8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,33 @@ +use pyo3::prelude::*; +use pyo3::pymodule; + +mod connection; +mod python; + +/// A Python module implemented in Rust. +#[pymodule] +fn rust_surrealdb(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(python::sum_as_string, m)?)?; + m.add_wrapped(wrap_pyfunction!(python::rust_connect)).expect("TODO: panic message"); + m.add_wrapped(wrap_pyfunction!(python::rust_execute)).expect("TODO: panic message"); + Ok(()) +} + +/// Wraps a future into a python object. The example code is the following: +/// ```rust +/// pyo3_asyncio::tokio::future_into_py(py, async move { +/// let wrapped_connection = make_connection(url).await +/// .map_err(|e| PyErr::new::(e))?; +/// Ok(wrapped_connection) +/// }) +/// ``` +#[macro_export] +macro_rules! py_future_wrapper { + ($py:expr, $func:ident($($arg:expr),*)) => { + pyo3_asyncio::tokio::future_into_py($py, async move { + let wrapped_connection = $func($($arg),*).await + .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{:?}", e)))?; + Ok(wrapped_connection) + }) + }; +} diff --git a/src/python.rs b/src/python.rs new file mode 100644 index 00000000..28ae7a86 --- /dev/null +++ b/src/python.rs @@ -0,0 +1,43 @@ +use crate::connection::{execute, make_connection, Adapter, AdapterConfig}; +use pyo3::exceptions::PyTypeError; +use pyo3::prelude::*; +use std::time::Duration; + +/// Formats the sum of two numbers as string. +#[pyfunction] +pub fn sum_as_string(a: usize, b: usize) -> PyResult { + Ok((a + b).to_string()) +} + +#[pyfunction] +pub fn rust_connect(_py: Python, address: &str) -> PyResult { + let connect = tokio::runtime::Runtime::new() + .unwrap() + .block_on(make_connection( + address, + AdapterConfig { + strict: true, + query_timeout: std::option::Option::from(Duration::new(10, 0)), + transaction_timeout: std::option::Option::from(Duration::new(10, 0)), + }, + )) + .map_err(|e| { + println!("{}", e); + PyErr::new::("error") + })?; + + Ok(connect) +} + +#[pyfunction] +pub fn rust_execute(_py: Python, adapter: &Adapter, request_data: Vec) -> PyResult> { + let connect = tokio::runtime::Runtime::new() + .unwrap() + .block_on(execute(adapter, request_data)) + .map_err(|e| { + println!("{}", e); + PyErr::new::("error") + })?; + + Ok(connect) +} diff --git a/surrealdb/VERSION.txt b/surrealdb/VERSION.txt index b217dcd1..1b71c507 100644 --- a/surrealdb/VERSION.txt +++ b/surrealdb/VERSION.txt @@ -1 +1 @@ -VERSION='0.1.0' \ No newline at end of file +VERSION='0.4.3' \ No newline at end of file diff --git a/surrealdb/connection_clib.py b/surrealdb/connection_clib.py index f2ce6960..5b3b54de 100644 --- a/surrealdb/connection_clib.py +++ b/surrealdb/connection_clib.py @@ -10,15 +10,15 @@ def get_lib_path() -> str: if platform.system() == "Linux": - lib_extension = ".so" + lib_file = "libsurrealdb_c.so" elif platform.system() == "Darwin": - lib_extension = ".dylib" + lib_file = "libsurrealdb_c.dylib" elif platform.system() == "Windows": - lib_extension = ".dll" + lib_file = "surrealdb_c.dll" else: raise SurrealDbConnectionError("Unsupported operating system") - lib_path = os.path.join(CLIB_FOLDER_PATH, f"libsurrealdb_c{lib_extension}") + lib_path = os.path.join(CLIB_FOLDER_PATH, f"{lib_file}") if os.path.isfile(lib_path) is not True: raise Exception(f"{lib_path} is missing") diff --git a/surrealdb/connection_rustlib.py b/surrealdb/connection_rustlib.py new file mode 100644 index 00000000..6f36b42e --- /dev/null +++ b/surrealdb/connection_rustlib.py @@ -0,0 +1,9 @@ +import rust_surrealdb + +from surrealdb.connection import Connection + + +class RustLibConnection(Connection): + async def connect(self): + print(rust_surrealdb.sum_as_string()) + self._connection = await self._make_connection(url=self.url) diff --git a/tests/unit/test_rustlib_connection.py b/tests/unit/test_rustlib_connection.py new file mode 100644 index 00000000..1889c95f --- /dev/null +++ b/tests/unit/test_rustlib_connection.py @@ -0,0 +1,31 @@ +import base64 +import logging +import uuid + +import rust_surrealdb + +from unittest import IsolatedAsyncioTestCase + +from surrealdb.connection import request_id +from surrealdb.connection_http import HTTPConnection +from surrealdb.data.cbor import encode, decode + + +class TestHTTPConnection(IsolatedAsyncioTestCase): + async def asyncSetUp(self): + logger = logging.getLogger(__name__) + + async def test_default_method(self): + enc_use = encode({ + "id": request_id(10), + "method": "use", + "params": ["test_ns", "test_db"], + }) + res = ''.join(format(x, '02x') for x in enc_use) + + print(res) + # print(rust_surrealdb.sum_as_string(1, 2)) + # try: + # con = rust_surrealdb.rust_connect("memory") + # except Exception as e: + # print("Err")