Skip to content

Commit 66dd901

Browse files
Copilotmseri
andcommitted
Move merge_entries_non_interactive to bibdedup CLI
Co-authored-by: mseri <[email protected]>
1 parent 8dd3e89 commit 66dd901

File tree

4 files changed

+36
-48
lines changed

4 files changed

+36
-48
lines changed

CHANGES.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
- bibfmt: added --quiet flag to suppress all output except errors.
44
- bibfmt: added --force flag to ignore parsing errors and output only
55
successfully parsed entries.
6-
- bibfmt: refactored deduplication functions - moved IO-dependent functions
7-
(deduplicate_entries, resolve_conflicts) from library to bibdedup CLI tool.
8-
The library now provides only pure functions (find_duplicate_groups,
9-
merge_entries_non_interactive) for programmatic use.
6+
- bibfmt: refactored deduplication functions - moved all deduplication logic
7+
(deduplicate_entries, resolve_conflicts, merge_entries_non_interactive) from
8+
library to bibdedup CLI tool. The library now provides only the core
9+
find_duplicate_groups function for programmatic use.
1010

1111
# 0.7.8 (2025-10-09)
1212

bibfmt/bin/bibdedup.ml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,37 @@ let resolve_conflicts duplicate_group =
100100

101101
{ base_entry with Bibtex.contents = merged_contents }
102102

103+
(* Merge entries non-interactively by keeping first occurrence of each field *)
104+
let merge_entries_non_interactive entries =
105+
if entries = [] then invalid_arg "merge_entries_non_interactive: empty list"
106+
else
107+
let base_entry = List.hd entries in
108+
let merged_fields = Hashtbl.create 16 in
109+
110+
(* Collect all fields, keeping first occurrence *)
111+
List.iter
112+
(fun entry ->
113+
List.iter
114+
(function
115+
| Bibtex.Field { name; value } ->
116+
let name_lower = String.lowercase_ascii name in
117+
if not (Hashtbl.mem merged_fields name_lower) then
118+
Hashtbl.replace merged_fields name_lower
119+
(name, Bibtex.string_of_field_value value)
120+
| Bibtex.EntryComment _ -> ())
121+
entry.Bibtex.contents)
122+
entries;
123+
124+
(* Build merged entry *)
125+
let merged_contents =
126+
Hashtbl.fold
127+
(fun _ (name, value) acc -> Bibtex.make_field name value :: acc)
128+
merged_fields []
129+
|> List.rev
130+
in
131+
132+
{ base_entry with Bibtex.contents = merged_contents }
133+
103134
(* Main deduplication function with IO *)
104135
let deduplicate_entries ?(keys = [ "title"; "author"; "year" ])
105136
?(interactive = true) entries =
@@ -128,7 +159,7 @@ let deduplicate_entries ?(keys = [ "title"; "author"; "year" ])
128159
List.map
129160
(fun group ->
130161
if interactive then resolve_conflicts group
131-
else Bibtex.merge_entries_non_interactive group.Bibtex.entries)
162+
else merge_entries_non_interactive group.Bibtex.entries)
132163
duplicate_groups
133164
in
134165

bibfmt/lib/bibtex.ml

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -861,35 +861,4 @@ let find_duplicate_groups ?(keys = [ "title"; "author"; "year" ]) entries =
861861
else None)
862862
grouped
863863

864-
(* Merge entries non-interactively by keeping first occurrence of each field *)
865-
let merge_entries_non_interactive entries =
866-
if entries = [] then invalid_arg "merge_entries_non_interactive: empty list"
867-
else
868-
let base_entry = List.hd entries in
869-
let merged_fields = Hashtbl.create 16 in
870-
871-
(* Collect all fields, keeping first occurrence *)
872-
List.iter
873-
(fun entry ->
874-
List.iter
875-
(function
876-
| Field { name; value } ->
877-
let name_lower = String.lowercase_ascii name in
878-
if not (Hashtbl.mem merged_fields name_lower) then
879-
Hashtbl.replace merged_fields name_lower
880-
(name, string_of_field_value value)
881-
| EntryComment _ -> ())
882-
entry.contents)
883-
entries;
884-
885-
(* Build merged entry *)
886-
let merged_contents =
887-
Hashtbl.fold
888-
(fun _ (name, value) acc -> make_field name value :: acc)
889-
merged_fields []
890-
|> List.rev
891-
in
892-
893-
{ base_entry with contents = merged_contents }
894-
895864

bibfmt/lib/bibtex.mli

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,18 +190,6 @@ val find_duplicate_groups :
190190
This is useful for inspecting duplicates before deciding how to handle them.
191191
Each group contains at least 2 entries that match on the specified keys. *)
192192

193-
val merge_entries_non_interactive : bibtex_entry list -> bibtex_entry
194-
(** [merge_entries_non_interactive entries] merges duplicate entries by keeping
195-
the first occurrence of each field.
196-
197-
@param entries List of duplicate entries to merge (must be non-empty)
198-
@return Merged entry with fields from the first entry taking precedence
199-
@raise Invalid_argument if the entries list is empty
200-
201-
This function does not prompt the user and simply takes the first value it
202-
encounters for each field. It's useful for batch processing or when you
203-
trust the ordering of your entries. *)
204-
205193
val string_of_field_value : field_value -> string
206194
(** [string_of_field_value fv] converts a field value to its string representation.
207195
@param fv The field value to convert

0 commit comments

Comments
 (0)