googleads-rs 23.2.0

A gRPC client library for Google Ads API, generated automatically from the API definition files.
Documentation
# API Usage Guide - GoogleAdsRow Field Access

## Overview

The `GoogleAdsRow::get()` method now uses Protocol Buffer reflection to provide dynamic field access. This document shows how to use the new API.

## Basic Usage

### Single Field Access

```rust
use googleads_rs::google::ads::googleads::v23::services::GoogleAdsRow;

let field_mask = response.field_mask.unwrap();
for row in response.results {
    for path in &field_mask.paths {
        print!("{}: {}\t", path, row.get(&path));
    }
    print!("\n");
}
```

### Multiple Field Access (Optimized)

The new `get_many()` method is more efficient when accessing multiple fields:

```rust
let fields = vec![
    "campaign.id",
    "campaign.name",
    "campaign.status",
    "campaign.bidding_strategy_type"
];

let values = row.get_many(&fields);
// values[0] = campaign.id
// values[1] = campaign.name
// values[2] = campaign.status
// values[3] = campaign.bidding_strategy_type
```

## Supported Field Paths

### Direct Fields
```rust
row.get("customer.id")
row.get("customer.descriptive_name")
row.get("customer.currency_code")
```

### Nested Fields
```rust
row.get("campaign.id")
row.get("campaign.name")
row.get("campaign.status")
row.get("campaign.start_date")
row.get("campaign.end_date")
row.get("campaign.optimization_score")
```

### Deep Nesting
```rust
row.get("campaign_budget.id")
row.get("campaign_budget.amount_micros")
row.get("campaignBudget.status")
```

### Segment Fields
```rust
row.get("segments.date")
row.get("segments.ad_network_type")
row.get("segments.click_type")
row.get("segments.device")
row.get("segments.keyword.info.match_type")
```

### Metrics Fields
```rust
row.get("metrics.impressions")
row.get("metrics.clicks")
row.get("metrics.cost_micros")
row.get("metrics.conversions")
row.get("metrics.all_conversions")
row.get("metrics.conversions_value")
```

### Enum Fields (Now Proto-Canonical Names)
```rust
// Status fields return SCREAMING_SNAKE_CASE
row.get("campaign.status")           // "ENABLED", "PAUSED", "REMOVED"
row.get("ad_group.status")           // "ENABLED", "PAUSED", "REMOVED"
row.get("customer.status")           // "ENABLED", "PAUSED", "CLOSED"

// Type fields
row.get("campaign.advertising_channel_type")  // "SEARCH", "DISPLAY", "VIDEO", "PERFORMANCE_MAX"
row.get("campaign.bidding_strategy_type")    // "MANUAL_CPC", "MAXIMIZE_CONVERSIONS", "TARGET_ROAS"
row.get("ad_group_criterion.keyword.match_type") // "EXACT", "PHRASE", "BROAD"

// Device
row.get("segments.device")            // "MOBILE", "DESKTOP", "TABLET"

// Day of week
row.get("segments.day_of_week")       // "MONDAY", "TUESDAY", etc.
```

### Repeated Fields
```rust
// Lists are joined with appropriate separators
row.get("campaign.primary_status_reasons")       // "REASON_1, REASON_2, REASON_3"
row.get("ad_group_asset.primary_status_details")  // "Detail1; Detail2; Detail3"

// Asset automation settings (special format)
row.get("campaign.asset_automation_settings")
// Returns: "TEXT_ASSET_AUTOMATION:OPTED_IN, GENERATE_VERTICAL_YOUTUBE_VIDEOS:OPTED_OUT"
```

### Oneof Fields
```rust
// Access oneof variant fields
row.get("ad_group_criterion.keyword.text")
row.get("ad_group_criterion.keyword.match_type")
row.get("campaign_criterion.location.geo_target_constant")
row.get("campaign_criterion.negative_keyword.text")
```

### Special Cases

#### Asset Automation Settings
```rust
// Special formatting: "TYPE:STATUS" pairs
row.get("campaign.asset_automation_settings")
// Example output:
// "TEXT_ASSET_AUTOMATION:OPTED_IN, GENERATE_VERTICAL_YOUTUBE_VIDEOS:OPTED OUT"
```

#### Responsive Search Ad Content
```rust
// GAQL stops at the repeated message, but .text is extracted automatically
row.get("ad_group_ad.ad.responsive_search_ad.headlines")
// Returns comma-separated text values
row.get("ad_group_ad.ad.responsive_search_ad.descriptions")
// Returns comma-separated text values
```

## Return Value Behavior

### Present Fields
```rust
row.get("campaign.name")            // "Summer Sale 2024"
row.get("metrics.clicks")           // "1234"
row.get("campaign.status")          // "ENABLED"
```

### Absent Resources
```rust
// Returns empty string instead of panicking
row.get("campaign.id")              // "" (if campaign not present)
row.get("ad_group.name")            // "" (if ad_group not present)
row.get("metrics.impressions")      // "" (if metrics not selected)
```

### Unknown Fields
```rust
row.get("nonexistent.field")        // "not implemented by googleads-rs"
```

## Enum Values Reference

### Campaign Status
- `UNSPECIFIED`
- `UNKNOWN`
- `ENABLED`
- `PAUSED`
- `REMOVED`

