Current section
Files
Jump to
Current section
Files
lib/csv_schema/parser.ex
defmodule Csv.Schema.Parser do
@moduledoc """
Utility module containing some functions for parsing and to read csv file
"""
@doc """
Given a csv file path try to parse as csv with headers. Use list of maps as
data representation
"""
@spec csv!(String.t()) :: map | no_return
def csv!(path) do
path
|> File.stream!()
|> CSV.decode(headers: true)
|> Stream.with_index(1)
|> Stream.map(fn
{{:error, reason}, _} -> raise "Failed to parse line with reason #{reason}"
{{:ok, row}, idx} -> Map.put(row, :__id__, idx)
end)
end
@doc """
Having a string or an atom as input cast value to atom. If something else is given
an exception is raised
If given argument is empty string or nil return value will be nil
## Examples
iex> Csv.Schema.Parser.atom!("")
nil
iex> Csv.Schema.Parser.atom!(nil)
nil
iex> Csv.Schema.Parser.atom!("id")
:id
iex> Csv.Schema.Parser.atom!(:id)
:id
iex> Csv.Schema.Parser.atom!(1)
** (RuntimeError) Cannot cast '1' to atom
"""
@spec atom!(String.t() | atom) :: atom | nil | no_return
def atom!(""), do: nil
def atom!(nil), do: nil
def atom!(value) when is_atom(value), do: value
def atom!(value) when is_binary(value), do: String.to_atom(value)
def atom!(value), do: raise("Cannot cast '#{value}' to atom")
@doc """
Having a string or an atom as input cast value to string. If something else is given
an exception is raised.
If given argument is empty string or nil return value will be nil
# Examples
iex> Csv.Schema.Parser.string!("")
nil
iex> Csv.Schema.Parser.string!(nil)
nil
iex> Csv.Schema.Parser.string!("id")
"id"
iex> Csv.Schema.Parser.string!(:id)
"id"
iex> Csv.Schema.Parser.string!(1)
** (RuntimeError) Cannot cast '1' to string
"""
@spec string!(String.t() | atom) :: String.t() | nil | no_return
def string!(""), do: nil
def string!(nil), do: nil
def string!(value) when is_binary(value), do: value
def string!(value) when is_atom(value), do: Atom.to_string(value)
def string!(value), do: raise("Cannot cast '#{value}' to string")
@doc """
Having a string as input cast value to integer. If something else is given
an exception is raised
If given argument is empty string or nil return value will be nil
# Examples
iex> Csv.Schema.Parser.integer!("")
nil
iex> Csv.Schema.Parser.integer!(nil)
nil
iex> Csv.Schema.Parser.integer!("1")
1
iex> Csv.Schema.Parser.integer!("1a")
** (RuntimeError) Cannot cast '1a' to integer
"""
@spec integer!(String.t()) :: number | nil | no_return
def integer!(""), do: nil
def integer!(nil), do: nil
def integer!(value) do
case Integer.parse(value) do
{val, ""} -> val
_ -> raise "Cannot cast '#{value}' to integer"
end
end
@doc """
Having a string as input cast value to float. If something else is given
an exception is raised
If given argument is empty string or nil return value will be nil
# Examples
iex> Csv.Schema.Parser.float!("")
nil
iex> Csv.Schema.Parser.float!(nil)
nil
iex> Csv.Schema.Parser.float!("1.2")
1.2
iex> Csv.Schema.Parser.float!("1a")
** (RuntimeError) Cannot cast '1a' to float
"""
@spec float!(String.t()) :: number | nil | no_return
def float!(""), do: nil
def float!(nil), do: nil
def float!(value) do
case Float.parse(value) do
{val, ""} -> val
_ -> raise "Cannot cast '#{value}' to float"
end
end
@doc """
Having a string as input representing string date to parse and a string representing
the date format try to cast value to date. If something else is given or format is invalid
or date is not parsable with given format an exception is raised
If given argument is empty string or nil return value will be nil
# Examples
iex> Csv.Schema.Parser.date!("", "whatever")
nil
iex> Csv.Schema.Parser.date!(nil, "whatever")
nil
iex> Csv.Schema.Parser.date!("18/01/2019", "{0D}/{0M}/{0YYYY}")
~N[2019-01-18 00:00:00]
iex> Csv.Schema.Parser.date!("18/01/2019", "{0M}/{0D}/{0YYYY}")
** (RuntimeError) Cannot cast '18/01/2019' to date with format '{0M}/{0D}/{0YYYY}'
iex> Csv.Schema.Parser.date!("18/01/2019", "MDY")
** (RuntimeError) Invalid date format 'MDY'
"""
@spec date!(String.t(), String.t()) :: DateTime.t() | nil | no_return
def date!("", _), do: nil
def date!(nil, _), do: nil
def date!(value, format) do
case Timex.validate_format(format) do
:ok ->
case Timex.parse(value, format) do
{:ok, date} -> date
{:error, _} -> raise "Cannot cast '#{value}' to date with format '#{format}'"
end
{:error, _} ->
raise "Invalid date format '#{format}'"
end
end
@doc """
Having a string as input cast value to boolean. If something else is given
an exception is raised
If given argument is empty string or nil return value will be nil
# Examples
iex> Csv.Schema.Parser.boolean!("")
nil
iex> Csv.Schema.Parser.boolean!(nil)
nil
iex> Csv.Schema.Parser.boolean!("true")
true
iex> Csv.Schema.Parser.boolean!("false")
false
iex> Csv.Schema.Parser.boolean!("1a")
** (RuntimeError) Cannot cast '1a' to boolean
"""
@spec boolean!(String.t()) :: boolean | nil | no_return
def boolean!(""), do: nil
def boolean!(nil), do: nil
def boolean!("true"), do: true
def boolean!("false"), do: false
def boolean!(value), do: raise("Cannot cast '#{value}' to boolean")
end