serper-sdk 0.1.3

A minimalistic yet ergonomic Rust SDK for the Serper Google Search API
Documentation
# Contributing to Serper SDK

We welcome contributions to the Serper SDK! This document provides guidelines for contributing to the project.

## Table of Contents

- [Code of Conduct]#code-of-conduct
- [Getting Started]#getting-started
- [Development Setup]#development-setup
- [Making Changes]#making-changes
- [Testing]#testing
- [Documentation]#documentation
- [Submitting Changes]#submitting-changes
- [Code Style]#code-style

## Code of Conduct

This project adheres to a code of conduct adapted from the [Contributor Covenant](https://www.contributor-covenant.org/). By participating, you are expected to uphold this code.

### Our Standards

- **Be respectful** and inclusive of differing viewpoints and experiences
- **Be collaborative** and constructive in discussions
- **Focus on what is best** for the community and the project
- **Show empathy** towards other community members

## Getting Started

1. **Fork the repository** on GitHub
2. **Clone your fork** locally
3. **Create a topic branch** from `main`
4. **Make your changes**
5. **Test your changes**
6. **Submit a pull request**

## Development Setup

### Prerequisites

- **Rust 1.83+** (we use Rust Edition 2024)
- **Git**
- A **Serper API key** for testing (optional, required only for integration tests)

### Setup Instructions

```bash
# Clone your fork
git clone https://github.com/YOUR_USERNAME/serper.git
cd serper

# Install dependencies
cargo build

# Run tests
cargo test

# Run clippy for code quality
cargo clippy -- -D warnings

# Format code
cargo fmt

# Check documentation
cargo doc --open
```

### Environment Variables

For testing with the real API (optional):

```bash
export SERPER_API_KEY="your-api-key-here"
```

## Making Changes

### Branch Naming

Use descriptive branch names:

- `feature/add-new-endpoint` - for new features
- `fix/http-timeout-issue` - for bug fixes
- `docs/improve-readme` - for documentation
- `refactor/simplify-error-handling` - for refactoring

### Commit Messages

Follow conventional commit format:

```
type(scope): description

[optional body]

[optional footer]
```

Examples:
- `feat(search): add concurrent search support`
- `fix(http): resolve timeout configuration bug`
- `docs(readme): update installation instructions`
- `refactor(core): simplify error type hierarchy`

## Testing

### Running Tests

```bash
# Run all tests
cargo test

# Run specific module tests
cargo test core::
cargo test search::
cargo test http::

# Run with output
cargo test -- --nocapture

# Run integration tests (requires API key)
SERPER_API_KEY="your-key" cargo test --test integration
```

### Test Requirements

- **Unit tests** are required for all new functionality
- **Integration tests** should be added for API interactions
- **Documentation tests** must pass (`cargo test --doc`)
- All tests must pass on CI

## Documentation

### Generating Documentation

The project automatically generates documentation on every push to main:

```bash
# Generate documentation locally
cargo doc --open

# Generate with private items (for development)
cargo doc --document-private-items --open

# Test documentation examples
cargo test --doc
```

### Documentation Guidelines

- All public APIs must have comprehensive documentation
- Include usage examples in doc comments
- Document error conditions and edge cases
- Keep examples up-to-date with the current API

The documentation is automatically deployed to GitHub Pages at https://rustsandbox.github.io/serper/

### Writing Tests

- Place unit tests in the same file as the code being tested (in `#[cfg(test)]` modules)
- Use descriptive test names that explain what is being tested
- Test both success and failure cases
- Mock external dependencies where appropriate

Example:

```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_search_query_validation_empty_string() {
        let result = SearchQuery::new("".to_string());
        assert!(result.is_err());
        assert!(matches!(result.unwrap_err(), SerperError::Validation { .. }));
    }

    #[test]
    fn test_search_query_valid_construction() {
        let query = SearchQuery::new("rust programming".to_string()).unwrap();
        assert_eq!(query.q, "rust programming");
        assert_eq!(query.location, None);
    }
}
```

## Documentation

### Documentation Requirements

- **Public APIs** must be documented with `///` comments
- **Examples** should be provided for complex functionality
- **Module-level documentation** should explain the module's purpose
- **README updates** are required for significant changes

### Documentation Style

```rust
/// Searches for the given query using the Serper API
///
/// # Arguments
///
/// * `query` - The search query to execute
///
/// # Returns
///
/// Returns a `Result` containing the search response or an error
///
/// # Examples
///
/// ```rust
/// use serper_sdk::{SearchService, SearchQuery};
///
/// # tokio_test::block_on(async {
/// let service = SearchService::new("api-key".to_string())?;
/// let query = SearchQuery::new("rust programming".to_string())?;
/// let response = service.search(&query).await?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// # });
/// ```
///
/// # Errors
///
/// Returns `SerperError::InvalidApiKey` if the API key is invalid
/// Returns `SerperError::Request` for network-related errors
pub async fn search(&self, query: &SearchQuery) -> Result<SearchResponse> {
    // implementation
}
```

## Submitting Changes

### Pull Request Process

1. **Ensure tests pass**: Run `cargo test` and `cargo clippy`
2. **Update documentation**: Update relevant docs and examples
3. **Update CHANGELOG**: Add your changes to the unreleased section
4. **Create pull request**: Use the provided template
5. **Address feedback**: Respond to review comments promptly

### Pull Request Template

```markdown
## Description

Brief description of what this PR does.

## Type of Change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation update

## Testing

- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] I have tested this manually

