Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update encode_options and decode_options typespecs and docs #118

Merged
merged 1 commit into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions lib/csv.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()}
Expand Down
21 changes: 8 additions & 13 deletions lib/csv/decoding/decoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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!()
Expand Down Expand Up @@ -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
Expand Down
10 changes: 1 addition & 9 deletions lib/csv/encoding/encoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down