package models import ( "bytes" "encoding/binary" "encoding/json" "fmt" "io" "time" ) // OplogEntry represents a single entry in the action log type OplogEntry struct { // ID is a monotonically increasing entry ID ID uint64 // Timestamp when the operation was performed Timestamp time.Time // Operation type (e.g., "save", "switch", "new", "sync") Operation string // Description of the operation Description string // StateBefore captures the state before the operation StateBefore *RepositoryState // StateAfter captures the state after the operation StateAfter *RepositoryState // Metadata contains operation-specific data Metadata map[string]string } // RepositoryState captures the state of the repository at a point in time type RepositoryState struct { // Refs maps reference names to their SHA-1 hashes Refs map[string]string // CurrentWorkstream is the active workstream name CurrentWorkstream string // WorkingTreeHash is the hash of the current working tree snapshot WorkingTreeHash string // IndexHash is the hash of the staging area IndexHash string } // Serialize converts an OplogEntry to binary format func (e *OplogEntry) Serialize() ([]byte, error) { buf := new(bytes.Buffer) // Write entry ID (8 bytes) if err := binary.Write(buf, binary.LittleEndian, e.ID); err != nil { return nil, fmt.Errorf("failed to write ID: %w", err) } // Write timestamp (8 bytes, Unix nano) timestamp := e.Timestamp.UnixNano() if err := binary.Write(buf, binary.LittleEndian, timestamp); err != nil { return nil, fmt.Errorf("failed to write timestamp: %w", err) } // Serialize the rest as JSON for flexibility payload := struct { Operation string `json:"operation"` Description string `json:"description"` StateBefore *RepositoryState `json:"state_before"` StateAfter *RepositoryState `json:"state_after"` Metadata map[string]string `json:"metadata"` }{ Operation: e.Operation, Description: e.Description, StateBefore: e.StateBefore, StateAfter: e.StateAfter, Metadata: e.Metadata, } jsonData, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("failed to marshal JSON: %w", err) } // Write JSON length (4 bytes) jsonLen := uint32(len(jsonData)) if err := binary.Write(buf, binary.LittleEndian, jsonLen); err != nil { return nil, fmt.Errorf("failed to write JSON length: %w", err) } // Write JSON data if _, err := buf.Write(jsonData); err != nil { return nil, fmt.Errorf("failed to write JSON data: %w", err) } return buf.Bytes(), nil } // Deserialize converts binary data back to an OplogEntry func DeserializeOplogEntry(data []byte) (*OplogEntry, error) { buf := bytes.NewReader(data) entry := &OplogEntry{} // Read entry ID (8 bytes) if err := binary.Read(buf, binary.LittleEndian, &entry.ID); err != nil { return nil, fmt.Errorf("failed to read ID: %w", err) } // Read timestamp (8 bytes) var timestamp int64 if err := binary.Read(buf, binary.LittleEndian, ×tamp); err != nil { return nil, fmt.Errorf("failed to read timestamp: %w", err) } entry.Timestamp = time.Unix(0, timestamp) // Read JSON length (4 bytes) var jsonLen uint32 if err := binary.Read(buf, binary.LittleEndian, &jsonLen); err != nil { return nil, fmt.Errorf("failed to read JSON length: %w", err) } // Read JSON data jsonData := make([]byte, jsonLen) if _, err := io.ReadFull(buf, jsonData); err != nil { return nil, fmt.Errorf("failed to read JSON data: %w", err) } // Unmarshal JSON payload := struct { Operation string `json:"operation"` Description string `json:"description"` StateBefore *RepositoryState `json:"state_before"` StateAfter *RepositoryState `json:"state_after"` Metadata map[string]string `json:"metadata"` }{} if err := json.Unmarshal(jsonData, &payload); err != nil { return nil, fmt.Errorf("failed to unmarshal JSON: %w", err) } entry.Operation = payload.Operation entry.Description = payload.Description entry.StateBefore = payload.StateBefore entry.StateAfter = payload.StateAfter entry.Metadata = payload.Metadata return entry, nil } // NewOplogEntry creates a new oplog entry func NewOplogEntry(id uint64, operation, description string, before, after *RepositoryState) *OplogEntry { return &OplogEntry{ ID: id, Timestamp: time.Now(), Operation: operation, Description: description, StateBefore: before, StateAfter: after, Metadata: make(map[string]string), } } // NewRepositoryState creates a new repository state snapshot func NewRepositoryState(refs map[string]string, currentWorkstream, workingTreeHash, indexHash string) *RepositoryState { return &RepositoryState{ Refs: refs, CurrentWorkstream: currentWorkstream, WorkingTreeHash: workingTreeHash, IndexHash: indexHash, } }