Home Ciência e Tecnologia Aplicativos multiplataforma com Rust 3: integração técnica e shell

Aplicativos multiplataforma com Rust 3: integração técnica e shell

8
0

fechar notícias

Este artigo também está disponível em inglês. Foi traduzido com assistência técnica e revisado editorialmente antes da publicação.

Após a introdução do framework Crux, a terceira e última parte da atual série de artigos trata de conceitos avançados e integração prática em aplicações do mundo real. Os seguintes pontos sobre tipos técnicos, sua divisão em diversas aplicações e seu uso em tecnologias específicas de shell fornecem uma visão prática detalhada.

Leia mais depois do anúncio

Marcel Koch e sua equipe de sete pessoas assessoram pequenas e médias empresas e desenvolvem aplicações multiplataforma para desktop e mobile, bem como aplicações web entre indústrias – preferencialmente com TypeScript, Rust, Flutter ou Java, suportadas por CI/CD e IaC. Eles contam com soluções pragmáticas e personalizadas porque o software não é o fim de tudo. Além de seu sólido conhecimento técnico, ele treina comunicação não violenta, análise e agilidade de transações e promove uma visão crítica do hype da nuvem. Marcel é palestrante especialista, autor de artigos e livros e pode ser ouvido regularmente no podcast.

Em projetos de software de longo prazo, recomenda-se definir o tipo técnico independente do framework utilizado. Como parte do aplicativo de e-mail mostrado na seção anterior, por exemplo, estão disponíveis tipos EmailAddress e: Este tipo contém a validação e representação de endereços de e-mail e garante que apenas valores válidos sejam utilizados no sistema. Lógica para verificar – por exemplo, quanto à presença de símbolos @ – está vinculado diretamente no tipo e não faz parte da aplicação Crux.

Isto significa que a lógica técnica permanece claramente separada dos detalhes técnicos. Ele pode ser testado, reutilizado e desenvolvido independentemente do Crux, de outros frameworks ou de plataformas específicas. Se o ambiente técnico mudar, a lógica central permanecerá intacta e não precisará ser reescrita.

Um tipo tão profissional EmailAddress melhore a capacidade de manutenção e compreensão do código e proteja-o contra erros, evitando estados inválidos durante a criação.

Leia mais depois do anúncio

Listagem 1: Tipo técnico EmailAddress


#(derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize))
pub struct EmailAddress(String);

#(derive(thiserror::Error, Debug))
pub enum EmailError {
    #(error("missing @"))
    MissingAt,
}

impl EmailAddress {
    /// Der Smart-Constructor garantiert eine gültige Adresse
    pub fn parse(s: impl Into) -> Result {
        let s = s.into();
        if s.contains('@') { Ok(Self(s)) } else { Err(EmailError::MissingAt) }
    }

    pub fn as_str(&self) -> &str { &self.0 }
}


Um tipo tão profissional EmailAddress pode ser testado independentemente da estrutura.

Listagem 2: Teste para EmailAddress


#(cfg(test))
mod tests {
    use super::*;

    #(test)
    fn valid_email_is_accepted() {
        let email = EmailAddress::parse("marcel.koch@example.org");
        assert!(email.is_ok());
        assert_eq!(email.unwrap().as_str(), "marcel.koch@example.org");
    }

    #(test)
    fn invalid_email_is_rejected() {
        let email = EmailAddress::parse("marcel.kochexample.org");
        assert!(email.is_err());
        assert_eq!(email.unwrap_err(), EmailError::MissingAt);
    }
}


O primeiro teste verifica se o endereço de e-mail correto foi recebido e armazenado corretamente. O segundo teste garante que o endereço de e-mail não é válido (sem @) é rejeitado e o erro correspondente (MissingAt) retornou.

