%% Copyright 2022 Nelson Vides, All Rights Reserved %% Copyright 2012 - 2014 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -module(erl_csv_file_stream). -moduledoc false. -compile({no_auto_import, [hd/1, tl/1]}). -include("erl_csv.hrl"). -export([hd/1, tl/1, foreach/2, map/2, list/1, list/2]). -export([new/0, new/1, new/2, read_file/2]). -define(DEFAULT_BUFFER_SIZE, 64 * 1024). -spec hd(erl_csv:csv_stream()) -> iodata(). hd(#csv_stream{hd = Head}) -> Head; hd(stream_end) -> <<>>. -spec tl(erl_csv:csv_stream()) -> erl_csv:maybe_csv_stream(). tl(#csv_stream{hd = <<>>, tl = Tail}) -> Tail(); tl(#csv_stream{hd = Head, tl = Tail}) -> case Tail() of stream_end -> stream_end; #csv_stream{hd = MoreHead} = NewStream -> NewStream#csv_stream{hd = iolist_to_binary([Head, MoreHead])}; {error, Reason} -> {error, Reason} end; tl(stream_end) -> stream_end. -spec foreach(fun((term()) -> term()), erl_csv:maybe_csv_stream()) -> stream_end | {error, term()}. foreach(_Fun, stream_end) -> stream_end; foreach(Fun, #csv_stream{hd = Head} = Stream) -> Fun(Head), foreach(Fun, tl(Stream)); foreach(_, {error, Reason}) -> {error, Reason}. -spec map(fun((T1) -> T2), erl_csv:maybe_csv_stream()) -> [T2 | {error, term()}] when T1 :: term(), T2 :: term(). map(Fun, stream_end) when is_function(Fun, 1) -> []; map(Fun, #csv_stream{hd = Head} = Stream) -> [Fun(Head) | map(Fun, tl(Stream))]; map(_, {error, Reason}) -> [{error, Reason}]. -spec list(erl_csv:csv_stream()) -> list(). list(#csv_stream{hd = Head} = Stream) -> [Head | list(tl(Stream))]; list(_) -> []. -spec list(integer(), erl_csv:csv_stream()) -> list(). list(N, #csv_stream{hd = Head} = Stream) when N > 0 -> [Head | list(N - 1, tl(Stream))]; list(_, _) -> []. %% create file csv_stream %% Options: %% * {iobuf, integer()} - size of i/o buffer -spec new() -> erl_csv:csv_stream(). new() -> stream_end. -spec new(binary()) -> erl_csv:csv_stream(). new(Head) -> new(Head, fun new/0). -spec new(iodata(), erl_csv:csv_stream_fun()) -> erl_csv:csv_stream(). new(Head, Fun) when is_function(Fun, 0) -> #csv_stream{hd = Head, tl = Fun}. -spec read_file(file:name_all(), map()) -> erl_csv:maybe_csv_stream(). read_file(File, Opts) -> BufferSize = maps:get(iobuf, Opts, ?DEFAULT_BUFFER_SIZE), {ok, FD} = file:open(File, [raw, binary, read, {encoding, utf8}, {read_ahead, BufferSize}]), istream(FD). -spec istream(term()) -> erl_csv:maybe_csv_stream(). istream(FD) when is_tuple(FD), erlang:element(1, FD) =:= file_descriptor -> case file:read_line(FD) of {ok, Chunk} -> new(Chunk, fun() -> istream(FD) end); eof -> file:close(FD), new(); {error, Reason} -> file:close(FD), {error, Reason} end.