Skip to content

Commit

Permalink
get rid of ocflv1.Enable (#111)
Browse files Browse the repository at this point in the history
This included some significant refactoring:

* ocflv1 implementation moved to top-level package
* validation codes don't need to import ocfl-go
* move validation codes in `validation` package and cleanup code gen.
* remove  ocfl implementation registry 
* ocfl implementation interface is not exported
* remove readobjectL just use *Object.
* remove Dockerfile
  • Loading branch information
srerickson authored Dec 20, 2024
1 parent 5d6b81a commit 2e03643
Show file tree
Hide file tree
Showing 26 changed files with 2,621 additions and 2,821 deletions.
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

0 comments on commit 2e03643

Please sign in to comment.