Criando um servidor de tracing distribuído (parte 1)
Tags: haskell, webdev, cybertank, programming
Published: 2023-09-11 04:30:14

Em Haskell tudo começa pelas tipagens e dados que você precisa. Primeiramente certifique-se que tenha instalado o `cabal` ou `stack` para criar projetos em Haskell.

### Criando projeto

Para criar o projeto é simples
```sh
$ cabal init -n tracer
```
O cabal irá criar um projeto de modo interativo (bem como o `npm init`).

### Tipos e dados

Primeiro precisamos definir o que será cada coisa, os componentes e a implementação.

Na estrutura temos o que chamaremos de ISO ou EDIO (Estrutura de instrumentação da observabilidade) que é nada mais que um mapa chave-valor, onde a chave é a ação originária e o valor é a uma lista de ações possíveis (ou ações alvos possíveis).

```haskell
module Instrumentation () where

newtype ISO = ISO [(String, [String])] deriving (Eq, Show)
```

O Map, bem utilizado em outras linguagens, se resume a uma lista de tuplas com apenas 2 valores, a chave e o valor em si. E pra isso teremos que criar algumas funções auxiliares para inserir, procurar ou deletar entradas nesse mapa.

### Funções auxiliares

No caso, por agora, precisamos ter uma função para criar um ISO vazio e um para inserir.

```haskell
newISO :: ISO
newISO = ISO []
```

A função de criação é bem simples pois só precisamos ter uma função que usa o método construtor `ISO` com uma lista vazia.

```haskell
insert :: (String, [String]) -> ISO -> ISO
insert (key, value) (ISO items) = ISO newIso
  where
    newIso = items ++ [(key, value)]
```

A função de inserção seria apenas a concatenação da lista anterior com uma lista nova. Em Haskell, podemos ter N formas de criar essa função, usei a notação `where`, pois ela nos dá uma forma mais fácil de compreender o que é cada parte da função. Nesse caso eu separei a concatenação de listas numa variável desse `where` para facilitar a legibilidade da função em si.

### Testes, pois sem testes não viveríamos

Em Haskell, testes são feitos por, acredite ou não, funções! Essas funções podem ser escritas de várias maneiras mas aqui usarei o mais comum na comunidade, que é o HUnit.

Primeiro nós instalamos o HUnit pelo cabal.
```sh
$ cabal install --lib HUnit
```

E precisamos alterar no nosso arquivo `tracer.cabal`

```cabal
test-suite tracer-test
    import:           warnings
    default-language: Haskell2010
    type:             exitcode-stdio-1.0
    hs-source-dirs:   test
    main-is:          Main.hs
    other-modules:    InstrumentationTest -- novo
    build-depends:
        base ^>=4.18.0.0,
        tracer,
        HUnit -- novo
```

Nesse arquivo adicionamos um novo módulo além do Main que o cabal cria para nós, e o que depende do build desse teste.

## Teste da estrutura ISO

Com isso precisamos criar os testes nesse arquivo `test/InstrumentationTest.hs`

```haskell
module InstrumentationTest () where

import Instrumentation (ISO (..), insert, newISO)
import Test.HUnit

createNewISOTest :: Test
createNewISOTest = TestCase (assertEqual "should return empty ISO" (ISO [] newISO)

insertEntryTest :: Test
insertEntryTest = TestCase (assertEqual "should return a ISO with entry" desired appliedFunction)
   where
     desired = ISO [("chave", ["valor1"])]
     appliedFunction = insert ("chave", ["valor1"]) newISO
```

Nesse arquivo criamos tanto o teste para checar a criação de uma nova ISO e a inserção de uma entrada. E agora precisamos colocar ele no arquivo `test/Main.hs`

```haskell
module Main (main) where

import Test.HUnit
import InstrumentationTest (createNewISOTest, insertEntryTest)
import qualified System.Exit as Exit

tests :: Test
tests = TestList [TestLabel "createNewISO" createNewISOTest, TestLabel "insertEntryTest" insertEntryTest]

main :: IO ()
main = do
  result <- runTestTT tests
  if failures result > 0 then Exit.exitFailure else Exit.exitSuccess
```

Nesse arquivo nós criamos uma suite de testes para que ele rode todos os testes do módulo `InstrumentationTest`

Para rodar os testes apenas rodamos
```sh
$ cabal test
```
E ele terá um resultado parecido com esse (além dos logs de build)

```
Test suite eyaara-test: RUNNING...
Cases: 2  Tried: 2  Errors: 0  Failures: 0
Test suite tracer-test: PASS
```

E por hoje é só! Nessa série de artigos eu irei recriar o sistema e estrutura que desenvolvi baseado no algoritmo Drain para parsing de logs.

## O ṣeun! 👋🏾