diff --git a/.github/workflows/build_test_android.yml b/.github/workflows/build_test_android.yml index ab30650..65fdb8f 100644 --- a/.github/workflows/build_test_android.yml +++ b/.github/workflows/build_test_android.yml @@ -3,8 +3,12 @@ name: Android on: push: branches: [ main ] + paths-ignore: + - "**/*.md" pull_request: branches: [ main, next ] + paths-ignore: + - "**/*.md" env: RUST_BACKTRACE: full diff --git a/.github/workflows/build_test_ios.yml b/.github/workflows/build_test_ios.yml index 10943d2..e6ad6cf 100644 --- a/.github/workflows/build_test_ios.yml +++ b/.github/workflows/build_test_ios.yml @@ -3,8 +3,12 @@ name: iOS on: push: branches: [ main ] + paths-ignore: + - "**/*.md" pull_request: branches: [ main, next ] + paths-ignore: + - "**/*.md" env: RUST_BACKTRACE: full diff --git a/.github/workflows/build_test_linux.yml b/.github/workflows/build_test_linux.yml index 6c5df93..12fbce1 100644 --- a/.github/workflows/build_test_linux.yml +++ b/.github/workflows/build_test_linux.yml @@ -3,8 +3,12 @@ name: Linux on: push: branches: [ main ] + paths-ignore: + - "**/*.md" pull_request: branches: [ main, next ] + paths-ignore: + - "**/*.md" env: RUST_BACKTRACE: full diff --git a/.github/workflows/build_test_macos.yml b/.github/workflows/build_test_macos.yml index 24af859..64ef47a 100644 --- a/.github/workflows/build_test_macos.yml +++ b/.github/workflows/build_test_macos.yml @@ -3,8 +3,12 @@ name: macOS on: push: branches: [ main ] + paths-ignore: + - "**/*.md" pull_request: branches: [ main, next ] + paths-ignore: + - "**/*.md" env: RUST_BACKTRACE: full diff --git a/.github/workflows/build_test_windows.yml b/.github/workflows/build_test_windows.yml index 865bd27..e05373a 100644 --- a/.github/workflows/build_test_windows.yml +++ b/.github/workflows/build_test_windows.yml @@ -3,8 +3,12 @@ name: Windows on: push: branches: [ main ] + paths-ignore: + - "**/*.md" pull_request: branches: [ main, next ] + paths-ignore: + - "**/*.md" env: RUST_BACKTRACE: full diff --git a/.github/workflows/release_automatic.yml b/.github/workflows/release_automatic.yml index 7c14cbd..3b74ab0 100644 --- a/.github/workflows/release_automatic.yml +++ b/.github/workflows/release_automatic.yml @@ -3,8 +3,12 @@ name: Release Automatic on: push: branches: [ main ] + paths-ignore: + - "**/*.md" pull_request: branches: [ main, next ] + paths-ignore: + - "**/*.md" jobs: diff --git a/.gitignore b/.gitignore index 6ea260c..c3d48fa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ package.json *.expanded.rs # Related to [Why do binaries have Cargo.lock in version control, but not libraries?](https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-have-cargolock-in-version-control-but-not-libraries) -Cargo.lock \ No newline at end of file +Cargo.lock + +./collected_comments.txt \ No newline at end of file diff --git a/README.md b/README.md index 4d19e73..bab329d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![Crates.io](https://img.shields.io/crates/v/native_db)](https://crates.io/crates/native_db) [![Documentation](https://docs.rs/native_db/badge.svg)](https://docs.rs/native_db) +[![Help with Chat GPT](https://img.shields.io/badge/Help%20with%20Chat%20GPT-purple?logo=openai)](https://chat.openai.com/g/g-gydO9ATky-native-db-helper) [![License](https://img.shields.io/crates/l/native_db)](LICENSE) diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/book/.gitignore @@ -0,0 +1 @@ +book diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 0000000..32d326b --- /dev/null +++ b/book/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["Vincent Herlemont"] +language = "en" +multilingual = false +src = "src" +title = "Native DB" diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 0000000..91a167e --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,12 @@ +# Summary + +- [Introduction](./introduction.md) +- [Basics](./basics.md) +- [Data Modeling](./data-modeling.md) +- [CRUD operations](./crud-operations.md) +- [Advanced queries](./advanced-queries.md) +- [Data migration](./data-migration.md) +- [Watching real-time updates](./wathing-real-time-updates.md) +- [Performance optimization](./performance-optimization.md) +- [Troubleshooting and Common Pitfalls](./troubleshooting-and-common-pitfalls.md) +- [Beyond the Basics](./beyond-the-basics.md) diff --git a/book/src/advanced-queries.md b/book/src/advanced-queries.md new file mode 100644 index 0000000..77741c4 --- /dev/null +++ b/book/src/advanced-queries.md @@ -0,0 +1 @@ +# Advanced queries diff --git a/book/src/basics.md b/book/src/basics.md new file mode 100644 index 0000000..25dcc52 --- /dev/null +++ b/book/src/basics.md @@ -0,0 +1 @@ +# Basics diff --git a/book/src/beyond-the-basics.md b/book/src/beyond-the-basics.md new file mode 100644 index 0000000..4d09203 --- /dev/null +++ b/book/src/beyond-the-basics.md @@ -0,0 +1 @@ +# Beyond the Basics diff --git a/book/src/crud-operations.md b/book/src/crud-operations.md new file mode 100644 index 0000000..1ddd1a4 --- /dev/null +++ b/book/src/crud-operations.md @@ -0,0 +1 @@ +# CRUD operations diff --git a/book/src/data-migration.md b/book/src/data-migration.md new file mode 100644 index 0000000..b63ad9c --- /dev/null +++ b/book/src/data-migration.md @@ -0,0 +1 @@ +# Data migration diff --git a/book/src/data-modeling.md b/book/src/data-modeling.md new file mode 100644 index 0000000..96ce742 --- /dev/null +++ b/book/src/data-modeling.md @@ -0,0 +1 @@ +# Data Modeling diff --git a/book/src/installation.md b/book/src/installation.md new file mode 100644 index 0000000..25267fe --- /dev/null +++ b/book/src/installation.md @@ -0,0 +1 @@ +# Installation diff --git a/book/src/introduction.md b/book/src/introduction.md new file mode 100644 index 0000000..e10b99d --- /dev/null +++ b/book/src/introduction.md @@ -0,0 +1 @@ +# Introduction diff --git a/book/src/performance-optimization.md b/book/src/performance-optimization.md new file mode 100644 index 0000000..ddf7ba2 --- /dev/null +++ b/book/src/performance-optimization.md @@ -0,0 +1 @@ +# Performance optimization diff --git a/book/src/troubleshooting-and-common-pitfalls.md b/book/src/troubleshooting-and-common-pitfalls.md new file mode 100644 index 0000000..20e3d5e --- /dev/null +++ b/book/src/troubleshooting-and-common-pitfalls.md @@ -0,0 +1 @@ +# Troubleshooting and Common Pitfalls diff --git a/book/src/wathing-real-time-updates.md b/book/src/wathing-real-time-updates.md new file mode 100644 index 0000000..0467328 --- /dev/null +++ b/book/src/wathing-real-time-updates.md @@ -0,0 +1 @@ +# Watching real-time updates diff --git a/collected_comments.txt b/collected_comments.txt new file mode 100644 index 0000000..d998eea --- /dev/null +++ b/collected_comments.txt @@ -0,0 +1,988 @@ +All database interactions here,[`r_transaction`](transaction/struct.RTransaction.html), [`rw_transaction`](transaction/struct.RwTransaction.html) and [`query`](transaction/query/index.html). +Watch data in real-time. +Macro which link [`native_model`](https://crates.io/crates/native_model) to the Native DB. See [`DatabaseBuilder.define`](struct.DatabaseBuilder.html#method.define) for more information. +All database interactions. +Read-only transaction. +Read-write transaction. +Get a value from the database. + /// +Same as [`RTransaction::get()`](struct.RTransaction.html#method.get). +Get values from the database. + /// +Same as [`RTransaction::scan()`](struct.RTransaction.html#method.scan). +Get the number of values in the database. + /// +Same as [`RTransaction::len()`](struct.RTransaction.html#method.len). +Get all values from the database. + /// +Same as [`RTransaction::drain()`](struct.RTransaction.html#method.drain). +Commit the transaction. +All changes will be applied to the database. If the commit fails, the transaction will be aborted. The +database will be unchanged. + /// +# Example +```rust +use native_db::*; + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + let db = builder.create_in_memory()?; + + // Open a read transaction + let rw = db.rw_transaction()?; + // Do some stuff.. + rw.commit()?; + /// + Ok(()) +} +``` +Insert a value into the database. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let rw = db.rw_transaction()?; + /// + // Insert a value + rw.insert(Data { id: 1 })?; + /// + // /!\ Don't forget to commit the transaction + rw.commit()?; + /// + Ok(()) +} +``` +Remove a value from the database. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let rw = db.rw_transaction()?; + /// + // Remove a value + rw.remove(Data { id: 1 })?; + /// + // /!\ Don't forget to commit the transaction + rw.commit()?; + /// + Ok(()) +} +``` +Update a value in the database. + /// +That allow to update all keys (primary and secondary) of the value. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let rw = db.rw_transaction()?; + /// + // Remove a value + rw.update(Data { id: 1 }, Data { id: 2 })?; + /// + // /!\ Don't forget to commit the transaction + rw.commit()?; + /// + Ok(()) +} +``` +Convert all values from the database. + /// +This is useful when you want to change the type/model of a value. +You have to define [`From for TargetModel`](https://doc.rust-lang.org/std/convert/trait.From.html) to convert the value. + /// +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize, Clone)] +#[native_model(id=1, version=1)] +#[native_db] +struct Dog { + #[primary_key] + name: String, +} + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=2, version=1)] +#[native_db] +struct Animal { + #[primary_key] + name: String, + #[secondary_key] + specie: String, +} + /// +impl From for Animal { + fn from(dog: Dog) -> Self { + Animal { + name: dog.name, + specie: "dog".to_string(), + } + } +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let rw = db.rw_transaction()?; + /// + // Convert all values from Dog to Animal + rw.convert_all::()?; + /// + // /!\ Don't forget to commit the transaction + rw.commit()?; + /// + Ok(()) +} +``` +Automatically migrate the data from the old model to the new model. **No matter the state of the database**, +if all models remain defined in the application as they are, the data will be migrated to the most recent version automatically. + /// +Native DB use the [`native_model`](https://crates.io/crates/native_model) identifier `id` to identify the model and `version` to identify the version of the model. +We can define a model with the same identifier `id` but with a different version `version`. + /// +In the example below we define one model with the identifier `id=1` with tow versions `version=1` and `version=2`. +- You **must** link the previous version from the new one with `from` option like `#[native_model(id=1, version=2, from=LegacyData)]`. +- You **must** define the interoperability between the two versions with implement `From for Data` and `From for LegacyData` or implement `TryFrom for Data` and `TryFrom for LegacyData`. +- You **must** define all models (by calling [`define`](#method.define)) before to call [`migration`](#method.migrate). +- You **must** call use the most recent/bigger version as the target version when you call [`migration`](#method.migrate): `migration::()`. + That means you can't call `migration::()` because `LegacyData` has version `1` and `Data` has version `2`. + /// +After call `migration::()` all data of the model `LegacyData` will be migrated to the model `Data`. + /// +Under the hood, when you call [`migration`](#method.migrate) `native_model` is used to convert the data from the old model to the new model +using the `From` or `TryFrom` implementation for each to target the version defined when you call [`migration::()`](#method.migrate). + /// +It's advisable to perform all migrations within a **single transaction** to ensure that all migrations are successfully completed. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize, Debug)] +#[native_model(id=1, version=1)] +#[native_db] +struct LegacyData { + #[primary_key] + id: u32, +} + /// +impl From for LegacyData { + fn from(data: Data) -> Self { + LegacyData { + id: data.id as u32, + } + } +} + /// +#[derive(Serialize, Deserialize, Debug)] +#[native_model(id=1, version=2, from=LegacyData)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +impl From for Data { + fn from(legacy_data: LegacyData) -> Self { + Data { + id: legacy_data.id as u64, + } + } +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + builder.define::()?; + let db = builder.create_in_memory()?; + /// + let rw = db.rw_transaction()?; + rw.migrate::()?; + // Other migrations if needed.. + rw.commit() +} +``` +Get a value from the database. +Get values from the database. +Get the number of values in the database. +Get values from the database. +Get a values from the database by primary key. +Get a values from the database by secondary key. +Scan values from the database. +Iterate over all values. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get all values + let _values: Vec = r.scan().primary()?.all().collect(); + Ok(()) +} +``` +Iterate over all values in a range. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get the values from 5 to the end + let _values: Vec = r.scan().primary()?.range(5u64..).collect(); + Ok(()) +} +``` +Iterate over all values starting with a prefix. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get the values starting with "victor" + let _values: Vec = r.scan().primary()?.start_with("victor").collect(); + Ok(()) +} +``` +Scan values from the database by secondary key. +Iterate over all values by secondary key. + /// +If the secondary key is [`optional`](struct.DatabaseBuilder.html#optional) you will +get all values that have the secondary key set. + /// +Anatomy of a secondary key it is a `enum` with the following structure: `Key::`. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key(optional)] + name: Option, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get only values that have the secondary key set (name is not None) + let _values: Vec = r.scan().secondary(DataKey::name)?.all().collect(); + Ok(()) +} +``` +Iterate over all values by secondary key. + /// +Anatomy of a secondary key it is a `enum` with the following structure: `Key::`. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get only values that have the secondary key name from C to the end + let _values: Vec = r.scan().secondary(DataKey::name)?.range("C"..).collect(); + Ok(()) +} +``` +Iterate over all values by secondary key. + /// +Anatomy of a secondary key it is a `enum` with the following structure: `Key::`. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get only values that have the secondary key name starting with "hello" + let _values: Vec = r.scan().secondary(DataKey::name)?.start_with("hello").collect(); + Ok(()) +} +``` +Get the number of values in the database. +Get the number of values. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get all values + let _number:u64 = r.len().primary::()?; + Ok(()) +} +``` +**TODO: needs to be implemented** + /// +Get the number of values by secondary key. + /// +Anatomy of a secondary key it is a `enum` with the following structure: `Key::`. + /// +If the secondary key is [`optional`](struct.DatabaseBuilder.html#optional) you will +get all values that have the secondary key set. +Get the number of values. + /// +Same as [`RLen::primary()`](struct.RLen.html#method.primary). +Get the number of values by secondary key. + /// +Same as [`RLen::secondary()`](struct.RLen.html#method.secondary). +Get a value from the database. +Get a value from the database by primary key. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get a value by primary key + let _value: Option = r.get().primary(1u64)?; + Ok(()) +} +``` +Get a value from the database by secondary key. + /// +/!\ The secondary key **must** be [`unique`](crate::DatabaseBuilder#unique) else this method will return an error [`SecondaryKeyConstraintMismatch`](crate::db_type::Error::SecondaryKeyConstraintMismatch). + If the secondary key is not unique, use [`scan()`](crate::transaction::RTransaction::scan) instead. + /// +Anatomy of a secondary key it is a `enum` with the following structure: `Key::`. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key(unique)] // Must be unique to use get() + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Get a value by primary key + let _value: Option = r.get().secondary(DataKey::name, "test")?; + Ok(()) +} +``` +Get a value from the database by primary key. + /// +Same as [`RGet::primary()`](struct.RGet.html#method.primary). +Get a value from the database by secondary key. + /// +Same as [`RGet::secondary()`](struct.RGet.html#method.secondary). +**TODO: needs to be implemented** +Builder that allows you to create a [`Database`](crate::Database) instance via [`create`](Self::create) or [`open`](Self::open) etc. and [define](Self::define) models. +Similar to [redb::Builder::new()](https://docs.rs/redb/latest/redb/struct.Builder.html#method.new). +Similar to [redb::Builder::set_cache_size()](https://docs.rs/redb/latest/redb/struct.Builder.html#method.set_cache_size). +Creates a new `Db` instance using the given path. + /// +Similar to [redb::Builder.create(...)](https://docs.rs/redb/latest/redb/struct.Builder.html#method.create) +Similar to [redb::Builder::open(...)](https://docs.rs/redb/latest/redb/struct.Builder.html#method.open) +Creates a new [`Database`](crate::Database) instance in memory. +Defines a table using the given model. + /// +Native DB depends of `native_model` to define the model. +And `native_model` by default uses [`serde`](https://serde.rs/) to serialize and deserialize the data but +you can use any other serialization library see the documentation of [`native_model`](https://github.com/vincent-herlemont/native_model) for more information. +So in the example below we import `serde` and we use the `Serialize` and `Deserialize` traits. + /// +# Primary key + /// +The primary key is *strict*, you **must**: +- define it. +- define only one. + /// +If the primary key is not defined, the compiler will return an error `Primary key is not set`. + /// +You can define with two ways: +- `#[primary_key]` on the field +- `#[native_db(primary_key())]` on any type `enum`, `struct`, `tuple struct` or `unit struct`. + /// +The primary key is **unique**, so you can't have two instances of the model with the same primary key saved in the database. + /// +## Define a simple model with a primary key +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::() +} +``` +## Define a model with a method as primary key +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db( + primary_key(custom_id) +)] +struct Data(u64); + /// +impl Data { + fn custom_id(&self) -> u32 { + (self.0 + 1) as u32 + } +} + /// +``` + /// +## Secondary key + /// +The secondary key is *flexible*, you can: +- define it or not. +- define one or more. + /// +You can define with two ways: +- `#[secondary_key]` on the field +- `#[native_db(secondary_key(, ))]` on any type `enum`, `struct`, `tuple struct` or `unit struct`. + /// +The secondary key can have two options: +- [`unique`](#unique) (default: false) +- [`optional`](#optional) (default: false) + /// +## Define a model with a secondary key +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, +} +``` + /// +## Define a model wit a secondary key optional and unique +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key(unique, optional)] + name: Option, +} +``` +- Note: the secondary key can be `unique` **or** `optional` as well. + /// +## Unique + /// +This means that each instance of the model must have a unique value for the secondary key. +If the value is not unique, the [`insert`](crate::transaction::RwTransaction::insert) method will return an error. + /// +## Optional + /// +This means that an instance of the model can have a value for the secondary key or not. +When`optional` is set the value **must** be an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html). +if the value is not an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) the compiler will return +an error `error[E0282]: type annotations needed: cannot infer type`. + +Under the hood, the secondary key is stored in a separate redb table. So if the secondary key is optional, +the value will be stored in the table only if the value is not `None`. + /// +# Define a model with a secondary key and a custom secondary key optional +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db( + secondary_key(custom_name, optional) +)] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, + flag: bool, +} + /// +impl Data { + fn custom_name(&self) -> Option { + if self.flag { + Some(self.name.clone().to_uppercase()) + } else { + None + } + } +} +``` +# Define multiple models + /// +To define multiple models, you **must** use different `id` for each model. If you use the same `id` for two models, +the program will panic with the message `The table has the same native model version as the table and it's not allowed`. + /// +Example: +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Animal { + #[primary_key] + name: String, +} + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=2, version=1)] +#[native_db] +struct Vegetable { + #[primary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + builder.define::() +} +``` +The database instance. Allows you to create [rw_transaction](database/struct.Database.html#method.rw_transaction) and [r_transaction](database/struct.Database.html#method.r_transaction), [watch](database/struct.Database.html#method.watch) queries, and [unwatch](database/struct.Database.html#method.unwatch) etc. +/// +# Example +```rust +use native_db::*; +/// +fn main() -> Result<(), db_type::Error> { + let builder = DatabaseBuilder::new(); + // Define models ... + let db = builder.create_in_memory()?; + // Open transactions + // Watch data + // Create snapshots + // etc... + Ok(()) +} +Creates a new read-write transaction. +Creates a new read-only transaction. +Watch queries. +Unwatch the given `id`. +You can get the `id` from the return value of [`watch`](Self::watch). +If the `id` is not valid anymore, this function will do nothing. +If the `id` is valid, the corresponding watcher will be removed. +Watch multiple values. +Watch multiple values. +Watch all values. +Watch all values by secondary key. +Watch all values. +Watch all values. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Watch all values + let (_recv, _id) = db.watch().scan().primary().all::()?; + Ok(()) +} +``` +**TODO: needs to be implemented** +Watch all values starting with the given key. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Watch all values starting with "test" + let (_recv, _id) = db.watch().scan().primary().start_with::("test")?; + Ok(()) +} +``` +Watch all values by secondary key. +Watch all values by secondary key. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Watch all values by secondary key "name" + let (_recv, _id) = db.watch().scan().secondary(DataKey::name).all::()?; + Ok(()) +} +``` +Watch all values starting with the given key. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Open a read transaction + let r = db.r_transaction()?; + + // Watch all values by secondary key "name" starting with "test" + let (_recv, _id) = db.watch().scan().secondary(DataKey::name).start_with::("test")?; + Ok(()) +} +``` +Watch queries. +Watch only one value. +Watch multiple values. +Watch only one value. +Watch the primary key. + /// +Returns a channel receiver and the watcher id. +The watcher id can be used to unwatch the channel. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Watch the primary key + let (_recv, _id) = db.watch().get().primary::(1u64)?; + Ok(()) +} +``` +Watch the secondary key. + /// +Returns a channel receiver and the watcher id. +The watcher id can be used to unwatch the channel. + /// +# Example +```rust +use native_db::*; +use native_model::{native_model, Model}; +use serde::{Deserialize, Serialize}; + /// +#[derive(Serialize, Deserialize)] +#[native_model(id=1, version=1)] +#[native_db] +struct Data { + #[primary_key] + id: u64, + #[secondary_key] + name: String, +} + /// +fn main() -> Result<(), db_type::Error> { + let mut builder = DatabaseBuilder::new(); + builder.define::()?; + let db = builder.create_in_memory()?; + + // Watch the secondary key name + let (_recv, _id) = db.watch().get().secondary::(DataKey::name, "test")?; + Ok(()) +} +``` +Index selection Enum for [#struct_name] diff --git a/justfile b/justfile index deee47d..2f16922 100644 --- a/justfile +++ b/justfile @@ -28,4 +28,11 @@ test_all: test_no_default test_default test_with_optional expand test_file_name: rm -f {{test_file_name}}.expanded.rs; \ - cargo expand --test {{test_file_name}} | save --raw {{test_file_name}}.expanded.rs \ No newline at end of file + cargo expand --test {{test_file_name}} | save --raw {{test_file_name}}.expanded.rs + +serve_docs: + cd book; mdbook serve --open + +bot_collect_rust_docs: + #!/usr/bin/env bash + find . -name '*.rs' -exec grep -h '^\s*///' {} \; | sed 's/^\s*\/\/\/ //' > collected_comments.txt