1use std::cmp::Ordering;
7use std::collections::{BTreeMap, HashMap, HashSet};
8use std::fmt::{self, Write as _};
9use std::mem;
10use std::str::FromStr;
11
12use anyhow::{bail, Result};
13use heck::*;
14use wit_bindgen_core::abi::{Bitcast, WasmType};
15use wit_bindgen_core::{
16 uwriteln, wit_parser::*, Direction, Files, InterfaceGenerator as _, Source, Types,
17 WorldGenerator,
18};
19
20mod bindgen;
21mod interface;
22
23#[derive(Default)]
24struct ResourceInfo {
25 direction: Direction,
31 owned: bool,
32}
33
34struct InterfaceName {
35 remapped: bool,
38
39 path: String,
41}
42
43#[derive(Default)]
44pub struct MoonBit {
45 types: Types,
46 src: Source,
47 opts: Opts,
48 import_modules: Vec<(String, Vec<ModuleName>)>,
49 export_modules: Vec<(String, Vec<ModuleName>)>,
50 skip: HashSet<String>,
51 interface_names: HashMap<InterfaceId, InterfaceName>,
52 resources: HashMap<TypeId, ResourceInfo>,
53 import_funcs_called: bool,
54 with_name_counter: usize,
55 export_traits: HashMap<String, String>,
56 imported_builtins: HashSet<&'static str>,
57 pub exported_symbols: HashMap<String, (String, bool)>,
58}
59
60#[derive(Clone, Eq)]
61struct ModuleName {
62 snake: String,
63 qual: String,
64}
65
66impl ModuleName {
67 fn root(name: impl AsRef<str>) -> Self {
68 let name = to_mbt_ident(name.as_ref());
69 Self {
70 snake: name.to_snake_case(),
71 qual: name.to_upper_camel_case(),
72 }
73 }
74
75 fn child(&self, name: impl AsRef<str>) -> Self {
76 let name = to_mbt_ident(name.as_ref());
77 Self {
78 snake: name.to_snake_case(),
79 qual: format!("{}{}", self.qual, name.to_upper_camel_case()),
80 }
81 }
82}
83
84impl PartialEq<Self> for ModuleName {
85 fn eq(&self, other: &Self) -> bool {
86 self.qual == other.qual
87 }
88}
89
90impl PartialOrd<Self> for ModuleName {
91 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92 self.qual.partial_cmp(&other.qual)
93 }
94}
95
96impl Ord for ModuleName {
97 fn cmp(&self, other: &Self) -> Ordering {
98 self.qual.cmp(&other.qual)
99 }
100}
101
102#[cfg(feature = "clap")]
103fn iterate_hashmap_string(s: &str) -> impl Iterator<Item = Result<(&str, &str), String>> {
104 s.split(',').map(move |entry| {
105 entry.split_once('=').ok_or_else(|| {
106 format!("expected string of form `<key>=<value>[,<key>=<value>...]`; got `{s}`")
107 })
108 })
109}
110
111#[cfg(feature = "clap")]
112fn parse_exports(s: &str) -> Result<HashMap<ExportKey, String>, String> {
113 if s.is_empty() {
114 Ok(HashMap::default())
115 } else {
116 iterate_hashmap_string(s)
117 .map(|entry| {
118 let (key, value) = entry?;
119 Ok((
120 match key {
121 "world" => ExportKey::World,
122 _ => ExportKey::Name(key.to_owned()),
123 },
124 value.to_owned(),
125 ))
126 })
127 .collect()
128 }
129}
130
131#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
132pub enum ExportKey {
133 World,
134 Name(String),
135}
136
137#[cfg(feature = "clap")]
138fn parse_with(s: &str) -> Result<HashMap<String, String>, String> {
139 if s.is_empty() {
140 Ok(HashMap::default())
141 } else {
142 iterate_hashmap_string(s)
143 .map(|entry| {
144 let (key, value) = entry?;
145 Ok((key.to_owned(), value.to_owned()))
146 })
147 .collect()
148 }
149}
150
151#[derive(Default, Debug, Clone)]
152#[cfg_attr(feature = "clap", derive(clap::Args))]
153pub struct Opts {
154 #[cfg_attr(feature = "clap", arg(long))]
157 pub std_feature: bool,
158
159 #[cfg_attr(feature = "clap", arg(long))]
164 pub raw_strings: bool,
165
166 #[cfg_attr(feature = "clap", arg(long))]
168 pub skip: Vec<String>,
169
170 #[cfg_attr(feature = "clap", arg(long, value_parser = parse_exports, default_value = ""))]
175 pub exports: HashMap<ExportKey, String>,
176
177 #[cfg_attr(feature = "clap", arg(long))]
180 pub stubs: bool,
181
182 #[cfg_attr(feature = "clap", arg(long))]
186 pub export_prefix: Option<String>,
187
188 #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))]
202 pub ownership: Ownership,
203
204 #[cfg_attr(feature = "clap", arg(long))]
208 pub runtime_path: Option<String>,
209
210 #[cfg_attr(feature = "clap", arg(long))]
214 pub bitflags_path: Option<String>,
215
216 #[cfg_attr(feature = "clap", arg(long = "additional_derive_attribute", short = 'd', default_values_t = Vec::<String>::new()))]
221 pub additional_derive_attributes: Vec<String>,
222
223 #[cfg_attr(feature = "clap", arg(long, value_parser = parse_with, default_value = ""))]
225 pub with: HashMap<String, String>,
226}
227
228impl Opts {
229 pub fn build(self) -> Box<dyn WorldGenerator> {
230 let mut r = MoonBit::new();
231 r.skip = self.skip.iter().cloned().collect();
232 r.opts = self;
233 Box::new(r)
234 }
235}
236
237impl MoonBit {
238 fn new() -> MoonBit {
239 MoonBit::default()
240 }
241
242 fn interface<'a>(
243 &'a mut self,
244 identifier: Identifier<'a>,
245 wasm_import_module: Option<&'a str>,
246 resolve: &'a Resolve,
247 in_import: bool,
248 ) -> interface::InterfaceGenerator<'a> {
249 let mut sizes = SizeAlign::default();
250 sizes.fill(resolve);
251
252 interface::InterfaceGenerator {
253 identifier,
254 wasm_import_module,
255 src: Source::default(),
256 in_import,
257 gen: self,
258 sizes,
259 resolve,
260 return_pointer_area_size: 0,
261 return_pointer_area_align: 0,
262 }
263 }
264
265 fn emit_modules(&mut self, modules: Vec<(String, Vec<ModuleName>)>, in_import: bool) {
266 #[derive(Default)]
267 struct Module {
268 submodules: BTreeMap<ModuleName, Module>,
269 contents: Vec<String>,
270 }
271 let mut map = Module::default();
272 for (module, path) in modules {
273 let mut cur = &mut map;
274 for name in path.iter() {
275 cur = cur
276 .submodules
277 .entry(name.clone())
278 .or_insert(Module::default());
279 }
280 cur.contents.push(module);
281 }
282 let inits = emit(&mut self.src, map, in_import);
283 if in_import {
284 for (snake, init) in inits {
285 uwriteln!(self.src, "pub let {}: {} = {init}\n", snake.snake, snake.qual);
286 }
287 }
288 fn emit(me: &mut Source, module: Module, in_import: bool) -> Vec<(ModuleName, String)> {
289 let mut rv = Vec::new();
290 for (name, submodule) in module.submodules {
291 if in_import {
292 if submodule.submodules.is_empty() {
293 uwriteln!(me, "pub(readonly) type {} Unit", name.qual);
294 } else {
295 uwriteln!(me, "pub(readonly) struct {} {{", name.qual);
296 for (name, _) in submodule.submodules.iter() {
297 uwriteln!(me, "{}: {}", name.snake, name.qual);
298 }
299 uwriteln!(me, "}}");
300 }
301 uwriteln!(me, "");
302 }
303
304 let sub_inits = emit(me, submodule, in_import);
305 if in_import {
306 let init = if sub_inits.is_empty() {
307 format!("{}(())", name.qual)
308 } else {
309 let init = sub_inits
310 .into_iter()
311 .map(|(name, init)| format!("{}: {init}", name.snake))
312 .collect::<Vec<_>>()
313 .join(", ");
314 format!("{{ {init} }}")
315 };
316 rv.push((name, init));
317 }
318 }
319 for submodule in module.contents {
320 uwriteln!(me, "{submodule}");
321 }
322 rv
323 }
324 }
325
326 fn runtime_path(&self) -> &str {
327 self.opts
328 .runtime_path
329 .as_deref()
330 .unwrap_or("wit_bindgen::rt")
331 }
332
333 fn bitflags_path(&self) -> &str {
334 self.opts
335 .bitflags_path
336 .as_deref()
337 .unwrap_or("wit_bindgen::bitflags")
338 }
339
340 fn lookup_export(&self, key: &ExportKey) -> Result<String> {
341 if let Some(key) = self.opts.exports.get(key) {
342 return Ok(key.clone());
343 }
344 if self.opts.stubs {
345 return Ok("Stub".to_owned());
346 }
347 let key = match key {
348 ExportKey::World => "world".to_owned(),
349 ExportKey::Name(name) => format!("\"{name}\""),
350 };
351 if self.opts.exports.is_empty() {
352 bail!("no `exports` map provided in configuration - provide an `exports` map a key `{key}`")
353 }
354 bail!("expected `exports` map to contain key `{key}`")
355 }
356
357 fn name_interface(
358 &mut self,
359 resolve: &Resolve,
360 id: InterfaceId,
361 name: &WorldKey,
362 is_export: bool,
363 ) -> bool {
364 let with_name = resolve.name_world_key(name);
365 let entry = if let Some(remapped_path) = self.opts.with.get(&with_name) {
366 let name = format!("__with_name{}", self.with_name_counter);
367 self.with_name_counter += 1;
368 uwriteln!(self.src, "use {remapped_path} as {name};");
369 InterfaceName {
370 remapped: true,
371 path: name,
372 }
373 } else {
374 let path = compute_module_path(name, resolve, is_export)
375 .into_iter()
376 .map(|n| n.snake)
377 .collect::<Vec<_>>()
378 .join("::");
379
380 InterfaceName {
381 remapped: false,
382 path,
383 }
384 };
385
386 let remapped = entry.remapped;
387 self.interface_names.insert(id, entry);
388
389 remapped
390 }
391}
392
393fn name_package_module(resolve: &Resolve, id: PackageId) -> String {
398 let pkg = &resolve.packages[id];
399 let versions_with_same_name = resolve
400 .packages
401 .iter()
402 .filter_map(|(_, p)| {
403 if p.name.namespace == pkg.name.namespace && p.name.name == pkg.name.name {
404 Some(&p.name.version)
405 } else {
406 None
407 }
408 })
409 .collect::<Vec<_>>();
410 let base = pkg.name.name.to_snake_case();
411 if versions_with_same_name.len() == 1 {
412 return base;
413 }
414
415 let version = match &pkg.name.version {
416 Some(version) => version,
417 None => return base,
421 };
422
423 let version = version
430 .to_string()
431 .replace('.', "_")
432 .replace('-', "_")
433 .replace('+', "_")
434 .to_snake_case();
435 format!("{base}{version}")
436}
437
438impl WorldGenerator for MoonBit {
439 fn preprocess(&mut self, resolve: &Resolve, _world: WorldId) {
440 wit_bindgen_core::generated_preamble(&mut self.src, env!("CARGO_PKG_VERSION"));
441 self.types.analyze(resolve);
442 }
443
444 fn import_interface(
445 &mut self,
446 resolve: &Resolve,
447 name: &WorldKey,
448 id: InterfaceId,
449 _files: &mut Files,
450 ) {
451 let wasm_import_module = resolve.name_world_key(name);
452 let mut gen = self.interface(
453 Identifier::Interface(id, name),
454 Some(&wasm_import_module),
455 resolve,
456 true,
457 );
458 let module_path = gen.start_append_submodule(name);
459 if gen.gen.name_interface(resolve, id, name, false) {
460 return;
461 }
462 gen.types(id);
463
464 gen.generate_imports(resolve.interfaces[id].functions.values(), module_path.last());
465
466 gen.finish_append_submodule(module_path);
467 }
468
469 fn import_funcs(
470 &mut self,
471 resolve: &Resolve,
472 world: WorldId,
473 funcs: &[(&str, &Function)],
474 _files: &mut Files,
475 ) {
476 self.import_funcs_called = true;
477
478 let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true);
479
480 gen.generate_imports(funcs.iter().map(|(_, func)| *func), None);
481
482 let src = gen.finish();
483 self.src.push_str(&src);
484 }
485
486 fn export_interface(
487 &mut self,
488 resolve: &Resolve,
489 name: &WorldKey,
490 id: InterfaceId,
491 _files: &mut Files,
492 ) -> Result<()> {
493 let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false);
494 let module_path = gen.start_append_submodule(name);
495 if gen.gen.name_interface(resolve, id, name, true) {
496 return Ok(());
497 }
498 gen.types(id);
499 gen.generate_exports(resolve.interfaces[id].functions.values(), module_path.last())?;
500 gen.finish_append_submodule(module_path);
501 Ok(())
502 }
503
504 fn export_funcs(
505 &mut self,
506 resolve: &Resolve,
507 world: WorldId,
508 funcs: &[(&str, &Function)],
509 _files: &mut Files,
510 ) -> Result<()> {
511 let mut gen = self.interface(Identifier::World(world), None, resolve, false);
512 gen.generate_exports(funcs.iter().map(|f| f.1), None)?;
513 let src = gen.finish();
514 self.src.push_str(&src);
515 Ok(())
516 }
517
518 fn import_types(
519 &mut self,
520 resolve: &Resolve,
521 world: WorldId,
522 types: &[(&str, TypeId)],
523 _files: &mut Files,
524 ) {
525 let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true);
526 for (name, ty) in types {
527 gen.define_type(name, *ty);
528 }
529 let src = gen.finish();
530 self.src.push_str(&src);
531 }
532
533 fn finish_imports(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) {
534 if !self.import_funcs_called {
535 self.import_funcs(resolve, world, &[], files);
539 }
540 }
541
542 fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) {
543 let name = &resolve.worlds[world].name;
544
545 let imports = mem::take(&mut self.import_modules);
546 self.emit_modules(imports, true);
547 let exports = mem::take(&mut self.export_modules);
548 self.emit_modules(exports, false);
549
550 if self.opts.stubs {
551 self.src.push_str("\n#[derive(Debug)]\npub struct Stub;\n");
552 let world_id = world;
553 let world = &resolve.worlds[world];
554 let mut funcs = Vec::new();
555 for (name, export) in world.exports.iter() {
556 let (pkg, name) = match name {
557 WorldKey::Name(name) => (None, name),
558 WorldKey::Interface(id) => {
559 let interface = &resolve.interfaces[*id];
560 (
561 Some(interface.package.unwrap()),
562 interface.name.as_ref().unwrap(),
563 )
564 }
565 };
566 match export {
567 WorldItem::Function(func) => {
568 funcs.push(func);
569 }
570 WorldItem::Interface(id) => {
571 for (resource, funcs) in
572 group_by_resource(resolve.interfaces[*id].functions.values())
573 {
574 let mut gen =
575 self.interface(Identifier::World(world_id), None, resolve, false);
576 let pkg = pkg.map(|pid| {
577 let namespace = resolve.packages[pid].name.namespace.clone();
578 let package_module = name_package_module(resolve, pid);
579 (namespace, package_module)
580 });
581 gen.generate_stub(resource, pkg, name, true, &funcs);
582 let stub = gen.finish();
583 self.src.push_str(&stub);
584 }
585 }
586 WorldItem::Type(_) => unreachable!(),
587 }
588 }
589
590 for (resource, funcs) in group_by_resource(funcs.into_iter()) {
591 let mut gen = self.interface(Identifier::World(world_id), None, resolve, false);
592 gen.generate_stub(resource, None, &world.name, false, &funcs);
593 let stub = gen.finish();
594 self.src.push_str(&stub);
595 }
596 }
597
598 uwriteln!(self.src, "struct GuestImpl {{");
599 for (trait_name, field) in self.export_traits.iter() {
600 uwriteln!(self.src, "mut {field}: Option[{trait_name}]");
601 }
602 uwriteln!(self.src, "}} derive(Default)");
603 uwriteln!(self.src, "");
604 uwriteln!(self.src, "let guest_impl: GuestImpl = GuestImpl::default()");
605 uwriteln!(self.src, "");
606 self.src.push_str("pub fn init_guest[T: ");
607 self.src.push_str(
608 &self
609 .export_traits
610 .keys()
611 .map(String::as_str)
612 .collect::<Vec<_>>()
613 .join(" + "),
614 );
615 uwriteln!(self.src, "](guest: T) {{");
616 for (trait_name, field) in self.export_traits.iter() {
617 uwriteln!(self.src, "guest_impl.{field} = Some(guest as {trait_name})");
618 }
619 uwriteln!(self.src, "}}\n");
620
621 let mut builtins = self.imported_builtins.iter().collect::<Vec<_>>();
622 builtins.sort();
623 for (i, builtin) in builtins.into_iter().enumerate() {
624 if i > 0 {
625 uwriteln!(self.src, "");
626 }
627 let def = match *builtin {
628 "_rael_malloc" => "(size: Int) -> Int = \"$rael.malloc\"",
629 "_rael_free" => "(ptr: Int) = \"$rael.free\"",
630 "_rael_memory_copy" => "(dst: Int, src: Int, len: Int) = \"$rael.memory_copy\"",
631 "_rael_load_i32" => "(ptr: Int) -> Int = \"$rael.load_i32\"",
632 "_rael_load_i64" => "(ptr: Int) -> Int64 = \"$rael.load_i64\"",
633 "_rael_bytes_data" => "(b: Bytes) -> Int = \"$rael.bytes_data\"",
634 "_mbt_string_data" => "(s: String) -> Int = \"$moonbit.string_data\"",
635 "_mbt_unsafe_make_string" => {
636 "(len: Int, val: Int) -> String = \"$moonbit.unsafe_make_string\""
637 },
638 _ => unreachable!()
639 };
640 uwriteln!(self.src, "fn {builtin}{def}");
641 }
642
643 let src = mem::take(&mut self.src);
644 let module_name = name.to_snake_case();
645 files.push(&format!("{module_name}.mbt"), src.as_bytes());
646 }
647}
648
649fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> Vec<ModuleName> {
650 let mut path = Vec::new();
651 if is_export {
652 path.push("exports".to_string());
653 }
654 match name {
655 WorldKey::Name(name) => {
656 path.push(name.to_snake_case());
657 }
658 WorldKey::Interface(id) => {
659 let iface = &resolve.interfaces[*id];
660 let pkg = iface.package.unwrap();
661 let pkgname = resolve.packages[pkg].name.clone();
662 path.push(pkgname.namespace.to_snake_case());
663 path.push(name_package_module(resolve, pkg));
664 path.push(iface.name.as_ref().unwrap().to_snake_case());
665 }
666 }
667 let mut iter = path.into_iter();
668 let first = ModuleName::root(iter.next().unwrap());
669 let (mut rv, last) = iter.fold((vec![], first), |(mut rv, prev), name| {
670 let name = prev.child(name);
671 rv.push(prev);
672 (rv, name)
673 });
674 rv.push(last);
675 rv
676}
677
678enum Identifier<'a> {
679 #[allow(dead_code)]
680 World(WorldId),
681 Interface(InterfaceId, &'a WorldKey),
682}
683
684fn group_by_resource<'a>(
685 funcs: impl Iterator<Item = &'a Function>,
686) -> BTreeMap<Option<TypeId>, Vec<&'a Function>> {
687 let mut by_resource = BTreeMap::<_, Vec<_>>::new();
688 for func in funcs {
689 match &func.kind {
690 FunctionKind::Freestanding => by_resource.entry(None).or_default().push(func),
691 FunctionKind::Method(ty) | FunctionKind::Static(ty) | FunctionKind::Constructor(ty) => {
692 by_resource.entry(Some(*ty)).or_default().push(func);
693 }
694 }
695 }
696 by_resource
697}
698
699#[derive(Debug, Copy, Clone, PartialEq)]
700enum TypeMode {
701 Owned,
702 AllBorrowed(&'static str),
703 HandlesBorrowed(&'static str),
704}
705
706#[derive(Default, Debug, Clone, Copy)]
707pub enum Ownership {
708 #[default]
711 Owning,
712
713 Borrowing {
717 duplicate_if_necessary: bool,
722 },
723}
724
725impl FromStr for Ownership {
726 type Err = String;
727
728 fn from_str(s: &str) -> Result<Self, Self::Err> {
729 match s {
730 "owning" => Ok(Self::Owning),
731 "borrowing" => Ok(Self::Borrowing {
732 duplicate_if_necessary: false,
733 }),
734 "borrowing-duplicate-if-necessary" => Ok(Self::Borrowing {
735 duplicate_if_necessary: true,
736 }),
737 _ => Err(format!(
738 "unrecognized ownership: `{s}`; \
739 expected `owning`, `borrowing`, or `borrowing-duplicate-if-necessary`"
740 )),
741 }
742 }
743}
744
745impl fmt::Display for Ownership {
746 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
747 f.write_str(match self {
748 Ownership::Owning => "owning",
749 Ownership::Borrowing {
750 duplicate_if_necessary: false,
751 } => "borrowing",
752 Ownership::Borrowing {
753 duplicate_if_necessary: true,
754 } => "borrowing-duplicate-if-necessary",
755 })
756 }
757}
758
759#[derive(Default)]
760struct FnSig {
761 async_: bool,
762 unsafe_: bool,
763 private: bool,
764 use_item_name: bool,
765 generics: Option<String>,
766 self_arg: Option<String>,
767 self_is_first_param: bool,
768 is_trait: bool,
769}
770
771pub fn to_mbt_ident(name: &str) -> String {
772 match name {
773 "as" => "as_".into(),
776 "break" => "break_".into(),
777 "continue" => "continue_".into(),
778 "else" => "else_".into(),
779 "enum" => "enum_".into(),
780 "false" => "false_".into(),
781 "fn" => "fn_".into(),
782 "if" => "if_".into(),
783 "loop" => "loop_".into(),
784 "match" => "match_".into(),
785 "mut" => "mut_".into(),
786 "pub" => "pub_".into(),
787 "return" => "return_".into(),
788 "self" => "self_".into(),
789 "struct" => "struct_".into(),
790 "trait" => "trait_".into(),
791 "true" => "true_".into(),
792 "type" => "type_".into(),
793 "while" => "while_".into(),
794 "priv" => "priv_".into(),
795 s => s.to_snake_case(),
796 }
797}
798
799fn to_upper_camel_case(name: &str) -> String {
800 match name {
801 "guest" => "Guest_".to_string(),
804 s => s.to_upper_camel_case(),
805 }
806}
807
808fn wasm_type(ty: WasmType) -> &'static str {
809 match ty {
810 WasmType::I32 => "Int",
811 WasmType::I64 => "Int64",
812 WasmType::F32 => "Float",
813 WasmType::F64 => "Float64",
814 }
815}
816
817fn int_repr(repr: Int) -> &'static str {
818 match repr {
819 Int::U8 => "u8",
820 Int::U16 => "u16",
821 Int::U32 => "u32",
822 Int::U64 => "u64",
823 }
824}
825
826fn bitcast(casts: &[Bitcast], operands: &[String], results: &mut Vec<String>) {
827 for (cast, operand) in casts.iter().zip(operands) {
828 results.push(match cast {
829 Bitcast::None => operand.clone(),
830 Bitcast::I32ToI64 => format!("i64::from({})", operand),
831 Bitcast::F32ToI32 => format!("({}).to_bits() as i32", operand),
832 Bitcast::F64ToI64 => format!("({}).to_bits() as i64", operand),
833 Bitcast::I64ToI32 => format!("{} as i32", operand),
834 Bitcast::I32ToF32 => format!("f32::from_bits({} as u32)", operand),
835 Bitcast::I64ToF64 => format!("f64::from_bits({} as u64)", operand),
836 Bitcast::F32ToI64 => format!("i64::from(({}).to_bits())", operand),
837 Bitcast::I64ToF32 => format!("f32::from_bits({} as u32)", operand),
838 });
839 }
840}
841
842enum MbtFlagsRepr {
843 U8,
844 U16,
845 U32,
846 U64,
847 U128,
848}
849
850impl MbtFlagsRepr {
851 fn new(f: &Flags) -> MbtFlagsRepr {
852 match f.repr() {
853 FlagsRepr::U8 => MbtFlagsRepr::U8,
854 FlagsRepr::U16 => MbtFlagsRepr::U16,
855 FlagsRepr::U32(1) => MbtFlagsRepr::U32,
856 FlagsRepr::U32(2) => MbtFlagsRepr::U64,
857 FlagsRepr::U32(3 | 4) => MbtFlagsRepr::U128,
858 FlagsRepr::U32(n) => panic!("unsupported number of flags: {}", n * 32),
859 }
860 }
861}
862
863impl fmt::Display for MbtFlagsRepr {
864 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
865 match self {
866 MbtFlagsRepr::U8 => "u8".fmt(f),
867 MbtFlagsRepr::U16 => "u16".fmt(f),
868 MbtFlagsRepr::U32 => "u32".fmt(f),
869 MbtFlagsRepr::U64 => "u64".fmt(f),
870 MbtFlagsRepr::U128 => "u128".fmt(f),
871 }
872 }
873}