Tutorial do leitor de RSS (Rust para Windows com VS Code)

O tópico anterior apresentou o Rust para Windows e o crate windows.

Agora vamos experimentar o Rust para Windows ao gravar um aplicativo de console simples que baixa os títulos de postagens de blog de um feed RSS (Really Simple Syndication).

  1. Inicialize um prompt de comando (cmd.exe) e cd para uma pasta onde você deseja manter seus projetos do Rust.

  2. Usando o Cargo, crie um projeto do Rust chamado rss_reader e cd na pasta recém-criada:

    > cargo new rss_reader
    >     Created binary (application) `rss_reader` package
    > cd rss_reader
    
  3. Em seguida, abra o projeto rss_reader no VS Code.

    code .
    
  4. Vamos implementar o projeto principal rss_reader. Primeiro, abra o arquivo Cargo.toml na raiz do projeto. Um arquivo Cargo.toml é um arquivo de texto que descreve um projeto do Rust, incluindo as dependências dele.

    Adicione uma dependência ao crate windows, conforme mostrado na listagem abaixo. O crate windows é grande. Para manter os tempos de build rápidos, selecionaremos apenas os recursos Foundation_Collections e Web_Syndication necessários para esse código.

    # Cargo.toml
    ...
    
    [dependencies.windows] 
    version = "0.43.0"
    features = [
        "Foundation_Collections",
        "Web_Syndication",
    ]
    
  5. Depois, abra o arquivo de código-fonte src/main.rs do projeto rss_reader. Lá, você encontrará o código "Olá, mundo!" padrão do Cargo. Adicione a seguinte instrução use ao início de main.rs:

    // src\main.rs
    use windows::{
        core::*,
        Foundation::Uri,
        Web::Syndication::SyndicationClient
    };
    
    fn main() {
        println!("Hello, world!");
    }
    

    A declaração use reduz o caminho para os tipos que usaremos. Há o tipo Uri que mencionamos anteriormente.

  6. Para criar um Uri, substitua a função main padrão do Cargo por esta:

    // src\main.rs
    ...
    
    fn main() -> Result<()> {
        let uri = Uri::CreateUri(h!("https://blogs.windows.com/feed"))?;
    
        Ok(())
    }
    

    Observe que o tipo de retorno da função main é um Resultado, com base em windows::core::. Isso facilitará tudo, pois é comum lidar com erros de APIs do SO (sistema operacional). O windows::core::Result nos ajuda com a propagação de erros e com o tratamento de erro conciso.

    Você pode ver o operador de ponto de interrogação no final da linha de código. Para economizar na digitação, fazemos isso para utilizar a lógica de propagação de erro e curto-circuito do Rust. Isso significa que não precisamos fazer muito tratamento de erro manual para este exemplo simples. Para obter mais informações sobre esse recurso do Rust, consulte O operador ? para obter um tratamento de erros mais fácil.

    Observe também a macro h! do crate windows. Usamos isso para construir uma referência HSTRING com base em um literal de cadeia de caracteres Rust. A API do WinRT usa o HSTRING extensivamente para valores de cadeia de caracteres.

  7. Para baixar este feed RSS, criaremos um objeto SyndicationClient.

    // src\main.rs
    ...
    
    fn main() -> windows::core::Result<()> {
        let uri = Uri::CreateUri(h!("https://blogs.windows.com/feed"))?;
        let client = SyndicationClient::new()?;
    
        Ok(())
    }
    

    A função new é um construtor Rust. Todos os objetos no crate windows seguem a convenção Rust e nomeiam os construtores deles como new.

  8. Agora, podemos usar SyndicationClient para recuperar o feed.

    // src\main.rs
    ...
    
    fn main() -> windows::core::Result<()> {
        let uri = Uri::CreateUri(h!("https://blogs.windows.com/feed"))?;
        let client = SyndicationClient::new()?;
        let feed = client.RetrieveFeedAsync(&uri)?.get()?;
    
        Ok(())
    }
    

    Como RetrieveFeedAsync é uma API assíncrona, usamos a função de bloqueio get para manter o exemplo simples. Como alternativa, poderíamos usar o operador await dentro de uma função async para aguardar os resultados de forma cooperativa. Um aplicativo mais complexo com uma interface do usuário gráfica usará asynccom frequência.

  9. Podemos iterar nos itens resultantes e imprimir apenas os títulos. Você também verá algumas linhas extras de código abaixo para definir um cabeçalho de agente de usuário, já que alguns RSS feeds exigem isso.

    // src\main.rs
    ...
    
    fn main() -> windows::core::Result<()> {
        let uri = Uri::CreateUri(h!("https://blogs.windows.com/feed"))?;
        let client = SyndicationClient::new()?;
    
        client.SetRequestHeader(
            h!("User-Agent"),
            h!("Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"),
        )?;
    
        let feed = client.RetrieveFeedAsync(&uri)?.get()?;
    
        for item in feed.Items()? {
            println!("{}", item.Title()?.Text()?);
        }
    
        Ok(())
    }
    
  10. Vamos confirmar que podemos criar e executar clicando em Executar>Executar sem Depuração (ou pressionando Ctrl + F5). Se você vir mensagens inesperadas, verifique se concluiu com êxito o tutorial Olá, mundo! (Rust com VS Code).

    Também há comandos Debug e Run inseridos dentro do editor de texto. Como alternativa, em um prompt de comando na pasta rss_reader, digite cargo run, que será criado e, em seguida, executará o programa.

    The Debug and Run commands embedded in the text editor

    Na parte inferior do painel Terminal do VS Code, você pode ver que o Cargo baixa e compila com êxito o crate windows, armazenando em cache os resultados e usando-os para fazer com que as compilações subsequentes sejam concluídas em menos tempo. Em seguida, ele compila o exemplo e executa-o, exibindo uma lista de títulos de postagem de blog.

    List of blog post titles

