simple-useragent 0.1.2

A simple user agent parser library based on the uap-core regexes
Documentation
/*
 * Copyright (c) 2025 Bastiaan van der Plaat
 *
 * SPDX-License-Identifier: MIT
 */

//! An user agent parser library

use std::env;
use std::fs::File;
use std::io::Write;

use serde::Deserialize;

// MARK: Rules
#[derive(Deserialize)]
struct Rules {
    user_agent_parsers: Vec<UserAgentRule>,
    os_parsers: Vec<OsRule>,
}

#[derive(Deserialize)]
struct UserAgentRule {
    regex: String,
    family_replacement: Option<String>,
    v1_replacement: Option<String>,
    v2_replacement: Option<String>,
    v3_replacement: Option<String>,
}

#[derive(Deserialize)]
struct OsRule {
    regex: String,
    os_replacement: Option<String>,
    os_v1_replacement: Option<String>,
    os_v2_replacement: Option<String>,
    os_v3_replacement: Option<String>,
}

// MARK: Main
fn main() {
    // Parse regexes.yaml
    let rules = serde_yaml::from_str::<Rules>(include_str!("regexes.yaml"))
        .expect("Can't parse regexes.yaml");

    // Write rules_data.rs
    let mut f = File::create(format!(
        "{}/rules_data.rs",
        env::var("OUT_DIR").expect("OUT_DIR not set")
    ))
    .expect("Can't create rules_data.rs");

    let user_agent_parsers = rules
        .user_agent_parsers
        .iter()
        .map(|rule| {
            format!(
                r##"UserAgentRule {{
    regex: r#"{}"#,
    family_replacement: {:?},
    v1_replacement: {:?},
    v2_replacement: {:?},
    v3_replacement: {:?},
}}"##,
                rule.regex,
                rule.family_replacement,
                rule.v1_replacement,
                rule.v2_replacement,
                rule.v3_replacement,
            )
        })
        .collect::<Vec<_>>();

    let os_parsers = rules
        .os_parsers
        .iter()
        .map(|rule| {
            format!(
                r##"OsRule {{
    regex: r#"{}"#,
    os_replacement: {:?},
    os_v1_replacement: {:?},
    os_v2_replacement: {:?},
    os_v3_replacement: {:?},
}}"##,
                rule.regex,
                rule.os_replacement,
                rule.os_v1_replacement,
                rule.os_v2_replacement,
                rule.os_v3_replacement,
            )
        })
        .collect::<Vec<_>>();

    _ = writeln!(
        f,
        r"// This file is generated by simple-useragent/build.rs

pub(crate) struct UserAgentRule {{
    pub(crate) regex: &'static str,
    pub(crate) family_replacement: Option<&'static str>,
    pub(crate) v1_replacement: Option<&'static str>,
    pub(crate) v2_replacement: Option<&'static str>,
    pub(crate) v3_replacement: Option<&'static str>,
}}

pub(crate) struct OsRule {{
    pub(crate) regex: &'static str,
    pub(crate) os_replacement: Option<&'static str>,
    pub(crate) os_v1_replacement: Option<&'static str>,
    pub(crate) os_v2_replacement: Option<&'static str>,
    pub(crate) os_v3_replacement: Option<&'static str>,
}}

pub(crate) static USER_AGENT_RULES: [UserAgentRule; {}] = [{}];

pub(crate) static OS_RULES: [OsRule; {}] = [{}];
",
        user_agent_parsers.len(),
        user_agent_parsers.join(",\n"),
        os_parsers.len(),
        os_parsers.join(",\n"),
    );
}