11package cldf
22
33import (
4+ "bufio"
45 "fmt"
6+ "gocldf/internal/pathutil"
7+ "io"
58 "os"
9+ "regexp"
610 "slices"
711 "strings"
812
@@ -18,7 +22,8 @@ type Source struct {
1822func NewSource (entry * bibtex.BibEntry ) * Source {
1923 fields := make (map [string ]string )
2024 for k , v := range entry .Fields {
21- fields [k ] = v .String ()
25+ // We reverse the temporary replacement for @ to appease the BibTeX parser.
26+ fields [k ] = strings .ReplaceAll (v .String (), "�" , "@" )
2227 }
2328 return & Source {
2429 Id : entry .CiteName ,
@@ -32,20 +37,54 @@ type Sources struct {
3237 FieldNames []string
3338}
3439
35- func NewSources (p string ) (* Sources , error ) {
36- f , err := os .Open (p )
40+ func normalizeBibtex (r io.Reader ) (io.Reader , error ) {
41+ var res []string
42+ comment := regexp .MustCompile ("^\\ s*comment\\ s*=" )
43+ atAtStart := regexp .MustCompile ("^\\ s*@" )
44+ scanner := bufio .NewScanner (r )
45+ for scanner .Scan () {
46+ line := scanner .Text ()
47+ if comment .MatchString (line ) {
48+ // For some reason "comment" seems to be forbidden as field name.
49+ line = strings .Replace (line , "comment" , "comments" , 1 )
50+ }
51+ if ! atAtStart .MatchString (line ) {
52+ line = strings .ReplaceAll (line , "@" , "�" )
53+ }
54+ res = append (res , line )
55+ }
56+
57+ if err := scanner .Err (); err != nil {
58+ return nil , err
59+ }
60+ return strings .NewReader (strings .Join (res , "\n " )), nil
61+ }
62+
63+ func NewSources (p string ) (sources * Sources , err error ) {
64+ f , err := pathutil .Reader (p )
3765 if err != nil {
3866 return nil , err
3967 }
40- entries , err := bibtex .Parse (f )
68+ defer func (file any ) {
69+ switch file .(type ) {
70+ case * os.File :
71+ err = file .(* os.File ).Close ()
72+ }
73+ }(f )
74+
75+ r , err := normalizeBibtex (f .(io.Reader ))
4176 if err != nil {
4277 return nil , err
4378 }
79+ entries , err := bibtex .Parse (r )
80+ if err != nil {
81+ return nil , fmt .Errorf ("error parsing %v: %w" , p , err )
82+ }
4483 res := make ([]* Source , len (entries .Entries ))
45- fields := []string {}
84+ var fields []string
4685 for i , entry := range entries .Entries {
4786 res [i ] = NewSource (entry )
48- for name , _ := range entry .Fields {
87+ for name := range entry .Fields {
4988 if ! slices .Contains (fields , name ) {
5089 fields = append (fields , name )
5190 }
@@ -56,11 +95,14 @@ func NewSources(p string) (*Sources, error) {
5695}
5796
5897func (s * Sources ) SqlCreate () string {
59- res := []string {}
98+ var res []string
6099 res = append (res , "CREATE TABLE IF NOT EXISTS `SourceTable` (" )
61100 res = append (res , "\t `id`\t TEXT," )
62101 res = append (res , "\t `genre`\t TEXT," )
63102 for _ , field := range s .FieldNames {
103+ if field == "type" || field == "id" {
104+ field += "_"
105+ }
64106 res = append (res , fmt .Sprintf ("\t `%s`\t TEXT," , field ))
65107 }
66108 res = append (res , "\t PRIMARY KEY(`id`)" )
@@ -80,6 +122,9 @@ func (s *Sources) itemsToSql() (rows [][]any, colNames []string, err error) {
80122 rows [i ][1 ] = item .Type
81123
82124 for j , field := range s .FieldNames {
125+ if field == "type" || field == "id" {
126+ field += "_"
127+ }
83128 if i == 0 {
84129 colNames = append (colNames , field )
85130 }
0 commit comments