fiscal-rsfiscal-rs

Bindings FFI: Uma Lib, Qualquer Linguagem

Estratégia de bindings FFI do fiscal-rs para Python, Node.js, WebAssembly e plataformas móveis

O fiscal-rs é escrito em Rust, mas foi projetado desde o início para ser consumido por qualquer linguagem. A estratégia de FFI (Foreign Function Interface) permite que um único núcleo de alta performance sirva aplicações em Python, Node.js, navegadores web e aplicações móveis nativas.

Por que Rust é ideal para FFI?

Rust possui características únicas que o tornam a melhor escolha para bibliotecas multi-linguagem:

CaracterísticaBenefício para FFI
Sem Garbage CollectorNão interfere com o GC da linguagem hospedeira (Python, JS, etc.)
Compatibilidade C ABIExporta funções extern "C" que qualquer linguagem pode chamar
Segurança de memóriaSem use-after-free, double-free ou data races — mesmo cruzando fronteiras FFI
Compilação para múltiplos alvosUm único codebase gera .so, .dylib, .dll, .wasm
Zero-cost abstractionsPerformance nativa sem overhead de runtime

Arquitetura: núcleo compartilhado

Todas as bindings compartilham o mesmo núcleo Rust. A lógica fiscal, cálculos de impostos e geração de XML existem em um único lugar — as bindings são camadas finas de tradução.

Loading diagram...

Bindings planejadas

Python via PyO3

PyO3 é o framework padrão para criar extensões nativas de Python em Rust. Combinado com maturin, permite publicar no PyPI com pip install.

use pyo3::prelude::*;
use fiscal_core::types::*;
use fiscal_core::xml_builder::InvoiceBuilder;

/// Gera o XML de uma NF-e a partir dos dados fornecidos.
#[pyfunction]
fn build_invoice_xml(
    cnpj: &str,
    ie: &str,
    razao_social: &str,
    items: Vec<PyInvoiceItem>,
) -> PyResult<PyInvoiceResult> {
    let issuer = IssuerData::new(cnpj, ie, razao_social, /* ... */);
    let mut builder = InvoiceBuilder::new(
        issuer,
        SefazEnvironment::Homologation,
        InvoiceModel::NFe,
    );

    for item in items {
        builder = builder.add_item(item.into());
    }

    let invoice = builder.build()
        .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(
            e.to_string()
        ))?;

    Ok(PyInvoiceResult {
        access_key: invoice.access_key().to_string(),
        xml: invoice.xml().to_string(),
    })
}

/// Modulo Python publicado como `fiscal_rs`.
#[pymodule]
fn fiscal_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(build_invoice_xml, m)?)?;
    Ok(())
}

Uso em Python:

from fiscal_rs import build_invoice_xml

resultado = build_invoice_xml(
    cnpj="25028332000105",
    ie="140950881119",
    razao_social="Minha Empresa",
    items=[{
        "codigo": "001",
        "descricao": "Produto Teste",
        "ncm": "18069000",
        "cfop": "5102",
        "valor_total": 10000,  # R$ 100,00
    }],
)

print(f"Chave: {resultado.access_key}")
print(f"XML: {resultado.xml}")

Node.js via napi-rs

napi-rs gera addons nativos para Node.js com definições TypeScript automáticas. A performance é 10-100x superior ao JavaScript puro para operações como geração de XML e cálculos fiscais.

use napi_derive::napi;
use fiscal_core::types::*;
use fiscal_core::xml_builder::InvoiceBuilder;

#[napi(object)]
pub struct InvoiceResult {
    pub access_key: String,
    pub xml: String,
}

#[napi]
pub fn build_invoice_xml(
    cnpj: String,
    ie: String,
    razao_social: String,
    items: Vec<JsInvoiceItem>,
) -> napi::Result<InvoiceResult> {
    let issuer = IssuerData::new(&cnpj, &ie, &razao_social, /* ... */);
    let mut builder = InvoiceBuilder::new(
        issuer,
        SefazEnvironment::Homologation,
        InvoiceModel::NFe,
    );

    for item in items {
        builder = builder.add_item(item.into());
    }

    let invoice = builder.build()
        .map_err(|e| napi::Error::from_reason(e.to_string()))?;

    Ok(InvoiceResult {
        access_key: invoice.access_key().to_string(),
        xml: invoice.xml().to_string(),
    })
}

Uso em TypeScript:

import { buildInvoiceXml } from '@fiscal-rs/core';

const resultado = buildInvoiceXml(
  '25028332000105',
  '140950881119',
  'Minha Empresa',
  [{
    codigo: '001',
    descricao: 'Produto Teste',
    ncm: '18069000',
    cfop: '5102',
    valorTotal: 10000,
  }],
);

console.log(`Chave: ${resultado.accessKey}`);
console.log(`XML: ${resultado.xml}`);

WebAssembly via wasm-bindgen

wasm-bindgen compila o código Rust para WebAssembly, executável em qualquer navegador moderno. Ideal para aplicações web que precisam gerar XML ou calcular impostos no cliente.

A binding WASM expõe apenas os módulos que não dependem de I/O de rede ou sistema de arquivos: tipos, cálculos fiscais, geração de XML e QR Code. Assinatura digital e comunicação com SEFAZ exigem a binding nativa (Python ou Node.js).

import init, { buildInvoiceXml } from '@fiscal-rs/wasm';

await init(); // carrega o módulo .wasm

const resultado = buildInvoiceXml(/* ... */);

UniFFI para plataformas móveis (futuro)

UniFFI (Mozilla) gera bindings Kotlin e Swift a partir de uma definição de interface (UDL ou proc-macros). Permite usar o fiscal-rs em aplicações Android e iOS para PDV móvel.

// Kotlin (Android) — gerado automaticamente pelo UniFFI
val resultado = FiscalRs.buildInvoiceXml(
    cnpj = "25028332000105",
    ie = "140950881119",
    razaoSocial = "Minha Empresa",
    items = listOf(invoiceItem),
)

println("Chave: ${resultado.accessKey}")

Pipeline de build por alvo

Cada binding possui seu próprio pipeline de build e publicação:

Loading diagram...

Estrutura do workspace

As bindings ficam no diretório bindings/ do workspace:

fiscal-rs/
  crates/
    fiscal-core/        # lógica fiscal (zero I/O)
    fiscal-crypto/      # certificados e assinatura
    fiscal-sefaz/       # comunicação SEFAZ
  bindings/
    fiscal-py/          # PyO3 + maturin
      Cargo.toml
      pyproject.toml
    fiscal-node/        # napi-rs
      Cargo.toml
      package.json
    fiscal-wasm/        # wasm-bindgen + wasm-pack
      Cargo.toml

Comparação entre bindings

Python (PyO3)Node.js (napi-rs)WASMUniFFI
Instalaçãopip installnpm installnpm installMaven / CocoaPods
PerformanceNativaNativa~80% nativaNativa
Assinatura XMLSimSimNaoSim
SEFAZ (mTLS)SimSimNaoSim
Cálculos fiscaisSimSimSimSim
Geração XMLSimSimSimSim
QR CodeSimSimSimSim
Caso de usoBackend, scriptsBackend, APIFrontend webMobile (PDV)

As bindings FFI estão em fase de planejamento. A API Rust nativa (fiscal-core, fiscal-crypto, fiscal-sefaz) é a interface estável atual. Contribuições para as bindings são bem-vindas!

On this page