+6
-3
bin/mlgpx_cli.ml
+6
-3
bin/mlgpx_cli.ml
···
3
open Cmdliner
4
open Gpx
5
6
(* Terminal and formatting setup *)
7
let setup_fmt style_renderer =
8
Fmt_tty.setup_std_outputs ?style_renderer ();
···
67
log_info "Reading GPX file: %a" (bold_style Fmt.string) input_file;
68
69
(* Read input GPX *)
70
-
let gpx = Gpx_eio.read ~fs input_file in
71
72
if verbose then
73
log_info "Found %d waypoints and %d existing tracks"
···
140
log_info "Writing output to: %a" (bold_style Fmt.string) output_file;
141
142
(* Write output GPX *)
143
-
Gpx_eio.write ~fs output_file output_gpx;
144
145
if verbose then (
146
Fmt.pf Format.std_formatter "%a\n" (success_style Fmt.string) "Conversion completed successfully!";
···
215
if verbose then
216
log_info "Analyzing GPX file: %a" (bold_style Fmt.string) input_file;
217
218
-
let gpx = Gpx_eio.read ~fs input_file in
219
220
(* Header *)
221
Fmt.pf Format.std_formatter "%a\n" (bold_style Fmt.string) "GPX File Information";
···
255
let duration_span = Ptime.diff stop_time start_time in
256
match Ptime.Span.to_int_s duration_span with
257
| Some seconds ->
258
let days = seconds / 86400 in
259
let hours = (seconds mod 86400) / 3600 in
260
let minutes = (seconds mod 3600) / 60 in
···
3
open Cmdliner
4
open Gpx
5
6
+
let ( / ) = Eio.Path.( / )
7
+
8
(* Terminal and formatting setup *)
9
let setup_fmt style_renderer =
10
Fmt_tty.setup_std_outputs ?style_renderer ();
···
69
log_info "Reading GPX file: %a" (bold_style Fmt.string) input_file;
70
71
(* Read input GPX *)
72
+
let gpx = Gpx_eio.read (fs / input_file) in
73
74
if verbose then
75
log_info "Found %d waypoints and %d existing tracks"
···
142
log_info "Writing output to: %a" (bold_style Fmt.string) output_file;
143
144
(* Write output GPX *)
145
+
Gpx_eio.write (fs / output_file) output_gpx;
146
147
if verbose then (
148
Fmt.pf Format.std_formatter "%a\n" (success_style Fmt.string) "Conversion completed successfully!";
···
217
if verbose then
218
log_info "Analyzing GPX file: %a" (bold_style Fmt.string) input_file;
219
220
+
let gpx = Gpx_eio.read (fs / input_file) in
221
222
(* Header *)
223
Fmt.pf Format.std_formatter "%a\n" (bold_style Fmt.string) "GPX File Information";
···
257
let duration_span = Ptime.diff stop_time start_time in
258
match Ptime.Span.to_int_s duration_span with
259
| Some seconds ->
260
+
let ( / ) = Int.div in
261
let days = seconds / 86400 in
262
let hours = (seconds mod 86400) / 3600 in
263
let minutes = (seconds mod 3600) / 60 in
+3
-3
lib/gpx_eio/gpx_eio.ml
+3
-3
lib/gpx_eio/gpx_eio.ml
···
3
module IO = Gpx_io
4
5
(** Read and parse GPX file *)
6
-
let read ?(validate=false) ~fs path = IO.read_file ~validate ~fs path
7
8
(** Write GPX to file *)
9
-
let write ?(validate=false) ~fs path gpx = IO.write_file ~validate ~fs path gpx
10
11
(** Write GPX to file with backup *)
12
-
let write_with_backup ?(validate=false) ~fs path gpx = IO.write_file_with_backup ~validate ~fs path gpx
13
14
(** Read GPX from Eio source *)
15
let from_source ?(validate=false) source = IO.read_source ~validate source
···
3
module IO = Gpx_io
4
5
(** Read and parse GPX file *)
6
+
let read ?(validate=false) path = IO.read_file ~validate path
7
8
(** Write GPX to file *)
9
+
let write ?(validate=false) path gpx = IO.write_file ~validate path gpx
10
11
(** Write GPX to file with backup *)
12
+
let write_with_backup ?(validate=false) path gpx = IO.write_file_with_backup ~validate path gpx
13
14
(** Read GPX from Eio source *)
15
let from_source ?(validate=false) source = IO.read_source ~validate source
+7
-10
lib/gpx_eio/gpx_eio.mli
+7
-10
lib/gpx_eio/gpx_eio.mli
···
38
These functions provide simple file I/O with the filesystem from {!Eio.Stdenv.fs}. *)
39
40
(** Read and parse GPX file.
41
-
@param fs Filesystem capability
42
@param path File path to read
43
@param ?validate Optional validation flag (default: false)
44
@return GPX document
45
@raises Gpx.Gpx_error on read or parse failure *)
46
-
val read : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t
47
48
(** Write GPX to file.
49
-
@param fs Filesystem capability
50
@param path File path to write
51
@param gpx GPX document to write
52
@param ?validate Optional validation flag (default: false)
53
@raises Gpx.Gpx_error on write failure *)
54
-
val write : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> unit
55
56
(** Write GPX to file with automatic backup.
57
-
@param fs Filesystem capability
58
@param path File path to write
59
@param gpx GPX document to write
60
@param ?validate Optional validation flag (default: false)
61
-
@return Backup file path (empty if no backup created)
62
@raises Gpx.Gpx_error on failure *)
63
-
val write_with_backup : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> string
64
65
(** {2 Stream Operations}
66
···
71
@param ?validate Optional validation flag (default: false)
72
@return GPX document
73
@raises Gpx.Gpx_error on read or parse failure *)
74
-
val from_source : ?validate:bool -> [> Eio.Flow.source_ty ] Eio.Resource.t -> Gpx.t
75
76
(** Write GPX to Eio sink.
77
@param sink Output flow
78
@param gpx GPX document
79
@param ?validate Optional validation flag (default: false)
80
@raises Gpx.Gpx_error on write failure *)
81
-
val to_sink : ?validate:bool -> [> Eio.Flow.sink_ty ] Eio.Resource.t -> Gpx.t -> unit
82
83
(** Print GPX statistics to sink.
84
@param sink Output sink
85
@param gpx GPX document *)
86
-
val print_stats : [> Eio.Flow.sink_ty ] Eio.Resource.t -> Gpx.t -> unit
···
38
These functions provide simple file I/O with the filesystem from {!Eio.Stdenv.fs}. *)
39
40
(** Read and parse GPX file.
41
@param path File path to read
42
@param ?validate Optional validation flag (default: false)
43
@return GPX document
44
@raises Gpx.Gpx_error on read or parse failure *)
45
+
val read : ?validate:bool -> _ Eio.Path.t -> Gpx.t
46
47
(** Write GPX to file.
48
@param path File path to write
49
@param gpx GPX document to write
50
@param ?validate Optional validation flag (default: false)
51
@raises Gpx.Gpx_error on write failure *)
52
+
val write : ?validate:bool -> _ Eio.Path.t -> Gpx.t -> unit
53
54
(** Write GPX to file with automatic backup.
55
@param path File path to write
56
@param gpx GPX document to write
57
@param ?validate Optional validation flag (default: false)
58
+
@return Backup file path ([None] if no backup created)
59
@raises Gpx.Gpx_error on failure *)
60
+
val write_with_backup : ?validate:bool -> 'a Eio.Path.t -> Gpx.t -> 'a Eio.Path.t option
61
62
(** {2 Stream Operations}
63
···
68
@param ?validate Optional validation flag (default: false)
69
@return GPX document
70
@raises Gpx.Gpx_error on read or parse failure *)
71
+
val from_source : ?validate:bool -> _ Eio.Flow.source -> Gpx.t
72
73
(** Write GPX to Eio sink.
74
@param sink Output flow
75
@param gpx GPX document
76
@param ?validate Optional validation flag (default: false)
77
@raises Gpx.Gpx_error on write failure *)
78
+
val to_sink : ?validate:bool -> _ Eio.Flow.sink -> Gpx.t -> unit
79
80
(** Print GPX statistics to sink.
81
@param sink Output sink
82
@param gpx GPX document *)
83
+
val print_stats : _ Eio.Flow.sink -> Gpx.t -> unit
+27
-29
lib/gpx_eio/gpx_io.ml
+27
-29
lib/gpx_eio/gpx_io.ml
···
1
(** GPX Eio I/O operations *)
2
3
(** Read GPX from file path *)
4
-
let read_file ?(validate=false) ~fs path =
5
-
let content = Eio.Path.load Eio.Path.(fs / path) in
6
match Gpx.parse_string ~validate content with
7
| Ok gpx -> gpx
8
| Error err -> raise (Gpx.Gpx_error err)
9
10
(** Write GPX to file path *)
11
-
let write_file ?(validate=false) ~fs path gpx =
12
match Gpx.write_string ~validate gpx with
13
| Ok xml_string ->
14
-
Eio.Path.save ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) xml_string
15
| Error err -> raise (Gpx.Gpx_error err)
16
17
(** Read GPX from Eio source *)
···
29
| Error err -> raise (Gpx.Gpx_error err)
30
31
(** Check if file exists *)
32
-
let file_exists ~fs path =
33
-
try
34
-
let _stat = Eio.Path.stat ~follow:true Eio.Path.(fs / path) in
35
-
true
36
-
with
37
-
| _ -> false
38
39
(** Get file size *)
40
-
let file_size ~fs path =
41
try
42
-
let stat = Eio.Path.stat ~follow:true Eio.Path.(fs / path) in
43
Optint.Int63.to_int stat.size
44
with
45
| exn -> raise (Gpx.Gpx_error (Gpx.Error.io_error (Printexc.to_string exn)))
46
47
(** Create backup of existing file *)
48
-
let create_backup ~fs path =
49
-
if file_exists ~fs path then
50
-
let backup_path = path ^ ".backup" in
51
-
let content = Eio.Path.load Eio.Path.(fs / path) in
52
-
Eio.Path.save ~create:(`Or_truncate 0o644) Eio.Path.(fs / backup_path) content;
53
-
backup_path
54
else
55
-
""
56
57
(** Write GPX to file with automatic backup *)
58
-
let write_file_with_backup ?(validate=false) ~fs path gpx =
59
-
let backup_path = create_backup ~fs path in
60
try
61
-
write_file ~validate ~fs path gpx;
62
backup_path
63
with
64
| Gpx.Gpx_error _ as err ->
65
(* Try to restore backup if write failed *)
66
-
if backup_path <> "" && file_exists ~fs backup_path then (
67
-
try
68
-
let backup_content = Eio.Path.load Eio.Path.(fs / backup_path) in
69
-
Eio.Path.save ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) backup_content
70
-
with _ -> () (* Ignore restore errors *)
71
-
);
72
-
raise err
···
1
(** GPX Eio I/O operations *)
2
3
(** Read GPX from file path *)
4
+
let read_file ?(validate=false) path =
5
+
let content = Eio.Path.load path in
6
match Gpx.parse_string ~validate content with
7
| Ok gpx -> gpx
8
| Error err -> raise (Gpx.Gpx_error err)
9
10
(** Write GPX to file path *)
11
+
let write_file ?(validate=false) path gpx =
12
match Gpx.write_string ~validate gpx with
13
| Ok xml_string ->
14
+
Eio.Path.save ~create:(`Or_truncate 0o644) path xml_string
15
| Error err -> raise (Gpx.Gpx_error err)
16
17
(** Read GPX from Eio source *)
···
29
| Error err -> raise (Gpx.Gpx_error err)
30
31
(** Check if file exists *)
32
+
let file_exists = Eio.Path.is_file
33
34
(** Get file size *)
35
+
let file_size path =
36
try
37
+
let stat = Eio.Path.stat ~follow:true path in
38
Optint.Int63.to_int stat.size
39
with
40
| exn -> raise (Gpx.Gpx_error (Gpx.Error.io_error (Printexc.to_string exn)))
41
42
(** Create backup of existing file *)
43
+
let create_backup ((fs, inner_path) as path) =
44
+
if file_exists path then
45
+
let backup_path = inner_path ^ ".backup" in
46
+
let content = Eio.Path.load path in
47
+
Eio.Path.save ~create:(`Or_truncate 0o644) (fs, backup_path) content;
48
+
Some (fs, backup_path)
49
else
50
+
None
51
52
(** Write GPX to file with automatic backup *)
53
+
let write_file_with_backup ?(validate=false) path gpx =
54
+
let backup_path = create_backup path in
55
try
56
+
write_file ~validate path gpx;
57
backup_path
58
with
59
| Gpx.Gpx_error _ as err ->
60
(* Try to restore backup if write failed *)
61
+
match backup_path with
62
+
| Some backup_path ->
63
+
if file_exists backup_path then (
64
+
try
65
+
let backup_content = Eio.Path.load backup_path in
66
+
Eio.Path.save ~create:(`Or_truncate 0o644) path backup_content
67
+
with _ -> () (* Ignore restore errors *)
68
+
);
69
+
raise err
70
+
| _ -> raise err
+7
-13
lib/gpx_eio/gpx_io.mli
+7
-13
lib/gpx_eio/gpx_io.mli
···
8
(** {1 File Operations} *)
9
10
(** Read GPX from file path.
11
-
@param fs Filesystem capability
12
@param path File path to read
13
@param ?validate Optional validation flag (default: false)
14
@return GPX document or error *)
15
-
val read_file : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t
16
17
(** Write GPX to file path.
18
-
@param fs Filesystem capability
19
@param path File path to write
20
@param gpx GPX document to write
21
@param ?validate Optional validation flag (default: false)
22
@raises Gpx.Gpx_error on write failure *)
23
-
val write_file : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> unit
24
25
(** {1 Stream Operations}
26
···
41
(** {1 Utility Functions} *)
42
43
(** Check if file exists.
44
-
@param fs Filesystem capability
45
@param path File path to check
46
@return [true] if file exists and is readable *)
47
-
val file_exists : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> bool
48
49
(** Get file size.
50
-
@param fs Filesystem capability
51
@param path File path
52
@return File size in bytes *)
53
-
val file_size : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> int
54
55
(** Create backup of existing file.
56
-
@param fs Filesystem capability
57
@param path Original file path
58
@return Backup file path *)
59
-
val create_backup : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> string
60
61
(** Write GPX to file with automatic backup.
62
Creates a backup of existing file before writing new content.
63
-
@param fs Filesystem capability
64
@param path File path to write
65
@param gpx GPX document to write
66
@param ?validate Optional validation flag (default: false)
67
-
@return Backup file path (empty string if no backup needed) *)
68
-
val write_file_with_backup : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> string
···
8
(** {1 File Operations} *)
9
10
(** Read GPX from file path.
11
@param path File path to read
12
@param ?validate Optional validation flag (default: false)
13
@return GPX document or error *)
14
+
val read_file : ?validate:bool -> _ Eio.Path.t -> Gpx.t
15
16
(** Write GPX to file path.
17
@param path File path to write
18
@param gpx GPX document to write
19
@param ?validate Optional validation flag (default: false)
20
@raises Gpx.Gpx_error on write failure *)
21
+
val write_file : ?validate:bool -> _ Eio.Path.t -> Gpx.t -> unit
22
23
(** {1 Stream Operations}
24
···
39
(** {1 Utility Functions} *)
40
41
(** Check if file exists.
42
@param path File path to check
43
@return [true] if file exists and is readable *)
44
+
val file_exists : _ Eio.Path.t -> bool
45
46
(** Get file size.
47
@param path File path
48
@return File size in bytes *)
49
+
val file_size : _ Eio.Path.t -> int
50
51
(** Create backup of existing file.
52
@param path Original file path
53
@return Backup file path *)
54
+
val create_backup : 'a Eio.Path.t -> 'a Eio.Path.t option
55
56
(** Write GPX to file with automatic backup.
57
Creates a backup of existing file before writing new content.
58
@param path File path to write
59
@param gpx GPX document to write
60
@param ?validate Optional validation flag (default: false)
61
+
@return Backup file path ([None] if no backup needed) *)
62
+
val write_file_with_backup : ?validate:bool -> 'a Eio.Path.t -> Gpx.t -> 'a Eio.Path.t option
+7
-5
test/test_corpus_unix_eio.ml
+7
-5
test/test_corpus_unix_eio.ml
···
2
3
open Alcotest
4
5
let test_data_dir = "test/data"
6
7
let test_files = [
···
44
let fs = Eio.Stdenv.fs env in
45
let path = Filename.concat test_data_dir filename in
46
try
47
-
let gpx = Gpx_eio.read ~fs path in
48
let validation = Gpx.validate_gpx gpx in
49
check bool "GPX is valid" true validation.is_valid;
50
check bool "Has some content" true (
···
68
try
69
Eio_main.run @@ fun env ->
70
let fs = Eio.Stdenv.fs env in
71
-
Ok (Gpx_eio.read ~fs path)
72
with
73
| Gpx.Gpx_error err -> Error err
74
in
···
120
let fs = Eio.Stdenv.fs env in
121
let path = Filename.concat test_data_dir filename in
122
try
123
-
let gpx_original = Gpx_eio.read ~fs path in
124
(* Write to temporary string via GPX core *)
125
match Gpx.write_string gpx_original with
126
| Ok xml_string ->
···
171
(try
172
Eio_main.run @@ fun env ->
173
let fs = Eio.Stdenv.fs env in
174
-
let _ = Gpx_eio.read ~fs path in
175
failf "Eio should have failed to parse invalid.gpx"
176
with
177
| Gpx.Gpx_error _ ->
···
190
let start_eio = Sys.time () in
191
let _ = Eio_main.run @@ fun env ->
192
let fs = Eio.Stdenv.fs env in
193
-
try Some (Gpx_eio.read ~fs path)
194
with Gpx.Gpx_error _ -> None
195
in
196
let eio_time = Sys.time () -. start_eio in
···
2
3
open Alcotest
4
5
+
let ( / ) = Eio.Path. ( / )
6
+
7
let test_data_dir = "test/data"
8
9
let test_files = [
···
46
let fs = Eio.Stdenv.fs env in
47
let path = Filename.concat test_data_dir filename in
48
try
49
+
let gpx = Gpx_eio.read (fs / path) in
50
let validation = Gpx.validate_gpx gpx in
51
check bool "GPX is valid" true validation.is_valid;
52
check bool "Has some content" true (
···
70
try
71
Eio_main.run @@ fun env ->
72
let fs = Eio.Stdenv.fs env in
73
+
Ok (Gpx_eio.read (fs / path))
74
with
75
| Gpx.Gpx_error err -> Error err
76
in
···
122
let fs = Eio.Stdenv.fs env in
123
let path = Filename.concat test_data_dir filename in
124
try
125
+
let gpx_original = Gpx_eio.read (fs / path) in
126
(* Write to temporary string via GPX core *)
127
match Gpx.write_string gpx_original with
128
| Ok xml_string ->
···
173
(try
174
Eio_main.run @@ fun env ->
175
let fs = Eio.Stdenv.fs env in
176
+
let _ = Gpx_eio.read (fs / path) in
177
failf "Eio should have failed to parse invalid.gpx"
178
with
179
| Gpx.Gpx_error _ ->
···
192
let start_eio = Sys.time () in
193
let _ = Eio_main.run @@ fun env ->
194
let fs = Eio.Stdenv.fs env in
195
+
try Some (Gpx_eio.read (fs / path))
196
with Gpx.Gpx_error _ -> None
197
in
198
let eio_time = Sys.time () -. start_eio in