diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..9a44545
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,65 @@
+name: Continuous integration
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
+
+env:
+ CARGO_TERM_COLOR: always
+ REDIS_RS_REDIS_JSON_PATH: "/tmp/librejson.so"
+
+jobs:
+ check:
+ name: Check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ - run: cargo check --all-features
+
+ test:
+ name: Test Suite
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ redis:
+ - 6.2.6-v6
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ - uses: Swatinem/rust-cache@v1
+ - name: Cache redis
+ id: cache-redis
+ uses: actions/cache@v2
+ with:
+ path: |
+ /usr/bin/redis-cli
+ /usr/bin/redis-server
+ key: ${{ runner.os }}-redis
+ - name: Cache RedisJSON
+ id: cache-redisjson
+ uses: actions/cache@v2
+ with:
+ path: |
+ /tmp/librejson.so
+ key: ${{ runner.os }}-redisjson
+ - name: Start Redis
+ uses: supercharge/redis-github-action@1.5.0
+ with:
+ redis-image: redis/redis-stack
+ redis-version: ${{ matrix.redis }}
+ - run: cargo test
+ - run: cargo test --manifest-path lib/Cargo.toml
+ - run: cargo test --manifest-path lib/Cargo.toml --features json
+ - run: cargo test --manifest-path lib/Cargo.toml --features json,tokio-comp
+ - run: cargo test --all-features
diff --git a/README.md b/README.md
index cd332f6..d09e2b0 100644
--- a/README.md
+++ b/README.md
@@ -1,51 +1,69 @@
-# redis-om-rust
+# redis-om
+[![MIT licensed][mit-badge]][mit-url]
+[![Build status][gh-actions-badge]][gh-actions-url]
+[![Crates.io][crates-badge]][crates-url]
-The Unofficial Redis Object mapping that makes it easy to model Redis data in Rust. _inspired by [redis-om-python](https://github.com/redis/redis-om-python)_
+[crates-badge]: https://img.shields.io/crates/v/redis-om.svg
+[crates-url]: https://crates.io/crates/redis-om
+[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
+[mit-url]: LICENSE
+[gh-actions-badge]: https://github.com/kkharji/redis-om/workflows/Continuous%20integration/badge.svg
+[gh-actions-url]: https://github.com/kkharji/redis-om/actions?query=workflow%3A%22Continuous+integration%22
-## State
+A Rust/Redis ORM-style library that simplify the development process and reduce the amount of boilerplate code needed to build programs that leverage [redis] powerful capabilities and use cases.
-Alpha
+**Status**: *WIP, fully testsed, possible breaking changes, stay tuned*
-## Async Support
+**Features**
-To enable asynchronous clients a feature for the underlying feature need to be activated.
+- ORM-style API to define/manipulate [redis data structures] (e.g. hashes, json, streams) using [derive macros].
+- Automatic serialization/desalinization between Redis data and rust objects.
+- Interoperability with [serde](https://serde.rs/), e.g. using `rename`, `rename_all` or `serde`.
+- Nested [hash datatype](#hash) support (e.g. `list.1` or nested models `account.balance` as keys).
-```toml
-# if you use tokio
-redis-om = { version = "*", features = ["tokio-comp"] }
+**Usage**
-# if you use async-std
-redis-om = { version = "*", features = ["async-std-comp"] }
-```
+- [Getting Started](#getting-started)
+- [Using Redis's Hash Datatype](#hash)
+- [Using Redis's Json Datatype](#json)
+- [Using Redis's Stream Datatype](#stream)
+
+**Roadmap**
+
+- 0.1.0
+ - [x] Enable users to define and derive Hash Model with most common methods
+ - [x] Enable users to define and derive JSON Model with most common methods
+ - [x] Enable users to define and derive streams with managers to publish-to/read-from them.
+ - [x] Support users to choose between asynchronous and synchronous runtime.
+- 0.2.0
+ - [ ] Enable Multi-Stream Manager Support to enable users to combine multiple `RedisModels`.
+ - [ ] Support Serializing/deserializing `HashModel` complex fields using serde.
+ - [ ] Support `RedisSearch` and provide query-building API.
+ - [ ] .....
+- 0.3.0
+ - [ ] Support validation of struct fields and enum values (most likely using [validator library]).
+ - [ ] .....
+
+
+## Getting Started
-## TLS Support
```toml
+redis-om = { version = "*" }
+# TLS support with async-std
redis-om = { version = "*", features = ["tls"] }
-
-# if you use tokio
+# async support with tokio
+redis-om = { version = "*", features = ["tokio-comp"] }
+# async support with async-std
+redis-om = { version = "*", features = ["async-std-comp"] }
+# TLS and async support with tokio
redis-om = { version = "*", features = ["tokio-native-tls-comp"] }
-
-# if you use async-std
+# TLS support with async-std
redis-om = { version = "*", features = ["async-std-tls-comp"] }
```
-## Features
-
-- [serde](https://serde.rs/) interop annotations such as `rename`, `rename_all`, alias and many more.
-- Use struct methods todo all kind of crud and redis specific operations.
-- Serialize [hash](#hash) model list-like and dict-like structs as prefix keys without needing JSON (i.e. list.1, account.balance).
-- Support for [json](#json) datatype
-- Support for [stream](#json) datatype
-
-## Usage
-
-- [hash datatype macro usage](#Hash)
-- [Json datatype macro usage](#json)
-- [Stream datatype macro usage](#Hash)
+## Hash
-### Hash
-
-```rust
+```rust ignore
use redis_om::HashModel;
#[derive(HashModel, Debug, PartialEq, Eq)]
@@ -71,7 +89,7 @@ let mut jane = Customer {
};
// Get client
-let client = redis::Client::open("redis://127.0.0.1/").unwrap();
+let client = redis_om::Client::open("redis://127.0.0.1/").unwrap();
// Get connection
let mut conn = client.get_connection().unwrap();
@@ -90,12 +108,12 @@ Customer::delete(&jane.id, &mut conn).unwrap();
assert_eq!(jane_db, jane);
```
-### Json
+## Json
redis-om support json data type through `redis_om::JsonModel`. It requires that the type
derives `serde::Deserialize` as well as `serde::Serialize`.
-```rust
+```rust ignore
use redis_om::JsonModel;
use serde::{Deserialize, Serialize};
@@ -125,7 +143,7 @@ let mut john = Account {
};
// Get client
-let client = redis::Client::open("redis://127.0.0.1/").unwrap();
+let client = redis_om::Client::open("redis://127.0.0.1/").unwrap();
// Get connection
let mut conn = client.get_connection().unwrap();
@@ -143,11 +161,11 @@ Account::delete(&john.id, &mut conn).unwrap();
assert_eq!(john_db, john);
```
-### Stream
+## Stream
redis-om support json data type through `redis_om::StreamModel`. It requires that any nested type to derives `redis_om::RedisTransportValue`.
-```rust
+```rust ignore
use redis_om::{RedisTransportValue, StreamModel};
/// An enum of room service kind
@@ -169,7 +187,7 @@ pub struct RoomServiceEvent {
}
// Get client
-let client = redis::Client::open("redis://127.0.0.1/").unwrap();
+let client = redis_om::Client::open("redis://127.0.0.1/").unwrap();
// Get connection
let mut conn = client.get_connection().unwrap();
@@ -198,25 +216,13 @@ let read = manager.read(None, None, &mut conn).unwrap();
let incoming_event = read.first().unwrap();
// Get first incoming event data
let incoming_event_data = incoming_event.data::().unwrap();
-// Acknowledge that you received the event, so other in the consumer group don't get it
-incoming_event.ack(&mut conn).unwrap();
+// Acknowledge that you received the event, so other in the consumers don't get it
+RoomServiceEventManager::ack(manager.group_name(), &[&incoming_event.id], &mut conn).unwrap();
-assert_eq!(incoming_event.room, event.room);
+assert_eq!(incoming_event_data.room, event.room);
```
-## Roadmap
-
-### 0.1.0
-
-- [x] Hash Models
-- [x] Json Model
-- [x] Stream Model
-- [x] Async support
-
-### 0.2.0
-- [ ] Multi Stream Manager
-- [ ] stream model enum support
-- [ ] serializing/deserializing hash model fields using serde for hash models
-- [ ] Correctly support RedisSearch Integration with embedded types
-- [ ] Internal managed connections, i.e. no requirement to pass conn around.
-- [ ] Values Validation Support
+[derive macros]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros
+[redis data structures]: https://redis.com/redis-enterprise/data-structures/
+[redis]: https://redis.com
+[validator library]: https://crates.io/crates/validator
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index 475c037..bdd43a7 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -1,32 +1,40 @@
[package]
name = "redis-om"
-description = "The Unofficial Object mapping for Redis and Rust. Inspired by redis-om-python"
+description = "Redis ORM-style library that simplify the development process and reduce the amount of boilerplate code needed to build programs that leverage [redis] powerful capabilities and use cases."
version = "0.1.0"
edition = "2021"
authors = [ "kkharji "]
license = "MIT"
documentation = "https://docs.rs/redis-om"
-homepage = "https://github.com/kkharji/redis-om-rust"
-repository = "https://github.com/kkharji/redis-om-rust"
+homepage = "https://github.com/kkharji/redis-om"
+repository = "https://github.com/kkharji/redis-om"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[features]
-default = []
+default = [ "tokio-comp" ]
+### Enables json model support
json = [ "redis/json", "serde", "serde_json", "redis-om-macros/json" ]
+##! ### Internal features
+### Enables aio (used in tokio-native-tls-comp, tokio-comp)
aio = [ "redis/aio", "async-trait", "redis-om-macros/aio", "futures" ]
+### Enables async-tokio support
tokio-comp = ["aio", "redis/tokio-comp"]
+### Enables async-std support
async-std-comp = ["aio", "redis/async-std-comp"]
+### Enables tls support
tls = ["redis/tls"]
+### Enables async-tokio with tls support
tokio-native-tls-comp = [ "redis/tokio-native-tls-comp" ]
+### Enables async-std with tls support
async-std-tls-comp = [ "redis/async-std-tls-comp" ]
[dependencies]
tap = { version = "1.0" }
thiserror = { version = "1.0" }
-redis-om-macros = { path = "../macros" }
+redis-om-macros = { version = "0.1.0", path = "../macros" }
redis = { version = "0.22.1" }
rusty_ulid = { version = "2.0.0" }
serde = { version = "1", features = [ "derive" ], optional = true }
@@ -38,6 +46,3 @@ futures = { version = "0.3.26", optional = true }
tokio = { version = "1.25.0", features = ["full"] }
trybuild = { version = "1.0.77" }
redis-swapplex = { version = "0.4.0" }
-
-[[example]]
-name = "readme"
diff --git a/lib/examples/readme.rs b/lib/examples/readme.rs
deleted file mode 100644
index d0e6ca4..0000000
--- a/lib/examples/readme.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2023 kkharji
-//
-// Use of this source code is governed by an MIT-style
-// license that can be found in the LICENSE file or at
-// https://opensource.org/licenses/MIT.
-use redis_om::HashModel;
-
-#[derive(HashModel, PartialEq, Eq, Default)]
-struct Customer {
- pub id: String,
- pub first_name: String,
- pub last_name: String,
- pub email: String,
- pub age: u32,
- pub bio: Option,
-}
-
-#[tokio::main]
-async fn main() -> Result<(), Box> {
- let _customer = Customer::default();
-
- Ok(())
-}
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index e20627a..25b68e4 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -1,8 +1,5 @@
-//! The Unofficial Redis Object mapping that makes it easy to model Redis data in Rust. _inspired by [redis-om-python](https://github.com/redis/redis-om-python)_
-
#![deny(missing_docs, unstable_features)]
-// #![doc = include_str!("../README.md")]
-
+#![doc = include_str!("../README.md")]
mod hash_model;
#[cfg(feature = "json")]
mod json_model;
@@ -72,7 +69,5 @@ pub use redis_model::RedisModel;
pub use redissearch_model::RedisSearchModel;
pub use stream_model::StreamModel;
-
-
#[cfg(feature = "aio")]
pub use async_trait::async_trait;
diff --git a/lib/src/stream_model/async.rs b/lib/src/stream_model/async.rs
index d6466a3..cb8621f 100644
--- a/lib/src/stream_model/async.rs
+++ b/lib/src/stream_model/async.rs
@@ -8,7 +8,7 @@ use super::reply::StreamReadReply;
use super::transformers;
impl Message {
- pub async fn ack(
+ pub async fn ack(
&self,
conn: &mut C,
) -> RedisResult<()> {
diff --git a/lib/src/stream_model/mod.rs b/lib/src/stream_model/mod.rs
index 4ec89de..86842fb 100644
--- a/lib/src/stream_model/mod.rs
+++ b/lib/src/stream_model/mod.rs
@@ -31,7 +31,8 @@ mod cmds {
cmd.arg("CREATE")
.arg(S::stream_key())
.arg(s.group_name())
- .arg("$");
+ .arg("$")
+ .arg("MKSTREAM");
Ok(cmd)
}
diff --git a/lib/src/stream_model/sync.rs b/lib/src/stream_model/sync.rs
index eff84cc..0246da4 100644
--- a/lib/src/stream_model/sync.rs
+++ b/lib/src/stream_model/sync.rs
@@ -8,7 +8,7 @@ use super::reply::StreamReadReply;
use super::transformers;
impl Message {
- pub fn ack(&self, conn: &mut C) -> RedisResult<()> {
+ pub fn ack(&self, conn: &mut C) -> RedisResult<()> {
Data::ack(&self.group, &[&self.id], conn)
}
}
@@ -140,4 +140,3 @@ pub trait StreamModel: Sized {
.map(transformers::stream_range_to_messages)?
}
}
-
diff --git a/lib/tests/hash_model_async.rs b/lib/tests/hash_model_async.rs
index 5ce7ff7..0defb2c 100644
--- a/lib/tests/hash_model_async.rs
+++ b/lib/tests/hash_model_async.rs
@@ -174,7 +174,7 @@ async fn expiring_keys() -> Result {
customer.save(&mut conn).await?;
customer.expire(1, &mut conn).await?;
- std::thread::sleep(Duration::from_secs(1));
+ tokio::time::sleep(Duration::from_secs(3)).await;
let count = Cusotmer::all_pks(&mut conn).await?.count().await;
diff --git a/lib/tests/stream_model_async.rs b/lib/tests/stream_model_async.rs
index 94f060a..3270cf3 100644
--- a/lib/tests/stream_model_async.rs
+++ b/lib/tests/stream_model_async.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "aio")]
+#![cfg(feature = "tokio-comp")]
use redis_om::{RedisTransportValue, StreamModel};
use std::error::Error;
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index aec399f..7ee4387 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -6,8 +6,8 @@ edition = "2021"
authors = ["kkharji "]
license = "MIT"
documentation = "https://docs.rs/redis-om-macros"
-homepage = "https://github.com/kkharji/redis-om-rust"
-repository = "https://github.com/kkharji/redis-om-rust"
+homepage = "https://github.com/kkharji/redis-om"
+repository = "https://github.com/kkharji/redis-om"
readme = "../README.md"
[lib]