## Checklist

- [ ] My code follows the project's style guidelines
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have updated the documentation accordingly
- [ ] My changes generate no new warnings
- [ ] I have updated CHANGELOG.md
```

## Code Style

### Rust Code Style

We follow standard Rust conventions:

- **Use `cargo fmt`** for formatting
- **Follow Clippy suggestions** (`cargo clippy -- -D warnings`)
- **Use meaningful variable names**
- **Prefer explicit types** when it improves clarity
- **Write self-documenting code**

### Module Organization

```rust
/// Module documentation
/// 
/// Explanation of what this module does and how it fits into the overall architecture.

// Imports
use std::collections::HashMap;
use crate::core::Result;

// Constants
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);

// Types
#[derive(Debug, Clone)]
pub struct MyStruct {
    // fields
}

// Implementations
impl MyStruct {
    /// Constructor
    pub fn new() -> Self { }
    
    /// Public methods
    pub fn do_something(&self) -> Result<()> { }
    
    /// Private methods
    fn internal_helper(&self) { }
}

// Trait implementations
impl Default for MyStruct { }

// Tests
#[cfg(test)]
mod tests { }
```

### Error Handling

- **Use `Result<T, SerperError>`** for fallible operations
- **Provide context** in error messages
- **Convert errors** at module boundaries
- **Document error conditions** in function docs

### Async Code

- **Use `async`/`await`** consistently
- **Avoid blocking** in async code
- **Use `tokio` primitives** for concurrency
- **Handle cancellation** appropriately

## Architecture Guidelines

### Modular Design

- **Single responsibility**: Each module should have a clear, focused purpose
- **Clean interfaces**: Modules should interact through well-defined APIs
- **Dependency direction**: Higher-level modules depend on lower-level ones
- **Loose coupling**: Minimize dependencies between modules

### Error Handling

- **Centralized errors**: Use the `core::SerperError` enum for all errors
- **Error propagation**: Use `?` operator for clean error propagation
- **Context preservation**: Include relevant context in error messages
- **Recovery strategies**: Implement retry logic where appropriate

### Performance

- **Async first**: Use async/await for I/O operations
- **Connection reuse**: Reuse HTTP connections when possible
- **Memory efficiency**: Avoid unnecessary allocations
- **Benchmark changes**: Profile performance-critical code

## Release Process

### Version Numbering

We follow [Semantic Versioning](https://semver.org/):

- **MAJOR**: Breaking changes
- **MINOR**: New features (backward compatible)
- **PATCH**: Bug fixes (backward compatible)

### Release Checklist

1. **Update version** in `Cargo.toml`
2. **Update CHANGELOG.md** with release notes
3. **Run full test suite** (`cargo test --all-features`)
4. **Check documentation** (`cargo doc --no-deps`)
5. **Create release tag** (`git tag v0.x.y`)
6. **Publish to crates.io** (`cargo publish`)
7. **Create GitHub release** with changelog

## Getting Help

- **Issues**: Use GitHub issues for bug reports and feature requests
- **Discussions**: Use GitHub discussions for questions and ideas
- **Security**: Email security@remolab.fr for security-related issues

Thank you for contributing to the Serper SDK! 🚀