]> git.giacomolli.uk - onechat/blobdiff - journal.go
Name and order the ChatGPT login code
[onechat] / journal.go
index ef49c6e306959cbd751e690820393f23bbc92510..cda9375a622f9f7162b34d259fe0260e515d2b3d 100644 (file)
@@ -133,7 +133,9 @@ func (j *journal) Close() error {
 }
 
 // Scan gives each line of the day files from place on to add, with its
-// position, oldest day first. The zero position starts at the first line.
+// position, oldest day first. The zero position starts at the first line. A
+// crash during a write can leave a part of a line at the end of the last day
+// file. Its write never returned, so Scan cuts it off.
 func (j *journal) Scan(ctx context.Context, place position, add func([]byte, position) error) error {
        paths, err := filepath.Glob(filepath.Join(j.dir, "*.jsonl"))
        if err != nil {
@@ -141,7 +143,7 @@ func (j *journal) Scan(ctx context.Context, place position, add func([]byte, pos
        }
 
        slices.Sort(paths)
-       for _, path := range paths {
+       for n, path := range paths {
                day, err := dayNumber(strings.TrimSuffix(filepath.Base(path), ".jsonl"))
                if err != nil {
                        return err
@@ -156,7 +158,7 @@ func (j *journal) Scan(ctx context.Context, place position, add func([]byte, pos
                        offset = place.offset
                }
 
-               err = scanFile(ctx, path, day, offset, add)
+               err = scanFile(ctx, path, day, offset, n == len(paths)-1, add)
                if err != nil {
                        return err
                }
@@ -165,8 +167,9 @@ func (j *journal) Scan(ctx context.Context, place position, add func([]byte, pos
        return nil
 }
 
-// scanFile gives each line of the day file at path from offset on to add.
-func scanFile(ctx context.Context, path string, day uint32, offset uint64, add func([]byte, position) error) (err error) {
+// scanFile gives each line of the day file at path from offset on to add. In
+// the last day file, it cuts a part of a line at the end.
+func scanFile(ctx context.Context, path string, day uint32, offset uint64, last bool, add func([]byte, position) error) (err error) {
        file, err := os.Open(path)
        if err != nil {
                return fmt.Errorf("open day file: %w", err)
@@ -192,6 +195,10 @@ func scanFile(ctx context.Context, path string, day uint32, offset uint64, add f
                        return nil
                }
 
+               if errors.Is(err, io.EOF) && last {
+                       return cutFile(path, offset)
+               }
+
                if errors.Is(err, io.EOF) {
                        return fmt.Errorf("%s ends in an incomplete line at %d", filepath.Base(path), offset)
                }
@@ -215,6 +222,16 @@ func scanFile(ctx context.Context, path string, day uint32, offset uint64, add f
        }
 }
 
+// cutFile cuts the file at path to size bytes.
+func cutFile(path string, size uint64) error {
+       err := os.Truncate(path, int64(size))
+       if err != nil {
+               return fmt.Errorf("cut incomplete line: %w", err)
+       }
+
+       return nil
+}
+
 // dayNumber returns the day YYYY-MM-DD as the number YYYYMMDD.
 func dayNumber(day string) (uint32, error) {
        number, err := strconv.ParseUint(strings.ReplaceAll(day, "-", ""), 10, 32)