Skip to content

Commit

Permalink
Keep File.stream! options as last argument
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Oct 4, 2023
1 parent 8f7416d commit 1bbcf67
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 19 deletions.
32 changes: 26 additions & 6 deletions lib/elixir/lib/file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,18 @@ defmodule File do
:file.close(io_device)
end

@doc """
Shortcut for `File.stream!/3`.
"""
@spec stream!(Path.t(), :line | pos_integer | [stream_mode]) :: File.Stream.t()
def stream!(path, line_or_bytes_modes \\ [])

def stream!(path, modes) when is_list(modes),
do: stream!(path, :line, modes)

def stream!(path, line_or_bytes) when is_integer(line_or_bytes) or line_or_bytes == :line,
do: stream!(path, line_or_bytes, [])

@doc ~S"""
Returns a `File.Stream` for the given `path` with the given `modes`.
Expand Down Expand Up @@ -1723,17 +1735,25 @@ defmodule File do
## Examples
# Read a utf8 text file which may include BOM
File.stream!("./test/test.txt", encoding: :utf8, trim_bom: true)
# Read in 2048 byte chunks rather than lines
File.stream!("./test/test.data", [], 2048)
#=> %File.Stream{line_or_bytes: 2048, modes: [:raw, :read_ahead, :binary],
#=> path: "./test/test.data", raw: true}
File.stream!("./test/test.data", 2048)
See `Stream.run/1` for an example of streaming into a file.
"""
@spec stream!(Path.t(), [stream_mode], :line | pos_integer) :: File.Stream.t()
def stream!(path, modes \\ [], line_or_bytes \\ :line) do
@spec stream!(Path.t(), :line | pos_integer, [stream_mode]) :: File.Stream.t()
def stream!(path, line_or_bytes, modes)

def stream!(path, modes, line_or_bytes) when is_list(modes) do
# TODO: Deprecate this on Elixir v1.20
stream!(path, line_or_bytes, modes)
end

def stream!(path, line_or_bytes, modes) do
modes = normalize_modes(modes, true)
File.Stream.__build__(IO.chardata_to_string(path), modes, line_or_bytes)
File.Stream.__build__(IO.chardata_to_string(path), line_or_bytes, modes)
end

@doc """
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/file/stream.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defmodule File.Stream do
@type t :: %__MODULE__{}

@doc false
def __build__(path, modes, line_or_bytes) do
def __build__(path, line_or_bytes, modes) do
raw = :lists.keyfind(:encoding, 1, modes) == false

modes =
Expand Down
28 changes: 16 additions & 12 deletions lib/elixir/test/elixir/file/stream_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ defmodule File.StreamTest do
:ok
end

defp stream!(node, src, modes \\ [], lines_or_bytes \\ :line) do
:erpc.call(node, File, :stream!, [src, modes, lines_or_bytes])
defp stream!(node, src, lines_or_bytes_or_modes \\ []) do
:erpc.call(node, File, :stream!, [src, lines_or_bytes_or_modes])
end

defp stream!(node, src, lines_or_bytes, modes) do
:erpc.call(node, File, :stream!, [src, lines_or_bytes, modes])
end

distributed_node = :"secondary@#{node() |> Atom.to_string() |> :binary.split("@") |> tl()}"
Expand Down Expand Up @@ -54,7 +58,7 @@ defmodule File.StreamTest do
stream = stream!(@node, src, [:utf8])
assert Enum.count(stream) == 1

stream = stream!(@node, src, [], 2)
stream = stream!(@node, src, 2)
assert Enum.count(stream) == 2
end

Expand Down Expand Up @@ -82,7 +86,7 @@ defmodule File.StreamTest do
dest = tmp_path("tmp_test.txt")

try do
stream = stream!(@node, src, [], 1)
stream = stream!(@node, src, 1)

File.open(dest, [:write], fn target ->
Enum.each(stream, fn <<char>> ->
Expand Down Expand Up @@ -145,11 +149,11 @@ defmodule File.StreamTest do
|> Enum.take(1) == [<<239, 187, 191>> <> "Русский\n"]

assert @node
|> stream!(src, [], 1)
|> stream!(src, 1)
|> Enum.take(5) == [<<239>>, <<187>>, <<191>>, <<208>>, <<160>>]

assert @node |> stream!(src, []) |> Enum.count() == 2
assert @node |> stream!(src, [], 1) |> Enum.count() == 22
assert @node |> stream!(src, 1) |> Enum.count() == 22
end

test "trims BOM via option when raw" do
Expand All @@ -164,7 +168,7 @@ defmodule File.StreamTest do
|> Enum.take(5) == [<<208>>, <<160>>, <<209>>, <<131>>, <<209>>]

assert @node |> stream!(src, [:trim_bom]) |> Enum.count() == 2
assert @node |> stream!(src, [:trim_bom], 1) |> Enum.count() == 19
assert @node |> stream!(src, 1, [:trim_bom]) |> Enum.count() == 19
end

test "keeps BOM with utf8 encoding" do
Expand All @@ -175,7 +179,7 @@ defmodule File.StreamTest do
|> Enum.take(1) == [<<239, 187, 191>> <> "Русский\n"]

assert @node
|> stream!(src, [{:encoding, :utf8}], 1)
|> stream!(src, 1, [{:encoding, :utf8}])
|> Enum.take(9) == ["\uFEFF", "Р", "у", "с", "с", "к", "и", "й", "\n"]
end

Expand All @@ -187,7 +191,7 @@ defmodule File.StreamTest do
|> Enum.take(1) == ["Русский\n"]

assert @node
|> stream!(src, [{:encoding, :utf8}, :trim_bom], 1)
|> stream!(src, 1, [{:encoding, :utf8}, :trim_bom])
|> Enum.take(8) == ["Р", "у", "с", "с", "к", "и", "й", "\n"]
end

Expand Down Expand Up @@ -215,7 +219,7 @@ defmodule File.StreamTest do
|> Enum.take(1) == ["Русский\n"]

assert @node
|> stream!(src, [{:encoding, {:utf16, :big}}, :trim_bom], 1)
|> stream!(src, 1, [{:encoding, {:utf16, :big}}, :trim_bom])
|> Enum.take(8) == ["Р", "у", "с", "с", "к", "и", "й", "\n"]
end

Expand All @@ -227,7 +231,7 @@ defmodule File.StreamTest do
|> Enum.take(1) == ["Русский\n"]

assert @node
|> stream!(src, [{:encoding, {:utf16, :little}}, :trim_bom], 1)
|> stream!(src, 1, [{:encoding, {:utf16, :little}}, :trim_bom])
|> Enum.take(8) == ["Р", "у", "с", "с", "к", "и", "й", "\n"]
end

Expand Down Expand Up @@ -255,7 +259,7 @@ defmodule File.StreamTest do
dest = tmp_path("tmp_test.txt")

try do
stream = stream!(@node, src, [:utf8], 1)
stream = stream!(@node, src, 1, [:utf8])

File.open(dest, [:write], fn target ->
Enum.each(stream, fn <<char::utf8>> ->
Expand Down

0 comments on commit 1bbcf67

Please sign in to comment.