### Advertising Channel Type
- `UNSPECIFIED`
- `UNKNOWN`
- `SEARCH`
- `DISPLAY`
- `SHOPPING`
- `HOTEL`
- `VIDEO`
- `MULTI_CHANNEL`
- `LOCAL`
- `SMART`
- `PERFORMANCE_MAX`
- `TRAVEL`
- `APP`
- `LOCAL_SERVICES`

### Bidding Strategy Type
- `UNSPECIFIED`
- `UNKNOWN`
- `COMMISSION`
- `MANUAL_CPC`
- `MANUAL_CPM`
- `MANUAL_CPV`
- `MAXIMIZE_CONVERSIONS`
- `MAXIMIZE_CONVERSION_VALUE`
- `MAXIMIZE_CLICKS`
- `MAXIMIZE_IMPRESSION_SHARE`
- `MAXIMIZE_TARGET_CPA`
- `TARGET_CPA`
- `TARGET_CPM`
- `TARGET_RETURN_ON_AD_SPEND`
- `TARGET_ROAS`
- `TARGET_IMPRESSION_SHARE`
- `TARGET_SPEND`
- `ENHANCED_CPC`
- `MANUAL_MERCHANT_CENTER_INCENTIVES`

### Keyword Match Type
- `UNSPECIFIED`
- `UNKNOWN`
- `EXACT`
- `PHRASE`
- `BROAD`

### Device
- `UNSPECIFIED`
- `UNKNOWN`
- `MOBILE`
- `DESKTOP`
- `TABLET`
- `CONNECTED_TV`
- `OTHER`

### Day of Week
- `UNSPECIFIED`
- `UNKNOWN`
- `MONDAY`
- `TUESDAY`
- `WEDNESDAY`
- `THURSDAY`
- `FRIDAY`
- `SATURDAY`
- `SUNDAY`

## Error Handling

### Graceful Degradation
The API is designed to never panic in production use:
- Unknown fields return "not implemented by googleads-rs"
- Absent resources return empty string
- Corrupted data returns some representation

### Error Codes
If you need error codes (optional future enhancement):
```rust
let result = row.get_opt("campaign.status");
match result {
    Ok(value) => println!("Value: {}", value),
    Err(e) => match e {
        GetError::FieldNotFound => { /* handle */ },
        GetError::ResourceAbsent => { /* handle */ },
    }
}
```

## Performance Tips

1. **Use `get_many()` for multiple fields**
   - Encodes/decodes once instead of multiple times
   - Significant performance improvement for 3+ fields

2. **Cache repeated lookups**
   - If you need the same field multiple times, store it
   - Avoid redundant reflection work

3. **Batch by resource**
   - Group `get()` calls by the same parent resource
   - Reduces path traversal overhead

## Integration Examples

### GAQL Query Processing
```rust
// Execute GAQL query
let response = client.search_google_ads_stream(&request);

// Process each row
for row in response.results {
    let field_mask = response.field_mask.unwrap();

    for path in &field_mask.paths {
        let value = row.get(&path);
        println!("{} = {}", path, value);
    }
}
```

### Report Generation
```rust
// Build report with multiple fields
let fields = vec![
    "campaign.id",
    "campaign.name",
    "campaign.status",
    "campaign.bidding_strategy_type",
    "metrics.impressions",
    "metrics.clicks",
    "metrics.conversions",
    "segments.date"
];

writeln!(writer, "Campaign ID,Name,Status,Bidding,Impressions,Clicks,Conversions,Date")?;

for row in response.results {
    let values = row.get_many(&fields);
    writeln!(
        writer,
        "{},{},{},{},{},{},{},{}",
        values[0], values[1], values[2], values[3],
        values[4], values[5], values[6], values[7]
    )?;
}
```

### Filtering and Aggregation
```rust
let mut campaign_stats: HashMap<String, (i64, i64)> = HashMap::new();

for row in response.results {
    let status = row.get("campaign.status");
    let impressions = row.get("metrics.impressions").parse::<i64>().unwrap_or(0);
    let clicks = row.get("metrics.clicks").parse::<i64>().unwrap_or(0);

    if status == "ENABLED" {
        campaign_stats
            .entry(status)
            .or_insert((0, 0))
            .0 += impressions;
        let (_, click_ref) = campaign_stats.get_mut(&status).unwrap();
        *click_ref += clicks;
    }
}
```

## Troubleshooting

### Field Not Found
```
value = "not implemented by googleads-rs"
```
- Check spelling of field path
- Verify the field exists in the current API version
- Ensure the resource is selected in your GAQL query

### Empty String Returned
```
value = ""
```
- The resource may not be present in this row
- Check your GAQL query includes the resource
- The field may have presence but is unset

### Unexpected Format
```
value = "Some(DebugFormat(...))"
```
- You're accessing a message field directly
- For lists, check if you need a specific sub-field
- For enums, verify proto-canonical naming

## Resources

- [Prost-Reflect Documentation]https://docs.rs/prost-reflect/0.16.0/prost_reflect/
- [Google Ads API v23 Field Reference]https://developers.google.com/google-ads/api/fields/v23
- [GAQL Query Language Guide]https://developers.google.com/google-ads/api/fields/query/overview