# not_redis
⚠️ **Warning**: This project was vibe-coded using [OpenCode](https://opencode.ai) and [MiniMax 2.1 Free](https://minimax.io). Code was generated by AI and may contain bugs, edge cases, or unexpected behavior. Review thoroughly before production use.
---
## Description
not_redis is a Redis-compatible in-memory data structure library written in Rust. It provides Redis-like APIs without the networking overhead, external service dependencies, or operational complexity of running a Redis server.
**Key Goals:**
- Zero-config, embeddable Redis-compatible storage
- Thread-safe concurrent access via Tokio and DashMap
- RESP-compatible data types and command semantics
- No network overhead - runs in-process with your application
- Minimal dependencies for security and simplicity
## Features
- **Strings**: GET, SET, DEL, EXISTS, EXPIRE, TTL, PERSIST
- **Hashes**: HSET, HGET, HGETALL, HDEL
- **Lists**: LPUSH, RPUSH, LLEN
- **Sets**: SADD, SMEMBERS
- **Utilities**: PING, ECHO, DBSIZE, FLUSHDB
## Installation
```toml
[dependencies]
not_redis = "0.1"
```
## Getting Started
```rust
use not_redis::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = Client::new();
client.start().await;
// String operations
client.set("user:1:name", "Alice").await?;
let name: String = client.get("user:1:name").await?;
println!("Name: {}", name);
// Hash operations
client.hset("user:1", "email", "alice@example.com").await?;
client.hset("user:1", "age", "30").await?;
let email: String = client.hget("user:1", "email").await?;
let profile: Vec<String> = client.hgetall("user:1").await?;
println!("Email: {}", email);
// List operations
client.lpush("user:1:todos", "buy milk").await?;
client.lpush("user:1:todos", "walk dog").await?;
let count: i64 = client.llen("user:1:todos").await?;
println!("Todos: {}", count);
// Set operations
client.sadd("user:1:tags", "rust").await?;
client.sadd("user:1:tags", "developer").await?;
let tags: Vec<String> = client.smembers("user:1:tags").await?;
println!("Tags: {:?}", tags);
// Expiration
client.set("temp:key", "expires soon").await?;
client.expire("temp:key", 60).await?;
let ttl: i64 = client.ttl("temp:key").await?;
println!("TTL: {} seconds", ttl);
// Utilities
let pong: String = client.ping().await?;
println!("Ping: {}", pong);
let size: i64 = client.dbsize().await?;
println!("DB size: {}", size);
// Cleanup
let _: String = client.flushdb().await?;
Ok(())
}
```
## Async Runtime
not_redis requires a Tokio runtime. If you're using it in a non-Tokio context:
```rust
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = Client::new();
client.start().await;
// ... your code ...
}
```
## API Reference
### Client
```rust
let mut client = Client::new();
client.start().await;
```
| `get(key)` | Get value by key |
| `set(key, value)` | Set key-value pair |
| `del(key)` | Delete key, returns count |
| `exists(key)` | Check if key exists |
| `expire(key, seconds)` | Set key expiration |
| `ttl(key)` | Get remaining TTL (-2=missing, -1=no expiry, >=0=seconds) |
| `persist(key)` | Remove expiration, returns success |
| `flushdb()` | Clear all keys |
### Hash Operations
| `hset(key, field, value)` | Set hash field |
| `hget(key, field)` | Get hash field |
| `hgetall(key)` | Get all fields/values |
| `hdel(key, field)` | Delete hash field |
### List Operations
| `lpush(key, value)` | Push to list head |
| `rpush(key, value)` | Push to list tail |
| `llen(key)` | Get list length |
### Set Operations
| `sadd(key, member)` | Add to set |
| `smembers(key)` | Get all members |
### Utility Operations
| `ping()` | Returns "PONG" |
| `echo(msg)` | Echo message |
| `dbsize()` | Number of keys |
## Thread Safety
not_redis uses `DashMap` for thread-safe concurrent access. Multiple threads can share a single `Client` instance.
```rust
use std::sync::Arc;
use not_redis::Client;
let client = Arc::new(Client::new());
let client_clone = client.clone();
tokio::spawn(async move {
client_clone.set("key", "value").await.unwrap();
});
```
## Error Handling
```rust
use not_redis::{Client, RedisError};
match client.get("nonexistent").await {
Ok(value) => println!("Found: {}", value),
Err(RedisError::NoSuchKey(key)) => println!("Key not found: {}", key),
Err(RedisError::WrongType) => println!("Wrong data type"),
Err(RedisError::ParseError) => println!("Parse error"),
Err(e) => println!("Other error: {:?}", e),
}
```
## Why not_redis?
| Setup | Requires Redis server | No setup needed |
| Network | TCP/IP overhead | In-process, zero-copy |
| Operations | Requires redis-cli or client | Direct API calls |
| Deployment | Additional service | Single binary |
## Limitations
- No persistence (data lost on restart)
- No networking layer
- No clustering or replication
- Limited command set (growing)
- No Lua scripting
- No pub/sub
## Contributing
Issues and PRs welcome. Note: This is a vibe-coded project - expect quirks.
## License
MIT