uniffi_bindgen 0.31.1

a multi-language bindings generator for rust (codegen and cli tooling)
Documentation
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use super::*;
use std::cmp::{max, min};

pub fn pass(en: &mut Enum) -> Result<()> {
    en.is_flat = match en.shape {
        EnumShape::Error { flat } => flat,
        EnumShape::Enum => en.variants.iter().all(|v| v.fields.is_empty()),
    };
    for v in en.variants.iter_mut() {
        v.fields_kind = if v.fields.is_empty() {
            FieldsKind::Unit
        } else if v.fields.iter().any(|f| f.name.is_empty()) {
            FieldsKind::Unnamed
        } else {
            FieldsKind::Named
        };
    }
    determine_discriminants(en)?;
    Ok(())
}

/// Set the `Enum::discr_type` and `Variant::discr` fields
///
/// If we get a value from the metadata, then those will be used.  Otherwise, we will calculate the
/// discriminants by following Rust's logic.
pub fn determine_discriminants(en: &mut Enum) -> Result<()> {
    let signed = match &en.meta_discr_type {
        Some(type_node) => match &type_node.ty {
            Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => false,
            Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => true,
            ty => bail!("Invalid enum discriminant type: {ty:?}"),
        },
        // If not specified, then the discriminant type is signed.  We'll calculate the width as we
        // go through the variant discriminants
        None => true,
    };

    // Calculate all variant discriminants.
    // Use a placeholder value for the type, since we don't necessarily know it yet.
    let mut max_value: u64 = 0;
    let mut min_value: i64 = 0;
    let mut last_variant: Option<&Variant> = None;

    for variant in en.variants.iter_mut() {
        let discr = match &variant.meta_discr {
            None => {
                let lit = match last_variant {
                    None => {
                        if signed {
                            Literal::Int(0, Radix::Decimal, TypeNode::default())
                        } else {
                            Literal::UInt(0, Radix::Decimal, TypeNode::default())
                        }
                    }
                    Some(variant) => match &variant.discr.lit {
                        Literal::UInt(val, _, _) => {
                            Literal::UInt(val + 1, Radix::Decimal, TypeNode::default())
                        }
                        Literal::Int(val, _, _) => {
                            Literal::Int(val + 1, Radix::Decimal, TypeNode::default())
                        }
                        lit => bail!("Invalid enum discriminant literal: {lit:?}"),
                    },
                };
                LiteralNode { lit }
            }
            Some(lit_node) => lit_node.clone(),
        };
        match &discr.lit {
            Literal::UInt(val, _, _) => {
                max_value = max(max_value, *val);
            }
            Literal::Int(val, _, _) => {
                if *val >= 0 {
                    max_value = max(max_value, *val as u64);
                } else {
                    min_value = min(min_value, *val);
                }
            }
            _ => unreachable!(),
        }
        variant.discr = discr;
        last_variant = Some(variant);
    }

    // Finally, we can figure out the discriminant type
    en.discr_type = match &en.meta_discr_type {
        Some(type_node) => type_node.clone(),
        None => {
            if min_value >= i8::MIN as i64 && max_value <= i8::MAX as u64 {
                TypeNode {
                    ty: Type::Int8,
                    ..TypeNode::default()
                }
            } else if min_value >= i16::MIN as i64 && max_value <= i16::MAX as u64 {
                TypeNode {
                    ty: Type::Int16,
                    ..TypeNode::default()
                }
            } else if min_value >= i32::MIN as i64 && max_value <= i32::MAX as u64 {
                TypeNode {
                    ty: Type::Int32,
                    ..TypeNode::default()
                }
            } else if max_value <= i64::MAX as u64 {
                // Note: no need to check `min_value` since that's always in the `i64` bounds.
                TypeNode {
                    ty: Type::Int64,
                    ..TypeNode::default()
                }
            } else {
                bail!("Enum repr not set and magnitude exceeds i64::MAX");
            }
        }
    };
    for variant in en.variants.iter_mut() {
        match &mut variant.discr.lit {
            Literal::UInt(_, _, type_node) | Literal::Int(_, _, type_node) => {
                *type_node = en.discr_type.clone();
            }
            _ => unreachable!(),
        }
    }

    Ok(())
}