From 2086024b5a374a48c8176a30d83fc55fbb2b2f3b Mon Sep 17 00:00:00 2001 From: Joseph Lozano Date: Thu, 3 Nov 2022 15:58:51 -0400 Subject: [PATCH] fix types and update docs --- lib/csv.ex | 23 ++++++++++++++++++----- lib/csv/decoding/decoder.ex | 21 ++++++++------------- lib/csv/encoding/encoder.ex | 10 +--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/csv.ex b/lib/csv.ex index 472a1fd..3ea6c90 100644 --- a/lib/csv.ex +++ b/lib/csv.ex @@ -126,6 +126,7 @@ defmodule CSV do | {:headers, [String.t() | atom()] | boolean()} | {:unescape_formulas, boolean()} | {:validate_row_length, boolean()} + | {:escape_character, char} @spec decode(Enumerable.t(), [decode_options()]) :: Enumerable.t() def decode(stream, options \\ []) do @@ -283,11 +284,19 @@ defmodule CSV do Must be a codepoint (syntax: ? + (your escape character)). * `:delimiter` – The delimiter token to use, defaults to `\\r\\n`. Must be a string. - * `:force_escaping – When set to `true`, will escape fields even if + * `:force_escaping` – When set to `true`, will escape fields even if they do not contain characters that require escaping - * `:escape_formulas – When set to `true`, will escape formulas + * `:escape_formulas` – When set to `true`, will escape formulas to prevent [CSV Injection](https://owasp.org/www-community/attacks/CSV_Injection). - + * `:headers` + * When set to `false` (default), will use the raw inputs as elements. When set to anything but `false`, all elements in the input stream are assumed to be maps. + * When set to `true`, uses the keys of the first map as the first + element in the stream. All subsequent elements are the values of the maps. + * When set to a list, will use the given list as the first element + in the stream and order all subsequent elements using that list. + * When set to a keyword list, will use the keys of the + keyword list to match the keys of the data, and the values of the + keyword list to be the values in the first row of the output. ## Examples Convert a stream of rows with fields into a stream of lines: @@ -320,12 +329,16 @@ defmodule CSV do iex> |> CSV.encode(escape_formulas: true) iex> |> Enum.take(2) [\"\\\"'@a\\\",\\\"'=b\\\"\\r\\n\", \"\\\"'-c\\\",\\\"'+d\\\"\\r\\n\"] - """ + Convert a stream of rows renaming the headers by passing in a keyword list + + iex> [%{a: "value!"}] |> CSV.encode(headers: [a: "x", b: "y"]) + ["x,y\\r\\n", "value!,\\r\\n"] + """ @type encode_options :: {:separator, char()} | {:escape_character, char()} - | {:headers, [String.t() | atom()] | boolean()} + | {:headers, [String.t() | atom()] | Keyword.t() | boolean()} | {:delimiter, String.t()} | {:force_escaping, boolean()} | {:escape_formulas, boolean()} diff --git a/lib/csv/decoding/decoder.ex b/lib/csv/decoding/decoder.ex index 0bd95ed..86a82f1 100644 --- a/lib/csv/decoding/decoder.ex +++ b/lib/csv/decoding/decoder.ex @@ -4,7 +4,7 @@ defmodule CSV.Decoding.Decoder do @moduledoc ~S""" The Decoder CSV module sends lines of delimited values from a stream to the parser and converts rows coming from the CSV parser module to a consumable - stream. + stream. """ alias CSV.Decoding.Parser alias CSV.RowLengthError @@ -22,9 +22,9 @@ defmodule CSV.Decoding.Decoder do Must be a codepoint (syntax: ? + (your separator)). * `:escape_character` – The escape character token to use, defaults to `?"`. Must be a codepoint (syntax: ? + (your escape character)). - * `:field_transform` – A function with arity 1 that will get called with + * `:field_transform` – A function with arity 1 that will get called with each field and can apply transformations. Defaults to identity function. - This function will get called for every field and therefore should return + This function will get called for every field and therefore should return quickly. * `:headers` – When set to `true`, will take the first row of the csv and use it as header values. @@ -33,9 +33,9 @@ defmodule CSV.Decoding.Decoder do When set to anything but `false`, the resulting rows in the matrix will be maps instead of lists. * `:validate_row_length` – When set to `true`, will take the first row of - the csv or its headers and validate that following rows are of the same + the csv or its headers and validate that following rows are of the same length. Defaults to `false`. - * `:escape_formulas` – When set to `true`, will remove formula escaping + * `:escape_formulas` – When set to `true`, will remove formula escaping inserted to prevent [CSV Injection](https://owasp.org/www-community/attacks/CSV_Injection). ## Examples @@ -82,7 +82,7 @@ defmodule CSV.Decoding.Decoder do [ok: [\"a\", \"b\"], ok: [\"d\", \"e\"]] Replace invalid codepoints: - + iex> \"../../../test/fixtures/broken-encoding.csv\" ...> |> Path.expand(__DIR__) ...> |> File.stream!() @@ -147,13 +147,8 @@ defmodule CSV.Decoding.Decoder do ] """ - @type decode_options :: - {:unescape_formulas, boolean()} - | {:headers, [String.t() | atom()] | boolean()} - | {:validate_row_length, boolean()} - | {:separator, char} - | {:escape_character, char} - | {:field_transform, (String.t() -> String.t())} + @type decode_options :: CSV.decode_options() + @spec decode(Enumerable.t(), [decode_options()]) :: Enumerable.t() def decode(stream, options \\ []) do options = options |> with_defaults diff --git a/lib/csv/encoding/encoder.ex b/lib/csv/encoding/encoder.ex index fd9a49f..6358480 100644 --- a/lib/csv/encoding/encoder.ex +++ b/lib/csv/encoding/encoder.ex @@ -52,15 +52,7 @@ defmodule CSV.Encoding.Encoder do [\"\\\"a\\nb\\\"\\t\\\"\\tc\\\"\\n\", \"de\\t\\\"\\tf\\\"\\\"\\\"\\n\"] """ - @type encode_options :: - {:separator, char()} - | {:escape_character, char()} - | {:headers, [String.t() | atom()] | boolean()} - | {:delimiter, String.t()} - | {:force_escaping, boolean()} - | {:escape_formulas, boolean()} - - @spec encode(Enumerable.t(), [encode_options()]) :: Enumerable.t() + @spec encode(Enumerable.t(), [CSV.encode_options()]) :: Enumerable.t() def encode(stream, options \\ []) do headers = options |> Keyword.get(:headers, false)