• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

camatcode / basenji / 88515d912fe7364f31cf92251fbc686601ac592d

02 Jul 2025 03:12AM UTC coverage: 56.813%. First build
88515d912fe7364f31cf92251fbc686601ac592d

Pull #10

github

camatcode
ci: coveralls
Pull Request #10: ci: introduce ci

53 of 94 new or added lines in 9 files covered. (56.38%)

246 of 433 relevant lines covered (56.81%)

104.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.67
/lib/basenji/reader/reader.ex
1
defmodule Basenji.Reader do
2
  @moduledoc false
3

4
  alias Basenji.Reader.CB7Reader
5
  alias Basenji.Reader.CBRReader
6
  alias Basenji.Reader.CBTReader
7
  alias Basenji.Reader.CBZReader
8
  alias Basenji.Reader.Process.JPEGOptimizer
9
  alias Basenji.Reader.Process.PNGOptimizer
10
  alias Porcelain.Result
11

12
  @readers [
13
    CBZReader,
14
    CBRReader,
15
    CB7Reader,
16
    CBTReader
17
  ]
18

19
  @optimizers [
20
    JPEGOptimizer,
21
    PNGOptimizer
22
  ]
23

24
  def info(location, opts \\ []) do
25
    reader = find_reader(location)
15✔
26

27
    info =
15✔
28
      if reader do
×
29
        title = location |> Path.basename() |> Path.rootname()
15✔
30
        {:ok, %{entries: entries}} = reader.read(location, opts)
15✔
31
        %{format: reader.format(), resource_location: location, title: title, page_count: Enum.count(entries)}
15✔
32
      else
33
        {:error, :unreadable}
34
      end
35

36
    info
37
    |> case do
15✔
38
      {:error, e} -> {:error, e}
×
39
      inf -> {:ok, inf}
15✔
40
    end
41
  end
42

43
  def exec(cmd, args, opts \\ []) do
44
    Porcelain.exec(cmd, args, opts)
45
    |> case do
351✔
46
      %Result{out: output, status: 0} ->
351✔
47
        {:ok, output |> String.trim()}
48

49
      other ->
×
50
        {:error, other}
51
    end
52
  end
53

54
  def create_resource(make_func) do
55
    Stream.resource(
65✔
56
      make_func,
57
      fn
58
        :halt -> {:halt, nil}
65✔
59
        func -> {func, :halt}
65✔
60
      end,
61
      fn _ -> nil end
65✔
62
    )
63
  end
64

65
  def sort_file_names(e), do: Enum.sort_by(e, & &1.file_name)
662✔
66

67
  def reject_macos_preview(e), do: Enum.reject(e, &String.contains?(&1.file_name, "__MACOSX"))
662✔
68

69
  def read(file_path, opts \\ []) do
70
    opts = Keyword.merge([optimize: false], opts)
633✔
71

72
    reader = find_reader(file_path)
633✔
73

74
    if reader do
633✔
75
      read_result = reader.read(file_path, opts)
633✔
76
      if opts[:optimize], do: optimize_entries(read_result), else: read_result
633✔
77
    else
78
      {:error, "No Reader found for: #{file_path}. Unknown file type"}
×
79
    end
80
  end
81

82
  defp find_reader(file_path) do
83
    @readers
84
    |> Enum.reduce_while(
648✔
85
      nil,
86
      fn reader, _acc ->
87
        if matches_magic?(reader, file_path), do: {:halt, reader}, else: {:cont, nil}
1,603✔
88
      end
89
    )
90
  end
91

92
  def stream_pages(file_path, opts \\ []) do
93
    opts = Keyword.merge([start_page: 1], opts)
8✔
94

95
    with {:ok, %{entries: entries}} <- read(file_path, opts) do
8✔
96
      stream =
8✔
97
        opts[:start_page]..Enum.count(entries)
98
        |> Stream.map(fn idx ->
99
          at = idx - 1
32✔
100
          Enum.at(entries, at).stream_fun.()
32✔
101
        end)
102

103
      {:ok, stream}
104
    end
105
  end
106

107
  def matches_magic?(reader, file_path) do
108
    reader.get_magic_numbers()
1,603✔
109
    |> Enum.reduce_while(
1,603✔
110
      nil,
111
      fn %{offset: offset, magic: magic}, _acc ->
112
        try do
1,603✔
113
          bytes =
1,603✔
114
            File.stream!(file_path, offset + Enum.count(magic), [])
115
            |> Enum.take(1)
116
            |> hd()
117
            |> :binary.bin_to_list()
118
            |> Enum.take(-1 * Enum.count(magic))
119

120
          if bytes == magic, do: {:halt, true}, else: {:cont, nil}
1,603✔
121
        rescue
NEW
122
          _ -> {:cont, nil}
×
123
        end
124
      end
125
    )
126
  end
127

128
  defp optimize_entries({:ok, result}) do
129
    updated_entries =
12✔
130
      Map.get(result, :entries)
131
      |> Enum.map(fn entry ->
132
        stream_fun = fn ->
48✔
133
          create_resource(fn -> [optimize(entry.stream_fun.()) |> :binary.bin_to_list()] end)
15✔
134
        end
135

136
        Map.put(entry, :stream_fun, stream_fun)
48✔
137
      end)
138

139
    result = Map.put(result, :entries, updated_entries)
12✔
140

141
    {:ok, result}
142
  end
143

144
  defp optimize_entries(other), do: other
×
145

146
  defp optimize(bytes) do
147
    @optimizers
148
    |> Enum.reduce(bytes |> Enum.to_list(), fn reader, bytes ->
15✔
149
      reader.optimize!(bytes)
30✔
150
    end)
151
  end
152
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc