230 lines
5.6 KiB
Go
230 lines
5.6 KiB
Go
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
|
|
|
package folio
|
|
|
|
import (
|
|
"encoding/json"
|
|
"gopkg.in/yaml.v3"
|
|
"decl/internal/mapper"
|
|
"decl/internal/data"
|
|
"decl/internal/transport"
|
|
"net/url"
|
|
"log/slog"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
var (
|
|
ErrRefMapperMismatch = errors.New("Ref type does not the provided mapper")
|
|
)
|
|
|
|
type Ref struct {
|
|
Uri URI `json:"uri" yaml:"uri"`
|
|
RefType ReferenceType `json:"type,omitempty" yaml:"type,omitempty"`
|
|
documentMapper mapper.Store[URI, *Document]
|
|
resourceMapper mapper.Store[URI, *Declaration]
|
|
}
|
|
|
|
type decodeRef Ref
|
|
|
|
func NewRef() *Ref {
|
|
return &Ref {
|
|
|
|
}
|
|
}
|
|
|
|
func (r *ReferenceType) UnmarshalValue(value string) error {
|
|
switch value {
|
|
case string(ReferenceTypeResource), string(ReferenceTypeDocument):
|
|
*r = ReferenceType(value)
|
|
default:
|
|
*r = ReferenceTypeResource
|
|
//return ErrInvalidReferenceType
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *ReferenceType) UnmarshalJSON(jsonData []byte) error {
|
|
var s string
|
|
if unmarshalReferenceTypeErr := json.Unmarshal(jsonData, &s); unmarshalReferenceTypeErr != nil {
|
|
return unmarshalReferenceTypeErr
|
|
}
|
|
return r.UnmarshalValue(s)
|
|
}
|
|
|
|
func (r *ReferenceType) UnmarshalYAML(value *yaml.Node) error {
|
|
var s string
|
|
if err := value.Decode(&s); err != nil {
|
|
return err
|
|
}
|
|
return r.UnmarshalValue(s)
|
|
}
|
|
|
|
func (r *Ref) UnmarshalValue(value *decodeRef) error {
|
|
slog.Info("Ref.UnmarshalValue", "decode", value)
|
|
r.Uri = value.Uri
|
|
switch value.RefType {
|
|
case ReferenceTypeResource:
|
|
r.RefType = value.RefType
|
|
case ReferenceTypeDocument:
|
|
r.RefType = value.RefType
|
|
r.SetMapper(DocumentRegistry.UriMap)
|
|
default:
|
|
r.RefType = ReferenceTypeResource
|
|
//return ErrInvalidReferenceType
|
|
}
|
|
slog.Info("Ref.UnmarshalValue", "stored", *r)
|
|
return nil
|
|
}
|
|
|
|
func (r *Ref) UnmarshalJSON(jsonData []byte) error {
|
|
decodeJsonToRef := &decodeRef{}
|
|
if unmarshalReferenceTypeErr := json.Unmarshal(jsonData, decodeJsonToRef); unmarshalReferenceTypeErr != nil {
|
|
return unmarshalReferenceTypeErr
|
|
}
|
|
return r.UnmarshalValue(decodeJsonToRef)
|
|
}
|
|
|
|
func (r *Ref) UnmarshalYAML(value *yaml.Node) error {
|
|
decodeYamlToRef := &decodeRef{}
|
|
if err := value.Decode(decodeYamlToRef); err != nil {
|
|
return err
|
|
}
|
|
return r.UnmarshalValue(decodeYamlToRef)
|
|
}
|
|
|
|
func (r *Ref) SetMapper(m any) error {
|
|
var ok bool
|
|
switch r.RefType {
|
|
case ReferenceTypeResource:
|
|
if r.resourceMapper, ok = m.(mapper.Store[URI, *Declaration]); ! ok {
|
|
return fmt.Errorf("%w - %T is not a %s", ErrRefMapperMismatch, m, r.RefType)
|
|
}
|
|
case ReferenceTypeDocument:
|
|
if r.documentMapper, ok = m.(mapper.Store[URI, *Document]); ! ok {
|
|
return fmt.Errorf("%w - %T is not a %s", ErrRefMapperMismatch, m, r.RefType)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Dereference[T ReferenceTypes](uri URI, look mapper.Map[URI, T]) T {
|
|
if uri != "" && look != nil {
|
|
if v, ok := look.Get(uri); ok {
|
|
slog.Info("Ref::Dereference()", "value", v, "mapper", look)
|
|
return v
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Ref) Lookup(look any) ContentReadWriter {
|
|
slog.Info("Ref.Lookup()", "ref", r, "mapper", look)
|
|
switch r.RefType {
|
|
case ReferenceTypeResource:
|
|
if resourceDeclaration := Dereference(r.Uri, look.(mapper.Store[URI, *Declaration])); resourceDeclaration != nil {
|
|
return resourceDeclaration.GetContentReadWriter()
|
|
}
|
|
case ReferenceTypeDocument:
|
|
if document := Dereference(r.Uri, look.(mapper.Store[URI, *Document])); document != nil {
|
|
return document.GetContentReadWriter()
|
|
}
|
|
}
|
|
return r.Uri
|
|
}
|
|
|
|
func (r *Ref) Dereference(look any) any {
|
|
switch r.RefType {
|
|
case ReferenceTypeResource:
|
|
if look == nil {
|
|
return Dereference(r.Uri, r.resourceMapper)
|
|
} else {
|
|
return Dereference(r.Uri, look.(mapper.Store[URI, *Declaration]))
|
|
}
|
|
case ReferenceTypeDocument:
|
|
if look == nil {
|
|
return Dereference(r.Uri, r.documentMapper)
|
|
} else {
|
|
return Dereference(r.Uri, look.(mapper.Store[URI, *Document]))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Ref) DereferenceDefault() any {
|
|
switch r.RefType {
|
|
case ReferenceTypeResource:
|
|
return Dereference(r.Uri, r.resourceMapper)
|
|
case ReferenceTypeDocument:
|
|
return Dereference(r.Uri, r.documentMapper)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Ref) Parse() data.URIParser {
|
|
return r.Uri.Parse()
|
|
}
|
|
|
|
func (r *Ref) Exists() bool {
|
|
return r.Uri.Exists()
|
|
}
|
|
|
|
func (r *Ref) ContentReaderStream() (*transport.Reader, error) {
|
|
return r.Uri.ContentReaderStream()
|
|
}
|
|
|
|
func (r *Ref) ContentWriterStream() (*transport.Writer, error) {
|
|
return r.Uri.ContentWriterStream()
|
|
}
|
|
|
|
func (r *Ref) GetContent(w io.Writer) (contentReader io.ReadCloser, err error) {
|
|
target := r.DereferenceDefault()
|
|
if targetContent, ok := target.(data.ContentGetSetter); ok {
|
|
return targetContent.GetContent(w)
|
|
}
|
|
return nil, fmt.Errorf("Ref target does not support ContentGetSetter: %s, %s, %#v", r.RefType, r.Uri, target)
|
|
}
|
|
|
|
func (r *Ref) SetContent(contentReader io.Reader) error {
|
|
target := r.DereferenceDefault()
|
|
if targetContent, ok := target.(data.ContentGetSetter); ok {
|
|
return targetContent.SetContent(contentReader)
|
|
}
|
|
return fmt.Errorf("Ref target does not support ContentGetSetter: %s, %s, %#v", r.RefType, r.Uri, target)
|
|
}
|
|
|
|
func (r *Ref) Reader() (reader io.ReadCloser, err error) {
|
|
switch r.RefType {
|
|
case ReferenceTypeResource:
|
|
return r.ContentReaderStream()
|
|
case ReferenceTypeDocument:
|
|
reader, err = r.GetContent(nil)
|
|
if reader == nil {
|
|
return r.ContentReaderStream()
|
|
}
|
|
return
|
|
}
|
|
return nil, ErrInvalidReferenceType
|
|
}
|
|
|
|
func (r *Ref) String() string {
|
|
return r.Uri.String()
|
|
}
|
|
|
|
func (r *Ref) SetURL(url *url.URL) {
|
|
r.Uri.SetURL(url)
|
|
}
|
|
|
|
func (r *Ref) Extension() (string, string) {
|
|
return r.Uri.Extension()
|
|
}
|
|
|
|
func (r *Ref) IsEmpty() bool {
|
|
return r.Uri.IsEmpty()
|
|
}
|
|
|
|
func (r *Ref) FindIn(s *SearchPath) {
|
|
r.Uri.FindIn(s)
|
|
}
|