Se a equipe de desenvolvimento de aplicativos estiver crescendo, pode fazer sentido dividir a equipe e o aplicativo em vários aplicativos e contêineres independentes. O gerenciamento de contatos pode estender seu aplicativo de e-mail existente e armazenar, atualizar ou excluir todos os dados de contato recebidos. O gerenciamento de contatos ganha seu próprio aplicativo Crux, gerenciado em uma caixa separada. Essas caixas separadas contêm recursos limitados e também são chamadas de caixas de recursos. Cada feature crate define seus próprios eventos, modelos, modelos de exibição e efeitos – portanto, os modelos fazem parte do recurso. O aplicativo principal combina caixas de recursos individuais e interações coordenadas.

Lista 3: contato (Feature-Crate)


// contacts/src/lib.rs
#(derive(Clone, Debug))
pub struct Contact {
    pub name: String,
    pub email: EmailAddress,
}

pub enum ContactsEvent {
    AddContact(Contact),
    RemoveContact(EmailAddress),
    EditContact(EmailAddress, Contact),
}

#(derive(Default))
pub struct ContactsModel {
    pub contacts: Vec,
}

pub struct ContactsViewModel {
    pub contacts: Vec,
}

pub enum ContactsEffect {
    ShowContactAdded(EmailAddress),
    ShowContactRemoved(EmailAddress),
}

pub fn update(event: ContactsEvent, model: &mut ContactsModel) -> Vec {
    match event {
        ContactsEvent::AddContact(contact) => {
            model.contacts.push(contact.clone());
            vec!(ContactsEffect::ShowContactAdded(contact.email))
        }
        ContactsEvent::RemoveContact(email) => {
            model.contacts.retain(|c| c.email != email);
            vec!(ContactsEffect::ShowContactRemoved(email))
        }
        ContactsEvent::EditContact(email, new_contact) => {
            if let Some(c) = model.contacts.iter_mut().find(|c| c.email == email) {
                *c = new_contact.clone();
            }
            vec!()
        }
    }
}

pub fn view(model: &ContactsModel) -> ContactsViewModel {
    ContactsViewModel {
        contacts: model.contacts.clone(),
    }
}


O recurso de e-mail assume a funcionalidade do aplicativo de e-mail anterior:

Lista 4: e-mail (Feature-Crate)


// email/src/lib.rs
pub enum EmailEvent {
    SendEmail(EmailAddress),
    EmailSent(bool),
}

#(derive(Default))
pub struct EmailModel {
    pub last_sent: Option,
}

pub struct EmailViewModel {
    pub last_sent: Option,
}

pub enum EmailEffect {
    SendEmailRequest(String),
}


O aplicativo principal coleta modelos de recursos e coordenadas de comunicação:

Listagem 5: Haupt-App-Integration (App-Crate)


// app/src/lib.rs
use contacts::{ContactsEvent, ContactsModel, ContactsViewModel, ContactsEffect};
use email::{EmailEvent, EmailModel, EmailViewModel, EmailEffect};

pub enum AppEvent {
    Email(EmailEvent),
    Contacts(ContactsEvent),
}

pub struct AppModel {
    pub email: EmailModel,
    pub contacts: ContactsModel,
}

pub struct AppViewModel {
    pub email: EmailViewModel,
    pub contacts: ContactsViewModel,
}

pub enum AppEffect {
    Email(EmailEffect),
    Contacts(ContactsEffect),
}

pub fn update(event: AppEvent, model: &mut AppModel) -> Vec {
    match event {
        AppEvent::Email(email_event) => {
            email::update(email_event, &mut model.email)
                .into_iter().map(AppEffect::Email).collect()
        }
        AppEvent::Contacts(contacts_event) => {
            contacts::update(contacts_event, &mut model.contacts)
                .into_iter().map(AppEffect::Contacts).collect()
        }
    }
}

pub fn view(model: &AppModel) -> AppViewModel {
    AppViewModel {
        email: email::view(&model.email),
        contacts: contacts::view(&model.contacts),
    }
}


Esta divisão mantém aplicações modulares, testáveis ​​e extensíveis. Cada recurso gerencia seu próprio estado e pode ser desenvolvido, testado e gerenciado de forma independente. O aplicativo principal cuida de orquestrar e combinar ViewModels individuais em uma interface consistente. Isso cria uma arquitetura escalável.

Fonte

LEAVE A REPLY

Please enter your comment!
Please enter your name here