lava 0.4.9

Rust wrapper to manipulate Vulkan more conveniently than with bindings.
Documentation
const {
    toPascalCase,
    getRawVkTypeName,
    getWrappedVkTypeName,
    getConstVkValueName,
    findEnumPrefix,
    toSnakeCase,
    getFullWrappedType,
    getFullRawType,
    blockToString,
    isCount,
    areCountAndArray,
    isPlural,
    cToRustVarName,
    argToString,
    getFieldInformation,
    documentType
} = require('./utils');

function generateVkBitFlagsDefinition(cDef) {
    cDef.rawTypeName = getRawVkTypeName(cDef.name);
    cDef.wrappedTypeName = getWrappedVkTypeName(cDef.name);

    const prefix = findEnumPrefix(cDef.name.replace('Flags', ''));
    const extSuffix = `_${cDef.extension.toUpperCase()}`;

    for (let field of cDef.fields) {
        const name = field.name.replace('_BIT', '');
        const suffix = name.endsWith(extSuffix) ? extSuffix : '';
        const strippedName = name.substring(prefix.length + 1, name.length - suffix.length);
        
        field.varName = formatBitFlagsFieldName(strippedName);
    }

    return [
        genUses(),
        genWrappedType(cDef),
        genRawType(cDef),
        genImplVkWrappedType(cDef),
        genImplVkRawType(cDef),
        genImplDefault(cDef),
        genImplFlags(cDef, 'u32')
    ];
}

function genUses() {
    return [
        `utils::vk_traits::*`
    ].map(str => `use ${str};`);
}

function genRawType(def) {
    return [
        `#[doc(hidden)]`,
        `pub type ${def.rawTypeName} = u32;`
    ];
}

function genFlagBitsDoc(def) {
    const fields = def.fields.slice(0, 2).map(f => f.varName);

    return [
        `Use the macro \`${def.wrappedTypeName}!\` as an alternative method to create a structure. For example, these two snippets return the same value:`,
        '```',
        `${def.wrappedTypeName}!(${fields.join(', ')})`,
        '```',
        '```',
        `${def.wrappedTypeName} {`,
        ...fields.map(f => `    ${f}: true,`),
        def.fields.length <= 2 ? '' : `    ..${def.wrappedTypeName}::none()`,
        `}`,
        '```'
    ].filter(x => x).map(x => `/// ${x}`).join('\n');
}

function genWrappedType(def) {
    return [
        documentType(def, genFlagBitsDoc(def)),
        `#[derive(Debug, Clone)]`,
        `pub struct ${def.wrappedTypeName}`,
        def.fields.map(field => `pub ${field.varName}: bool,`)
    ];
}

function genImplVkRawType(def) {
    return [
        `impl VkRawType<${def.wrappedTypeName}> for ${def.rawTypeName}`, [
            `fn vk_to_wrapped(src: &${def.rawTypeName}) -> ${def.wrappedTypeName}`, [
                def.wrappedTypeName,
                def.fields.map(field => `${field.varName}: (src & ${field.value}) != 0,`)
            ]
        ]
    ];
}

function genImplVkWrappedType(def) {
    return [
        `impl VkWrappedType<${def.rawTypeName}> for ${def.wrappedTypeName}`, [
            `fn vk_to_raw(src: &${def.wrappedTypeName}, dst: &mut ${def.rawTypeName})`, [
                `*dst = 0;`,
                ...def.fields.map(field => `if src.${field.varName} { *dst |= ${field.value}; }`),
            ]
        ]
    ];
}

function genImplDefault(def) {
    return [
        `impl Default for ${def.wrappedTypeName}`, [
            `fn default() -> ${def.wrappedTypeName}`, [
                def.wrappedTypeName,
                def.fields.map(field => `${field.varName}: false,`)
            ]
        ]
    ];
}

function formatBitFlagsFieldName(name) {
    return toSnakeCase(name)
        .replace(/^(\d)/, '_$1');
}

const NONE_DOC = '/// Return a structure with all flags to `false`.\n'
const ALL_DOC = '/// Return a structure with all flags to `true`.\n'
const TO_U32_DOC = '/// Return the numerical bit flags corresponding to the structure (as described in the Vulkan specs).\n';
const FROM_U32_DOC = '/// Create a structure corresponding to the specified numerical bit flags.\n';

const MACRO_NAME_CONFLICTS = [
    'VkExternalMemoryFeatureFlags',
    'VkExternalMemoryHandleTypeFlags'
];

function genImplFlags(def, intType) {
    let macroSuffix = '';

    // Manually solve conflicts for now
    if (MACRO_NAME_CONFLICTS.includes(def.wrappedTypeName)) {
        macroSuffix = def.extension.capitalize();
    }

    const toIntDoc = intType === 'u32' ? TO_U32_DOC : '';
    const fromIntDoc = intType === 'u32' ? FROM_U32_DOC : '';

    return [
        `impl ${def.wrappedTypeName}`, [
            `\n${NONE_DOC}pub fn none() -> Self`, [
                def.wrappedTypeName,
                def.fields.map(field => `${field.varName}: false,`)
            ],
            `\n${ALL_DOC}pub fn all() -> Self`, [
                def.wrappedTypeName,
                def.fields.map(field => `${field.varName}: true,`)
            ],
            `\n${toIntDoc}pub fn to_${intType}(&self) -> ${intType}`, [
                `0${def.fields.map(field => `\n+ if self.${field.varName} { ${field.value} } else { 0 }`).join('')}`
            ],
            `\n${fromIntDoc}pub fn from_${intType}(value: ${intType}) -> Self`, [
                def.wrappedTypeName,
                def.fields.map(field => `${field.varName}: value & ${field.value} > 0,`)
            ]
        ],
        ``,
        `#[doc(hidden)]`,
        `#[macro_export]`,
        `macro_rules! ${def.wrappedTypeName}${macroSuffix}`, [
            `( $( $x:ident ),* ) =>`, [
                `${def.wrappedTypeName}`, [
                    `$($x: true,)*`,
                    `..${def.wrappedTypeName}::none()`
                ]
            ]
        ]
    ];
}

module.exports = {
    generateVkBitFlagsDefinition,
    genImplFlags,
    genFlagBitsDoc
};