# Quick Start Guide

## Using the New High-Level Search API

### 1. Simple Search (One Line!)

```rust
use sunocore::models::search::SearchRequest;

// Just search for a string
let request = SearchRequest::simple_public_song("jazz")?;
```

### 2. Compound Search (Multiple Types)

```rust
use sunocore::models::search::SearchRequest;

let request = SearchRequest::builder()
    .public_song("jazz")
    .user("artist_name")
    .playlist("chill")
    .build()?;
```

### 3. Search with Filters

```rust
use sunocore::models::search::{SearchQuery, SearchFilters};

let filters = SearchFilters::builder()
    .full_song()
    .no_covers()
    .build();

let request = SearchRequest::builder()
    .add_query(
        SearchQuery::public_song("ambient")
            .filters(filters)
            .is_instrumental(true)
            .rank_by("trending")
            .build()?
    )
    .build()?;
```

### 4. Find Similar Songs

```rust
use sunocore::models::search::SearchQuery;

let query = SearchQuery::similar_to("550e8400-e29b-41d4-a716-446655440000")
    .size(20)
    .build()?;
```

### 5. With Pagination

```rust
use sunocore::models::search::{SearchQuery, Pagination};

let page1 = Pagination::first_page(20)?;

let query = SearchQuery::public_song("jazz")
    .pagination(page1)
    .build()?;

// Navigate to next page
let page2 = page1.next_page();
```

## Making API Calls

### With reqwest

```rust
use sunocore::models::search::SearchRequest;
use reqwest::blocking::Client;

let client = Client::new();
let request = SearchRequest::simple_public_song("ambient")?;

let response = client
    .post("https://api.example.com/api/search")
    .header("Authorization", "Bearer YOUR_API_KEY")
    .json(&request)
    .send()?;

let search_response: SearchResponse = response.json()?;
```

### Using sunocore Services

```rust
use sunocore::services::search::CompoundSearchRequest;

let request = CompoundSearchRequest::simple_search(
    "https://api.example.com",
    "jazz",
    Some("your-api-key".to_string()),
)?;

// Now use with Tower service layer...
```

## Running Tests

### Unit Tests Only (Fast)
```bash
cd sunocore
cargo test --lib
# 34 tests in ~0s
```

### All Tests with nextest
```bash
cargo nextest run
# 53 tests in ~2s
```

### Integration Tests Against Real API
```bash
export SUNO_BASE_URL="http://127.0.0.1:8000"
export SUNO_API_KEY="your-api-key"
cargo test --test integration_discover_search test_search_with_new_ergonomic_api -- --ignored --nocapture
```

## All Convenience Constructors

```rust
use sunocore::models::search::SearchQuery;

// Public songs
SearchQuery::public_song("term").build()?

// Library search
SearchQuery::library_song("term").build()?

// Similar songs (requires song_id)
SearchQuery::similar_to("song-id").build()?

// Users
SearchQuery::user("term").build()?

// Playlists
SearchQuery::playlist("term").build()?

// Suno Shorts (videos)
SearchQuery::suno_shorts("term").build()?

// Custom search type
SearchQuery::custom(SearchType::GenreForYou).build()?
```

## All Builder Methods

```rust
use sunocore::models::search::SearchQuery;

let query = SearchQuery::public_song("test")
    .term("search term")                     // Search term
    .rank_by("trending")                     // Ranking: most_relevant, trending, most_recent
    .size(20)                                // Page size (1-100)
    .from_index(0)                           // Pagination offset
    .is_instrumental(true)                   // Filter instrumental
    .is_public(true)                         // Filter public
    .is_liked(false)                         // Filter liked
    .filters(filters)                        // Complex filters
    .pagination(Pagination::first_page(20)?) // Pagination helper
    .genre("pop")                            // Genre filter
    .user_id(123)                            // User ID filter
    .project_id("proj-1")                    // Project ID filter
    .song_id("song-123")                     // Song ID filter
    .item_type("clip")                       // Item type
    .order("desc")                           // Sort order
    .languages(vec!["en".to_string()])       // Languages
    .model_version("v4.5")                   // Model version
    .exclude_song_ids(vec!["id1".to_string()]) // Exclude songs
    .user_ids(vec![1, 2, 3])                 // User IDs for feed
    .boosted_user_handles(vec!["handle".to_string()]) // Boosted handles
    .build()?;
```

## All Filter Methods

