this repo has no description

refactor: deduplicate functions between platform modules and core

Move Statistics to Core:
- Add Doc.stats type and function (comprehensive GPX statistics)
- Add Doc.pp_stats for pretty-printing statistics using Format
- Remove duplicate stats implementations from Gpx_eio and Gpx_unix

Remove Function Duplication:
- Remove redundant accessor functions (waypoint_coords, track_coords, route_coords)
- Remove duplicate constructor functions (make_track_from_coords, make_route_from_coords)
- Remove duplicate count_points implementations
- Users should call core functions directly (Waypoint.to_floats, Track.to_coords, etc.)

Simplify Platform APIs:
- Platform modules now only contain I/O operations and print_stats
- Gpx_eio.print_stats and Gpx_unix.print_stats use Doc.pp_stats
- Removed unused Result binding operators
- Cleaner, more focused platform-specific functionality

This eliminates code duplication while keeping I/O operations
appropriately separated in platform-specific modules.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+14 -241
+8 -12
lib/gpx/doc.ml
··· 149 149 has_time = has_time t; 150 150 } 151 151 152 + (** Pretty print statistics *) 153 + let pp_stats ppf t = 154 + let s = stats t in 155 + Format.fprintf ppf "@[<v>GPX Statistics:@, Waypoints: %d@, Routes: %d@, Tracks: %d@, Total points: %d@, Has elevation data: %s@, Has time data: %s@]" 156 + s.waypoint_count s.route_count s.track_count s.total_points 157 + (if s.has_elevation then "yes" else "no") 158 + (if s.has_time then "yes" else "no") 159 + 152 160 (** {2 Comparison and Utilities} *) 153 161 154 162 (** Compare documents *) ··· 176 184 t.version t.creator 177 185 stats.waypoint_count stats.route_count stats.track_count stats.total_points 178 186 179 - (** Print document statistics *) 180 - let print_stats t = 181 - let stats = stats t in 182 - Printf.printf "GPX Statistics:\n"; 183 - Printf.printf " Version: %s\n" t.version; 184 - Printf.printf " Creator: %s\n" t.creator; 185 - Printf.printf " Waypoints: %d\n" stats.waypoint_count; 186 - Printf.printf " Routes: %d\n" stats.route_count; 187 - Printf.printf " Tracks: %d\n" stats.track_count; 188 - Printf.printf " Total points: %d\n" stats.total_points; 189 - Printf.printf " Has elevation data: %s\n" (if stats.has_elevation then "yes" else "no"); 190 - Printf.printf " Has time data: %s\n" (if stats.has_time then "yes" else "no")
+4 -4
lib/gpx/doc.mli
··· 113 113 (** Get document statistics *) 114 114 val stats : t -> stats 115 115 116 + (** Pretty print statistics *) 117 + val pp_stats : Format.formatter -> t -> unit 118 + 116 119 (** {2 Comparison and Utilities} *) 117 120 118 121 (** Compare documents *) ··· 122 125 val equal : t -> t -> bool 123 126 124 127 (** Pretty print document *) 125 - val pp : Format.formatter -> t -> unit 126 - 127 - (** Print document statistics to stdout *) 128 - val print_stats : t -> unit 128 + val pp : Format.formatter -> t -> unit
+1 -57
lib/gpx_eio/gpx_eio.ml
··· 28 28 { wpt with name; desc } 29 29 | (Error e, _) | (_, Error e) -> failwith ("Invalid coordinate: " ^ e) 30 30 31 - (** Create simple track from coordinate list *) 32 - let make_track_from_coords ~fs:_ ~name coords = 33 - Gpx.Track.make_from_coords ~name coords 34 - 35 - (** Create simple route from coordinate list *) 36 - let make_route_from_coords ~fs:_ ~name coords = 37 - Gpx.Route.make_from_coords ~name coords 38 - 39 - (** Extract coordinates from waypoints *) 40 - let waypoint_coords wpt = Gpx.Waypoint.to_floats wpt 41 - 42 - (** Extract coordinates from track *) 43 - let track_coords trk = Gpx.Track.to_coords trk 44 - 45 - (** Extract coordinates from route *) 46 - let route_coords rte = Gpx.Route.to_coords rte 47 - 48 - (** Count total points in GPX *) 49 - let count_points gpx = 50 - let waypoints = Gpx.Doc.waypoints gpx in 51 - let routes = Gpx.Doc.routes gpx in 52 - let tracks = Gpx.Doc.tracks gpx in 53 - List.length waypoints + 54 - List.fold_left (fun acc r -> acc + List.length (Gpx.Route.points r)) 0 routes + 55 - List.fold_left (fun acc t -> acc + Gpx.Track.point_count t) 0 tracks 56 - 57 - (** Get GPX statistics *) 58 - type gpx_stats = { 59 - waypoint_count : int; 60 - route_count : int; 61 - track_count : int; 62 - total_points : int; 63 - has_elevation : bool; 64 - has_time : bool; 65 - } 66 - 67 - let stats gpx = 68 - let waypoints = Gpx.Doc.waypoints gpx in 69 - let routes = Gpx.Doc.routes gpx in 70 - let tracks = Gpx.Doc.tracks gpx in 71 - { 72 - waypoint_count = List.length waypoints; 73 - route_count = List.length routes; 74 - track_count = List.length tracks; 75 - total_points = count_points gpx; 76 - has_elevation = List.exists (fun w -> Gpx.Waypoint.elevation w <> None) waypoints; 77 - has_time = List.exists (fun w -> Gpx.Waypoint.time w <> None) waypoints; 78 - } 79 - 80 31 (** Pretty print GPX statistics *) 81 32 let print_stats gpx = 82 - let stats = stats gpx in 83 - Printf.printf "GPX Statistics:\\n"; 84 - Printf.printf " Waypoints: %d\\n" stats.waypoint_count; 85 - Printf.printf " Routes: %d\\n" stats.route_count; 86 - Printf.printf " Tracks: %d\\n" stats.track_count; 87 - Printf.printf " Total Points: %d\\n" stats.total_points; 88 - Printf.printf " Has Elevation: %b\\n" stats.has_elevation; 89 - Printf.printf " Has Time: %b\\n" stats.has_time 33 + Format.printf "%a@." Gpx.Doc.pp_stats gpx
-51
lib/gpx_eio/gpx_eio.mli
··· 93 93 @raises Gpx.Gpx_error on invalid coordinates *) 94 94 val make_waypoint : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> lat:float -> lon:float -> ?name:string -> ?desc:string -> unit -> Gpx.Waypoint.t 95 95 96 - (** Create track from coordinate list. 97 - @param fs Filesystem capability (unused, for API consistency) 98 - @param name Track name 99 - @param coords List of (latitude, longitude) pairs 100 - @return Track with single segment 101 - @raises Gpx.Gpx_error on invalid coordinates *) 102 - val make_track_from_coords : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> name:string -> (float * float) list -> Gpx.Track.t 103 - 104 - (** Create route from coordinate list. 105 - @param fs Filesystem capability (unused, for API consistency) 106 - @param name Route name 107 - @param coords List of (latitude, longitude) pairs 108 - @return Route 109 - @raises Gpx.Gpx_error on invalid coordinates *) 110 - val make_route_from_coords : fs:[> Eio.Fs.dir_ty ] Eio.Path.t -> name:string -> (float * float) list -> Gpx.Route.t 111 - 112 - (** Extract coordinates from waypoint. 113 - @param wpt Waypoint data 114 - @return (latitude, longitude) as floats *) 115 - val waypoint_coords : Gpx.Waypoint.t -> float * float 116 - 117 - (** Extract coordinates from track. 118 - @param track Track 119 - @return List of (latitude, longitude) pairs *) 120 - val track_coords : Gpx.Track.t -> (float * float) list 121 - 122 - (** Extract coordinates from route. 123 - @param route Route 124 - @return List of (latitude, longitude) pairs *) 125 - val route_coords : Gpx.Route.t -> (float * float) list 126 - 127 - (** Count total points in GPX document. 128 - @param gpx GPX document 129 - @return Total number of waypoints, route points, and track points *) 130 - val count_points : Gpx.t -> int 131 - 132 - (** GPX statistics record *) 133 - type gpx_stats = { 134 - waypoint_count : int; (** Number of waypoints *) 135 - route_count : int; (** Number of routes *) 136 - track_count : int; (** Number of tracks *) 137 - total_points : int; (** Total geographic points *) 138 - has_elevation : bool; (** Document contains elevation data *) 139 - has_time : bool; (** Document contains time data *) 140 - } 141 - 142 - (** Get GPX document statistics. 143 - @param gpx GPX document 144 - @return Statistics summary *) 145 - val stats : Gpx.t -> gpx_stats 146 - 147 96 (** Print GPX statistics to stdout. 148 97 @param gpx GPX document *) 149 98 val print_stats : Gpx.t -> unit
+1 -86
lib/gpx_unix/gpx_unix.ml
··· 1 1 (** High-level Unix API for GPX operations *) 2 2 3 - (** Result binding operators *) 4 - let (let*) = Result.bind 5 - 6 3 (* Re-export IO module *) 7 4 module IO = Gpx_io 8 5 ··· 41 38 Ok wpt 42 39 | (Error e, _) | (_, Error e) -> Error (Gpx.Error.invalid_coordinate e) 43 40 44 - (** Create simple track from coordinate list *) 45 - let make_track_from_coords ~name coords = 46 - let make_trkpt (lat, lon) = 47 - match (Coordinate.latitude lat, Coordinate.longitude lon) with 48 - | (Ok lat, Ok lon) -> Ok (Waypoint.make lat lon) 49 - | (Error e, _) | (_, Error e) -> Error (Gpx.Error.invalid_coordinate e) 50 - in 51 - let rec convert_coords acc = function 52 - | [] -> Ok (List.rev acc) 53 - | coord :: rest -> 54 - match make_trkpt coord with 55 - | Ok trkpt -> convert_coords (trkpt :: acc) rest 56 - | Error e -> Error e 57 - in 58 - let* _trkpts = convert_coords [] coords in 59 - Ok (Track.make_from_coords ~name coords) 60 - 61 - (** Create simple route from coordinate list *) 62 - let make_route_from_coords ~name coords = 63 - let make_rtept (lat, lon) = 64 - match (Coordinate.latitude lat, Coordinate.longitude lon) with 65 - | (Ok lat, Ok lon) -> Ok (Waypoint.make lat lon) 66 - | (Error e, _) | (_, Error e) -> Error (Gpx.Error.invalid_coordinate e) 67 - in 68 - let rec convert_coords acc = function 69 - | [] -> Ok (List.rev acc) 70 - | coord :: rest -> 71 - match make_rtept coord with 72 - | Ok rtept -> convert_coords (rtept :: acc) rest 73 - | Error e -> Error e 74 - in 75 - let* _rtepts = convert_coords [] coords in 76 - Ok (Route.make_from_coords ~name coords) 77 - 78 - (** Extract coordinates from waypoints *) 79 - let waypoint_coords wpt = Waypoint.to_floats wpt 80 - 81 - (** Extract coordinates from track *) 82 - let track_coords track = Track.to_coords track 83 - 84 - (** Extract coordinates from route *) 85 - let route_coords route = Route.to_coords route 86 - 87 - (** Count total points in GPX *) 88 - let count_points gpx = 89 - let waypoints = Doc.waypoints gpx in 90 - let routes = Doc.routes gpx in 91 - let tracks = Doc.tracks gpx in 92 - List.length waypoints + 93 - List.fold_left (fun acc r -> acc + List.length (Route.points r)) 0 routes + 94 - List.fold_left (fun acc t -> acc + Track.point_count t) 0 tracks 95 - 96 - (** Get GPX statistics *) 97 - type gpx_stats = { 98 - waypoint_count : int; 99 - route_count : int; 100 - track_count : int; 101 - total_points : int; 102 - has_elevation : bool; 103 - has_time : bool; 104 - } 105 - 106 - let stats gpx = 107 - let waypoints = Doc.waypoints gpx in 108 - let routes = Doc.routes gpx in 109 - let tracks = Doc.tracks gpx in 110 - { 111 - waypoint_count = List.length waypoints; 112 - route_count = List.length routes; 113 - track_count = List.length tracks; 114 - total_points = count_points gpx; 115 - has_elevation = List.exists (fun w -> Waypoint.elevation w <> None) waypoints; 116 - has_time = List.exists (fun w -> Waypoint.time w <> None) waypoints; 117 - } 118 - 119 41 (** Pretty print GPX statistics *) 120 42 let print_stats gpx = 121 - let stats = stats gpx in 122 - Printf.printf "GPX Statistics:\n"; 123 - Printf.printf " Waypoints: %d\n" stats.waypoint_count; 124 - Printf.printf " Routes: %d\n" stats.route_count; 125 - Printf.printf " Tracks: %d\n" stats.track_count; 126 - Printf.printf " Total points: %d\n" stats.total_points; 127 - Printf.printf " Has elevation data: %s\n" (if stats.has_elevation then "yes" else "no"); 128 - Printf.printf " Has time data: %s\n" (if stats.has_time then "yes" else "no") 43 + Format.printf "%a@." Doc.pp_stats gpx
-31
lib/gpx_unix/gpx_unix.mli
··· 32 32 (** Create simple waypoint *) 33 33 val make_waypoint : lat:float -> lon:float -> ?name:string -> ?desc:string -> unit -> (Waypoint.t, error) result 34 34 35 - (** Create simple track from coordinate list *) 36 - val make_track_from_coords : name:string -> (float * float) list -> (Track.t, error) result 37 - 38 - (** Create simple route from coordinate list *) 39 - val make_route_from_coords : name:string -> (float * float) list -> (Route.t, error) result 40 - 41 - (** Extract coordinates from waypoints *) 42 - val waypoint_coords : Waypoint.t -> float * float 43 - 44 - (** Extract coordinates from track *) 45 - val track_coords : Track.t -> (float * float) list 46 - 47 - (** Extract coordinates from route *) 48 - val route_coords : Route.t -> (float * float) list 49 - 50 - (** Count total points in GPX *) 51 - val count_points : t -> int 52 - 53 - (** GPX statistics *) 54 - type gpx_stats = { 55 - waypoint_count : int; 56 - route_count : int; 57 - track_count : int; 58 - total_points : int; 59 - has_elevation : bool; 60 - has_time : bool; 61 - } 62 - 63 - (** Get GPX statistics *) 64 - val stats : t -> gpx_stats 65 - 66 35 (** Pretty print GPX statistics *) 67 36 val print_stats : t -> unit