back

How I met my dream language

Hey everyone! 👋
Today, I wanted to share my recent adventure diving into the world of Rust and eframe. Specifically, I had the chance to play around with eframe templates and integrate my own service using Moralis API to fetch wallet tokens. Let me tell you, it was quite the ride! 🚀
First things first, let's talk a bit about Rust. It's a language that's been gaining a lot of traction lately, known for its performance, reliability, and concurrency features. And eframe? Well, it's a neat framework for building GUI applications in Rust, making it a perfect match for what I wanted to accomplish.
Now, onto the fun part – integrating Moralis API into my Rust project. I started by setting up a simple service to fetch wallet tokens using the API. Here's a snippet of the code I wrote:
pub struct ApiService {
   pub api_key: String,
   pub endpoint: Endpoint,
   pub network: Network,
}
........

impl ApiService {
    pub fn new(api_key: String) -> Self {
        Self {
            api_key,
            endpoint: Endpoint::AccountTokens,
            network: Network::Devnet,
        }
    }

    fn build_url(&self, network: &Network, address: String) -> String {
        let endpoint = get_endpoint(&self.endpoint);
        let network = get_network(network);

        endpoint(network, address)
    }

    pub fn get_account_tokens(
        &self,
        network: &Network,
        address: String,
    ) -> Result<AccountTokensResponse, WalletApiError> {
        let url = self.build_url(network, address);
        let req = ureq::get(&url).set("X-API-Key", &self.api_key);
        let resp: Vec<Token> = req.call()?.into_json()?;

        Ok(AccountTokensResponse { tokens: resp })
    }
}
Here is the skeleton of the service :) The rest is available in my github. Then I have conducted my humble UI using egui:
impl eframe::App for TemplateApp {
    fn save(&mut self, storage: &mut dyn eframe::Storage) {
        eframe::set_value(storage, eframe::APP_KEY, self);
    }

    fn persist_egui_memory(&self) -> bool {
        true
    }

    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
            egui::menu::bar(ui, |ui| {
                let is_web = cfg!(target_arch = "wasm32");
                if !is_web {
                    ui.menu_button("Settings", |ui| {
                        egui::widgets::global_dark_light_mode_buttons(ui);
                        ui.separator();

                        ui.horizontal(|ui| {
                            ui.label("API key: ");
                            ui.text_edit_singleline(&mut self.api_key);
                        });

                        ui.add_space(30.0);

                        let reset_button = ui.button("Reset").on_hover_text("Reset the app state");

                        if reset_button.clicked() {
                            *self = Default::default();
                        }
                    });
                    ui.add_space(16.0);
                }

                egui::warn_if_debug_build(ui);
            });
        });

        egui::CentralPanel::default().show(ctx, |ui| {
            ui.horizontal(|ui| {
                ui.heading("Check your wallet ballance");

                ui.menu_button(get_network(&self.network), |ui| {
                    ui.selectable_value(&mut self.network, Network::Mainnet, "Mainnet");
                    ui.selectable_value(&mut self.network, Network::Devnet, "Devnet");
                });
            });

            ui.add_space(10.0);

            ui.horizontal(|ui| {
                ui.label("Wallet address: ");
                ui.text_edit_singleline(&mut self.wallet_address);
                let check_button = ui.button("Check");
                let clear_button = ui.button("Clear").labelled_by(Id::new("clear_button"));

                if clear_button.clicked() {
                    self.wallet_address.clear();
                }

                if check_button.clicked() {
                    let tokens = fetch_wallet_balance(
                        &self.network,
                        self.wallet_address.clone(),
                        self.api_key.clone(),
                    );

                    self.tokens = tokens;
                }
            });

            ui.add_space(15.0);

            ui.separator();

            egui::ScrollArea::vertical()
                .id_source("scroll_area")
                .show(ui, |ui| {
                    for token in self.tokens.iter() {
                        render_token_card(ui, token);
                    }
                });
        });

        egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
            ui.add_space(5.0);

            ui.horizontal(|ui| {
                powered_by_egui_and_eframe(ui);
                ui.separator();
                powered_by_moralis_api(ui);
            });
            ui.add_space(5.0);
        });
    }
}
The render_token_card function takes care of displaying token information elegantly, while fetch_wallet_balance orchestrates the interaction with Moralis API to retrieve wallet balances. It's all about making the user experience smooth and intuitive!
And of course, I couldn't resist adding a touch of personality to the UI. At the bottom, you'll find a couple of badges proudly proclaiming that the app is powered by eframe, egui, and Moralis API. Gotta give credit where credit's due, right? 😄
All in all, working with eframe and Rust has been a blast. The combination of Rust's performance and eframe's simplicity made for a delightful development experience. And with Moralis API in the mix, the possibilities are endless! If you're thinking about diving into GUI development with Rust, I highly recommend giving eframe a try – you won't be disappointed.
That's it for today's blog post! Thanks for joining me on this journey, and until next time, happy coding! 🎉