diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-02-11 16:20:24 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-02-21 17:56:41 +0100 |
commit | 90da7664bf1f3a0ca2e18144b5deacf532c6e3cf (patch) | |
tree | 78d8ac72ebb2ccee4ca4bbeeb9add3365c743e90 /resources | |
parent | 0afec0a9f4aace1f5f4af6822aeda6223ee3e3a9 (diff) |
Add page fragments support to Related
The main topic of this commit is that you can now index fragments (content heading identifiers) when calling `.Related`.
You can do this by:
* Configure one or more indices with type `fragments`
* The name of those index configurations maps to an (optional) front matter slice with fragment references. This allows you to link
page<->fragment and page<->page.
* This also will index all the fragments (heading identifiers) of the pages.
It's also possible to use type `fragments` indices in shortcode, e.g.:
```
{{ $related := site.RegularPages.Related .Page }}
```
But, and this is important, you need to include the shortcode using the `{{<` delimiter. Not doing so will create infinite loops and timeouts.
This commit also:
* Adds two new methods to Page: Fragments (can also be used to build ToC) and HeadingsFiltered (this is only used in Related Content with
index type `fragments` and `enableFilter` set to true.
* Consolidates all `.Related*` methods into one, which takes either a `Page` or an options map as its only argument.
* Add `context.Context` to all of the content related Page API. Turns out it wasn't strictly needed for this particular feature, but it will
soon become usefil, e.g. in #9339.
Closes #10711
Updates #9339
Updates #10725
Diffstat (limited to 'resources')
-rw-r--r-- | resources/errorResource.go | 3 | ||||
-rw-r--r-- | resources/image_test.go | 3 | ||||
-rw-r--r-- | resources/page/page.go | 41 | ||||
-rw-r--r-- | resources/page/page_lazy_contentprovider.go | 81 | ||||
-rw-r--r-- | resources/page/page_marshaljson.autogen.go | 34 | ||||
-rw-r--r-- | resources/page/page_nop.go | 35 | ||||
-rw-r--r-- | resources/page/pagegroup.go | 10 | ||||
-rw-r--r-- | resources/page/pagegroup_test.go | 15 | ||||
-rw-r--r-- | resources/page/pages.go | 15 | ||||
-rw-r--r-- | resources/page/pages_related.go | 77 | ||||
-rw-r--r-- | resources/page/pages_related_test.go | 25 | ||||
-rw-r--r-- | resources/page/pages_sort.go | 5 | ||||
-rw-r--r-- | resources/page/pages_sort_test.go | 9 | ||||
-rw-r--r-- | resources/page/pagination_test.go | 23 | ||||
-rw-r--r-- | resources/page/testhelpers_test.go | 34 | ||||
-rw-r--r-- | resources/postpub/postpub.go | 5 | ||||
-rw-r--r-- | resources/resource.go | 3 | ||||
-rw-r--r-- | resources/resource/resourcetypes.go | 6 | ||||
-rw-r--r-- | resources/resource_transformers/integrity/integrity_test.go | 3 | ||||
-rw-r--r-- | resources/resource_transformers/minifier/minify_test.go | 3 | ||||
-rw-r--r-- | resources/transform.go | 5 | ||||
-rw-r--r-- | resources/transform_test.go | 21 |
22 files changed, 251 insertions, 205 deletions
diff --git a/resources/errorResource.go b/resources/errorResource.go index e9411c3db..42edb0bd0 100644 --- a/resources/errorResource.go +++ b/resources/errorResource.go @@ -14,6 +14,7 @@ package resources import ( + "context" "image" "github.com/gohugoio/hugo/common/hugio" @@ -55,7 +56,7 @@ func (e *errorResource) ReadSeekCloser() (hugio.ReadSeekCloser, error) { panic(e.ResourceError) } -func (e *errorResource) Content() (any, error) { +func (e *errorResource) Content(context.Context) (any, error) { panic(e.ResourceError) } diff --git a/resources/image_test.go b/resources/image_test.go index 655454390..3cb1089f4 100644 --- a/resources/image_test.go +++ b/resources/image_test.go @@ -14,6 +14,7 @@ package resources import ( + "context" "fmt" "image" "image/gif" @@ -436,7 +437,7 @@ func TestSVGImageContent(t *testing.T) { svg := fetchResourceForSpec(spec, c, "circle.svg") c.Assert(svg, qt.Not(qt.IsNil)) - content, err := svg.Content() + content, err := svg.Content(context.Background()) c.Assert(err, qt.IsNil) c.Assert(content, hqt.IsSameType, "") c.Assert(content.(string), qt.Contains, `<svg height="100" width="100">`) diff --git a/resources/page/page.go b/resources/page/page.go index eeb2cdb28..00e716e83 100644 --- a/resources/page/page.go +++ b/resources/page/page.go @@ -16,10 +16,12 @@ package page import ( + "context" "html/template" "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/markup/converter" + "github.com/gohugoio/hugo/markup/tableofcontents" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/tpl" @@ -76,40 +78,40 @@ type ChildCareProvider interface { // ContentProvider provides the content related values for a Page. type ContentProvider interface { - Content() (any, error) + Content(context.Context) (any, error) // Plain returns the Page Content stripped of HTML markup. - Plain() string + Plain(context.Context) string // PlainWords returns a string slice from splitting Plain using https://pkg.go.dev/strings#Fields. - PlainWords() []string + PlainWords(context.Context) []string // Summary returns a generated summary of the content. // The breakpoint can be set manually by inserting a summary separator in the source file. - Summary() template.HTML + Summary(context.Context) template.HTML // Truncated returns whether the Summary is truncated or not. - Truncated() bool + Truncated(context.Context) bool // FuzzyWordCount returns the approximate number of words in the content. - FuzzyWordCount() int + FuzzyWordCount(context.Context) int // WordCount returns the number of words in the content. - WordCount() int + WordCount(context.Context) int // ReadingTime returns the reading time based on the length of plain text. - ReadingTime() int + ReadingTime(context.Context) int // Len returns the length of the content. // This is for internal use only. - Len() int + Len(context.Context) int } // ContentRenderer provides the content rendering methods for some content. type ContentRenderer interface { // RenderContent renders the given content. // For internal use only. - RenderContent(content []byte, renderTOC bool) (converter.Result, error) + RenderContent(ctx context.Context, content []byte, renderTOC bool) (converter.Result, error) } // FileProvider provides the source file. @@ -167,6 +169,11 @@ type Page interface { PageWithoutContent } +type PageFragment interface { + resource.ResourceLinksProvider + resource.ResourceMetaProvider +} + // PageMetaProvider provides page metadata, typically provided via front matter. type PageMetaProvider interface { // The 4 page dates @@ -252,7 +259,7 @@ type PageMetaProvider interface { // PageRenderProvider provides a way for a Page to render content. type PageRenderProvider interface { // Render renders the given layout with this Page as context. - Render(layout ...string) (template.HTML, error) + Render(ctx context.Context, layout ...string) (template.HTML, error) // RenderString renders the first value in args with tPaginatorhe content renderer defined // for this Page. // It takes an optional map as a second argument: @@ -260,7 +267,7 @@ type PageRenderProvider interface { // display (“inline”): // - inline or block. If inline (default), surrounding <p></p> on short snippets will be trimmed. // markup (defaults to the Page’s markup) - RenderString(args ...any) (template.HTML, error) + RenderString(ctx context.Context, args ...any) (template.HTML, error) } // PageWithoutContent is the Page without any of the content methods. @@ -323,6 +330,14 @@ type PageWithoutContent interface { // Used in change/dependency tracking. identity.Provider + // Fragments returns the fragments for this page. + Fragments(context.Context) *tableofcontents.Fragments + + // Headings returns the headings for this page when a filter is set. + // This is currently only triggered with the Related content feature + // and the "fragments" type of index. + HeadingsFiltered(context.Context) tableofcontents.Headings + DeprecatedWarningPageMethods } @@ -387,7 +402,7 @@ type SitesProvider interface { // TableOfContentsProvider provides the table of contents for a Page. type TableOfContentsProvider interface { // TableOfContents returns the table of contents for the page rendered as HTML. - TableOfContents() template.HTML + TableOfContents(context.Context) template.HTML } // TranslationsProvider provides access to any translations. diff --git a/resources/page/page_lazy_contentprovider.go b/resources/page/page_lazy_contentprovider.go index 2e4ddc352..e497718f9 100644 --- a/resources/page/page_lazy_contentprovider.go +++ b/resources/page/page_lazy_contentprovider.go @@ -14,6 +14,7 @@ package page import ( + "context" "html/template" "github.com/gohugoio/hugo/lazy" @@ -57,7 +58,7 @@ func NewLazyContentProvider(f func() (OutputFormatContentProvider, error)) *Lazy init: lazy.New(), cp: NopCPageContentRenderer, } - lcp.init.Add(func() (any, error) { + lcp.init.Add(func(context.Context) (any, error) { cp, err := f() if err != nil { return nil, err @@ -72,67 +73,67 @@ func (lcp *LazyContentProvider) Reset() { lcp.init.Reset() } -func (lcp *LazyContentProvider) Content() (any, error) { - lcp.init.Do() - return lcp.cp.Content() +func (lcp *LazyContentProvider) Content(ctx context.Context) (any, error) { + lcp.init.Do(ctx) + return lcp.cp.Content(ctx) } -func (lcp *LazyContentProvider) Plain() string { - lcp.init.Do() - return lcp.cp.Plain() +func (lcp *LazyContentProvider) Plain(ctx context.Context) string { + lcp.init.Do(ctx) + return lcp.cp.Plain(ctx) } -func (lcp *LazyContentProvider) PlainWords() []string { - lcp.init.Do() - return lcp.cp.PlainWords() +func (lcp *LazyContentProvider) PlainWords(ctx context.Context) []string { + lcp.init.Do(ctx) + return lcp.cp.PlainWords(ctx) } -func (lcp *LazyContentProvider) Summary() template.HTML { - lcp.init.Do() - return lcp.cp.Summary() +func (lcp *LazyContentProvider) Summary(ctx context.Context) template.HTML { + lcp.init.Do(ctx) + return lcp.cp.Summary(ctx) } -func (lcp *LazyContentProvider) Truncated() bool { - lcp.init.Do() - return lcp.cp.Truncated() +func (lcp *LazyContentProvider) Truncated(ctx context.Context) bool { + lcp.init.Do(ctx) + return lcp.cp.Truncated(ctx) } -func (lcp *LazyContentProvider) FuzzyWordCount() int { - lcp.init.Do() - return lcp.cp.FuzzyWordCount() +func (lcp *LazyContentProvider) FuzzyWordCount(ctx context.Context) int { + lcp.init.Do(ctx) + return lcp.cp.FuzzyWordCount(ctx) } -func (lcp *LazyContentProvider) WordCount() int { - lcp.init.Do() - return lcp.cp.WordCount() +func (lcp *LazyContentProvider) WordCount(ctx context.Context) int { + lcp.init.Do(ctx) + return lcp.cp.WordCount(ctx) } -func (lcp *LazyContentProvider) ReadingTime() int { - lcp.init.Do() - return lcp.cp.ReadingTime() +func (lcp *LazyContentProvider) ReadingTime(ctx context.Context) int { + lcp.init.Do(ctx) + return lcp.cp.ReadingTime(ctx) } -func (lcp *LazyContentProvider) Len() int { - lcp.init.Do() - return lcp.cp.Len() +func (lcp *LazyContentProvider) Len(ctx context.Context) int { + lcp.init.Do(ctx) + return lcp.cp.Len(ctx) } -func (lcp *LazyContentProvider) Render(layout ...string) (template.HTML, error) { - lcp.init.Do() - return lcp.cp.Render(layout...) +func (lcp *LazyContentProvider) Render(ctx context.Context, layout ...string) (template.HTML, error) { + lcp.init.Do(context.TODO()) + return lcp.cp.Render(ctx, layout...) } -func (lcp *LazyContentProvider) RenderString(args ...any) (template.HTML, error) { - lcp.init.Do() - return lcp.cp.RenderString(args...) +func (lcp *LazyContentProvider) RenderString(ctx context.Context, args ...any) (template.HTML, error) { + lcp.init.Do(ctx) + return lcp.cp.RenderString(ctx, args...) } -func (lcp *LazyContentProvider) TableOfContents() template.HTML { - lcp.init.Do() - return lcp.cp.TableOfContents() +func (lcp *LazyContentProvider) TableOfContents(ctx context.Context) template.HTML { + lcp.init.Do(ctx) + return lcp.cp.TableOfContents(ctx) } -func (lcp *LazyContentProvider) RenderContent(content []byte, renderTOC bool) (converter.Result, error) { - lcp.init.Do() - return lcp.cp.RenderContent(content, renderTOC) +func (lcp *LazyContentProvider) RenderContent(ctx context.Context, content []byte, renderTOC bool) (converter.Result, error) { + lcp.init.Do(ctx) + return lcp.cp.RenderContent(ctx, content, renderTOC) } diff --git a/resources/page/page_marshaljson.autogen.go b/resources/page/page_marshaljson.autogen.go index 373257878..c3524ec36 100644 --- a/resources/page/page_marshaljson.autogen.go +++ b/resources/page/page_marshaljson.autogen.go @@ -25,24 +25,10 @@ import ( "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/navigation" "github.com/gohugoio/hugo/source" - "html/template" "time" ) func MarshalPageToJSON(p Page) ([]byte, error) { - content, err := p.Content() - if err != nil { - return nil, err - } - plain := p.Plain() - plainWords := p.PlainWords() - summary := p.Summary() - truncated := p.Truncated() - fuzzyWordCount := p.FuzzyWordCount() - wordCount := p.WordCount() - readingTime := p.ReadingTime() - length := p.Len() - tableOfContents := p.TableOfContents() rawContent := p.RawContent() resourceType := p.ResourceType() mediaType := p.MediaType() @@ -93,16 +79,6 @@ func MarshalPageToJSON(p Page) ([]byte, error) { getIdentity := p.GetIdentity() s := struct { - Content interface{} - Plain string - PlainWords []string - Summary template.HTML - Truncated bool - FuzzyWordCount int - WordCount int - ReadingTime int - Len int - TableOfContents template.HTML RawContent string ResourceType string MediaType media.Type @@ -152,16 +128,6 @@ func MarshalPageToJSON(p Page) ([]byte, error) { Store *maps.Scratch GetIdentity identity.Identity }{ - Content: content, - Plain: plain, - PlainWords: plainWords, - Summary: summary, - Truncated: truncated, - FuzzyWordCount: fuzzyWordCount, - WordCount: wordCount, - ReadingTime: readingTime, - Len: length, - TableOfContents: tableOfContents, RawContent: rawContent, ResourceType: resourceType, MediaType: mediaType, diff --git a/resources/page/page_nop.go b/resources/page/page_nop.go index c4af3f554..8946926a2 100644 --- a/resources/page/page_nop.go +++ b/resources/page/page_nop.go @@ -17,11 +17,13 @@ package page import ( "bytes" + "context" "html/template" "time" "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/markup/converter" + "github.com/gohugoio/hugo/markup/tableofcontents" "github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/tpl" @@ -105,7 +107,7 @@ func (p *nopPage) BundleType() files.ContentClass { return "" } -func (p *nopPage) Content() (any, error) { +func (p *nopPage) Content(context.Context) (any, error) { return "", nil } @@ -179,7 +181,7 @@ func (p *nopPage) FirstSection() Page { return nil } -func (p *nopPage) FuzzyWordCount() int { +func (p *nopPage) FuzzyWordCount(context.Context) int { return 0 } @@ -279,7 +281,7 @@ func (p *nopPage) Lastmod() (t time.Time) { return } -func (p *nopPage) Len() int { +func (p *nopPage) Len(context.Context) int { return 0 } @@ -363,11 +365,11 @@ func (p *nopPage) Permalink() string { return "" } -func (p *nopPage) Plain() string { +func (p *nopPage) Plain(context.Context) string { return "" } -func (p *nopPage) PlainWords() []string { +func (p *nopPage) PlainWords(context.Context) []string { return nil } @@ -399,7 +401,7 @@ func (p *nopPage) RawContent() string { return "" } -func (p *nopPage) ReadingTime() int { +func (p *nopPage) ReadingTime(context.Context) int { return 0 } @@ -415,11 +417,11 @@ func (p *nopPage) RelRef(argsm map[string]any) (string, error) { return "", nil } -func (p *nopPage) Render(layout ...string) (template.HTML, error) { +func (p *nopPage) Render(ctx context.Context, layout ...string) (template.HTML, error) { return "", nil } -func (p *nopPage) RenderString(args ...any) (template.HTML, error) { +func (p *nopPage) RenderString(ctx context.Context, args ...any) (template.HTML, error) { return "", nil } @@ -475,11 +477,11 @@ func (p *nopPage) String() string { return "nopPage" } -func (p *nopPage) Summary() template.HTML { +func (p *nopPage) Summary(context.Context) template.HTML { return "" } -func (p *nopPage) TableOfContents() template.HTML { +func (p *nopPage) TableOfContents(context.Context) template.HTML { return "" } @@ -499,7 +501,7 @@ func (p *nopPage) Translations() Pages { return nil } -func (p *nopPage) Truncated() bool { +func (p *nopPage) Truncated(context.Context) bool { return false } @@ -519,7 +521,7 @@ func (p *nopPage) Weight() int { return 0 } -func (p *nopPage) WordCount() int { +func (p *nopPage) WordCount(context.Context) int { return 0 } @@ -527,9 +529,16 @@ func (p *nopPage) GetIdentity() identity.Identity { return identity.NewPathIdentity("content", "foo/bar.md") } +func (p *nopPage) Fragments(context.Context) *tableofcontents.Fragments { + return nil +} +func (p *nopPage) HeadingsFiltered(context.Context) tableofcontents.Headings { + return nil +} + type nopContentRenderer int -func (r *nopContentRenderer) RenderContent(content []byte, renderTOC bool) (converter.Result, error) { +func (r *nopContentRenderer) RenderContent(ctx context.Context, content []byte, renderTOC bool) (converter.Result, error) { b := &bytes.Buffer{} return b, nil } diff --git a/resources/page/pagegroup.go b/resources/page/pagegroup.go index 3b32a1fae..bac5d8327 100644 --- a/resources/page/pagegroup.go +++ b/resources/page/pagegroup.go @@ -14,6 +14,7 @@ package page import ( + "context" "errors" "fmt" "reflect" @@ -110,7 +111,7 @@ var ( // GroupBy groups by the value in the given field or method name and with the given order. // Valid values for order is asc, desc, rev and reverse. -func (p Pages) GroupBy(key string, order ...string) (PagesGroup, error) { +func (p Pages) GroupBy(ctx context.Context, key string, order ...string) (PagesGroup, error) { if len(p) < 1 { return nil, nil } @@ -158,7 +159,12 @@ func (p Pages) GroupBy(key string, order ...string) (PagesGroup, error) { case reflect.StructField: fv = ppv.Elem().FieldByName(key) case reflect.Method: - fv = hreflect.GetMethodByName(ppv, key).Call([]reflect.Value{})[0] + var args []reflect.Value + fn := hreflect.GetMethodByName(ppv, key) + if fn.Type().NumIn() > 0 && fn.Type().In(0).Implements(hreflect.ContextInterface) { + args = []reflect.Value{reflect.ValueOf(ctx)} + } + fv = fn.Call(args)[0] } if !fv.IsValid() { continue diff --git a/resources/page/pagegroup_test.go b/resources/page/pagegroup_test.go index ef0d24471..91f05b24a 100644 --- a/resources/page/pagegroup_test.go +++ b/resources/page/pagegroup_test.go @@ -14,6 +14,7 @@ package page import ( + "context" "reflect" "strings" "testing" @@ -68,7 +69,7 @@ func TestGroupByWithFieldNameArg(t *testing.T) { {Key: 3, Pages: Pages{pages[0], pages[1]}}, } - groups, err := pages.GroupBy("Weight") + groups, err := pages.GroupBy(context.Background(), "Weight") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } @@ -85,7 +86,7 @@ func TestGroupByWithMethodNameArg(t *testing.T) { {Key: "section2", Pages: Pages{pages[3], pages[4]}}, } - groups, err := pages.GroupBy("Type") + groups, err := pages.GroupBy(context.Background(), "Type") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } @@ -102,7 +103,7 @@ func TestGroupByWithSectionArg(t *testing.T) { {Key: "section2", Pages: Pages{pages[3], pages[4]}}, } - groups, err := pages.GroupBy("Section") + groups, err := pages.GroupBy(context.Background(), "Section") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } @@ -120,7 +121,7 @@ func TestGroupByInReverseOrder(t *testing.T) { {Key: 1, Pages: Pages{pages[3], pages[4]}}, } - groups, err := pages.GroupBy("Weight", "desc") + groups, err := pages.GroupBy(context.Background(), "Weight", "desc") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } @@ -132,7 +133,7 @@ func TestGroupByInReverseOrder(t *testing.T) { func TestGroupByCalledWithEmptyPages(t *testing.T) { t.Parallel() var pages Pages - groups, err := pages.GroupBy("Weight") + groups, err := pages.GroupBy(context.Background(), "Weight") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } @@ -154,12 +155,12 @@ func TestReverse(t *testing.T) { t.Parallel() pages := preparePageGroupTestPages(t) - groups1, err := pages.GroupBy("Weight", "desc") + groups1, err := pages.GroupBy(context.Background(), "Weight", "desc") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } - groups2, err := pages.GroupBy("Weight") + groups2, err := pages.GroupBy(context.Background(), "Weight") if err != nil { t.Fatalf("Unable to make PagesGroup array: %s", err) } diff --git a/resources/page/pages.go b/resources/page/pages.go index f47af5114..77e56a062 100644 --- a/resources/page/pages.go +++ b/resources/page/pages.go @@ -132,21 +132,6 @@ func (pages Pages) ProbablyEq(other any) bool { return true } -func (ps Pages) removeFirstIfFound(p Page) Pages { - ii := -1 - for i, pp := range ps { - if p.Eq(pp) { - ii = i - break - } - } - - if ii != -1 { - ps = append(ps[:ii], ps[ii+1:]...) - } - return ps -} - // PagesFactory somehow creates some Pages. // We do a lot of lazy Pages initialization in Hugo, so we need a type. type PagesFactory func() Pages diff --git a/resources/page/pages_related.go b/resources/page/pages_related.go index 35bb2965a..74bb1713e 100644 --- a/resources/page/pages_related.go +++ b/resources/page/pages_related.go @@ -14,11 +14,13 @@ package page import ( + "context" "fmt" "sync" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/related" + "github.com/mitchellh/mapstructure" "github.com/spf13/cast" ) @@ -34,74 +36,90 @@ type PageGenealogist interface { // Template example: // {{ $related := .RegularPages.Related . }} - Related(doc related.Document) (Pages, error) + Related(ctx context.Context, opts any) (Pages, error) // Template example: // {{ $related := .RegularPages.RelatedIndices . "tags" "date" }} - RelatedIndices(doc related.Document, indices ...any) (Pages, error) + // Deprecated: Use Related instead. + RelatedIndices(ctx context.Context, doc related.Document, indices ...any) (Pages, error) // Template example: // {{ $related := .RegularPages.RelatedTo ( keyVals "tags" "hugo", "rocks") ( keyVals "date" .Date ) }} - RelatedTo(args ...types.KeyValues) (Pages, error) + // Deprecated: Use Related instead. + RelatedTo(ctx context.Context, args ...types.KeyValues) (Pages, error) } // Related searches all the configured indices with the search keywords from the // supplied document. -func (p Pages) Related(doc related.Document) (Pages, error) { - result, err := p.searchDoc(doc) - if err != nil { - return nil, err +func (p Pages) Related(ctx context.Context, optsv any) (Pages, error) { + if len(p) == 0 { + return nil, nil + } + + var opts related.SearchOpts + switch v := optsv.(type) { + case related.Document: + opts.Document = v + case map[string]any: + if err := mapstructure.WeakDecode(v, &opts); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("invalid argument type %T", optsv) } - if page, ok := doc.(Page); ok { |