Isso é tão simples quanto programar o Rust para Windows. Mas, no fundo, grande parte da simplicidade consiste em criar as ferramentas para que o Rust possa analisar arquivos .winmd com base em ECMA-335 (Common Language Infrastructure ou CLI) e também usar de forma segura a ABI (interface binária de aplicativos) baseada em COM em tempo de execução, sempre colocando a segurança e a eficiência em primeiro lugar.

Mostrar uma caixa de mensagem

Dissemos que o Rust para Windows permite que você chame qualquer API do Windows (antiga, atual e futura). Portanto, nesta seção, mostraremos algumas caixas de mensagens do Windows.

  1. Assim como fizemos no projeto RSS, no prompt de comando cd para a pasta com seus projetos do Rust.

  2. Crie um projeto chamado message_box e abra-o no VS Code:

    > cargo new message_box
    >     Created binary (application) `message_box` package
    > cd message_box
    > code .
    
  3. No VS Code, abra o Cargo.toml e adicione as dependências do Windows para este projeto:

     # message_box\Cargo.toml
     ...
    
     [dependencies.windows]
     version = "0.43.0"
     features = [
         "Win32_Foundation",
         "Win32_UI_WindowsAndMessaging",
     ]
    
  4. Agora abra o arquivo src/main.rs do projeto e adicione as declarações use com os novos namespaces (conforme mostrado abaixo). E, por fim, adicione código para chamar as funções MessageBoxA e MessageBoxW. Os documentos da API do Windows são principalmente escritos com C/C++ em mente, então é útil comparar os documentos da API com os documentos para as projeções do Rust no crate windows: MessageBoxA (Rust) e MessageBoxW (Rust).

    // src\main.rs
    use windows::{
        core::*,
        Win32::UI::WindowsAndMessaging::*
    };
    
    fn main() {
        unsafe {
            MessageBoxA(None, s!("Ansi"), s!("World"), MB_OK);
            MessageBoxW(None, w!("Wide"), w!("World"), MB_OK);
        }
    }
    

    Como você pode ver, precisamos usar essas APIs Win32 em um bloco unsafe (confira Blocos não seguros). Observe também as macros s! e w!, que criam argumentos LPCSTR e LPCWSTR de literais de cadeia de caracteres UTF-8 Rust; assim como criamos um HSTRING com a macro h! para rss_reader. O Rust é, nativamente, um Unicode com cadeias de caracteres UTF-8, portanto, o usdo das APIs amplas do Windows Unicode (sufixo W) é preferencial ao das APIs ANSI (sufixo A). Isso poderá ser importante se você usar texto não inglês em seu código.

Dessa vez, ao criar e executar, o Rust exibe duas caixas de mensagens do Windows.