Bug 1965313 - Reimplement enum discriminents, r=bgruber

Differential Revision: https://phabricator.services.mozilla.com/D248491
This commit is contained in:
Ben Dean-Kawamura
2025-05-14 18:41:55 +00:00
committed by bdeankawamura@mozilla.com
parent 90368cc237
commit 02d50105b9
14 changed files with 348 additions and 38 deletions

View File

@@ -1268,47 +1268,47 @@ export const Interest = {
/**
* INCONCLUSIVE
*/
INCONCLUSIVE: 1,
INCONCLUSIVE: 0,
/**
* ANIMALS
*/
ANIMALS: 2,
ANIMALS: 1,
/**
* ARTS
*/
ARTS: 3,
ARTS: 2,
/**
* AUTOS
*/
AUTOS: 4,
AUTOS: 3,
/**
* BUSINESS
*/
BUSINESS: 5,
BUSINESS: 4,
/**
* CAREER
*/
CAREER: 6,
CAREER: 5,
/**
* EDUCATION
*/
EDUCATION: 7,
EDUCATION: 6,
/**
* FASHION
*/
FASHION: 8,
FASHION: 7,
/**
* FINANCE
*/
FINANCE: 9,
FINANCE: 8,
/**
* FOOD
*/
FOOD: 10,
FOOD: 9,
/**
* GOVERNMENT
*/
GOVERNMENT: 11,
GOVERNMENT: 10,
/**
* HOBBIES
*/
@@ -1347,6 +1347,7 @@ Object.freeze(Interest);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeInterest extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return Interest.INCONCLUSIVE
@@ -1392,6 +1393,7 @@ export class FfiConverterTypeInterest extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === Interest.INCONCLUSIVE) {
dataStream.writeInt32(1);
return;

View File

@@ -591,6 +591,7 @@ RemoteSettingsServer.Custom = class extends RemoteSettingsServer{
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsServer extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return new RemoteSettingsServer.Prod(
@@ -611,6 +612,7 @@ export class FfiConverterTypeRemoteSettingsServer extends FfiConverterArrayBuffe
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value instanceof RemoteSettingsServer.Prod) {
dataStream.writeInt32(1);
return;

View File

@@ -356,17 +356,18 @@ export const JsonEngineMethod = {
/**
* POST
*/
POST: 1,
POST: 2,
/**
* GET
*/
GET: 2,
GET: 1,
};
Object.freeze(JsonEngineMethod);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeJSONEngineMethod extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return JsonEngineMethod.POST
@@ -378,6 +379,7 @@ export class FfiConverterTypeJSONEngineMethod extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === JsonEngineMethod.POST) {
dataStream.writeInt32(1);
return;
@@ -1039,17 +1041,18 @@ export const SearchEngineClassification = {
/**
* GENERAL
*/
GENERAL: 1,
GENERAL: 2,
/**
* UNKNOWN
*/
UNKNOWN: 2,
UNKNOWN: 1,
};
Object.freeze(SearchEngineClassification);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSearchEngineClassification extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return SearchEngineClassification.GENERAL
@@ -1061,6 +1064,7 @@ export class FfiConverterTypeSearchEngineClassification extends FfiConverterArra
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === SearchEngineClassification.GENERAL) {
dataStream.writeInt32(1);
return;
@@ -2044,6 +2048,7 @@ Object.freeze(SearchUpdateChannel);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSearchUpdateChannel extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return SearchUpdateChannel.NIGHTLY
@@ -2063,6 +2068,7 @@ export class FfiConverterTypeSearchUpdateChannel extends FfiConverterArrayBuffer
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === SearchUpdateChannel.NIGHTLY) {
dataStream.writeInt32(1);
return;
@@ -2131,6 +2137,7 @@ Object.freeze(SearchApplicationName);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSearchApplicationName extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return SearchApplicationName.FIREFOX_ANDROID
@@ -2148,6 +2155,7 @@ export class FfiConverterTypeSearchApplicationName extends FfiConverterArrayBuff
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === SearchApplicationName.FIREFOX_ANDROID) {
dataStream.writeInt32(1);
return;
@@ -2204,6 +2212,7 @@ Object.freeze(SearchDeviceType);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSearchDeviceType extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return SearchDeviceType.SMARTPHONE
@@ -2217,6 +2226,7 @@ export class FfiConverterTypeSearchDeviceType extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === SearchDeviceType.SMARTPHONE) {
dataStream.writeInt32(1);
return;

View File

@@ -755,21 +755,22 @@ export const GeonameMatchType = {
/**
* For U.S. states, abbreviations are the usual two-letter codes ("CA").
*/
ABBREVIATION: 1,
ABBREVIATION: 0,
/**
* AIRPORT_CODE
*/
AIRPORT_CODE: 2,
AIRPORT_CODE: 1,
/**
* This includes any names that aren't abbreviations or airport codes.
*/
NAME: 3,
NAME: 2,
};
Object.freeze(GeonameMatchType);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeGeonameMatchType extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return GeonameMatchType.ABBREVIATION
@@ -783,6 +784,7 @@ export class FfiConverterTypeGeonameMatchType extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === GeonameMatchType.ABBREVIATION) {
dataStream.writeInt32(1);
return;
@@ -1455,6 +1457,7 @@ Suggestion.Dynamic = class extends Suggestion{
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return new Suggestion.Amp(
@@ -1552,6 +1555,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value instanceof Suggestion.Amp) {
dataStream.writeInt32(1);
FfiConverterString.write(dataStream, value.title);
@@ -2070,6 +2074,7 @@ Object.freeze(SuggestionProvider);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return SuggestionProvider.AMP
@@ -2095,6 +2100,7 @@ export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === SuggestionProvider.AMP) {
dataStream.writeInt32(1);
return;
@@ -2327,6 +2333,7 @@ Object.freeze(AmpMatchingStrategy);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeAmpMatchingStrategy extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return AmpMatchingStrategy.NO_KEYWORD_EXPANSION
@@ -2340,6 +2347,7 @@ export class FfiConverterTypeAmpMatchingStrategy extends FfiConverterArrayBuffer
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === AmpMatchingStrategy.NO_KEYWORD_EXPANSION) {
dataStream.writeInt32(1);
return;
@@ -2925,17 +2933,18 @@ export const GeonameType = {
/**
* CITY
*/
CITY: 1,
CITY: 0,
/**
* REGION
*/
REGION: 2,
REGION: 1,
};
Object.freeze(GeonameType);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeGeonameType extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return GeonameType.CITY
@@ -2947,6 +2956,7 @@ export class FfiConverterTypeGeonameType extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === GeonameType.CITY) {
dataStream.writeInt32(1);
return;
@@ -2976,22 +2986,23 @@ export const InterruptKind = {
/**
* Interrupt read operations like [SuggestStore::query]
*/
READ: 1,
READ: 0,
/**
* Interrupt write operations. This mostly means [SuggestStore::ingest], but
* other operations may also be interrupted.
*/
WRITE: 2,
WRITE: 1,
/**
* Interrupt both read and write operations,
*/
READ_WRITE: 3,
READ_WRITE: 2,
};
Object.freeze(InterruptKind);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeInterruptKind extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return InterruptKind.READ
@@ -3005,6 +3016,7 @@ export class FfiConverterTypeInterruptKind extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === InterruptKind.READ) {
dataStream.writeInt32(1);
return;
@@ -3197,6 +3209,7 @@ SuggestProviderConfig.Weather = class extends SuggestProviderConfig{
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSuggestProviderConfig extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return new SuggestProviderConfig.Weather(
@@ -3209,6 +3222,7 @@ export class FfiConverterTypeSuggestProviderConfig extends FfiConverterArrayBuff
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value instanceof SuggestProviderConfig.Weather) {
dataStream.writeInt32(1);
FfiConverterFloat64.write(dataStream, value.score);

View File

@@ -293,33 +293,34 @@ export const DeviceType = {
/**
* DESKTOP
*/
DESKTOP: 1,
DESKTOP: 0,
/**
* MOBILE
*/
MOBILE: 2,
MOBILE: 1,
/**
* TABLET
*/
TABLET: 3,
TABLET: 2,
/**
* VR
*/
VR: 4,
VR: 3,
/**
* TV
*/
TV: 5,
TV: 4,
/**
* UNKNOWN
*/
UNKNOWN: 6,
UNKNOWN: 5,
};
Object.freeze(DeviceType);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return DeviceType.DESKTOP
@@ -339,6 +340,7 @@ export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === DeviceType.DESKTOP) {
dataStream.writeInt32(1);
return;

View File

@@ -842,6 +842,7 @@ RemoteCommand.CloseTab = class extends RemoteCommand{
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteCommand extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return new RemoteCommand.CloseTab(
@@ -853,6 +854,7 @@ export class FfiConverterTypeRemoteCommand extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value instanceof RemoteCommand.CloseTab) {
dataStream.writeInt32(1);
FfiConverterString.write(dataStream, value.url);

View File

@@ -693,21 +693,22 @@ export const QuotaReason = {
/**
* TOTAL_BYTES
*/
TOTAL_BYTES: 1,
TOTAL_BYTES: 0,
/**
* ITEM_BYTES
*/
ITEM_BYTES: 2,
ITEM_BYTES: 1,
/**
* MAX_ITEMS
*/
MAX_ITEMS: 3,
MAX_ITEMS: 2,
};
Object.freeze(QuotaReason);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeQuotaReason extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return QuotaReason.TOTAL_BYTES
@@ -721,6 +722,7 @@ export class FfiConverterTypeQuotaReason extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === QuotaReason.TOTAL_BYTES) {
dataStream.writeInt32(1);
return;

View File

@@ -0,0 +1,63 @@
/* 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::*;
pub fn pass(en: &mut Enum) -> Result<()> {
// Determine the actual discriminants for each variant.
//
// TODO: the UniFFI general pipeline should probably do this. If this was introduced earlier
// in the pipeline, we wouldn't need to hand-code so many fields.
en.resolved_discr_type = match &en.discr_type {
Some(type_node) => type_node.clone(),
None => TypeNode {
ty: Type::UInt8,
canonical_name: "UInt8".to_string(),
ffi_converter: "FfiConverterUInt8".to_string(),
is_used_as_error: false,
ffi_type: FfiTypeNode {
ty: FfiType::UInt8,
type_name: "uint8_t".to_string(),
},
},
};
let discr_type = &en.resolved_discr_type;
let mut last_variant: Option<&Variant> = None;
for variant in en.variants.iter_mut() {
match &variant.discr {
None => {
let lit = match last_variant {
None => match &discr_type.ty {
Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => {
Literal::UInt(0, Radix::Decimal, discr_type.clone())
}
Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => {
Literal::Int(0, Radix::Decimal, discr_type.clone())
}
ty => bail!("Invalid enum discriminant type: {ty:?}"),
},
Some(variant) => match &variant.resolved_discr.lit {
Literal::UInt(val, _, _) => {
Literal::UInt(val + 1, Radix::Decimal, discr_type.clone())
}
Literal::Int(val, _, _) => {
Literal::Int(val + 1, Radix::Decimal, discr_type.clone())
}
lit => bail!("Invalid enum discriminant literal: {lit:?}"),
},
};
variant.resolved_discr = LiteralNode {
lit,
..LiteralNode::default()
};
}
Some(lit_node) => {
variant.resolved_discr = lit_node.clone();
}
}
last_variant = Some(variant);
}
Ok(())
}

View File

@@ -12,6 +12,7 @@ mod cpp_ffi_types;
mod cpp_names;
mod cpp_scaffolding_calls;
mod docs;
mod enums;
mod interfaces;
mod js_docstrings;
mod js_filename;
@@ -21,7 +22,7 @@ mod modules;
mod types;
use crate::Config;
use anyhow::Result;
use anyhow::{bail, Result};
pub use nodes::*;
use std::collections::HashMap;
use uniffi_bindgen::pipeline::{general, initial};
@@ -33,6 +34,7 @@ pub fn gecko_js_pipeline(pipeline_map: HashMap<String, Config>) -> GeckoPipeline
general::pipeline()
.convert_ir_pass::<Root>()
.pass(modules::pass(pipeline_map))
.pass(enums::pass)
.pass(callables::pass)
.pass(interfaces::pass)
.pass(callback_interfaces::pass)

View File

@@ -301,6 +301,7 @@ pub struct Enum {
pub remote: bool,
pub variants: Vec<Variant>,
pub discr_type: Option<TypeNode>,
pub resolved_discr_type: TypeNode,
pub non_exhaustive: bool,
pub js_docstring: String,
pub docstring: Option<String>,
@@ -312,6 +313,7 @@ pub struct Enum {
pub struct Variant {
pub name: String,
pub discr: Option<LiteralNode>,
pub resolved_discr: LiteralNode,
pub fields: Vec<Field>,
pub docstring: Option<String>,
pub js_docstring: String,

View File

@@ -4,7 +4,7 @@
export const {{ enum_.name }} = {
{%- for variant in enum_.variants %}
{{ variant.js_docstring|indent(4) }}
{{ variant.name }}: {{loop.index}},
{{ variant.name }}: {{ variant.resolved_discr.js_lit }},
{%- endfor %}
};
Object.freeze({{ enum_.name }});
@@ -12,6 +12,7 @@ Object.freeze({{ enum_.name }});
// Export the FFIConverter object to make external types work.
export class {{ enum_|ffi_converter }} extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
{%- for variant in enum_.variants %}
case {{ loop.index }}:
@@ -23,6 +24,7 @@ export class {{ enum_|ffi_converter }} extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
{%- for variant in enum_.variants %}
if (value === {{ enum_.name }}.{{ variant.name }}) {
dataStream.writeInt32({{ loop.index }});
@@ -67,6 +69,7 @@ export class {{ enum_.name }} {}
// Export the FFIConverter object to make external types work.
export class {{ enum_|ffi_converter }} extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
{%- for variant in enum_.variants %}
case {{ loop.index }}:
@@ -82,6 +85,7 @@ export class {{ enum_|ffi_converter }} extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
{%- for variant in enum_.variants %}
if (value instanceof {{ enum_.name }}.{{ variant.name }}) {
dataStream.writeInt32({{ loop.index }});

View File

@@ -2264,21 +2264,22 @@ export const EnumNoData = {
/**
* A
*/
A: 1,
A: 0,
/**
* B
*/
B: 2,
B: 1,
/**
* C
*/
C: 3,
C: 2,
};
Object.freeze(EnumNoData);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeEnumNoData extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return EnumNoData.A
@@ -2292,6 +2293,7 @@ export class FfiConverterTypeEnumNoData extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === EnumNoData.A) {
dataStream.writeInt32(1);
return;
@@ -2357,6 +2359,7 @@ EnumWithData.C = class extends EnumWithData{
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeEnumWithData extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return new EnumWithData.A(
@@ -2375,6 +2378,7 @@ export class FfiConverterTypeEnumWithData extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value instanceof EnumWithData.A) {
dataStream.writeInt32(1);
FfiConverterUInt8.write(dataStream, value.value);
@@ -2457,6 +2461,7 @@ ComplexEnum.C = class extends ComplexEnum{
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeComplexEnum extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return new ComplexEnum.A(
@@ -2476,6 +2481,7 @@ export class FfiConverterTypeComplexEnum extends FfiConverterArrayBuffer {
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value instanceof ComplexEnum.A) {
dataStream.writeInt32(1);
FfiConverterTypeEnumNoData.write(dataStream, value.value);
@@ -2519,6 +2525,162 @@ export class FfiConverterTypeComplexEnum extends FfiConverterArrayBuffer {
}
}
/**
* ExplicitValuedEnum
*/
export const ExplicitValuedEnum = {
/**
* FIRST
*/
FIRST: 1,
/**
* SECOND
*/
SECOND: 2,
/**
* FOURTH
*/
FOURTH: 4,
/**
* TENTH
*/
TENTH: 10,
/**
* ELEVENTH
*/
ELEVENTH: 11,
/**
* THIRTEENTH
*/
THIRTEENTH: 13,
};
Object.freeze(ExplicitValuedEnum);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeExplicitValuedEnum extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return ExplicitValuedEnum.FIRST
case 2:
return ExplicitValuedEnum.SECOND
case 3:
return ExplicitValuedEnum.FOURTH
case 4:
return ExplicitValuedEnum.TENTH
case 5:
return ExplicitValuedEnum.ELEVENTH
case 6:
return ExplicitValuedEnum.THIRTEENTH
default:
throw new UniFFITypeError("Unknown ExplicitValuedEnum variant");
}
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === ExplicitValuedEnum.FIRST) {
dataStream.writeInt32(1);
return;
}
if (value === ExplicitValuedEnum.SECOND) {
dataStream.writeInt32(2);
return;
}
if (value === ExplicitValuedEnum.FOURTH) {
dataStream.writeInt32(3);
return;
}
if (value === ExplicitValuedEnum.TENTH) {
dataStream.writeInt32(4);
return;
}
if (value === ExplicitValuedEnum.ELEVENTH) {
dataStream.writeInt32(5);
return;
}
if (value === ExplicitValuedEnum.THIRTEENTH) {
dataStream.writeInt32(6);
return;
}
throw new UniFFITypeError("Unknown ExplicitValuedEnum variant");
}
static computeSize(value) {
return 4;
}
static checkType(value) {
if (!Number.isInteger(value) || value < 1 || value > 6) {
throw new UniFFITypeError(`${value} is not a valid value for ExplicitValuedEnum`);
}
}
}
/**
* GappedEnum
*/
export const GappedEnum = {
/**
* ONE
*/
ONE: 10,
/**
* TWO
*/
TWO: 11,
/**
* THREE
*/
THREE: 14,
};
Object.freeze(GappedEnum);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeGappedEnum extends FfiConverterArrayBuffer {
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
switch (dataStream.readInt32()) {
case 1:
return GappedEnum.ONE
case 2:
return GappedEnum.TWO
case 3:
return GappedEnum.THREE
default:
throw new UniFFITypeError("Unknown GappedEnum variant");
}
}
static write(dataStream, value) {
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
if (value === GappedEnum.ONE) {
dataStream.writeInt32(1);
return;
}
if (value === GappedEnum.TWO) {
dataStream.writeInt32(2);
return;
}
if (value === GappedEnum.THREE) {
dataStream.writeInt32(3);
return;
}
throw new UniFFITypeError("Unknown GappedEnum variant");
}
static computeSize(value) {
return 4;
}
static checkType(value) {
if (!Number.isInteger(value) || value < 1 || value > 3) {
throw new UniFFITypeError(`${value} is not a valid value for GappedEnum`);
}
}
}
/**
* Error enum
*/

View File

@@ -5,9 +5,11 @@ const {
roundtripEnumNoData,
roundtripEnumWithData,
roundtripComplexEnum,
ComplexEnum,
EnumNoData,
EnumWithData,
ComplexEnum,
ExplicitValuedEnum,
GappedEnum,
SimpleRec,
} = ChromeUtils.importESModule(
"resource://gre/modules/RustUniffiBindingsTests.sys.mjs"
@@ -40,3 +42,23 @@ Assert.deepEqual(
roundtripComplexEnum(new ComplexEnum.C(new SimpleRec({ a: 30 }))),
new ComplexEnum.C(new SimpleRec({ a: 30 }))
);
// Test that the enum discriminant values
// No discriminant specified, start at 0 then increment by 1
Assert.equal(EnumNoData.A, 0);
Assert.equal(EnumNoData.B, 1);
Assert.equal(EnumNoData.C, 2);
// All discriminants specified, use the specified values
Assert.equal(ExplicitValuedEnum.FIRST, 1);
Assert.equal(ExplicitValuedEnum.SECOND, 2);
Assert.equal(ExplicitValuedEnum.FOURTH, 4);
Assert.equal(ExplicitValuedEnum.TENTH, 10);
Assert.equal(ExplicitValuedEnum.ELEVENTH, 11);
Assert.equal(ExplicitValuedEnum.THIRTEENTH, 13);
// Some discriminants specified, increment by one for any unspecified variants
Assert.equal(GappedEnum.ONE, 10);
Assert.equal(GappedEnum.TWO, 11); // Sequential value after ONE (10+1)
Assert.equal(GappedEnum.THREE, 14); // Explicit value again

View File

@@ -25,6 +25,27 @@ pub enum ComplexEnum {
C { value: SimpleRec },
}
// Test enum with explicit discriminant values and gaps
#[repr(u8)]
#[derive(Debug, Clone, Copy, uniffi::Enum)]
pub enum ExplicitValuedEnum {
First = 1,
Second = 2,
Fourth = 4,
Tenth = 10,
Eleventh = 11,
Thirteenth = 13,
}
// Example with sequential and explicit values mixed
#[repr(u8)]
#[derive(Debug, Clone, Copy, uniffi::Enum)]
pub enum GappedEnum {
One = 10,
Two, // should be 11
Three = 14,
}
#[uniffi::export]
pub fn roundtrip_enum_no_data(en: EnumNoData) -> EnumNoData {
en