123 lines
2.7 KiB
Go
123 lines
2.7 KiB
Go
package artifact
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/defaults"
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
|
)
|
|
|
|
var (
|
|
ErrUnsupportedRefType = errors.New("unsupported artifact reference type")
|
|
ErrMissingInlineBody = errors.New("missing body for inline artifact")
|
|
ErrMissingFilePath = errors.New("missing file path for file artifact")
|
|
)
|
|
|
|
// Reader resolves artifact references into actual artifacts.
|
|
type Reader interface {
|
|
Read(ctx context.Context, ref domain.ArtifactRef) (*domain.Artifact, error)
|
|
}
|
|
|
|
// CompositeReader routes artifact resolution based on the reference type.
|
|
type CompositeReader struct {
|
|
inlineReader *inlineReader
|
|
fileReader Reader
|
|
}
|
|
|
|
func NewCompositeReader() Reader {
|
|
return &CompositeReader{
|
|
inlineReader: &inlineReader{},
|
|
fileReader: &fileReader{},
|
|
}
|
|
}
|
|
|
|
func (c *CompositeReader) Read(ctx context.Context, ref domain.ArtifactRef) (*domain.Artifact, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
switch ref.Type {
|
|
case domain.ArtifactRefInline:
|
|
return c.inlineReader.Read(ctx, ref)
|
|
case domain.ArtifactRefFile:
|
|
return c.fileReader.Read(ctx, ref)
|
|
default:
|
|
return nil, fmt.Errorf("%w: %s", ErrUnsupportedRefType, ref.Type)
|
|
}
|
|
}
|
|
|
|
type inlineReader struct{}
|
|
|
|
func (r *inlineReader) Read(ctx context.Context, ref domain.ArtifactRef) (*domain.Artifact, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
if ref.Body == "" {
|
|
return nil, ErrMissingInlineBody
|
|
}
|
|
|
|
body := []byte(ref.Body)
|
|
return &domain.Artifact{
|
|
ContentType: defaults.ContentTypeTextPlain,
|
|
Body: body,
|
|
Size: int64(len(body)),
|
|
Hash: fmt.Sprintf("%x", sha256.Sum256(body)),
|
|
URI: ref.URI,
|
|
}, nil
|
|
}
|
|
|
|
type fileReader struct{}
|
|
|
|
func (r *fileReader) Read(ctx context.Context, ref domain.ArtifactRef) (*domain.Artifact, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
if ref.URI == "" {
|
|
return nil, ErrMissingFilePath
|
|
}
|
|
|
|
return readFileArtifact(ref.URI)
|
|
}
|
|
|
|
func readFileArtifact(path string) (*domain.Artifact, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read file %s: %w", path, err)
|
|
}
|
|
defer file.Close()
|
|
|
|
data, err := io.ReadAll(file)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read file %s: %w", path, err)
|
|
}
|
|
|
|
contentType := mime.TypeByExtension(filepath.Ext(path))
|
|
if contentType == "" {
|
|
contentType = defaults.ContentTypeTextPlain
|
|
}
|
|
|
|
return &domain.Artifact{
|
|
Name: filepath.Base(path),
|
|
ContentType: contentType,
|
|
Body: data,
|
|
URI: path,
|
|
Size: int64(len(data)),
|
|
Hash: fmt.Sprintf("%x", sha256.Sum256(data)),
|
|
}, nil
|
|
}
|