From 755dac2c2715505b743adc2b0d2e0c85e378c89f Mon Sep 17 00:00:00 2001 From: Patrick Ferris Date: Sat, 23 Aug 2025 10:20:36 +0100 Subject: [PATCH] Tidy Eio interface Instead of passing in an FS capability and a path, the interface now only takes an _ Eio.Path.t. --- bin/mlgpx_cli.ml | 9 ++++-- lib/gpx_eio/gpx_eio.ml | 6 ++-- lib/gpx_eio/gpx_eio.mli | 17 +++++------ lib/gpx_eio/gpx_io.ml | 56 +++++++++++++++++------------------- lib/gpx_eio/gpx_io.mli | 20 +++++-------- test/test_corpus_unix_eio.ml | 12 ++++---- 6 files changed, 57 insertions(+), 63 deletions(-) diff --git a/bin/mlgpx_cli.ml b/bin/mlgpx_cli.ml index 4735e26..58b81f5 100644 --- a/bin/mlgpx_cli.ml +++ b/bin/mlgpx_cli.ml @@ -3,6 +3,8 @@ open Cmdliner open Gpx +let ( / ) = Eio.Path.( / ) + (* Terminal and formatting setup *) let setup_fmt style_renderer = Fmt_tty.setup_std_outputs ?style_renderer (); @@ -67,7 +69,7 @@ let convert_waypoints_to_trackset input_file output_file track_name track_desc log_info "Reading GPX file: %a" (bold_style Fmt.string) input_file; (* Read input GPX *) - let gpx = Gpx_eio.read ~fs input_file in + let gpx = Gpx_eio.read (fs / input_file) in if verbose then log_info "Found %d waypoints and %d existing tracks" @@ -140,7 +142,7 @@ let convert_waypoints_to_trackset input_file output_file track_name track_desc log_info "Writing output to: %a" (bold_style Fmt.string) output_file; (* Write output GPX *) - Gpx_eio.write ~fs output_file output_gpx; + Gpx_eio.write (fs / output_file) output_gpx; if verbose then ( Fmt.pf Format.std_formatter "%a\n" (success_style Fmt.string) "Conversion completed successfully!"; @@ -215,7 +217,7 @@ let info_command input_file verbose style_renderer = if verbose then log_info "Analyzing GPX file: %a" (bold_style Fmt.string) input_file; - let gpx = Gpx_eio.read ~fs input_file in + let gpx = Gpx_eio.read (fs / input_file) in (* Header *) Fmt.pf Format.std_formatter "%a\n" (bold_style Fmt.string) "GPX File Information"; @@ -255,6 +257,7 @@ let info_command input_file verbose style_renderer = let duration_span = Ptime.diff stop_time start_time in match Ptime.Span.to_int_s duration_span with | Some seconds -> + let ( / ) = Int.div in let days = seconds / 86400 in let hours = (seconds mod 86400) / 3600 in let minutes = (seconds mod 3600) / 60 in diff --git a/lib/gpx_eio/gpx_eio.ml b/lib/gpx_eio/gpx_eio.ml index 43a98c9..b88f2c6 100644 --- a/lib/gpx_eio/gpx_eio.ml +++ b/lib/gpx_eio/gpx_eio.ml @@ -3,13 +3,13 @@ module IO = Gpx_io (** Read and parse GPX file *) -let read ?(validate=false) ~fs path = IO.read_file ~validate ~fs path +let read ?(validate=false) path = IO.read_file ~validate path (** Write GPX to file *) -let write ?(validate=false) ~fs path gpx = IO.write_file ~validate ~fs path gpx +let write ?(validate=false) path gpx = IO.write_file ~validate path gpx (** Write GPX to file with backup *) -let write_with_backup ?(validate=false) ~fs path gpx = IO.write_file_with_backup ~validate ~fs path gpx +let write_with_backup ?(validate=false) path gpx = IO.write_file_with_backup ~validate path gpx (** Read GPX from Eio source *) let from_source ?(validate=false) source = IO.read_source ~validate source diff --git a/lib/gpx_eio/gpx_eio.mli b/lib/gpx_eio/gpx_eio.mli index 51ac8b6..f568521 100644 --- a/lib/gpx_eio/gpx_eio.mli +++ b/lib/gpx_eio/gpx_eio.mli @@ -38,29 +38,26 @@ module IO = Gpx_io These functions provide simple file I/O with the filesystem from {!Eio.Stdenv.fs}. *) (** Read and parse GPX file. - @param fs Filesystem capability @param path File path to read @param ?validate Optional validation flag (default: false) @return GPX document @raises Gpx.Gpx_error on read or parse failure *) -val read : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t +val read : ?validate:bool -> _ Eio.Path.t -> Gpx.t (** Write GPX to file. - @param fs Filesystem capability @param path File path to write @param gpx GPX document to write @param ?validate Optional validation flag (default: false) @raises Gpx.Gpx_error on write failure *) -val write : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> unit +val write : ?validate:bool -> _ Eio.Path.t -> Gpx.t -> unit (** Write GPX to file with automatic backup. - @param fs Filesystem capability @param path File path to write @param gpx GPX document to write @param ?validate Optional validation flag (default: false) - @return Backup file path (empty if no backup created) + @return Backup file path ([None] if no backup created) @raises Gpx.Gpx_error on failure *) -val write_with_backup : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> string +val write_with_backup : ?validate:bool -> 'a Eio.Path.t -> Gpx.t -> 'a Eio.Path.t option (** {2 Stream Operations} @@ -71,16 +68,16 @@ val write_with_backup : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> st @param ?validate Optional validation flag (default: false) @return GPX document @raises Gpx.Gpx_error on read or parse failure *) -val from_source : ?validate:bool -> [> Eio.Flow.source_ty ] Eio.Resource.t -> Gpx.t +val from_source : ?validate:bool -> _ Eio.Flow.source -> Gpx.t (** Write GPX to Eio sink. @param sink Output flow @param gpx GPX document @param ?validate Optional validation flag (default: false) @raises Gpx.Gpx_error on write failure *) -val to_sink : ?validate:bool -> [> Eio.Flow.sink_ty ] Eio.Resource.t -> Gpx.t -> unit +val to_sink : ?validate:bool -> _ Eio.Flow.sink -> Gpx.t -> unit (** Print GPX statistics to sink. @param sink Output sink @param gpx GPX document *) -val print_stats : [> Eio.Flow.sink_ty ] Eio.Resource.t -> Gpx.t -> unit +val print_stats : _ Eio.Flow.sink -> Gpx.t -> unit diff --git a/lib/gpx_eio/gpx_io.ml b/lib/gpx_eio/gpx_io.ml index eab9309..dd3064c 100644 --- a/lib/gpx_eio/gpx_io.ml +++ b/lib/gpx_eio/gpx_io.ml @@ -1,17 +1,17 @@ (** GPX Eio I/O operations *) (** Read GPX from file path *) -let read_file ?(validate=false) ~fs path = - let content = Eio.Path.load Eio.Path.(fs / path) in +let read_file ?(validate=false) path = + let content = Eio.Path.load path in match Gpx.parse_string ~validate content with | Ok gpx -> gpx | Error err -> raise (Gpx.Gpx_error err) (** Write GPX to file path *) -let write_file ?(validate=false) ~fs path gpx = +let write_file ?(validate=false) path gpx = match Gpx.write_string ~validate gpx with | Ok xml_string -> - Eio.Path.save ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) xml_string + Eio.Path.save ~create:(`Or_truncate 0o644) path xml_string | Error err -> raise (Gpx.Gpx_error err) (** Read GPX from Eio source *) @@ -29,44 +29,42 @@ let write_sink ?(validate=false) sink gpx = | Error err -> raise (Gpx.Gpx_error err) (** Check if file exists *) -let file_exists ~fs path = - try - let _stat = Eio.Path.stat ~follow:true Eio.Path.(fs / path) in - true - with - | _ -> false +let file_exists = Eio.Path.is_file (** Get file size *) -let file_size ~fs path = +let file_size path = try - let stat = Eio.Path.stat ~follow:true Eio.Path.(fs / path) in + let stat = Eio.Path.stat ~follow:true path in Optint.Int63.to_int stat.size with | exn -> raise (Gpx.Gpx_error (Gpx.Error.io_error (Printexc.to_string exn))) (** Create backup of existing file *) -let create_backup ~fs path = - if file_exists ~fs path then - let backup_path = path ^ ".backup" in - let content = Eio.Path.load Eio.Path.(fs / path) in - Eio.Path.save ~create:(`Or_truncate 0o644) Eio.Path.(fs / backup_path) content; - backup_path +let create_backup ((fs, inner_path) as path) = + if file_exists path then + let backup_path = inner_path ^ ".backup" in + let content = Eio.Path.load path in + Eio.Path.save ~create:(`Or_truncate 0o644) (fs, backup_path) content; + Some (fs, backup_path) else - "" + None (** Write GPX to file with automatic backup *) -let write_file_with_backup ?(validate=false) ~fs path gpx = - let backup_path = create_backup ~fs path in +let write_file_with_backup ?(validate=false) path gpx = + let backup_path = create_backup path in try - write_file ~validate ~fs path gpx; + write_file ~validate path gpx; backup_path with | Gpx.Gpx_error _ as err -> (* Try to restore backup if write failed *) - if backup_path <> "" && file_exists ~fs backup_path then ( - try - let backup_content = Eio.Path.load Eio.Path.(fs / backup_path) in - Eio.Path.save ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) backup_content - with _ -> () (* Ignore restore errors *) - ); - raise err + match backup_path with + | Some backup_path -> + if file_exists backup_path then ( + try + let backup_content = Eio.Path.load backup_path in + Eio.Path.save ~create:(`Or_truncate 0o644) path backup_content + with _ -> () (* Ignore restore errors *) + ); + raise err + | _ -> raise err diff --git a/lib/gpx_eio/gpx_io.mli b/lib/gpx_eio/gpx_io.mli index cff8695..4b5072a 100644 --- a/lib/gpx_eio/gpx_io.mli +++ b/lib/gpx_eio/gpx_io.mli @@ -8,19 +8,17 @@ (** {1 File Operations} *) (** Read GPX from file path. - @param fs Filesystem capability @param path File path to read @param ?validate Optional validation flag (default: false) @return GPX document or error *) -val read_file : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t +val read_file : ?validate:bool -> _ Eio.Path.t -> Gpx.t (** Write GPX to file path. - @param fs Filesystem capability @param path File path to write @param gpx GPX document to write @param ?validate Optional validation flag (default: false) @raises Gpx.Gpx_error on write failure *) -val write_file : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> unit +val write_file : ?validate:bool -> _ Eio.Path.t -> Gpx.t -> unit (** {1 Stream Operations} @@ -41,28 +39,24 @@ val write_sink : ?validate:bool -> [> Eio.Flow.sink_ty ] Eio.Resource.t -> Gpx.t (** {1 Utility Functions} *) (** Check if file exists. - @param fs Filesystem capability @param path File path to check @return [true] if file exists and is readable *) -val file_exists : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> bool +val file_exists : _ Eio.Path.t -> bool (** Get file size. - @param fs Filesystem capability @param path File path @return File size in bytes *) -val file_size : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> int +val file_size : _ Eio.Path.t -> int (** Create backup of existing file. - @param fs Filesystem capability @param path Original file path @return Backup file path *) -val create_backup : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> string +val create_backup : 'a Eio.Path.t -> 'a Eio.Path.t option (** Write GPX to file with automatic backup. Creates a backup of existing file before writing new content. - @param fs Filesystem capability @param path File path to write @param gpx GPX document to write @param ?validate Optional validation flag (default: false) - @return Backup file path (empty string if no backup needed) *) -val write_file_with_backup : ?validate:bool -> fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> string -> Gpx.t -> string + @return Backup file path ([None] if no backup needed) *) +val write_file_with_backup : ?validate:bool -> 'a Eio.Path.t -> Gpx.t -> 'a Eio.Path.t option diff --git a/test/test_corpus_unix_eio.ml b/test/test_corpus_unix_eio.ml index 07e0ad4..140b720 100644 --- a/test/test_corpus_unix_eio.ml +++ b/test/test_corpus_unix_eio.ml @@ -2,6 +2,8 @@ open Alcotest +let ( / ) = Eio.Path. ( / ) + let test_data_dir = "test/data" let test_files = [ @@ -44,7 +46,7 @@ let test_eio_parsing filename () = let fs = Eio.Stdenv.fs env in let path = Filename.concat test_data_dir filename in try - let gpx = Gpx_eio.read ~fs path in + let gpx = Gpx_eio.read (fs / path) in let validation = Gpx.validate_gpx gpx in check bool "GPX is valid" true validation.is_valid; check bool "Has some content" true ( @@ -68,7 +70,7 @@ let test_unix_eio_equivalence filename () = try Eio_main.run @@ fun env -> let fs = Eio.Stdenv.fs env in - Ok (Gpx_eio.read ~fs path) + Ok (Gpx_eio.read (fs / path)) with | Gpx.Gpx_error err -> Error err in @@ -120,7 +122,7 @@ let test_eio_round_trip filename () = let fs = Eio.Stdenv.fs env in let path = Filename.concat test_data_dir filename in try - let gpx_original = Gpx_eio.read ~fs path in + let gpx_original = Gpx_eio.read (fs / path) in (* Write to temporary string via GPX core *) match Gpx.write_string gpx_original with | Ok xml_string -> @@ -171,7 +173,7 @@ let test_error_handling () = (try Eio_main.run @@ fun env -> let fs = Eio.Stdenv.fs env in - let _ = Gpx_eio.read ~fs path in + let _ = Gpx_eio.read (fs / path) in failf "Eio should have failed to parse invalid.gpx" with | Gpx.Gpx_error _ -> @@ -190,7 +192,7 @@ let test_performance_comparison filename () = let start_eio = Sys.time () in let _ = Eio_main.run @@ fun env -> let fs = Eio.Stdenv.fs env in - try Some (Gpx_eio.read ~fs path) + try Some (Gpx_eio.read (fs / path)) with Gpx.Gpx_error _ -> None in let eio_time = Sys.time () -. start_eio in -- 2.50.1