Metadata-Version: 2.4
Name: rustybase
Version: 0.1.1
Summary: Official Python SDK for RustyBase Database
Author-email: RustyBase Team <info@rustybase.io>
Project-URL: Homepage, https://rustybase.io
Project-URL: Repository, https://github.com/diptanshumahish/rustybase-python-sdk
Project-URL: Issues, https://github.com/diptanshumahish/rustybase-python-sdk/issues
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24.0
Requires-Dist: pymongo>=4.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: respx>=0.20.1; extra == "dev"
Requires-Dist: streamlit>=1.20.0; extra == "dev"
Dynamic: license-file

# RustyBase Python SDK 🐍

The official Python client for **RustyBase**, a high-performance, embedded-friendly database system with a MongoDB-like API.

[![PyPI version](https://badge.fury.io/py/rustybase.svg)](https://badge.fury.io/py/rustybase)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## 📚 Table of Contents

- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [Client Configuration](#-client-configuration)
- [Authentication](#-authentication)
- [CRUD Operations](#-crud-operations)
  - [Insert](#insert)
  - [Find & Query](#find--query)
  - [Update](#update)
  - [Delete](#delete)
- [Filtering & Operators](#-filtering--operators)
- [Aggregation Framework](#-aggregation-framework)
- [Async Support](#-async-support)
- [Admin Operations](#-admin-operations)
- [Error Handling](#-error-handling)

---

## 📦 Installation

```bash
pip install rustybase
```

## 🚀 Quick Start

```python
from rustybase import RustyBaseClient

# 1. Connect
client = RustyBaseClient(connection_string="rustybase://admin:password@localhost:3000/mydb")

# 2. Authenticate
client.login()

# 3. Get Collection
users = client.db("mydb").collection("users")

# 4. Insert Data
users.insert_one({"name": "Alice", "role": "engineer", "age": 28})

# 5. Find Data
alice = users.find_one({"name": "Alice"})
print(f"Found user: {alice}")
```

---

## 🛠 Client Configuration

You can initialize the client using a connection string or individual parameters.

### Connection String (Recommended)

```python
client = RustyBaseClient(
    connection_string="rustybase://username:password@host:port/database?authSource=admin"
)
```

### Individual Parameters

```python
client = RustyBaseClient(
    host="127.0.0.1",
    port=3000,
    username="admin",
    password="password",
    database="default",
    timeout=30.0,
    use_signing=False  # Enable HMAC request signing for extra security
)
```

| Parameter           | Type       | Default | Description                                                           |
| :------------------ | :--------- | :------ | :-------------------------------------------------------------------- |
| `connection_string` | `str`      | `None`  | URI formatted connection string.                                      |
| `use_signing`       | `bool`     | `False` | If `True`, adds `X-RB-Signature` headers to requests.                 |
| `timeout`           | `float`    | `30.0`  | Request timeout in seconds.                                           |
| `request_callback`  | `Callable` | `None`  | Optional callback (`fn(method, url, payload, response)`) for logging. |

---

## 🔐 Authentication

RustyBase uses JWT authentication. You must login before performing operations. The SDK handles **automatic token refreshing** for you.

```python
# Authenticate and obtain tokens
client.login()

# ... perform operations ...

# Tokens are auto-refreshed on 401 errors.
```

---

## 💾 CRUD Operations

### Insert

**Insert One**

```python
doc = {"name": "Bob", "tags": ["developer", "python"]}
result = users.insert_one(doc)
# result: {"inserted_count": 1, "inserted_ids": [...]}
```

**Insert Many**

```python
docs = [
    {"name": "Charlie", "age": 25},
    {"name": "Dana", "age": 30}
]
users.insert_many(docs)
```

### Find & Query

The `find` method is powerful and supports filtering, projection, sorting, implementation, and pagination.

```python
results = users.find(
    filter={"age": {"$gt": 25}},   # Query criteria
    projection={"name": 1},        # Fields to include (1) or exclude (0)
    sort={"age": -1},              # 1 for Ascending, -1 for Descending
    limit=10,                      # Max documents to return
    skip=0,                        # Documents to skip
    auto_indexing=True             # Allow ad-hoc indexing for performance
)
```

**Find One**
Helper to return a single document or `None`.

```python
user = users.find_one({"_id": "some_id"})
```

### Update

Updates a single document matching the filter.

```python
users.update_one(
    filter={"name": "Alice"},
    update={"$set": {"active": True}, "$inc": {"age": 1}},
    upsert=True  # Create if it doesn't exist
)
```

### Delete

Removes a document matching the filter.

```python
users.delete_one({"name": "Charlie"})
```

---

## 🔍 Filtering & Operators

RustyBase supports a rich set of MongoDB-compatible query operators.

### Comparison Operators

| Operator | Description                                                         | Example                                       |
| :------- | :------------------------------------------------------------------ | :-------------------------------------------- |
| `$eq`    | Matches values that are equal to a specified value.                 | `{"age": {"$eq": 20}}` or `{"age": 20}`       |
| `$gt`    | Matches values that are greater than a specified value.             | `{"age": {"$gt": 20}}`                        |
| `$gte`   | Matches values that are greater than or equal to a specified value. | `{"age": {"$gte": 20}}`                       |
| `$lt`    | Matches values that are less than a specified value.                | `{"age": {"$lt": 20}}`                        |
| `$lte`   | Matches values that are less than or equal to a specified value.    | `{"age": {"$lte": 20}}`                       |
| `$ne`    | Matches all values that are not equal to a specified value.         | `{"age": {"$ne": 20}}`                        |
| `$in`    | Matches any of the values specified in an array.                    | `{"status": {"$in": ["active", "pending"]}}`  |
| `$nin`   | Matches none of the values specified in an array.                   | `{"status": {"$nin": ["banned", "deleted"]}}` |

### Logical Operators

| Operator | Description                               | Example                                                              |
| :------- | :---------------------------------------- | :------------------------------------------------------------------- |
| `$and`   | Joins query clauses with a logical AND.   | `{"$and": [{"price": {"$ne": 1.99}}, {"price": {"$exists": true}}]}` |
| `$or`    | Joins query clauses with a logical OR.    | `{"$or": [{"qty": {"$lt": 20}}, {"price": 10}]}`                     |
| `$not`   | Inverts the effect of a query expression. | `{"price": {"$not": {"$gt": 1.99}}}`                                 |
| `$nor`   | Joins query clauses with a logical NOR.   | `{"$nor": [{"price": 1.99}, {"sale": true}]}`                        |

### Element Operators

| Operator  | Description                                            | Example                          |
| :-------- | :----------------------------------------------------- | :------------------------------- |
| `$exists` | Matches documents that have the specified field.       | `{"qty": {"$exists": true}}`     |
| `$type`   | Selects documents if a field is of the specified type. | `{"price": {"$type": "number"}}` |

---

## 📊 Aggregation Framework

Perform complex data analysis using aggregation pipelines.

```python
pipeline = [
    # Stage 1: Filter documents
    {"$match": {"status": "active"}},

    # Stage 2: Group by a field and calculate metrics
    {"$group": {
        "_id": "$department",
        "total_salary": {"$sum": "$salary"},
        "avg_age": {"$avg": "$age"}
    }},

    # Stage 3: Sort the results
    {"$sort": {"total_salary": -1}}
]

results = users.aggregate(pipeline)
```

### Supported Aggregation Stages

- `$match`: Filters the documents.
- `$group`: Groups input documents by the specified `_id`.
- `$sort`: Sorts the documents.
- `$project`: Reshapes each document in the stream (include/exclude fields).
- `$limit`: Limits the number of documents.
- `$skip`: Skips the specified number of documents.
- `$unwind`: Deconstructs an array field from the input documents.
- `$count`: Counts the number of documents.

### Accumulators (for `$group`)

- `$sum`, `$avg`, `$min`, `$max`, `$first`, `$last`, `$push`, `$addToSet`.

---

## ⚡ Async Support

The SDK includes a full **Asynchronous Client** built on `httpx.AsyncClient`. It mirrors the synchronous API perfectly, but all network methods are awaitable.

```python
import asyncio
from rustybase import AsyncRustyBaseClient

async def main():
    client = AsyncRustyBaseClient(connection_string="...")
    await client.login()

    db = client.db("test")
    users = db.collection("users")

    # Awaitable methods
    await users.insert_one({"name": "Async User"})
    docs = await users.find({"name": "Async User"})
    print(docs)

    await client.close()

asyncio.run(main())
```

---

## 🛡 Admin Operations

Manage databases and users programmatically.

```python
# Create a new database
client.create_database("new_db")

# List all databases
dbs = client.list_databases()

# Create a new user
client.create_user("new_user", "secure_password", "new_db", roles=["readWrite"])

# Wipe all data (Danger!)
# client.wipe_all_data()
```

---

## ⚠️ Error Handling

The SDK raises specific exceptions for different failure scenarios. All exceptions inherit from `RustyBaseError`.

```python
from rustybase import AuthenticationError, ConnectionError, RequestError

try:
    client.login()
except AuthenticationError:
    print("Check your username/password!")
except ConnectionError:
    print("Is the server running?")
except RequestError as e:
    print(f"API Error: {e}")
```

---

## License

This SDK is available under the [MIT License](LICENSE).
