112 / Systems

include_bytes

include_bytes("path") reads a file AT COMPILE TIME and puts its bytes into the binary, so the resource cannot get lost along the way — the real case: a UI that silently degrades to another typeface because someone copied only the executable, not the assets folder. The path is a literal, resolved from the project root and never from the file that uses it: with a variable the compiler rejects it with NYX1033.

112-include-bytes.nxSource →
// Nyx by Example: un recurso binario que viaja DENTRO del ejecutable.
//
// El caso real, en chico: un ERP que promete instalarse copiando un solo
// ejecutable servía sus tipografías desde disco. Si alguien copiaba únicamente
// el binario, la interfaz se degradaba a otra tipografía SIN AVISAR — el
// servidor respondía 200 con un cuerpo vacío. `include_bytes` lee el archivo AL
// COMPILAR y mete sus bytes en el binario, así que el recurso no se puede
// perder por el camino.
//
// Lo que esta receta muestra:
//   1. que los bytes llegan intactos (NUL incluidos — un .woff2 está lleno);
//   2. que el valor sirve TAL CUAL como cuerpo de una respuesta HTTP;
//   3. que dos llamadas a la misma ruta comparten un solo global.
//
// No abre un puerto a propósito: arma la respuesta en memoria y la describe,
// así la receta corre sola bajo `make test-examples`.

import "std/web"

// La ruta es un LITERAL y se resuelve desde la raíz del proyecto, nunca desde
// este archivo. Con una variable el compilador la rechaza con NYX1033: la ruta
// tiene que conocerse al compilar.
fn tipografia() -> String {
    return include_bytes("examples/by-example/assets/tipografia.woff2")
}

fn servir_tipografia() -> Response {
    let cuerpo: String = tipografia()
    var r: Response = response_new(200, cuerpo)
    // headers_flat es intercalado [nombre, valor, nombre, valor, ...].
    r.headers_flat.push("Content-Type")
    r.headers_flat.push("font/woff2")
    // Un recurso empotrado es inmutable por definición: cambia solo si se
    // recompila, así que puede cachearse para siempre.
    r.headers_flat.push("Cache-Control")
    r.headers_flat.push("public, max-age=31536000, immutable")
    return r
}

fn main() {
    let datos: String = tipografia()

    // str_byte_length y NO length(): length() cuenta codepoints UTF-8, y esto
    // son bytes arbitrarios. Es el gotcha `strings-are-bytes` del repo.
    println("bytes: " + int_to_string(str_byte_length(datos)))

    // La firma del formato sobrevive, y el byte 8 es un NUL: si algo tratara
    // el recurso como cadena de C, el cuerpo se cortaría justo acá.
    println("firma: " + datos.substring(0, 4))
    println("byte 8 (NUL): " + int_to_string(datos.charAt(8)))

    // La identidad byte a byte, medida: este sha256 es el del archivo en disco
    // (sha256sum examples/by-example/assets/tipografia.woff2).
    println("sha256: " + sha256(datos))

    // La respuesta HTTP lleva el recurso entero, sin leer nada del disco.
    let resp: Response = servir_tipografia()
    println("respuesta: " + int_to_string(resp.status) + " con " + int_to_string(str_byte_length(resp.body)) + " bytes")

    // Dos llamadas a la MISMA ruta comparten un solo global: el recurso ocupa
    // sus bytes una vez, no una por uso.
    let otra: String = tipografia()
    if str_byte_length(otra) == str_byte_length(datos) {
        println("segunda llamada: el mismo recurso")
    }

    // Para leer un archivo AL EJECUTAR —algo que el usuario elige en tiempo de
    // corrida— lo que corresponde es read_file/try_read_file. include_bytes no
    // existe en el intérprete ni en el REPL: ahí responde NYX3007.
}
Outputstdout
bytes: 776
firma: wOF2
byte 8 (NUL): 0
sha256: 0d4f9fe0394d9008f67caddb00171ffe7a3a63aaac4ec225d66f5fadcc79c1d2
respuesta: 200 con 776 bytes
segunda llamada: el mismo recurso

How it works

The bytes arrive intact, NULs included: a real .woff2 is full of them, which is why the recipe measures with str_byte_length and not length() — length() counts UTF-8 codepoints, these are arbitrary bytes (the repo's strings-are-bytes gotcha). Byte 8 of the file is a real NUL, and it is still a NUL once embedded.

The value works AS IS as an HTTP response body: servir_tipografia assigns it straight to Response.body, reading nothing from disk at runtime. Since the resource is immutable by definition — it only changes if the program is recompiled — the response can be cached forever (Cache-Control: public, max-age=31536000, immutable).

Two calls to the same path share a single global: the resource occupies its bytes ONCE in the binary, not once per use. For a file the user picks at runtime, the right tool is read_file/try_read_file, not include_bytes — which also does not exist in the interpreter or the REPL, where it answers NYX3007.