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