# Feishu SDK Rust

[![Crates.io](https://img.shields.io/crates/v/feishu-sdk.svg)](https://crates.io/crates/feishu-sdk)
[![Documentation](https://docs.rs/feishu-sdk/badge.svg)](https://docs.rs/feishu-sdk)
[![License](https://img.shields.io/crates/l/feishu-sdk.svg)](https://github.com/your-repo/feishu-sdk-rust)

> Notice: This is a community-maintained crate and is not an official Feishu/Lark SDK.

Rust SDK for Feishu/Lark Open Platform API. Provides a complete, type-safe interface to interact with Feishu APIs.

## Features

- **Complete API Coverage**: 1448+ operations from Feishu Open Platform
- **Token Management**: Automatic app_access_token and tenant_access_token handling
- **Event Processing**: Event subscription and card callback handling
- **WebSocket Support**: Long connection for real-time messaging
- **HTTP Server**: Built-in Salvo server for webhook handling
- **Type Safety**: Generated operation constants and typed APIs
- **Async/Await**: Full async support with tokio runtime
- **Pluggable Components**: Customizable cache, logger, serializer, and HTTP client
- **Code Generation**: Operation wrappers + typed model stubs from endpoint catalog

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
feishu-sdk = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

### Optional Features

```toml
[dependencies]
feishu-sdk = { version = "0.1", features = ["server", "websocket"] }
```

- `server`: Enable HTTP server for event handling (Salvo)
- `websocket`: Enable WebSocket client for long connections

## Quick Start

### Basic Usage

```rust
use feishu_sdk::core::{Config, FEISHU_BASE_URL};
use feishu_sdk::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::builder("app_id", "app_secret")
        .base_url(FEISHU_BASE_URL)
        .build();

    let client = Client::new(config)?;

    // Get chat list
    let resp = client
        .operation("im.v1.chat.list")
        .query_param("page_size", "20")
        .send()
        .await?;

    println!("Status: {}", resp.status);
    println!("Body: {}", String::from_utf8_lossy(&resp.body));

    Ok(())
}
```

### Send Message

```rust
use feishu_sdk::core::Config;
use feishu_sdk::Client;

async fn send_message(client: &Client, chat_id: &str) -> Result<(), Box<dyn std::error::Error>> {
    let message = serde_json::json!({
        "receive_id": chat_id,
        "msg_type": "text",
        "content": r#"{"text":"Hello!"}"#
    });

    let resp = client
        .operation("im.v1.messages.create")
        .path_param("receive_id_type", "chat_id")
        .body_json(&message)?
        .send()
        .await?;

    println!("Message sent: {:?}", resp.json_value()?);
    Ok(())
}
```

### Event Handling Server

```rust
use feishu_sdk::core::{noop_logger, Config};
use feishu_sdk::event::{EventDispatcher, EventDispatcherConfig};
use feishu_sdk::server::FeishuServer;

#[tokio::main]
async fn main() {
    let event_config = EventDispatcherConfig::new()
        .verification_token("your_token");

    let dispatcher = EventDispatcher::new(event_config, noop_logger());

    // Register event handlers
    dispatcher.on_event("im.message.receive_v1", |event| {
        Box::pin(async move {
            println!("Received message: {:?}", event);
            Ok(None)
        })
    });

    // Start server
    let server = FeishuServer::new(dispatcher).port(8080);
    server.run().await;
}
```

### WebSocket Client

```rust
use feishu_sdk::core::noop_logger;
use feishu_sdk::ws::{WebSocketClient, WsConfig};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = WsConfig::new("wss://ws.feishu.cn/...")
        .heartbeat_interval(Duration::from_secs(30))
        .reconnect_delay(Duration::from_secs(5));

    let client = WebSocketClient::new(config, noop_logger()).await?;

    // Check connection status
    if client.is_connected().await {
        println!("Connected to WebSocket");
    }

    Ok(())
}
```

## Configuration

### Basic Configuration

```rust
use feishu_sdk::core::{Config, LogLevel, FEISHU_BASE_URL, LARK_BASE_URL};
use std::time::Duration;

let config = Config::builder("app_id", "app_secret")
    .base_url(FEISHU_BASE_URL)        // or LARK_BASE_URL for international
    .log_level(LogLevel::Debug)        // Set log level
    .request_timeout(Duration::from_secs(30))  // Request timeout
    .enable_token_cache(true)          // Enable token caching
    .build();
```

### Marketplace App Configuration

```rust
let config = Config::builder("app_id", "app_secret")
    .marketplace_app()                 // Enable marketplace mode
    .app_ticket("your_app_ticket")     // Required for marketplace apps
    .build();
```

### Helpdesk Configuration

```rust
let config = Config::builder("app_id", "app_secret")
    .helpdesk("helpdesk_id", "helpdesk_token")
    .build();
```

### Client Options

```rust
use feishu_sdk::client::{ClientBuilder, with_http_client, with_log_level, with_request_timeout, with_serializer};
use feishu_sdk::core::{Config, LogLevel};

let client = ClientBuilder::new(Config::builder("app_id", "app_secret").build())
    .with(with_log_level(LogLevel::Debug))
    .with(with_request_timeout(Duration::from_secs(60)))
    .with(with_http_client(custom_http_client))
    .with(with_serializer(custom_serializer))
    .build()?;
```

## API Reference

### Operation IDs

Operations use the format: `{service}.{version}.{resource}.{method}`

Examples:
- `im.v1.messages.create` - Send message
- `im.v1.chat.list` - List chats
- `contact.v3.user.get` - Get user info
- `auth.v3.tenant_access_token.internal.post` - Get tenant token

### Typed API Wrappers

```rust
use feishu_sdk::api::all_services;

// Get chat info
let builder = all_services::im::v1::chat::get(&client, "oc_xxx")
    .query_param("user_id_type", "open_id");

// List users
let builder = all_services::contact::v3::user::list(&client)
    .query_param("department_id", "0")
    .query_param("page_size", "50");
```

## Examples

See the `examples/` directory:

- `get_token.rs` - Get tenant access token
- `send_message.rs` - Send a message
- `get_user.rs` - Get user information
- `im_v1_message.rs` - IM message operations
- `drive_v1_file.rs` - Drive file operations
- `calendar_v4.rs` - Calendar operations
- `docx_v1.rs` - Docx document operations
- `sheets_v3.rs` - Sheets operations
- `bitable_v1.rs` - Bitable operations
- `approval_v4.rs` - Approval operations
- `event_server.rs` - Event handling server
- `card_handler.rs` - Card callback server
- `websocket_client.rs` - WebSocket client
- `full_app.rs` - End-to-end app example (client + event + card)

Run examples:

```bash
# Set environment variables
export FEISHU_APP_ID=your_app_id
export FEISHU_APP_SECRET=your_app_secret

# Run example
cargo run --example get_token

# Run server example (requires server feature)
cargo run --features server --example event_server

# Run websocket example (requires websocket feature)
cargo run --features websocket --example websocket_client
```

## Documentation

- Usage guide: [`docs/USAGE.md`](docs/USAGE.md)
- FAQ: [`docs/FAQ.md`](docs/FAQ.md)
- Migration guide: [`docs/MIGRATION.md`](docs/MIGRATION.md)
- Best practices: [`docs/BEST_PRACTICES.md`](docs/BEST_PRACTICES.md)

## Error Handling

```rust
use feishu_sdk::core::Error;

match client.operation("im.v1.messages.create").send().await {
    Ok(resp) => println!("Success: {:?}", resp.json_value()?),
    Err(Error::Api(api_error)) => {
        println!("API Error: code={}, msg={}", api_error.code, api_error.msg);
    }
    Err(Error::RequestTimeout(_)) => println!("Request timed out"),
    Err(e) => println!("Error: {}", e),
}
```

## License

MIT License
