Select the types of activity you want to include in your feed.
Add update_col and delete_col to csvt
Typed column updates: decode → transform → re-encode a specific column by name. delete_col removes a column from header and row. 4 new tests (50 total).
···242242243243let get_col name t = Row.(obj Fun.id |> col name t ~enc:Fun.id |> finish)
244244245245+(* {1 Update support} *)
246246+247247+let find_col_by_name header name =
248248+ let name = String.trim name in
249249+ let n = Array.length header in
250250+ let rec go i =
251251+ if i >= n then -1
252252+ else if String.equal (String.trim header.(i)) name then i
253253+ else go (i + 1)
254254+ in
255255+ go 0
256256+257257+let update_col name codec f header row =
258258+ let idx = find_col_by_name header name in
259259+ if idx < 0 then Error (Missing_column name)
260260+ else if idx >= Array.length row then
261261+ Error
262262+ (Truncated_row { row = 0; expected = idx + 1; got = Array.length row })
263263+ else
264264+ let s = row.(idx) in
265265+ match decode_field codec s with
266266+ | Error msg -> Error (Bad_value { row = 0; column = name; value = s; msg })
267267+ | Ok v ->
268268+ let v' = f v in
269269+ let result = Array.copy row in
270270+ result.(idx) <- encode_field codec v';
271271+ Ok result
272272+273273+let delete_col name header row =
274274+ let idx = find_col_by_name header name in
275275+ if idx < 0 then (header, row)
276276+ else
277277+ let n = Array.length header in
278278+ let new_header =
279279+ Array.init (n - 1) (fun i ->
280280+ if i < idx then header.(i) else header.(i + 1))
281281+ in
282282+ let nr = Array.length row in
283283+ let new_row =
284284+ Array.init
285285+ (max 0 (nr - 1))
286286+ (fun i ->
287287+ if i < idx then row.(i) else if i + 1 < nr then row.(i + 1) else "")
288288+ in
289289+ (new_header, new_row)
290290+245291(* {1 Header resolution} *)
246292247293type header = string array
+20
lib/csvt.mli
···157157val decode_row : 'a resolved -> int -> string array -> ('a, error) result
158158(** [decode_row resolved row_num fields] decodes a single CSV row. *)
159159160160+(** {1:update Update} *)
161161+162162+val update_col :
163163+ string ->
164164+ 'a t ->
165165+ ('a -> 'a) ->
166166+ header ->
167167+ string array ->
168168+ (string array, error) result
169169+(** [update_col name codec f header row] decodes column [name] from [row] using
170170+ [codec], applies [f] to the typed value, re-encodes, and returns the updated
171171+ row. Other columns are unchanged. *)
172172+173173+val delete_col : string -> header -> string array -> header * string array
174174+(** [delete_col name header row] removes the column [name] from both the header
175175+ and the row. Returns the new header and row. If [name] is not found, returns
176176+ both unchanged. *)
177177+178178+(** {1:encode Encoding} *)
179179+160180val encode_header : 'a t -> header
161181(** [encode_header t] returns the CSV header for encoding. *)
162182
+41
test/test_csvt.ml
···672672 Alcotest.(check (list string)) "col_names" [ "score" ] (Csvt.col_names codec);
673673 Alcotest.(check int) "col_count" 1 (Csvt.col_count codec)
674674675675+let test_update_col_int () =
676676+ let header = [| "id"; "name"; "score" |] in
677677+ let row = [| "1"; "alice"; "95" |] in
678678+ match Csvt.update_col "score" Csvt.int (fun x -> x + 5) header row with
679679+ | Ok row' ->
680680+ Alcotest.(check string) "id unchanged" "1" row'.(0);
681681+ Alcotest.(check string) "name unchanged" "alice" row'.(1);
682682+ Alcotest.(check string) "score updated" "100" row'.(2)
683683+ | Error e -> Alcotest.failf "update_col: %s" (Csvt.error_to_string e)
684684+685685+let test_update_col_missing () =
686686+ let header = [| "id"; "name" |] in
687687+ let row = [| "1"; "alice" |] in
688688+ match Csvt.update_col "score" Csvt.int (fun x -> x + 5) header row with
689689+ | Ok _ -> Alcotest.fail "should fail on missing column"
690690+ | Error (Csvt.Missing_column "score") -> ()
691691+ | Error e -> Alcotest.failf "wrong error: %s" (Csvt.error_to_string e)
692692+693693+let test_delete_col () =
694694+ let header = [| "id"; "name"; "score" |] in
695695+ let row = [| "1"; "alice"; "95" |] in
696696+ let header', row' = Csvt.delete_col "name" header row in
697697+ Alcotest.(check int) "header len" 2 (Array.length header');
698698+ Alcotest.(check int) "row len" 2 (Array.length row');
699699+ Alcotest.(check string) "h0" "id" header'.(0);
700700+ Alcotest.(check string) "h1" "score" header'.(1);
701701+ Alcotest.(check string) "r0" "1" row'.(0);
702702+ Alcotest.(check string) "r1" "95" row'.(1)
703703+704704+let test_delete_col_missing () =
705705+ let header = [| "id"; "name" |] in
706706+ let row = [| "1"; "alice" |] in
707707+ let header', row' = Csvt.delete_col "score" header row in
708708+ Alcotest.(check int) "header unchanged" 2 (Array.length header');
709709+ Alcotest.(check int) "row unchanged" 2 (Array.length row')
710710+675711let suite =
676712 ( "csvt",
677713 [
···735771 Alcotest.test_case "get_col missing" `Quick test_get_col_missing;
736772 Alcotest.test_case "get_col encode" `Quick test_get_col_encode;
737773 Alcotest.test_case "get_col col_names" `Quick test_get_col_col_names;
774774+ (* Update *)
775775+ Alcotest.test_case "update_col int" `Quick test_update_col_int;
776776+ Alcotest.test_case "update_col missing" `Quick test_update_col_missing;
777777+ Alcotest.test_case "delete_col" `Quick test_delete_col;
778778+ Alcotest.test_case "delete_col missing" `Quick test_delete_col_missing;
738779 ] )