jx/internal/folio/ref.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)
}