#
# relies on implementation details !!!
# may have undesirable side effects !!!
# USE AT YOUR OWN RISK !!!
#
defmodule Nimble do
@separator ";"
@line_separator "\n"
@escape "\""
# http://elixir-recipes.github.io/strings/list-with-alphabet/
@lower for n <- ?a..?z, do: << n :: utf8 >>
@upper for n <- ?A..?Z, do: << n :: utf8 >>
@numeric for n <- ?0..?9, do: << n :: utf8 >>
@symbol for n <- [?_,?-,?+,?/,?*], do: << n :: utf8 >>
@reserved [@escape, @separator, @line_separator] ++ @lower ++ @upper ++ @numeric ++ @symbol
def make_options do
[
separator: @separator,
escape: @escape,
line_separator: @line_separator,
reserved: @reserved
]
end
end
NimbleCSV.define(MyParser, Nimble.make_options())
iex(1)> data = [["firstname", "lastname"], ["Foo", "Bar"]]
[["firstname", "lastname"], ["Foo", "Bar"]]
iex(2)> IO.iodata_to_binary MyParser.dump_to_iodata(data)
"\"firstname\";\"lastname\"\n\"Foo\";\"Bar\"\n"
iex(3)>
… and that still doesn’t cover an empty string.
This seems like a more reasonable approach:
defmodule Nimble do
@separator ?;
@line_separator ?\n
@escape ?"
def make_options do
[
separator: << @separator :: utf8 >>,
line_separator: << @line_separator :: utf8 >>,
escape: << @escape :: utf8 >>
]
end
defp escape_entry([@escape, value, @escape] = entry) when is_binary(value),
do: entry
defp escape_entry(value) when is_binary(value),
do: [@escape, value, @escape]
defp escape_row([], ys),
do: :lists.reverse(ys)
defp escape_row([entry, sep | xs], ys),
do: escape_row(xs, [sep, escape_entry(entry)|ys])
def escape_row(row),
do: escape_row(row,[])
defp escape_list([], ys),
do: :lists.reverse(ys)
defp escape_list([row | xs], ys),
do: escape_list(xs, [escape_row(row)|ys])
def escape_list(list),
do: escape_list(list,[])
end
NimbleCSV.define(MyParser, Nimble.make_options())
iex(1)> data = [["firstname", "lastname"], ["Fo;o", "Bar"]]
[["firstname", "lastname"], ["Fo;o", "Bar"]]
iex(2)> IO.iodata_to_binary(Nimble.escape_list(MyParser.dump_to_iodata(data)))
"\"firstname\";\"lastname\"\n\"Fo;o\";\"Bar\"\n"
iex(3)> data = [["firstname", "lastname"], ["", "Bar"]]
[["firstname", "lastname"], ["", "Bar"]]
iex(4)> IO.iodata_to_binary(Nimble.escape_list(MyParser.dump_to_iodata(data)))
"\"firstname\";\"lastname\"\n\"\";\"Bar\"\n"
iex(5)>