// Copyright 2024 Matthew Rich . All rights reserved. package resource import ( "context" "fmt" "net/url" "path/filepath" "decl/internal/data" "decl/internal/folio" "log/slog" "errors" ) type UriSchemeValidator func(scheme string) bool type UriNormalize func() error type Common struct { SchemeCheck UriSchemeValidator `json:"-" yaml:"-"` NormalizePath UriNormalize `json:"-" yaml:"-"` includeQueryParamsInURI bool `json:"-" yaml:"-"` resourceType TypeName `json:"-" yaml:"-"` parsedURI *url.URL `json:"-" yaml:"-"` Path string `json:"path,omitempty" yaml:"path,omitempty"` absPath string `json:"-" yaml:"-"` exttype string `json:"-" yaml:"-"` fileext string `json:"-" yaml:"-"` normalizePath bool `json:"-" yaml:"-"` State string `json:"state,omitempty" yaml:"state,omitempty"` config data.ConfigurationValueGetter Resources data.ResourceMapper `json:"-" yaml:"-"` Errors []error `json:"-" yaml:"-"` } func NewCommon(resourceType TypeName, includeQueryParams bool) *Common { c := &Common{ includeQueryParamsInURI: includeQueryParams, resourceType: resourceType } c.SchemeCheck = c.IsValidResourceScheme c.NormalizePath = c.NormalizeFilePath return c } func (c *Common) IsValidResourceScheme(scheme string) bool { return c.Type() == scheme } func (c *Common) ContentType() string { if c.parsedURI.Scheme != "file" { return c.parsedURI.Scheme } return c.exttype } func (c *Common) SetResourceMapper(resources data.ResourceMapper) { c.Resources = resources } func (c *Common) Clone() *Common { return &Common { SchemeCheck: c.SchemeCheck, NormalizePath: c.NormalizePath, includeQueryParamsInURI: c.includeQueryParamsInURI, resourceType: c.resourceType, parsedURI: c.parsedURI, Path: c.Path, absPath: c.absPath, exttype: c.exttype, fileext: c.fileext, normalizePath: c.normalizePath, State: c.State, config: c.config, Resources: c.Resources, } } func (c *Common) PathNormalization(flag bool) { c.normalizePath = flag } func (c *Common) URIPath() string { return c.Path } func (c *Common) URI() folio.URI { slog.Info("Common.URI", "parsed", c.parsedURI) return folio.URI(c.parsedURI.String()) } func (c *Common) SetParsedURI(u data.URIParser) (err error) { if u != nil { slog.Info("Common.SetParsedURI()", "parsed", u, "uri", c) c.parsedURI = u.URL() c.exttype, c.fileext = u.Extension() if c.SchemeCheck(c.parsedURI.Scheme) { if c.includeQueryParamsInURI { c.Path = filepath.Join(c.parsedURI.Hostname(), c.parsedURI.RequestURI()) } else { c.Path = filepath.Join(c.parsedURI.Hostname(), c.parsedURI.Path) } if c.config != nil { if prefixPath, configErr := c.config.GetValue("prefix"); configErr == nil { c.Path = filepath.Join(prefixPath.(string), c.Path) } } if c.absPath, err = filepath.Abs(c.Path); err != nil { return } if err = c.NormalizePath(); err != nil { return } return } } err = fmt.Errorf("%w: %s is not a %s resource, parsed: %t", ErrInvalidResourceURI, c.URI(), c.Type(), (u != nil)) return } func (c *Common) UseConfig(config data.ConfigurationValueGetter) { c.config = config } func (c *Common) ResolveId(ctx context.Context) string { var err error if c.absPath, err = filepath.Abs(c.Path); err != nil { panic(err) } if err = c.NormalizePath(); err != nil { panic(err) } return c.Path } // Common path normalization for a file resource. func (c *Common) NormalizeFilePath() (err error) { if c.normalizePath { c.Path = c.absPath } return } func (c *Common) Type() string { return string(c.resourceType) } // If a resource update has errors but the resource is not actually absent func (c *Common) IsResourceInconsistent() (result bool) { for _, err := range c.Errors { if ! errors.Is(err, ErrResourceStateAbsent) { result = true } } return } func (c *Common) AddError(err error) (error) { if err != nil { c.Errors = append(c.Errors, err) } return err }