Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

ELIXIR-VIDEO-009

live-books/009-recursion.livemd

ELIXIR-VIDEO-009

Request

request =
  """
  POST /books HTTP/1.1
  Host: example.com
  User-Agent: Browser/1.0
  Accept: */*
  Content-Type: application/x-www-form-urlencoded
  Content-Length: 21

  name=Os primeiros casos de Poirot&author=Agatha Christie
  """

Conv Struct

defmodule Servidor.Conv do
  # defstruct method: "",
  #           path: "",
  #           protocol: "",
  #           resp_body: "",
  #           status: nil,

  # defstruct method: "",
  #           path: "",
  #           protocol: "",
  #           resp_body: "",
  #           status: nil,
  #           params: nil

  defstruct method: "",
            path: "",
            protocol: "",
            resp_body: "",
            status: nil,
            params: nil,
            headers: nil
end

Parse

[method, path, protocol] =
  request
  |> String.replace("\r\n", "\n")
  |> String.split("\n")
  |> List.first()
  |> String.split()

%Servidor.Conv{
  method: method,
  path: path,
  protocol: protocol,
  status: 200
}
# Resultado
# %Servidor.Conv{
#   method: "POST",
#   path: "/books",
#   protocol: "HTTP/1.1",
#   resp_body: "",
#   status: 200
# }

# Resultado no App

Parse: Request / Body

# Separar o Request do Body
# Split em um item na lista para cada linha da string

request
|> String.replace("\r\n", "\n")
|> String.split("\n")
# Resultado
# O body é separado do header por 2 saltos de linha
[
  "POST /books HTTP/1.1",
  "Host: example.com",
  "User-Agent: Browser/1.0",
  "Accept: */*",
  "Content-Type: application/x-www-form-urlencoded",
  "Content-Length: 21",
  "",
  "name=Os primeiros casos de Poirot&author=Agatha Christie",
  ""
]
# Split em 2 itens item na lista
request
|> String.replace("\r\n", "\n")
|> String.split("\n\n")
# Resultado (header / body)
[
  "POST /books HTTP/1.1\nHost: example.com\nUser-Agent: Browser/1.0\nAccept: */*\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 21",
  "name=Os primeiros casos de Poirot&author=Agatha Christie\n"
]
# Separa header / body
[header_lines, params_line] =
  request
  |> String.replace("\r\n", "\n")
  |> String.split("\n\n")

IO.puts("== HEADER ==")
IO.puts(header_lines)

IO.puts("")
IO.puts("== PARAMS ==")
IO.puts(params_line)
# A partir do header_lines nosso Parse continua funcionando
[method, path, protocol] =
  header_lines
  |> String.split("\n")
  |> List.first()
  |> String.split()

%Servidor.Conv{
  method: method,
  path: path,
  protocol: protocol,
  status: 200
}

# Adicionar o field "params" em %Servidor.Conv antes de prosseguir

# %Servidor.Conv{
#   method: method,
#   path: path,
#   protocol: protocol,
#   status: 200,
#   params: params_line
# }
# Tranferir resultados para o código

Parse: Body => Params => Map

# Transformar o params_line em um MAP
params = URI.decode_query(params_line)

# Limpando salto de linha
params = URI.decode_query(String.trim(params_line))

# Macete pra transformar strings em atomos
# Pq não fazer nesse caso
# for {key, val} <- params, into: %{}, do: {String.to_atom(key), val}
# Tranferir para o código e criar o Resp Body baseado nos "params"

Content-Type

# application/x-www-form-urlencoded
# multipart/form-data
# application/json
# application/xml
# text/plain
# application/octet-stream
# application/graphql
# application/pdf
# application/javascript
# application/zip
# application/vnd.api+json

Parse: TopLine / Headers

# header_lines no momento
header_lines

# "POST /books HTTP/1.1\nHost: example.com\nUser-Agent: Browser/1.0\nAccept: */*\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 21"
# Tranformação atual despreza os headers
header_lines
  |> String.split("\n")
  |> List.first()
  |> String.split()
# Conteúdo de todas as linhas
top_lines =
  header_lines
  |> String.split("\n")
# Resultado em linhas
[
  "POST /books HTTP/1.1",
  "Host: example.com",
  "User-Agent: Browser/1.0",
  "Accept: */*",
  "Content-Type: application/x-www-form-urlencoded",
  "Content-Length: 21"
]