```rust
use sunocore::models::search::SearchFilters;

let filters = SearchFilters::builder()
    .full_song()                // Full-length songs
    .no_covers()                // No covers (is_cover = false)
    .is_cover(false)            // Covers filter
    .is_extend(false)           // Extended versions
    .is_infill(false)           // Infilled songs
    .is_persona(false)          // Persona-generated
    .is_scene(false)            // Image-to-song
    .is_upsample(false)         // Upsampled
    .is_video_to_song(false)    // Video-to-song
    .hide_gen_stems(true)       // Hide generated stems
    .is_gen_stem(false)         // Generated stems only
    .build();
```

## Error Handling

```rust
use sunocore::models::search::{SearchQuery, ValidationError};

match SearchQuery::public_song("test").build() {
    Ok(query) => {
        println!("Query name: {}", query.name);
    }
    Err(ValidationError::InvalidSize { size }) => {
        eprintln!("Invalid size: {} (must be 1-100)", size);
    }
    Err(ValidationError::MissingRequiredField { field, search_type }) => {
        eprintln!("Missing {} for {}", field, search_type);
    }
    Err(e) => eprintln!("Error: {}", e),
}
```

## Serialization

```rust
use sunocore::models::search::SearchRequest;

let request = SearchRequest::simple_public_song("jazz")?;

// Convert to JSON
let json = serde_json::to_string(&request)?;
println!("{}", json);

// Parse from JSON
let parsed: SearchRequest = serde_json::from_str(&json)?;
```

## Common Patterns

### Pattern 1: Search and Paginate
```rust
let mut page = Pagination::first_page(20)?;

loop {
    let query = SearchQuery::public_song("jazz")
        .pagination(page)
        .build()?;

    let request = SearchRequest::builder()
        .add_query(query)
        .build()?;

    // Make API call...
    // If results.len() < 20, break
    // Otherwise: page = page.next_page()
}
```

### Pattern 2: Multi-Type Search
```rust
let request = SearchRequest::builder()
    .public_song("ambient")           // Songs
    .user("producer")                 // Users
    .playlist("relaxation")           // Playlists
    .build()?;

// Single API call returns all 3 result types
```

### Pattern 3: Advanced Filtering
```rust
let filters = SearchFilters::builder()
    .full_song()
    .is_cover(false)
    .is_extend(false)
    .build();

let query = SearchQuery::public_song("jazz")
    .filters(filters)
    .rank_by("trending")
    .is_instrumental(true)
    .build()?;
```

### Pattern 4: Similar Songs Discovery
```rust
// Find songs similar to a known good song
let similar = SearchQuery::similar_to("55044e8400-e29b-41d4-a716-446655440000")
    .size(30)
    .build()?;

let request = SearchRequest::builder()
    .add_query(similar)
    .build()?;
```

## API Response Structure

```rust
use sunocore::models::search::{SearchResponse, SearchResultItem};

let response: SearchResponse = /* from API */;

// Iterate over results
for (query_name, search_result) in response.result {
    println!("Query: {}", query_name);
    println!("Total hits: {}", search_result.total_hits);

    for item in search_result.result {
        match item {
            SearchResultItem::Song(clip) => {
                println!("  Song: {} - {}", clip.title, clip.id);
            }
            SearchResultItem::Profile(user) => {
                println!("  User: {} ({})", user.display_name, user.handle);
            }
            SearchResultItem::Playlist(playlist) => {
                println!("  Playlist: {}", playlist.name);
            }
            SearchResultItem::Genre(genre) => {
                println!("  Genre: {}", genre.genre);
            }
            SearchResultItem::Persona(persona) => {
                println!("  Persona: {}", persona.name);
            }
        }
    }
}
```

## Documentation

For comprehensive documentation:
- `SEARCH_API_GUIDE.md` - Full user guide with examples
- `BEFORE_AFTER_EXAMPLES.md` - Shows 82% boilerplate reduction
- `LIVE_API_TESTS.md` - How to run integration tests
- `TEST_COVERAGE_MATRIX.md` - Complete test matrix

## Key Takeaways

✨ **Simple** - `SearchRequest::simple_public_song("jazz")?`
✨ **Flexible** - Builder pattern for full control
✨ **Safe** - Validation happens before API calls
✨ **Typed** - Compile-time type safety
✨ **Documented** - Comprehensive examples and guides
✨ **Tested** - 34 unit tests + integration tests
✨ **Compatible** - 100% backward compatible

## Next Steps

1. Read `SEARCH_API_GUIDE.md` for comprehensive API reference
2. Look at examples in `BEFORE_AFTER_EXAMPLES.md`
3. Run the tests: `cargo test --lib`
4. Try the integration tests with real API (see `LIVE_API_TESTS.md`)
5. Start using the new API in your code!

Happy searching! 🎉
