Documentation
¶
Index ¶
- type FileStorage
- func (fs *FileStorage) Close() error
- func (fs *FileStorage) Delete(ctx context.Context, id string) error
- func (fs *FileStorage) GetByID(ctx context.Context, id string) (*StoredReport, error)
- func (fs *FileStorage) GetLatest(ctx context.Context, projectPath string) (*StoredReport, error)
- func (fs *FileStorage) List(ctx context.Context, projectPath string, opts ListOptions) ([]*ReportMetadata, error)
- func (fs *FileStorage) Save(ctx context.Context, report *StoredReport) error
- type ListOptions
- type ReportMetadata
- type SQLiteStorage
- func (ss *SQLiteStorage) Close() error
- func (ss *SQLiteStorage) Delete(ctx context.Context, id string) error
- func (ss *SQLiteStorage) GetByID(ctx context.Context, id string) (*StoredReport, error)
- func (ss *SQLiteStorage) GetLatest(ctx context.Context, projectPath string) (*StoredReport, error)
- func (ss *SQLiteStorage) List(ctx context.Context, projectPath string, opts ListOptions) ([]*ReportMetadata, error)
- func (ss *SQLiteStorage) Save(ctx context.Context, report *StoredReport) error
- type Storage
- type StoredReport
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type FileStorage ¶
type FileStorage struct {
// contains filtered or unexported fields
}
FileStorage implements Storage using JSON files in a directory hierarchy.
Reports are stored in a directory structure like:
{basePath}/history/{project-hash}/{timestamp}_{id-prefix}.json
Where:
- basePath: Root storage directory (e.g., ~/.cra or ./.cra)
- project-hash: SHA256 hash of project path (first 16 chars)
- timestamp: YYYY-MM-DD_HH-MM-SS format
- id-prefix: First 6 chars of report UUID
This structure provides:
- Easy manual inspection (human-readable filenames)
- Project isolation (each project in its own directory)
- Chronological ordering (timestamp in filename)
- Uniqueness (UUID prefix prevents collisions)
Example directory structure:
~/.cra/history/
a1b2c3d4e5f6g7h8/
2025-12-20_14-30-45_abc123.json
2025-12-20_15-22-10_def456.json
9f8e7d6c5b4a3210/
2025-12-19_10-15-30_ghi789.json
func NewFileStorage ¶
func NewFileStorage(basePath string) (*FileStorage, error)
NewFileStorage creates a new file-based storage backend.
The basePath parameter specifies where reports will be stored. The directory will be created if it doesn't exist. A typical setup uses:
- ~/.cra for user-level storage
- ./.cra for project-level storage
Returns an error if the directory cannot be created or accessed.
Example:
storage, err := NewFileStorage(filepath.Join(homeDir, ".cra"))
if err != nil {
return fmt.Errorf("failed to create storage: %w", err)
}
defer storage.Close()
func (*FileStorage) Close ¶
func (fs *FileStorage) Close() error
Close cleans up resources (no-op for file storage).
func (*FileStorage) Delete ¶
func (fs *FileStorage) Delete(ctx context.Context, id string) error
Delete removes a report by ID.
Searches for and deletes the file containing the report. Returns an error if the report doesn't exist.
func (*FileStorage) GetByID ¶
func (fs *FileStorage) GetByID(ctx context.Context, id string) (*StoredReport, error)
GetByID retrieves a specific report by ID.
Searches all project directories for a report with a matching ID. This is slower than GetLatest as it may need to search multiple directories.
func (*FileStorage) GetLatest ¶
func (fs *FileStorage) GetLatest(ctx context.Context, projectPath string) (*StoredReport, error)
GetLatest retrieves the most recent report for a project.
Reports are sorted by filename (which includes timestamp) to find the latest. Returns nil if no reports exist for the project.
func (*FileStorage) List ¶
func (fs *FileStorage) List(ctx context.Context, projectPath string, opts ListOptions) ([]*ReportMetadata, error)
List returns metadata for reports matching the criteria.
Reports are filtered by project path and ListOptions, then sorted by timestamp (newest first). Only metadata is returned for efficiency.
func (*FileStorage) Save ¶
func (fs *FileStorage) Save(ctx context.Context, report *StoredReport) error
Save stores a report as a JSON file.
The report is written to a file named with the timestamp and ID prefix. If a file with the same name exists, it will be overwritten.
The write is atomic: the file is first written to a temporary location and then renamed to the final path to prevent partial writes.
type ListOptions ¶
type ListOptions struct {
// Maximum number of results to return (0 = unlimited)
Limit int
// Number of results to skip (for pagination)
Offset int
// Only include reports after this time (inclusive)
Since time.Time
// Only include reports before this time (inclusive)
Until time.Time
}
ListOptions specifies filtering and pagination for Storage.List().
Use these options to:
- Limit the number of results (pagination)
- Skip results (offset-based pagination)
- Filter by date range (since/until)
Example:
// Get last 10 reports from the past week
opts := ListOptions{
Limit: 10,
Since: time.Now().AddDate(0, 0, -7),
}
type ReportMetadata ¶
type ReportMetadata struct {
// Unique identifier for this report
ID string `json:"id"`
// Absolute path to the analyzed project
ProjectPath string `json:"project_path"`
// When this analysis was performed
Timestamp time.Time `json:"timestamp"`
// Git commit hash at time of analysis (optional)
GitCommit string `json:"git_commit,omitempty"`
// Git branch name at time of analysis (optional)
GitBranch string `json:"git_branch,omitempty"`
// Summary metrics (optional, for quick display)
TotalFiles int `json:"total_files,omitempty"`
TotalLines int `json:"total_lines,omitempty"`
AvgComplexity float64 `json:"avg_complexity,omitempty"`
AvgCoverage float64 `json:"avg_coverage,omitempty"`
IssueCount int `json:"issue_count,omitempty"`
}
ReportMetadata contains summary information about a stored report.
This type provides lightweight report information for listing operations, without loading the full AnalysisResult. Useful for displaying report history or selecting reports for comparison.
Returned by Storage.List() for efficient browsing of report history.
type SQLiteStorage ¶
type SQLiteStorage struct {
// contains filtered or unexported fields
}
SQLiteStorage implements Storage using a SQLite database.
Reports are stored in a relational database with the following schema:
- reports table: Full report data with denormalized metrics
- Indexes on project_path and timestamp for efficient queries
This backend provides:
- Efficient querying and filtering
- Structured storage with integrity constraints
- Better performance for large numbers of reports
- ACID guarantees for concurrent access
The database uses WAL (Write-Ahead Logging) mode for better concurrency and prepared statements for performance.
Example usage:
storage, err := NewSQLiteStorage("/path/to/history.db")
if err != nil {
return err
}
defer storage.Close()
func NewSQLiteStorage ¶
func NewSQLiteStorage(dbPath string) (*SQLiteStorage, error)
NewSQLiteStorage creates a new SQLite-based storage backend.
The dbPath parameter specifies the database file location. The file and any necessary parent directories will be created if they don't exist.
The database is initialized with:
- Schema creation (reports table and indexes)
- WAL mode enabled for better concurrency
- Foreign keys enabled
Returns an error if the database cannot be opened or initialized.
Example:
storage, err := NewSQLiteStorage(filepath.Join(homeDir, ".cra", "history.db"))
if err != nil {
return fmt.Errorf("failed to create storage: %w", err)
}
defer storage.Close()
func (*SQLiteStorage) Close ¶
func (ss *SQLiteStorage) Close() error
Close closes the database connection.
Should be called when the storage is no longer needed to release database resources.
func (*SQLiteStorage) Delete ¶
func (ss *SQLiteStorage) Delete(ctx context.Context, id string) error
Delete removes a report by ID.
Returns an error if the report doesn't exist.
func (*SQLiteStorage) GetByID ¶
func (ss *SQLiteStorage) GetByID(ctx context.Context, id string) (*StoredReport, error)
GetByID retrieves a specific report by ID.
Uses a primary key lookup for fast retrieval.
func (*SQLiteStorage) GetLatest ¶
func (ss *SQLiteStorage) GetLatest(ctx context.Context, projectPath string) (*StoredReport, error)
GetLatest retrieves the most recent report for a project.
Uses an indexed query on project_path and timestamp for efficiency. Returns nil if no reports exist for the project.
func (*SQLiteStorage) List ¶
func (ss *SQLiteStorage) List(ctx context.Context, projectPath string, opts ListOptions) ([]*ReportMetadata, error)
List returns metadata for reports matching the criteria.
Efficiently queries using denormalized metrics without loading full JSON. Supports filtering by date range and pagination.
func (*SQLiteStorage) Save ¶
func (ss *SQLiteStorage) Save(ctx context.Context, report *StoredReport) error
Save stores a report in the database.
The report is inserted with both the full JSON data and denormalized metrics for efficient querying. If a report with the same ID exists, it will be replaced.
type Storage ¶
type Storage interface {
// Save stores an analysis report with metadata.
//
// The report is assigned a unique ID and stored with the provided metadata
// (timestamp, Git commit, branch). Returns an error if storage fails.
//
// Example:
// report := &StoredReport{
// ID: "abc-123",
// ProjectPath: "/path/to/project",
// Timestamp: time.Now(),
// GitCommit: "def456",
// Result: analysisResult,
// }
// err := storage.Save(ctx, report)
Save(ctx context.Context, report *StoredReport) error
// GetLatest retrieves the most recent report for a project.
//
// Returns the latest report based on timestamp, or nil if no reports
// exist for the project. The projectPath should be an absolute path
// for consistent matching across runs.
//
// Example:
// latest, err := storage.GetLatest(ctx, "/path/to/project")
// if latest != nil {
// // Compare with current analysis
// }
GetLatest(ctx context.Context, projectPath string) (*StoredReport, error)
// GetByID retrieves a specific report by its unique ID.
//
// Returns the report with the given ID, or an error if not found.
// Useful for retrieving specific historical reports for comparison.
//
// Example:
// report, err := storage.GetByID(ctx, "abc-123")
GetByID(ctx context.Context, id string) (*StoredReport, error)
// List returns reports for a project, optionally filtered and paginated.
//
// Returns metadata for matching reports ordered by timestamp (newest first).
// Use ListOptions to filter by date range and implement pagination.
//
// Example:
// opts := ListOptions{Limit: 10, Since: oneWeekAgo}
// reports, err := storage.List(ctx, "/path/to/project", opts)
List(ctx context.Context, projectPath string, opts ListOptions) ([]*ReportMetadata, error)
// Delete removes a report by ID.
//
// Returns an error if the report doesn't exist or deletion fails.
// Use with caution as this operation is irreversible.
//
// Example:
// err := storage.Delete(ctx, "abc-123")
Delete(ctx context.Context, id string) error
// Close cleans up storage resources.
//
// Should be called when the storage is no longer needed to release
// any open connections, file handles, or other resources.
//
// Example:
// defer storage.Close()
Close() error
}
Storage defines the interface for persisting and retrieving analysis reports.
Implementations provide different storage backends (file-based, database, etc.) while maintaining a consistent API for saving and loading reports. All operations accept a context for cancellation and timeout support.
The storage layer enables historical tracking and comparison features by persisting analysis results with metadata like timestamps and Git information.
type StoredReport ¶
type StoredReport struct {
// Unique identifier for this report (UUID)
ID string `json:"id"`
// Absolute path to the analyzed project
ProjectPath string `json:"project_path"`
// When this analysis was performed
Timestamp time.Time `json:"timestamp"`
// Git commit hash at time of analysis (optional)
GitCommit string `json:"git_commit,omitempty"`
// Git branch name at time of analysis (optional)
GitBranch string `json:"git_branch,omitempty"`
// The complete analysis result
Result *analyzer.AnalysisResult `json:"result"`
}
StoredReport represents a saved analysis report with metadata.
This type wraps an AnalysisResult with additional metadata needed for historical tracking and comparison:
- Unique ID for retrieval
- Project path for grouping reports
- Timestamp for chronological ordering
- Git metadata for correlation with code changes
StoredReport is the primary type passed to Storage.Save() and returned from Storage.Get*() methods.