# mimic-rs

High-performance User-Agent to Sec-CH-UA Client Hints converter for Rust.

[![Crates.io](https://img.shields.io/crates/v/mimic-rs.svg)](https://crates.io/crates/mimic-rs)
[![Documentation](https://docs.rs/mimic-rs/badge.svg)](https://docs.rs/mimic-rs)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Overview

`mimic-rs` parses User-Agent strings from Chromium-based browsers and generates the corresponding `Sec-CH-UA` (Client Hints) headers. This is useful for:

- HTTP clients that need to send proper Client Hints headers
- Browser fingerprinting research
- Web scraping with accurate browser emulation
- Testing servers that rely on Client Hints

## Features

- Zero-copy parsing where possible
- Support for Chrome, Edge, Brave, Opera, Vivaldi, Samsung Internet, Yandex, and generic Chromium
- Accurate greased brand generation matching Chromium's algorithm
- Platform detection (Windows, macOS, Linux, Android, Chrome OS)
- Mobile browser detection
- Optional `serde` support for serialization
- No unsafe code

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
mimic-rs = "0.1"
```

With serde support:

```toml
[dependencies]
mimic-rs = { version = "0.1", features = ["serde"] }
```

## Usage

### Basic Usage

```rust
use mimic_rs::ClientHints;

fn main() {
    let user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
                      (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";

    let hints = ClientHints::from_ua(user_agent).unwrap();

    println!("sec-ch-ua: {}", hints.sec_ch_ua());
    println!("sec-ch-ua-mobile: {}", hints.sec_ch_ua_mobile());
    println!("sec-ch-ua-platform: {}", hints.sec_ch_ua_platform());
}
```

Output:
```
sec-ch-ua: "Not A(Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
```

### Manual Construction

```rust
use mimic_rs::{ClientHints, Brand, Platform};

let hints = ClientHints::new(
    Brand::Chrome,
    120,
    "120.0.6099.129",
    Platform::Windows,
    false,
);

println!("sec-ch-ua: {}", hints.sec_ch_ua());
println!("sec-ch-ua-full-version-list: {}", hints.sec_ch_ua_full_version_list());
```

### Getting All Headers at Once

```rust
use mimic_rs::ClientHints;

let ua = "Mozilla/5.0 (Linux; Android 13) Chrome/120.0.0.0 Mobile Safari/537.36";
let hints = ClientHints::from_ua(ua).unwrap();

let (sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform) = hints.all_headers();
```

### Supported Browsers

| Browser | Detection Token | Brand Name |
|---------|----------------|------------|
| Chrome | `Chrome/` | `Google Chrome` |
| Edge | `Edg/`, `EdgA/`, `EdgiOS/` | `Microsoft Edge` |
| Brave | `Brave` | `Brave` |
| Opera | `OPR/`, `Opera/` | `Opera` |
| Vivaldi | `Vivaldi/` | `Vivaldi` |
| Samsung Internet | `SamsungBrowser/` | `Samsung Internet` |
| Yandex | `YaBrowser/`, `Yandex/` | `Yandex Browser` |
| Chromium | `Chromium/` | `Chromium` |

### Supported Platforms

- Windows
- macOS
- Linux
- Android
- Chrome OS

## API Reference

### `ClientHints`

Main struct containing parsed browser information.

#### Methods

- `from_ua(user_agent: &str) -> Result<ClientHints, ParseError>` - Parse a User-Agent string
- `new(brand, major_version, full_version, platform, is_mobile)` - Create manually
- `brand() -> Brand` - Get the detected browser brand
- `major_version() -> u32` - Get the major version number
- `full_version() -> &str` - Get the full version string
- `platform() -> Platform` - Get the detected platform
- `is_mobile() -> bool` - Check if mobile browser
- `sec_ch_ua() -> String` - Generate `sec-ch-ua` header value
- `sec_ch_ua_full_version_list() -> String` - Generate `sec-ch-ua-full-version-list` header
- `sec_ch_ua_mobile() -> &'static str` - Generate `sec-ch-ua-mobile` header (`?0` or `?1`)
- `sec_ch_ua_platform() -> String` - Generate `sec-ch-ua-platform` header
- `all_headers() -> (String, &'static str, String)` - Get all three main headers

### `Brand`

Enum representing browser brands:
- `Chrome`, `Edge`, `Brave`, `Opera`, `Vivaldi`, `Samsung`, `Yandex`, `Chromium`

### `Platform`

Enum representing operating systems:
- `Windows`, `MacOS`, `Linux`, `Android`, `ChromeOS`, `Unknown`

### `ParseError`

Error types:
- `EmptyUserAgent` - Empty input string
- `NotChromiumBased` - Not a Chromium browser
- `InvalidVersion` - Could not parse version
- `ChromeTokenNotFound` - No Chrome/Chromium token found

## Performance

The library is optimized for speed:

- No regex dependencies
- Minimal allocations
- Preallocated string buffers
- `#[inline]` on hot paths

Run benchmarks:
```bash
cargo bench
```

## How Greased Brands Work

Chromium browsers include a "greased" fake brand in the `sec-ch-ua` header to prevent servers from relying on specific patterns. The greased brand changes based on the major version:

- Characters cycle through: ` `, `(`, `:`, `-`, `.`
- Versions cycle through: `8`, `99`, `24`
- Order of brands is permuted based on version

This library accurately replicates Chromium's greasing algorithm.

## License

MIT License - see [LICENSE](LICENSE) for details.

## Acknowledgments

Inspired by [mimic](https://github.com/saucesteals/mimic) (Go).
