diff --git a/artifactregistry.go b/artifactregistry.go index 15029f4..ca14a51 100644 --- a/artifactregistry.go +++ b/artifactregistry.go @@ -85,6 +85,8 @@ func (r *InMemoryArtifactRegistry) Load() error { return nil } func (r *InMemoryArtifactRegistry) Save() error { return nil } +const DefaultJSONArtifactRegistryFileName = ".specter.json" + type JsonArtifactRegistryEntry struct { ArtifactID ArtifactID `json:"artifactId"` Metadata map[string]any `json:"metadata"` @@ -178,18 +180,6 @@ func (r *JSONArtifactRegistry) FindAll(processorName string) ([]ArtifactRegistry return entries, nil } -// NewJSONArtifactRegistry returns a new artifact file registry. -func NewJSONArtifactRegistry(fileName string, fs FileSystem) *JSONArtifactRegistry { - return &JSONArtifactRegistry{ - Entries: make(map[string][]JsonArtifactRegistryEntry), - FilePath: fileName, - CurrentTimeProvider: func() time.Time { - return time.Now() - }, - FileSystem: fs, - } -} - func (r *JSONArtifactRegistry) Load() error { r.mu.Lock() defer r.mu.Unlock() diff --git a/assembly.go b/assembly.go new file mode 100644 index 0000000..3d21b85 --- /dev/null +++ b/assembly.go @@ -0,0 +1,101 @@ +package specter + +import ( + "os" + "time" +) + +// New allows creating a new specter instance using the provided options. +func New(opts ...Option) *Specter { + s := &Specter{ + Logger: NewDefaultLogger(DefaultLoggerConfig{DisableColors: true, Writer: os.Stdout}), + ExecutionMode: FullMode, + } + for _, o := range opts { + o(s) + } + return s +} + +// Option represents an option to configure a specter instance. +type Option func(s *Specter) + +// WithLogger configures the Logger of a Specter instance. +func WithLogger(l Logger) Option { + return func(s *Specter) { + s.Logger = l + } +} + +// WithSourceLoaders configures the SourceLoader of a Specter instance. +func WithSourceLoaders(loaders ...SourceLoader) Option { + return func(s *Specter) { + s.SourceLoaders = append(s.SourceLoaders, loaders...) + } +} + +// WithLoaders configures the SpecificationLoader of a Specter instance. +func WithLoaders(loaders ...SpecificationLoader) Option { + return func(s *Specter) { + s.Loaders = append(s.Loaders, loaders...) + } +} + +// WithProcessors configures the SpecProcess of a Specter instance. +func WithProcessors(processors ...SpecificationProcessor) Option { + return func(s *Specter) { + s.Processors = append(s.Processors, processors...) + } +} + +// WithArtifactProcessors configures the ArtifactProcessor of a Specter instance. +func WithArtifactProcessors(processors ...ArtifactProcessor) Option { + return func(s *Specter) { + s.ArtifactProcessors = append(s.ArtifactProcessors, processors...) + } +} + +// WithExecutionMode configures the ExecutionMode of a Specter instance. +func WithExecutionMode(m ExecutionMode) Option { + return func(s *Specter) { + s.ExecutionMode = m + } +} +func WithArtifactRegistry(r ArtifactRegistry) Option { + return func(s *Specter) { + s.ArtifactRegistry = r + } +} + +// DEFAULTS SPECTER OPTIONS + +func WithDefaultLogger() Option { + return WithLogger(NewDefaultLogger(DefaultLoggerConfig{DisableColors: false, Writer: os.Stdout})) +} + +func WithJSONArtifactRegistry(fileName string, fs FileSystem) Option { + return WithArtifactRegistry(NewJSONArtifactRegistry(fileName, fs)) +} + +// NewFileSystemLoader FileSystem +func NewFileSystemLoader(fs FileSystem) *FileSystemLoader { + return &FileSystemLoader{fs: fs} +} + +func NewLocalFileSourceLoader() FileSystemLoader { + return FileSystemLoader{fs: LocalFileSystem{}} +} + +// ARTIFACT REGISTRIES + +// NewJSONArtifactRegistry returns a new artifact file registry. +func NewJSONArtifactRegistry(fileName string, fs FileSystem) *JSONArtifactRegistry { + return &JSONArtifactRegistry{ + Entries: make(map[string][]JsonArtifactRegistryEntry), + FilePath: fileName, + CurrentTimeProvider: func() time.Time { + return time.Now() + }, + FileSystem: fs, + } +} diff --git a/fileartifactprocessor.go b/fileartifactprocessor.go index a34fdb8..5017673 100644 --- a/fileartifactprocessor.go +++ b/fileartifactprocessor.go @@ -153,13 +153,13 @@ func (p FileArtifactProcessor) processFileArtifact(ctx ArtifactProcessingContext if fa.IsDir() { ctx.Logger.Info(fmt.Sprintf("Creating directory %q ...", filePath)) - ctx.Logger.Trace(fmt.Sprintf("making directory %q for %q ...", filePath, fa.ID())) + ctx.Logger.Trace(fmt.Sprintf("making directory %q for %q ...", filePath, fa.ID())) if err := p.FileSystem.WriteFile(filePath, fa.Data, os.ModePerm); err != nil { return err } } else { ctx.Logger.Info(fmt.Sprintf("Writing file %q ...", filePath)) - ctx.Logger.Trace(fmt.Sprintf("creating directory %q for %q ...", filePath, fa.ID())) + ctx.Logger.Trace(fmt.Sprintf("creating file %q for %q ...", filePath, fa.ID())) if err := p.FileSystem.WriteFile(filePath, fa.Data, os.ModePerm); err != nil { return err } @@ -183,6 +183,7 @@ func (p FileArtifactProcessor) cleanRegistry(ctx ArtifactProcessingContext) erro var wg sync.WaitGroup var errs errors.Group + ctx.Logger.Info(fmt.Sprintf("Cleaning file artifacts ...")) entries, err := ctx.ArtifactRegistry.FindAll() if err != nil { return err @@ -190,13 +191,20 @@ func (p FileArtifactProcessor) cleanRegistry(ctx ArtifactProcessingContext) erro for _, entry := range entries { if entry.Metadata == nil { - continue // TODO Error (?) + ctx.Logger.Trace(fmt.Sprintf("invalid registry entry %q: no metadata", entry.ArtifactID)) + continue } writeMode, ok := entry.Metadata["writeMode"] - if !ok || writeMode != RecreateMode { + if ok { + ctx.Logger.Trace(fmt.Sprintf("invalid registry entry %q: no write mode", entry.ArtifactID)) + continue + } + + if writeMode != RecreateMode { continue } + wg.Add(1) go func(entry ArtifactRegistryEntry) { defer wg.Done() @@ -214,13 +222,16 @@ func (p FileArtifactProcessor) cleanRegistry(ctx ArtifactProcessingContext) erro func (p FileArtifactProcessor) cleanArtifact(ctx ArtifactProcessingContext, entry ArtifactRegistryEntry) error { if entry.Metadata == nil { - return nil // TODO Error (?) + ctx.Logger.Trace(fmt.Sprintf("invalid registry entry %q: no metadata", entry.ArtifactID)) + return nil } path, ok := entry.Metadata["path"].(string) if !ok || path == "" { + ctx.Logger.Trace(fmt.Sprintf("invalid registry entry %q: no path", entry.ArtifactID)) return nil } + ctx.Logger.Info(fmt.Sprintf(fmt.Sprintf("cleaning file artifact %q ...", entry.ArtifactID))) if err := p.FileSystem.Remove(path); err != nil { return err } diff --git a/hcl.go b/hcl.go index 897a04a..22cc83d 100644 --- a/hcl.go +++ b/hcl.go @@ -50,7 +50,7 @@ func (l HCLGenericSpecLoader) Load(s Source) ([]Specification, error) { } file, diags := l.ParseHCL(s.Data, s.Location) - if diags.HasErrors() { + if diags != nil && diags.HasErrors() { return nil, errors.Wrap(diags, InvalidHCLErrorCode) } @@ -115,7 +115,7 @@ func (l HCLGenericSpecLoader) extractAttributesFromBlock(ctx *hcl.EvalContext, b // Detect attributes in current block. for _, a := range block.Body.Attributes { value, d := a.Expr.Value(ctx) - if d.HasErrors() { + if d != nil && d.HasErrors() { d = append(diags, d...) continue } @@ -206,7 +206,7 @@ func (l HCLSpecLoader) Load(s Source) ([]Specification, error) { for _, b := range body.Blocks { if b.Type == "const" { v, d := b.Body.Attributes["value"].Expr.Value(ctx) - if d.HasErrors() { + if d != nil && d.HasErrors() { diags = append(diags, d...) } else { ctx.Variables[b.Labels[0]] = v @@ -219,11 +219,14 @@ func (l HCLSpecLoader) Load(s Source) ([]Specification, error) { err := hclsimple.Decode(s.Location, s.Data, ctx, fileConf) if err != nil { - d := err.(hcl.Diagnostics) + var d hcl.Diagnostics + if !errors.As(err, &d) { + return nil, err + } diags = append(diags, d...) } - if diags.HasErrors() { + if diags != nil && diags.HasErrors() { return nil, diags } diff --git a/logging.go b/logging.go index 3c48c17..0f43abc 100644 --- a/logging.go +++ b/logging.go @@ -53,7 +53,7 @@ func NewDefaultLogger(c DefaultLoggerConfig) *DefaultLogger { } func (l *DefaultLogger) Trace(msg string) { - l.Log(l.color.Faint(msg).String()) + l.Log(l.color.Faint(fmt.Sprintf("--- %s", msg)).String()) } func (l *DefaultLogger) Info(msg string) { diff --git a/logging_test.go b/logging_test.go index a5b512a..f18824b 100644 --- a/logging_test.go +++ b/logging_test.go @@ -62,7 +62,7 @@ func TestDefaultLogger_Trace(t *testing.T) { }) logger.Trace("hello world") - assert.Equal(t, aurora.Faint("hello world").String()+"\n", string(buffer.Bytes())) + assert.Equal(t, aurora.Faint("--- hello world").String()+"\n", string(buffer.Bytes())) } func TestDefaultLogger_Info(t *testing.T) { diff --git a/source.go b/source.go index 6e194e2..352d01a 100644 --- a/source.go +++ b/source.go @@ -42,9 +42,6 @@ type FileSystemLoader struct { fs FileSystem } -func NewLocalFileSourceLoader() FileSystemLoader { - return FileSystemLoader{fs: LocalFileSystem{}} -} func (l FileSystemLoader) Supports(target string) bool { if target == "" { return false diff --git a/specter.go b/specter.go index aa764ba..042996d 100644 --- a/specter.go +++ b/specter.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/morebec/go-errors/errors" - "os" "time" ) @@ -239,6 +238,12 @@ func (s Specter) ProcessArtifacts(ctx context.Context, specifications []Specific return fmt.Errorf("failed loading artifact registry: %w", err) } + defer func() { + if err := s.ArtifactRegistry.Save(); err != nil { + s.Logger.Error(fmt.Errorf("failed saving artifact registry: %w", err).Error()) + } + }() + for _, p := range s.ArtifactProcessors { if err := CheckContextDone(ctx); err != nil { return err @@ -262,78 +267,6 @@ func (s Specter) ProcessArtifacts(ctx context.Context, specifications []Specific } } - if err := s.ArtifactRegistry.Save(); err != nil { - return fmt.Errorf("failed saving artifact registry: %w", err) - } - s.Logger.Success("FindAll processed successfully.") return nil } - -// New allows creating a new specter instance using the provided options. -func New(opts ...Option) *Specter { - s := &Specter{ - Logger: NewDefaultLogger(DefaultLoggerConfig{DisableColors: true, Writer: os.Stdout}), - ExecutionMode: FullMode, - } - for _, o := range opts { - o(s) - } - return s -} - -// Option represents an option to configure a specter instance. -type Option func(s *Specter) - -// WithLogger configures the Logger of a Specter instance. -func WithLogger(l Logger) Option { - return func(s *Specter) { - s.Logger = l - } -} - -// WithSourceLoaders configures the SourceLoader of a Specter instance. -func WithSourceLoaders(loaders ...SourceLoader) Option { - return func(s *Specter) { - s.SourceLoaders = append(s.SourceLoaders, loaders...) - } -} - -// WithLoaders configures the SpecificationLoader of a Specter instance. -func WithLoaders(loaders ...SpecificationLoader) Option { - return func(s *Specter) { - s.Loaders = append(s.Loaders, loaders...) - } -} - -// WithProcessors configures the SpecProcess of a Specter instance. -func WithProcessors(processors ...SpecificationProcessor) Option { - return func(s *Specter) { - s.Processors = append(s.Processors, processors...) - } -} - -// WithArtifactProcessors configures the ArtifactProcessor of a Specter instance. -func WithArtifactProcessors(processors ...ArtifactProcessor) Option { - return func(s *Specter) { - s.ArtifactProcessors = append(s.ArtifactProcessors, processors...) - } -} - -// WithExecutionMode configures the ExecutionMode of a Specter instance. -func WithExecutionMode(m ExecutionMode) Option { - return func(s *Specter) { - s.ExecutionMode = m - } -} -func WithArtifactRegistry(r ArtifactRegistry) Option { - return func(s *Specter) { - s.ArtifactRegistry = r - } -} - -// Defaults - -func WithDefaultLogger() Option { - return WithLogger(NewDefaultLogger(DefaultLoggerConfig{DisableColors: false, Writer: os.Stdout})) -}