Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get rid of ocflv1.Enable #111

Merged
merged 13 commits into from
Dec 20, 2024
8 changes: 0 additions & 8 deletions Dockerfile

This file was deleted.

2 changes: 0 additions & 2 deletions examples/commit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/srerickson/ocfl-go"
"github.com/srerickson/ocfl-go/backend/local"
"github.com/srerickson/ocfl-go/digest"
"github.com/srerickson/ocfl-go/ocflv1"
)

var (
Expand All @@ -26,7 +25,6 @@ var (

func main() {
ctx := context.Background()
ocflv1.Enable()
flag.StringVar(&srcDir, "obj", "", "directory of object to commit to")
flag.StringVar(&srcDir, "src", "", "directory with new version content")
flag.StringVar(&msg, "msg", "", "message field for new version")
Expand Down
2 changes: 0 additions & 2 deletions examples/listobjects/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ import (
"github.com/srerickson/ocfl-go"
"github.com/srerickson/ocfl-go/backend/s3"
"github.com/srerickson/ocfl-go/logging"
"github.com/srerickson/ocfl-go/ocflv1"
)

var numgos int

func main() {
ocflv1.Enable()
ctx := context.Background()
// logging.SetDefaultLevel(slog.LevelDebug)
logger := logging.DefaultLogger()
Expand Down
2 changes: 0 additions & 2 deletions examples/validate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import (

"github.com/charmbracelet/log"
"github.com/srerickson/ocfl-go"
"github.com/srerickson/ocfl-go/ocflv1"
)

var objPath string

func main() {
ctx := context.Background()
ocflv1.Enable() // setup ocflv1
flag.Parse()
handl := log.New(os.Stderr)
handl.SetLevel(log.WarnLevel)
Expand Down
142 changes: 110 additions & 32 deletions inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"path"
"regexp"
"slices"
"sort"
"strings"
"time"

Expand All @@ -21,7 +22,7 @@ var (
invSidecarContentsRexp = regexp.MustCompile(`^([a-fA-F0-9]+)\s+inventory\.json[\n]?$`)
)

type ReadInventory interface {
type Inventory interface {
FixitySource
ContentDirectory() string
Digest() string
Expand All @@ -30,7 +31,6 @@ type ReadInventory interface {
ID() string
Manifest() DigestMap
Spec() Spec
Validate() *Validation
Version(int) ObjectVersion
FixityAlgorithms() []string
}
Expand All @@ -48,30 +48,52 @@ type User struct {
Address string `json:"address,omitempty"`
}

func ReadSidecarDigest(ctx context.Context, fsys FS, name string) (digest string, err error) {
file, err := fsys.OpenFile(ctx, name)
// ReadInventory reads the 'inventory.json' file in dir and validates it. It returns
// an error if the inventory cann't be paresed or if it is invalid.
func ReadInventory(ctx context.Context, fsys FS, dir string) (inv Inventory, err error) {
var byts []byte
var imp ocfl
byts, err = ReadAll(ctx, fsys, path.Join(dir, inventoryBase))
if err != nil {
return
}
defer file.Close()
cont, err := io.ReadAll(file)
imp, err = getInventoryOCFL(byts)
if err != nil {
return
}
matches := invSidecarContentsRexp.FindSubmatch(cont)
return imp.NewInventory(byts)
}

// ReadSidecarDigest reads the digest from an inventory.json sidecar file
func ReadSidecarDigest(ctx context.Context, fsys FS, name string) (string, error) {
byts, err := ReadAll(ctx, fsys, name)
if err != nil {
return "", err
}
matches := invSidecarContentsRexp.FindSubmatch(byts)
if len(matches) != 2 {
err = fmt.Errorf("reading %s: %w", name, ErrInventorySidecarContents)
return
err := fmt.Errorf("reading %s: %w", name, ErrInventorySidecarContents)
return "", err
}
return string(matches[1]), nil
}

// ValidateInventoryBytes parses and fully validates the byts as contents of an
// inventory.json file.
func ValidateInventoryBytes(byts []byte) (Inventory, *Validation) {
imp, _ := getInventoryOCFL(byts)
if imp == nil {
// use default OCFL spec
imp = defaultOCFL()
}
digest = string(matches[1])
return
return imp.ValidateInventoryBytes(byts)
}

// ValidateInventorySidecar reads the inventory sidecar with inv's digest
// algorithm (e.g., inventory.json.sha512) in directory dir and return an error
// if the sidecar content is not formatted correctly or if the inv's digest
// doesn't match the value found in the sidecar.
func ValidateInventorySidecar(ctx context.Context, inv ReadInventory, fsys FS, dir string) error {
func ValidateInventorySidecar(ctx context.Context, inv Inventory, fsys FS, dir string) error {
sideCar := path.Join(dir, inventoryBase+"."+inv.DigestAlgorithm().ID())
expSum, err := ReadSidecarDigest(ctx, fsys, sideCar)
if err != nil {
Expand All @@ -88,30 +110,86 @@ func ValidateInventorySidecar(ctx context.Context, inv ReadInventory, fsys FS, d
return nil
}

// return a ReadInventory for an inventory that may use any version of the ocfl spec.
func readUnknownInventory(ctx context.Context, ocfls *OCLFRegister, fsys FS, dir string) (ReadInventory, error) {
f, err := fsys.OpenFile(ctx, path.Join(dir, inventoryBase))
func validateInventory(inv Inventory) *Validation {
imp, err := getOCFL(inv.Spec())
if err != nil {
return nil, err
}
defer func() {
if closeErr := f.Close(); closeErr != nil {
err = errors.Join(err, closeErr)
}
}()
raw, err := io.ReadAll(f)
if err != nil {
return nil, err
v := &Validation{}
err := fmt.Errorf("inventory uses unknown or unspecified OCFL version")
v.AddFatal(err)
return v
}
return imp.ValidateInventory(inv)
}

// get the ocfl implementation declared in the inventory bytes
func getInventoryOCFL(byts []byte) (ocfl, error) {
invFields := struct {
Type InvType `json:"type"`
Type InventoryType `json:"type"`
}{}
if err = json.Unmarshal(raw, &invFields); err != nil {
if err := json.Unmarshal(byts, &invFields); err != nil {
return nil, err
}
invOCFL, err := ocfls.Get(invFields.Type.Spec)
if err != nil {
return nil, err
return getOCFL(invFields.Type.Spec)
}

// rawInventory represents the contents of an object's inventory.json file
type rawInventory struct {
ID string `json:"id"`
Type InventoryType `json:"type"`
DigestAlgorithm string `json:"digestAlgorithm"`
Head VNum `json:"head"`
ContentDirectory string `json:"contentDirectory,omitempty"`
Manifest DigestMap `json:"manifest"`
Versions map[VNum]*rawInventoryVersion `json:"versions"`
Fixity map[string]DigestMap `json:"fixity,omitempty"`
}

func (inv rawInventory) getFixity(dig string) digest.Set {
paths := inv.Manifest[dig]
if len(paths) < 1 {
return nil
}
set := digest.Set{}
for fixAlg, fixMap := range inv.Fixity {
fixMap.EachPath(func(p, fixDigest string) bool {
if slices.Contains(paths, p) {
set[fixAlg] = fixDigest
return false
}
return true
})
}
return invOCFL.NewReadInventory(raw)
return set
}

func (inv rawInventory) version(v int) *rawInventoryVersion {
if inv.Versions == nil {
return nil
}
if v == 0 {
return inv.Versions[inv.Head]
}
vnum := V(v, inv.Head.Padding())
return inv.Versions[vnum]
}

// vnums returns a sorted slice of vnums corresponding to the keys in the
// inventory's 'versions' block.
func (inv rawInventory) vnums() []VNum {
vnums := make([]VNum, len(inv.Versions))
i := 0
for v := range inv.Versions {
vnums[i] = v
i++
}
sort.Sort(VNums(vnums))
return vnums
}

// Version represents object version state and metadata
type rawInventoryVersion struct {
Created time.Time `json:"created"`
State DigestMap `json:"state"`
Message string `json:"message,omitempty"`
User *User `json:"user,omitempty"`
}
Loading
Loading