List / Head / Tail / Cons Operator / Função: inspect

# Como dividir em top_line (primeira linha) e headers_lines (restante)
lista = [1, 2, 3, 4, 5]
[a, b, c, d, e] = lista
IO.puts("#{a}, #{b}, #{c}, #{d}, #{e}")

# Conceito de Head e Tail de uma List / Cons Operator "|"
[head | tail] = lista
# IO.puts("head: #{head} / tail: #{tail}")
IO.puts("head: #{head} / tail: #{inspect(tail)}")
# Usando o cons operator em top_lines
[top_line | headers_lines] = top_lines

IO.puts(inspect(top_line))
IO.puts(inspect(headers_lines))
# Resultado 
# "POST /books HTTP/1.1"

[
  "Host: example.com",
  "User-Agent: Browser/1.0",
  "Accept: */*",
  "Content-Type: application/x-www-form-urlencoded",
  "Content-Length: 21"
]
#  A partir do novo header nosso Parse continua funcionando

[method, path, protocol] =
  top_line
  |> String.split()

# %Servidor.Conv{
#   method: method,
#   path: path,
#   protocol: protocol,
#   status: 200,
#   params: params
# }

# Adicionar o field "headers" em %Servidor.Conv antes de prosseguir
%Servidor.Conv{
  method: method,
  path: path,
  protocol: protocol,
  status: 200,
  params: params,
  headers: headers_lines
}
# Transferir headers para o código

Parse: Headers => Map

# - Transformar os headers num Map
headers_lines
# [
#   "Host: example.com",
#   "User-Agent: Browser/1.0",
#   "Accept: */*",
#   "Content-Type: application/x-www-form-urlencoded",
#   "Content-Length: 21"
# ]
# Conceito de Head e Tail de uma List / Cons Operator "|"
lista = [1, 2, 3, 4, 5]

[head | tail] = lista
IO.puts("head: #{head} / tail: #{inspect(tail)}")
# [head | tail] = tail
# IO.puts("head: #{head} / tail: #{inspect(tail)}")
# [head | tail] = tail
# IO.puts("head: #{head} / tail: #{inspect(tail)}")
# [head | tail] = tail
# IO.puts("head: #{head} / tail: #{inspect(tail)}")
# [head | tail] = tail
# IO.puts("head: #{head} / tail: #{inspect(tail)}")
# [head | tail] = tail
# IO.puts("head: #{head} / tail: #{inspect(tail)}")
# Loop recursivo pela lista
defmodule ListaLoop do
  def loop(lista) do
    IO.puts("lista: #{inspect(lista)}")
    
    # [head | tail] = lista
    # IO.puts("head: #{head} / tail: #{inspect(tail)}")
  end

  # def loop([head | tail]) do
  #   IO.puts("head: #{head} / tail: #{inspect(tail)}")
  #   loop(tail)
  # end

  # def loop([]), do: IO.puts("fim")
end
ListaLoop.loop(lista)
ListaLoop.loop(12345)
ListaLoop.loop("Roger")
# ListaLoop.loop(headers_lines)
# headers_lines
# [
#   "Host: example.com",
#   "User-Agent: Browser/1.0",
#   "Accept: */*",
#   "Content-Type: application/x-www-form-urlencoded",
#   "Content-Length: 21"
# ]

head_list = String.split("Host: example.com", ": ")

# [key, value] = head_list 
# IO.puts("key: #{key} / value: #{inspect(value)}")
# %{key => value}
# header = Map.put(%{}, key, value)

headers_lines
defmodule Utils do
  def list_to_map([head | tail]) do
    IO.puts("head: #{head} / tail: #{inspect(tail)}")
    list_to_map(tail)
  end

  def list_to_map([]), do: "fim"
  
  # def list_to_map([head | tail], map) do
  #   IO.puts("head: #{head} / tail: #{inspect(tail)}")
  #   head_list = String.split(head, ": ")
  #   [key, value] = head_list
  #   new_map = Map.put(map, key, value)
  #   list_to_map(tail, new_map)
  # end

  # def list_to_map([], map), do: map
end
Utils.list_to_map(headers_lines)
# Utils.list_to_map(headers_lines, %{})

# TAIL CALL OPTIMIZATION
# Tranferir para o código