Skip to main content

moverox_codegen/
lib.rs

1#![cfg_attr(nightly, feature(doc_cfg))]
2
3//! Generate Rust code from Move IR parsed by move-syn.
4//!
5//! Defines extension traits to generate Rust code from Move intermediate representation.
6//!
7//! `thecrate` in arguments here is the path to a crate/module which exports:
8//! - a `types` module with `Address` and `U256` types from `moverox-types`
9//! - a `traits` module with `HasKey`, `MoveDatatype` and `MoveType` traits from `moverox-traits`
10//! - the `serde` crate
11
12use std::collections::HashMap;
13
14use move_syn::{Attributes, Item, Module};
15use proc_macro2::{Ident, TokenStream};
16use quote::quote;
17use unsynn::LiteralString;
18
19mod attributes;
20mod generics;
21mod move_enum;
22mod move_struct;
23mod move_type;
24mod named_fields;
25mod positional_fields;
26#[cfg(test)]
27mod tests;
28
29use self::move_struct::StructGen as _;
30
31type BoxError = Box<dyn std::error::Error + 'static>;
32type Result<T = (), E = BoxError> = std::result::Result<T, E>;
33
34#[sealed::sealed]
35pub trait ModuleGen {
36    fn to_rust(
37        &self,
38        thecrate: &TokenStream,
39        package: Option<&LiteralString>,
40        address_map: &HashMap<Ident, TokenStream>,
41    ) -> Result<TokenStream>;
42}
43
44#[sealed::sealed]
45impl ModuleGen for Module {
46    fn to_rust(
47        &self,
48        thecrate: &TokenStream,
49        package: Option<&LiteralString>,
50        address_map: &HashMap<Ident, TokenStream>,
51    ) -> Result<TokenStream> {
52        let (docs, other) = crate::attributes::extract(&self.attrs)
53            .map_err(|err| format!("Parsing `moverox` attributes: {err}"))?;
54
55        if !other.is_empty() {
56            return Err("Move modules cannot have custom `moverox` attributes".into());
57        }
58
59        let ident = &self.ident;
60        let item_ctx = ItemContext {
61            thecrate,
62            package,
63            module: Some(ident),
64            address_map,
65        };
66        let datatypes: TokenStream = self
67            .items()
68            .map(|item| item.to_rust(item_ctx))
69            .collect::<Result<_>>()?;
70
71        Ok(quote! {
72            #docs
73            #[allow(rustdoc::all)]
74            pub mod #ident {
75                #[allow(non_camel_case_types, unused)]
76                type address = #thecrate::types::Address;
77                #[allow(non_camel_case_types, unused)]
78                type u256 = #thecrate::types::U256;
79                #[allow(non_camel_case_types, unused)]
80                type vector<T> = ::std::vec::Vec<T>;
81
82                #datatypes
83            }
84        })
85    }
86}
87
88/// Context for Rust code generation from a Move item.
89#[derive(Clone, Copy)]
90pub struct ItemContext<'a> {
91    /// Path to a crate/module which exports:
92    /// - a `types` module with `Address` and `U256` types from `moverox-types`
93    /// - a `traits` module with `HasKey`, `MoveDatatype` and `MoveType` traits from `moverox-traits`
94    /// - the `serde` crate
95    /// - an `Otw` type
96    pub thecrate: &'a TokenStream,
97    /// Move package address as an `0x`-prefixed hex string.
98    pub package: Option<&'a LiteralString>,
99    /// Move module name.
100    pub module: Option<&'a Ident>,
101    /// Mapping of Move named addresses to Rust paths.
102    ///
103    /// Used to map Move datatype paths to Rust-equivalents.
104    pub address_map: &'a HashMap<Ident, TokenStream>,
105}
106
107#[sealed::sealed]
108pub trait ItemGen {
109    fn to_rust(&self, ctx: ItemContext<'_>) -> Result<TokenStream>;
110}
111
112#[sealed::sealed]
113impl ItemGen for Item {
114    fn to_rust(&self, ctx: ItemContext<'_>) -> Result<TokenStream> {
115        use move_syn::ItemKind as K;
116        let Self { attrs, kind, .. } = self;
117
118        let (docs, generated) = match kind {
119            K::Struct(s) => {
120                let err_ctx = |err| format!("struct {}: {err}", s.ident);
121                let (docs, otw_types) = crate::attributes::extract(attrs).map_err(err_ctx)?;
122                let generated = s.to_rust(otw_types, ctx).map_err(err_ctx)?;
123                (docs, generated)
124            }
125            K::Enum(e) => {
126                let err_ctx = |err| format!("enum {}: {err}", e.ident);
127                let (docs, otw_types) = crate::attributes::extract(attrs).map_err(err_ctx)?;
128                let generated = self::move_enum::to_rust(e, otw_types, ctx).map_err(err_ctx)?;
129                (docs, generated)
130            }
131            _ => return non_datatype_gen(attrs),
132        };
133
134        Ok(quote! {
135            #docs
136            #generated
137        })
138    }
139}
140
141fn non_datatype_gen(attrs: &[Attributes]) -> Result<TokenStream> {
142    if attrs.iter().flat_map(self::attributes::as_moverox).count() > 0 {
143        return Err(
144            "Move items other than enums/structs cannot be annotated with custom \
145            `moverox` attributes"
146                .into(),
147        );
148    }
149    Ok(TokenStream::new())
150}