dawn-codegen 0.1.2

Code generator for Dawn WebGPU API
Documentation
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};

pub(crate) fn type_name(name: &str) -> String {
    let raw = camel_case_with_acronyms(name);
    normalize_digit_prefix_camel(&raw)
}
pub(crate) fn enum_variant_name_camel(name: &str) -> String {
    let raw = camel_case_with_acronyms(name);
    normalize_digit_prefix_camel(&raw)
}
pub(crate) fn ffi_enum_const_name(ffi_type: &str, variant: &str) -> String {
    format!("{ffi_type}_{ffi_type}_{variant}")
}
pub(crate) fn ffi_bitmask_const_name(ffi_type: &str, variant: &str) -> String {
    format!("{ffi_type}_{variant}")
}
pub(crate) fn ffi_enum_value_name(name: &str) -> String {
    if name.trim().eq_ignore_ascii_case("srgb") {
        return "SRGB".to_string();
    }
    let mut out = String::new();
    for word in name.split_whitespace() {
        let mut parts = Vec::new();
        for part in word.split('-') {
            if part.is_empty() {
                continue;
            }
            let value = if part.chars().all(|c| c.is_ascii_digit()) {
                part.to_string()
            } else if part
                .chars()
                .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
            {
                part.to_string()
            } else {
                let mut chars = part.chars();
                let mut s = String::new();
                if let Some(first) = chars.next() {
                    s.push(first.to_ascii_uppercase());
                    s.push_str(&chars.as_str().to_ascii_lowercase());
                }
                s
            };
            parts.push(value);
        }
        out.push_str(&parts.join("_"));
    }
    for (from, to) in acronym_fixes() {
        out = out.replace(from, to);
    }
    let extra_fixes = [
        ("Opengles", "OpenGLES"),
        ("Opengl", "OpenGL"),
        ("Gpu", "GPU"),
        ("Cpu", "CPU"),
        ("Winui", "WinUI"),
    ];
    for (from, to) in extra_fixes {
        out = out.replace(from, to);
    }
    out
}
pub(crate) fn bitmask_variant_name(name: &str) -> String {
    let raw = shouty_snake_case_name(name);
    if let Some(first) = raw.chars().next() {
        if first.is_ascii_digit() {
            return normalize_digit_prefix(&raw);
        }
    }
    raw
}
pub(crate) fn safe_ident(name: &str) -> String {
    let keywords = [
        "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn",
        "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref",
        "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe",
        "use", "where", "while", "async", "await", "dyn",
    ];

    if keywords.contains(&name) {
        format!(r#"r#{name}"#, name = name)
    } else {
        name.to_string()
    }
}
pub(crate) fn normalize_digit_prefix_camel(value: &str) -> String {
    let mut chars = value.chars();
    let Some(first) = chars.next() else {
        return value.to_string();
    };
    if !first.is_ascii_digit() {
        return value.to_string();
    }
    let Some(second) = chars.next() else {
        return value.to_string();
    };
    let mut rest: String = chars.collect();
    rest.insert(0, first);
    let mut out = String::new();
    out.push(second.to_ascii_uppercase());
    out.push_str(&rest);
    out
}
pub(crate) fn normalize_digit_prefix(raw: &str) -> String {
    let mut chars = raw.chars().peekable();
    let mut digits = String::new();
    while let Some(c) = chars.peek() {
        if c.is_ascii_digit() {
            digits.push(*c);
            chars.next();
        } else {
            break;
        }
    }

    let rest: String = chars.collect();
    if let Some(stripped) = rest.strip_prefix('D') {
        format!("D{digits}{stripped}", digits = digits, stripped = stripped)
    } else {
        format!("D{digits}{rest}", digits = digits, rest = rest)
    }
}
pub(crate) fn normalize_digit_prefix_snake(raw: &str) -> String {
    let mut chars = raw.chars();
    let Some(first) = chars.next() else {
        return raw.to_string();
    };
    if !first.is_ascii_digit() {
        return raw.to_string();
    }
    let Some(second) = chars.next() else {
        return raw.to_string();
    };
    let mut rest: String = chars.collect();
    rest.insert(0, first);
    let mut out = String::new();
    out.push(second);
    out.push_str(&rest);
    out
}
pub(crate) fn uppercase_after_digits(value: &str) -> String {
    let mut out = String::with_capacity(value.len());
    let mut prev_digit = false;
    for ch in value.chars() {
        if prev_digit && ch.is_ascii_lowercase() {
            out.push(ch.to_ascii_uppercase());
        } else {
            out.push(ch);
        }
        prev_digit = ch.is_ascii_digit();
    }
    out
}
pub(crate) fn ffi_type_name(canonical: &str, c_prefix: &str) -> String {
    if canonical == "INTERNAL_HAVE_EMDAWNWEBGPU_HEADER" {
        return format!("{c_prefix}{canonical}");
    }
    format!("{}{}", c_prefix, camel_case_with_acronyms(canonical))
}
pub(crate) fn acronym_fixes() -> &'static [(&'static str, &'static str)] {
    &[
        ("WebGpu", "WebGPU"),
        ("WebXr", "WebXR"),
        ("Webgpu", "WebGPU"),
        ("Webxr", "WebXR"),
        ("Wgpu", "WGPU"),
        ("Wgsl", "WGSL"),
        ("Hlsl", "HLSL"),
        ("Html", "HTML"),
        ("Spirv", "SPIRV"),
        ("Dxgi", "DXGI"),
        ("Mtl", "MTL"),
        ("IoSurface", "IOSurface"),
        ("Hwnd", "HWND"),
        ("Uwp", "UWP"),
        ("WinUi", "WinUI"),
        ("Oom", "OOM"),
        ("Fd", "FD"),
        ("D3d12", "D3D12"),
        ("D3d11", "D3D11"),
        ("D3d", "D3D"),
        ("Vk", "Vk"),
        ("Xcb", "XCB"),
        ("OpenGl", "OpenGL"),
        ("OpenGles", "OpenGLES"),
        ("Egl", "EGL"),
    ]
}

pub(crate) fn camel_case_name(name: &str) -> String {
    name.to_upper_camel_case()
}

pub(crate) fn snake_case_name(name: &str) -> String {
    let raw = name.to_snake_case();
    normalize_digit_prefix_snake(&raw)
}

pub(crate) fn shouty_snake_case_name(name: &str) -> String {
    let raw = name.to_shouty_snake_case();
    if let Some(first) = raw.chars().next() {
        if first.is_ascii_digit() {
            return normalize_digit_prefix(&raw);
        }
    }
    raw
}

pub(crate) fn camel_case_with_acronyms(name: &str) -> String {
    let mut out = camel_case_name(name);
    for (from, to) in acronym_fixes() {
        out = out.replace(from, to);
    }
    uppercase_after_digits(&out)
}