Add custom types for type safety throughout codebase
- Add FileCount, FileSize, RelFilePath, AbsFilePath, ModTime, Multihash types - Add UnixSeconds and UnixNanos types for timestamp handling - Add URL types (ManifestURL, FileURL, BaseURL) with safe path joining - Consolidate scanner package into mfer package - Update checker to use custom types in Result and CheckStatus - Add ModTime.Timestamp() method for protobuf conversion - Update all tests to use proper custom types
This commit is contained in:
+60
-14
@@ -2,6 +2,7 @@ package mfer
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -9,9 +10,42 @@ import (
|
||||
"github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
// RelFilePath represents a relative file path within a manifest.
|
||||
type RelFilePath string
|
||||
|
||||
// AbsFilePath represents an absolute file path on the filesystem.
|
||||
type AbsFilePath string
|
||||
|
||||
// FileSize represents the size of a file in bytes.
|
||||
type FileSize int64
|
||||
|
||||
// FileCount represents a count of files.
|
||||
type FileCount int64
|
||||
|
||||
// ModTime represents a file's modification time.
|
||||
type ModTime time.Time
|
||||
|
||||
// UnixSeconds represents seconds since Unix epoch.
|
||||
type UnixSeconds int64
|
||||
|
||||
// UnixNanos represents the nanosecond component of a timestamp (0-999999999).
|
||||
type UnixNanos int32
|
||||
|
||||
// Timestamp converts ModTime to a protobuf Timestamp.
|
||||
func (m ModTime) Timestamp() *Timestamp {
|
||||
t := time.Time(m)
|
||||
return &Timestamp{
|
||||
Seconds: t.Unix(),
|
||||
Nanos: int32(t.Nanosecond()),
|
||||
}
|
||||
}
|
||||
|
||||
// Multihash represents a multihash-encoded file hash (typically SHA2-256).
|
||||
type Multihash []byte
|
||||
|
||||
// FileHashProgress reports progress during file hashing.
|
||||
type FileHashProgress struct {
|
||||
BytesRead int64 // Total bytes read so far for the current file
|
||||
BytesRead FileSize // Total bytes read so far for the current file
|
||||
}
|
||||
|
||||
// Builder constructs a manifest by adding files one at a time.
|
||||
@@ -33,24 +67,24 @@ func NewBuilder() *Builder {
|
||||
// Progress updates are sent to the progress channel (if non-nil) without blocking.
|
||||
// Returns the number of bytes read.
|
||||
func (b *Builder) AddFile(
|
||||
path string,
|
||||
size int64,
|
||||
mtime time.Time,
|
||||
path RelFilePath,
|
||||
size FileSize,
|
||||
mtime ModTime,
|
||||
reader io.Reader,
|
||||
progress chan<- FileHashProgress,
|
||||
) (int64, error) {
|
||||
) (FileSize, error) {
|
||||
// Create hash writer
|
||||
h := sha256.New()
|
||||
|
||||
// Read file in chunks, updating hash and progress
|
||||
var totalRead int64
|
||||
var totalRead FileSize
|
||||
buf := make([]byte, 64*1024) // 64KB chunks
|
||||
|
||||
for {
|
||||
n, err := reader.Read(buf)
|
||||
if n > 0 {
|
||||
h.Write(buf[:n])
|
||||
totalRead += int64(n)
|
||||
totalRead += FileSize(n)
|
||||
sendFileHashProgress(progress, FileHashProgress{BytesRead: totalRead})
|
||||
}
|
||||
if err == io.EOF {
|
||||
@@ -69,12 +103,12 @@ func (b *Builder) AddFile(
|
||||
|
||||
// Create file entry
|
||||
entry := &MFFilePath{
|
||||
Path: path,
|
||||
Size: size,
|
||||
Path: string(path),
|
||||
Size: int64(size),
|
||||
Hashes: []*MFFileChecksum{
|
||||
{MultiHash: mh},
|
||||
},
|
||||
Mtime: newTimestampFromTime(mtime),
|
||||
Mtime: mtime.Timestamp(),
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
@@ -104,19 +138,31 @@ func (b *Builder) FileCount() int {
|
||||
|
||||
// AddFileWithHash adds a file entry with a pre-computed hash.
|
||||
// This is useful when the hash is already known (e.g., from an existing manifest).
|
||||
func (b *Builder) AddFileWithHash(path string, size int64, mtime time.Time, hash []byte) {
|
||||
// Returns an error if path is empty, size is negative, or hash is nil/empty.
|
||||
func (b *Builder) AddFileWithHash(path RelFilePath, size FileSize, mtime ModTime, hash Multihash) error {
|
||||
if path == "" {
|
||||
return errors.New("path cannot be empty")
|
||||
}
|
||||
if size < 0 {
|
||||
return errors.New("size cannot be negative")
|
||||
}
|
||||
if len(hash) == 0 {
|
||||
return errors.New("hash cannot be nil or empty")
|
||||
}
|
||||
|
||||
entry := &MFFilePath{
|
||||
Path: path,
|
||||
Size: size,
|
||||
Path: string(path),
|
||||
Size: int64(size),
|
||||
Hashes: []*MFFileChecksum{
|
||||
{MultiHash: hash},
|
||||
},
|
||||
Mtime: newTimestampFromTime(mtime),
|
||||
Mtime: mtime.Timestamp(),
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
b.files = append(b.files, entry)
|
||||
b.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build finalizes the manifest and writes it to the writer.
|
||||
|
||||
Reference in New Issue
Block a user