Capítulo 08 8 min de leitura

Módulos e pacotes

Até agora, todos os exemplos cabiam em um único arquivo. Bases de código reais crescem rapidamente além disso. Este capítulo contém tudo o que você precisa para organizar código Glide em qualquer escala — de um "CLI pequeno" a um "serviço com cinco bibliotecas".

Módulos: um arquivo, um namespace

Em Glide, um arquivo é um módulo. O nome do módulo vem do caminho dentro de src/ com o .glide removido:

output
hello/
  glide.glide
  src/
    main.glide          ← módulo: main
    storage/
      sqlite.glide      ← módulo: storage::sqlite
      memory.glide      ← módulo: storage::memory

Para usar algo definido em outro módulo, importe-o:

import storage::sqlite::*;     // traz tudo que é pub desse arquivo
import storage::memory::*;

Leia o caminho usando :: como separador. O ::* no final significa "traga todos os itens públicos para o escopo, para que eu possa usá-los pelo nome curto".

Público vs privado

Por padrão, tudo que é definido em um módulo é privado — visível apenas dentro daquele arquivo. Para exportar, use o prefixo pub:

storage/sqlite.glide
pub struct Db {
    handle: *void,
}

pub fn open(path: string) -> !Db {
    // ...
}

fn _internal_helper() -> i32 {
    // sem pub: permanece privado a este arquivo
    return 0;
}

A partir de main.glide:

import storage::sqlite::*;

fn main() -> i32 {
    let r: !Db = open("data.db");      // funciona — open é pub
    return 0;
}

Formas diferentes de importar

// Importa tudo que é pub de um módulo:
import storage::sqlite::*;

// Importa o módulo pelo nome curto, acessa as coisas de forma qualificada:
import storage::sqlite;        // depois: sqlite::open("data.db")

// Importa apenas um item pelo nome:
import storage::sqlite::{open};

A forma com * é a mais comum. A forma com nome qualificado é útil para resolver colisões quando dois módulos definem o mesmo nome.

O manifesto glide.glide

Todo projeto tem um manifesto na raiz. Não é um arquivo de configuração — é um valor Glide:

glide.glide
let manifest: Package = Package {
    name:        "myapp",
    version:     "0.1.0",
    description: "Example application",
    author:      "Your Name",
    license:     "MIT",
    repository:  "https://github.com/you/myapp",
    bin:         "src/main.glide",   // ponto de entrada
    deps: vec_of(
        Dep::git("discord_lib", "https://github.com/you/discord-lib", "v0.1.0"),
        Dep::path("local_lib", "../local-lib"),
    ),
};

O CLI lê os campos literalmente. Os mais importantes:

  • `name` — o nome do pacote. Deve coincidir com o nome do diretório e com o nome do arquivo de entrada da biblioteca (mais sobre isso abaixo).
  • `version` — incrementado manualmente a cada release; consumidores fixam uma revisão específica em vez dessa versão.
  • `bin` — o arquivo-fonte que contém fn main(). Para um pacote de biblioteca, defina como "".
  • `deps` — uma lista vec_of(...) de valores Dep. Dois tipos: Dep::git(name, url, rev) e Dep::path(name, local_path).

Adicionando uma dependência

Você pode editar glide.glide manualmente ou usar o CLI:

shell
glide add discord_lib github.com/you/discord-lib v0.1.0

Isso acrescenta a dependência ao seu manifesto. Em seguida, baixe-a:

shell
glide fetch

glide fetch puxa cada dependência para um cache endereçado por conteúdo (~/.glide/cache/<sha256>) e cria um link simbólico em glide_modules/<dep-name> apontando para ela. Você commita seu glide.glide e glide.lock no git; você não commita glide_modules/ (o script de instalação o regenera).

Use a dependência importando o nome do pacote:

import discord_lib::*;
// ou, se os símbolos estão em submódulos:
import discord_lib::gateway::*;
import discord_lib::rest::*;

Publicando uma biblioteca

Quer que outros dependam do seu código? Três passos:

  1. Estrutura: crie o diretório com este layout (use glide new mylib --lib):
output
mylib/
  glide.glide        # name: "mylib", bin: ""
  src/
    mylib.glide      # ponto de entrada — mesmo nome do pacote
    # ... mais arquivos aqui, importáveis como mylib::other_file

O nome do arquivo de entrada deve coincidir com o nome do pacote. É assim que import mylib; encontra o arquivo correto.

  1. Publicar: envie para o GitHub e crie uma tag de versão:
shell
git tag v0.1.0
git push origin v0.1.0
  1. Consumir: qualquer pessoa pode agora adicionar sua biblioteca:
shell
glide add mylib github.com/you/mylib v0.1.0
glide fetch

Compilando para produção

Quando estiver pronto para distribuir, glide build --release gera o binário otimizado:

shell
glide build --release
# → ./build/myapp (ou myapp.exe no Windows)

Esse binário é autossuficiente: não precisa do Glide instalado na máquina de destino. Copie-o para o seu servidor, execute-o, pronto.

Para compilar para outro sistema operacional sem sair da sua máquina:

shell
glide build --release --target=x86_64-linux-gnu       # Linux
glide build --release --target=aarch64-apple-darwin   # macOS ARM
glide build --release --target=x86_64-windows-gnu     # Windows

O zig cc embutido cuida da compilação cruzada — nenhum toolchain adicional precisa ser instalado.

Lendo a árvore de dependências

glide.lock registra a revisão exata (SHA do commit + hash do conteúdo) de cada dependência que você baixou, de modo que um clone novo resolva para o mesmo código. Commite-o no git assim como package-lock.json ou Cargo.lock.

Inspecione o que realmente foi baixado:

shell
ls glide_modules/         # dependências de nível superior
ls glide_modules/<dep>    # código-fonte de uma dependência

glide_modules/ é uma lista plana de links simbólicos para o cache endereçado por conteúdo. Dois projetos que dependem da mesma revisão da mesma biblioteca compartilham uma única cópia no disco.

Um layout realista de projeto

Como um serviço HTTP fica depois de alguns milhares de linhas:

output
myservice/
  glide.glide                        # name, version, deps
  glide.lock
  src/
    main.glide                       # fn main() — conecta tudo
    config.glide                     # parsing de env, validação
    handlers/
      health.glide
      users.glide
      posts.glide
    storage/
      pool.glide                     # pool de conexões
      queries.glide                  # SQL
    middleware/
      auth.glide
      logging.glide
  book/                              # este site, talvez
  static/
  tests/
  glide_modules/                     # não commitado; populado por glide fetch
    sqlite/
    redis/
    auth_jwt/
  build/                             # não commitado; saída de glide build

Dentro de main.glide você veria:

import handlers::health::*;
import handlers::users::*;
import handlers::posts::*;
import middleware::auth::*;
import middleware::logging::*;
import sqlite::*;

fn main() -> i32 {
    // ... configura rotas, inicia servidor
    return 0;
}

Recap

O que vem a seguir

Esse é o núcleo da linguagem e suas ferramentas. Os capítulos seguintes colocam tudo em prática: o próximo — Um tour pela biblioteca padrão — percorre as baterias que o Glide oferece, as APIs que você vai usar no dia a dia.

Depois disso: construindo um servidor HTTP, testes e benchmarks, e FFI e escotilhas de escape.