-module(glexif). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/glexif.gleam"). -export([get_exif_data_for_file/1, error_to_string/1]). -export_type([exif_error/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Read standard EXIF metadata from JPEG APP1 segments.\n"). -type exif_error() :: {file_open_error, binary()} | {file_read_error, binary()} | {file_close_error, binary()} | invalid_jpeg_header | exif_marker_not_found | unexpected_end_of_file | {invalid_segment_size, integer()} | invalid_exif_header | invalid_tiff_header | {invalid_entry, integer()} | {invalid_offset, integer()} | {offset_cycle, integer()} | traversal_limit_exceeded. -file("src/glexif.gleam", 80). -spec parse_error(glexif@internal@raw:exif_parse_error()) -> exif_error(). parse_error(Error) -> case Error of {stream_read_error, Error@1} -> {file_read_error, file_streams@file_stream_error:describe(Error@1)}; exif_marker_not_found -> exif_marker_not_found; unexpected_end_of_file -> unexpected_end_of_file; invalid_jpeg_header -> invalid_jpeg_header; {invalid_segment_size, Size} -> {invalid_segment_size, Size}; invalid_exif_header -> invalid_exif_header; invalid_tiff_header -> invalid_tiff_header; {invalid_entry, Offset} -> {invalid_entry, Offset}; {invalid_offset, Offset@1} -> {invalid_offset, Offset@1}; {offset_cycle, Offset@2} -> {offset_cycle, Offset@2}; traversal_limit_exceeded -> traversal_limit_exceeded end. -file("src/glexif.gleam", 71). -spec result_map_parse_error( {ok, glexif@exif_tag:exif_tag_record()} | {error, glexif@internal@raw:exif_parse_error()} ) -> {ok, glexif@exif_tag:exif_tag_record()} | {error, exif_error()}. result_map_parse_error(Result) -> case Result of {ok, Record} -> {ok, Record}; {error, Error} -> {error, parse_error(Error)} end. -file("src/glexif.gleam", 47). ?DOC( " Read EXIF metadata from a JPEG file without panicking for invalid input.\n" "\n" " A readable JPEG with no `Exif`-prefixed APP1 candidate returns\n" " `Error(ExifMarkerNotFound)`. A malformed candidate returns the applicable\n" " EXIF or TIFF error instead.\n" ). -spec get_exif_data_for_file(binary()) -> {ok, glexif@exif_tag:exif_tag_record()} | {error, exif_error()}. get_exif_data_for_file(File_path) -> case file_streams@file_stream:open_read(File_path) of {error, Error} -> {error, {file_open_error, file_streams@file_stream_error:describe(Error)}}; {ok, Stream} -> Outcome = case glexif@internal@raw:read_exif_segment(Stream) of {error, Error@1} -> {error, parse_error(Error@1)}; {ok, Segment} -> _pipe = glexif@internal@raw:parse_exif_data_as_record( Segment ), result_map_parse_error(_pipe) end, Close_outcome = file_streams@file_stream:close(Stream), case {Outcome, Close_outcome} of {{error, Error@2}, _} -> {error, Error@2}; {{ok, _}, {error, Error@3}} -> {error, {file_close_error, file_streams@file_stream_error:describe(Error@3)}}; {{ok, Record}, {ok, nil}} -> {ok, Record} end end. -file("src/glexif.gleam", 98). ?DOC(" Format an EXIF error for logs or user-facing diagnostics.\n"). -spec error_to_string(exif_error()) -> binary(). error_to_string(Error) -> case Error of {file_open_error, Message} -> <<"Could not open file: "/utf8, Message/binary>>; {file_read_error, Message@1} -> <<"Could not read file: "/utf8, Message@1/binary>>; {file_close_error, Message@2} -> <<"Could not close file: "/utf8, Message@2/binary>>; invalid_jpeg_header -> <<"Invalid JPEG header"/utf8>>; exif_marker_not_found -> <<"No EXIF APP1 segment was found"/utf8>>; unexpected_end_of_file -> <<"Unexpected end of file"/utf8>>; {invalid_segment_size, Size} -> <<"Invalid JPEG segment size: "/utf8, (erlang:integer_to_binary(Size))/binary>>; invalid_exif_header -> <<"Invalid EXIF header"/utf8>>; invalid_tiff_header -> <<"Invalid TIFF header"/utf8>>; {invalid_entry, Offset} -> <<"Invalid TIFF entry at offset "/utf8, (erlang:integer_to_binary(Offset))/binary>>; {invalid_offset, Offset@1} -> <<"Invalid TIFF offset: "/utf8, (erlang:integer_to_binary(Offset@1))/binary>>; {offset_cycle, Offset@2} -> <<"TIFF offset cycle detected at "/utf8, (erlang:integer_to_binary(Offset@2))/binary>>; traversal_limit_exceeded -> <<"TIFF traversal limit exceeded"/utf8>> end.