func AddFileIndexes(catalog *coal.Catalog) {
	catalog.AddIndex(&File{}, false, 0, "State", "Updated")
}
AddFileIndexes will add files indexes to the specified catalog.
package blaze
import (
	"mime"
	"time"
	"github.com/256dpi/xo"
	"go.mongodb.org/mongo-driver/bson/bsontype"
	"github.com/256dpi/fire/coal"
	"github.com/256dpi/fire/stick"
)
const maxFileNameLength = 256
type Link struct {
	Ref string `json:"ref"`
	Name string `json:"name" bson:"-"`
	Type string `json:"type" bson:"-"`
	Size int64 `json:"size" bson:"-"`
	ClaimKey string `json:"claim-key" bson:"-"`
	ViewKey string `json:"view-key" bson:"-"`
	File coal.ID `json:"-" bson:"file_id"`
	FileName string `json:"-" bson:"name"`
	FileType string `json:"-" bson:"type"`
	FileSize int64  `json:"-" bson:"size"`
}
func (l *Link) Validate(requireFilename bool, whitelist ...string) error {
	if l.Ref == "" {
		l.Ref = coal.New().Hex()
	}
	return stick.Validate(l, func(v *stick.Validator) {
		v.Value("Ref", false, stick.IsNotZero)
		v.Value("File", false, stick.IsNotZero)
		if requireFilename {
			v.Value("FileName", false, stick.IsNotZero)
		}
		v.Value("FileType", false, func(stick.Subject) error {
			return ValidateType(l.FileType, whitelist...)
		})
		v.Value("FileSize", false, stick.IsMinInt(1))
	})
}
func IsValidLink(requireFilename bool, whitelist ...string) func(sub stick.Subject) error {
	return func(sub stick.Subject) error {
		if !sub.Unwrap() {
			return nil
		}
		link, ok := sub.IValue.(Link)
		if !ok {
			panic("blaze: expected link value")
		}
		return link.Validate(requireFilename, whitelist...)
	}
}
type Links []Link
func (l *Links) UnmarshalBSONValue(typ bsontype.Type, bytes []byte) error {
	return stick.BSON.UnmarshalKeyedList(stick.InternalBSONValue(typ, bytes), l, "Ref")
}
func (l *Links) UnmarshalJSON(bytes []byte) error {
	return stick.JSON.UnmarshalKeyedList(bytes, l, "Ref")
}
func (l Links) Validate(uniqueFilenames bool, whitelist ...string) error {
	refs := make(map[string]bool, len(l))
	names := make(map[string]bool, len(l))
	for _, link := range l {
		err := link.Validate(uniqueFilenames, whitelist...)
		if err != nil {
			return err
		}
		if refs[link.Ref] {
			return xo.SF("ambiguous reference")
		}
		if uniqueFilenames && names[link.FileName] {
			return xo.SF("ambiguous filename")
		}
		refs[link.Ref] = true
		names[link.FileName] = true
	}
	return nil
}
func IsValidLinks(uniqueFilenames bool, whitelist ...string) func(sub stick.Subject) error {
	return func(sub stick.Subject) error {
		if !sub.Unwrap() {
			return nil
		}
		links, ok := sub.IValue.(Links)
		if !ok {
			panic("blaze: expected links value")
		}
		return links.Validate(uniqueFilenames, whitelist...)
	}
}
type State string
const (
	Uploading State = "uploading"
	Uploaded  State = "uploaded"
	Claimed   State = "claimed"
	Released  State = "released"
	Deleting  State = "deleting"
)
func (s State) Valid() bool {
	switch s {
	case Uploading, Uploaded, Claimed, Released, Deleting:
		return true
	default:
		return false
	}
}
type File struct {
	coal.Base `json:"-" bson:",inline" coal:"files"`
	State State `json:"state"`
	Updated time.Time `json:"updated-at" bson:"updated_at"`
	Name string `json:"name"`
	Type string `json:"type"`
	Size int64 `json:"size"`
	Handle Handle `json:"handle"`
	Binding string `json:"binding"`
	Owner *coal.ID `json:"owner"`
}
func (f *File) Validate() error {
	return stick.Validate(f, func(v *stick.Validator) {
		v.Value("State", false, stick.IsValid)
		v.Value("Updated", false, stick.IsNotZero)
		v.Value("Name", false, stick.IsMaxLen(maxFileNameLength))
		v.Value("Type", false, func(stick.Subject) error {
			return ValidateType(f.Type)
		})
		if f.State != Uploading {
			v.Value("Size", false, stick.IsMinInt(1))
		}
		v.Value("Handle", false, stick.IsNotEmpty)
		if f.State == Claimed {
			v.Value("Binding", false, stick.IsNotZero)
			v.Value("Owner", false, stick.IsNotZero)
		} else {
			v.Value("Binding", false, stick.IsZero)
			v.Value("Owner", false, stick.IsZero)
		}
	})
}