Skip to content

Commit 6c7210d

Browse files
authored
Merge pull request #16 from warpfork/catalogsite
catalogsite: first pass
2 parents f2eb65a + 90edaf3 commit 6c7210d

6 files changed

Lines changed: 315 additions & 0 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{{- /*
2+
This template is for the index page made at the root of the catalog html view.
3+
It just lists and links to all the modules in the catalog.
4+
5+
A couple of todos for this document:
6+
- We should print CIDs of the modules!
7+
... But we don't right now because the our accessor functions don't make it easy to get. (Work needed on workspace.Catalog.)
8+
- Farther future: we should have a CID of the entire catalog tree root snapshot somewhere, too.
9+
... But we don't right now, because that should probably use prolly trees (or some other scalable hash tree thing), which is not available and rigged up as a convenient library yet.
10+
11+
*/ -}}
12+
<html>
13+
<head>
14+
<link rel="stylesheet" href="{{ (url "css.css") }}" />
15+
</head>
16+
<body>
17+
<div style="border: 1px solid; padding 0.5em;">
18+
<h1 style="display:inline">catalog</h1>
19+
</div>
20+
<h2>modules</h2>
21+
<ul>
22+
{{- range $moduleName := . }}
23+
<li><a href="{{ (url (string $moduleName) "_module.html") }}">{{ $moduleName }}</a></li>
24+
{{- end }}
25+
</ul>
26+
</body>
27+
</html>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" href="{{ (url "css.css") }}" />
4+
</head>
5+
<body>
6+
<div style="border: 1px solid; padding 0.5em;">
7+
<i>module:</i>
8+
<h1 style="display:inline">{{ .Name }}</h1>
9+
</div>
10+
(<a href="{{ (url "index.html") }}">back to root</a>)
11+
<h2>releases</h2>
12+
<ul>
13+
{{- $dot := . -}}
14+
{{- range $releaseKey := .Releases.Keys }}
15+
<li><a href="{{ (url (string $dot.Name) "_releases" (print $releaseKey ".html")) }}">{{ $releaseKey }}</a> <small>(cid: {{ index $dot.Releases.Values $releaseKey }})</small></li>
16+
{{- end }}
17+
</ul>
18+
<h2>metadata</h2>
19+
{{- range $metadataKey := .Metadata.Keys }}
20+
<dt>{{ $metadataKey }}</dt><dd>{{ index $dot.Metadata.Values $metadataKey }}</dd>
21+
{{- end }}
22+
</body>
23+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" href="{{ (url "css.css") }}" />
4+
</head>
5+
<body>
6+
<div style="border: 1px solid; padding 0.5em;">
7+
<i>module:</i>
8+
<h1 style="display:inline">{{ .Module.Name }}</h1>
9+
<i>release:</i>
10+
<h1 style="display:inline">{{ .Release.ReleaseName }}</h1>
11+
</div>
12+
(<a href="{{ (url "index.html") }}">back to root</a>; <a href="{{ (url (string .Module.Name) "_module.html") }}">back to module index</a>)
13+
<h2>items</h2>
14+
<ul>
15+
{{- $dot := .Release -}}
16+
{{- range $itemKey := .Release.Items.Keys }}
17+
<li>{{ $itemKey }} : {{ index $dot.Items.Values $itemKey }}</li>
18+
{{- end }}
19+
</ul>
20+
<h2>metadata</h2>
21+
{{- range $metadataKey := .Release.Metadata.Keys }}
22+
<dt>{{ $metadataKey }}</dt><dd>{{ index $dot.Metadata.Values $metadataKey }}</dd>
23+
{{- end }}
24+
</body>
25+
</html>

pkg/cataloghtml/cataloghtml.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package cataloghtml
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"html/template"
7+
"os"
8+
"path"
9+
"path/filepath"
10+
"reflect"
11+
12+
"github.com/warpfork/warpforge/pkg/workspace"
13+
"github.com/warpfork/warpforge/wfapi"
14+
)
15+
16+
var (
17+
//go:embed catalogIndex.tmpl.html
18+
catalogIndexTemplate string
19+
20+
//go:embed catalogModule.tmpl.html
21+
catalogModuleTemplate string
22+
23+
//go:embed catalogRelease.tmpl.html
24+
catalogReleaseTemplate string
25+
26+
//go:embed css.css
27+
cssBody []byte
28+
29+
// FUTURE: consider the use of `embed.FS` and `template.ParseFS()`, if there grow to be many files here.
30+
// It has slightly less compile-time safety checks on filenames, though.
31+
)
32+
33+
type SiteConfig struct {
34+
Ctx context.Context
35+
36+
// Data Access Broker for getting Catalog info.
37+
// Some functions pass around data in memory,
38+
// but sometimes those objects just contain CIDs, which we'll need to go load.
39+
// This has helper functions that do the loading.
40+
// Arguably should be a parameter, but would end up in almost every single function, so, eh.
41+
Cat_dab workspace.Catalog
42+
43+
// A plain string for output path prefix is used because golang still lacks
44+
// an interface for filesystem *writing* -- io/fs is only reading. Sigh.
45+
OutputPath string
46+
47+
// Set to "/" if you'll be publishing at the root of a subdomain.
48+
URLPrefix string
49+
}
50+
51+
func (cfg SiteConfig) tfuncs() map[string]interface{} {
52+
return map[string]interface{}{
53+
"string": func(x interface{}) string {
54+
// Very small helper function to stringify things.
55+
// This is useful for things that are literally typedefs of string but the template package isn't smart enough to be calm about unboxing it.
56+
// (It also does return something for values of non-string types, but not something very useful.)
57+
return reflect.ValueOf(x).String()
58+
},
59+
"url": func(parts ...string) string {
60+
return path.Join(append([]string{cfg.URLPrefix}, parts...)...)
61+
},
62+
}
63+
}
64+
65+
// CatalogAndChildrenToHtml performs CatalogToHtml, and also
66+
// procedes to invoke the html'ing of all modules within.
67+
// Additionally, it does all the other "once" things
68+
// (namely, outputs a copy of the css).
69+
//
70+
// Errors:
71+
//
72+
// - warpforge-error-io -- in case of errors writing out the new html content.
73+
// - warpforge-error-internal -- in case of templating errors.
74+
// - warpforge-error-catalog-invalid -- in case the catalog data is invalid.
75+
// - warpforge-error-catalog-parse -- in case the catalog data failed to parse entirely.
76+
func (cfg SiteConfig) CatalogAndChildrenToHtml() error {
77+
// Emit catalog index.
78+
if err := cfg.CatalogToHtml(); err != nil {
79+
return err
80+
}
81+
// Emit the "once" stuff.
82+
if err := os.WriteFile(filepath.Join(cfg.OutputPath, "css.css"), cssBody, 0644); err != nil {
83+
return wfapi.ErrorIo("couldn't open file for css as part of cataloghtml emission", nil, err)
84+
}
85+
// Emit all modules within.
86+
modNames := cfg.Cat_dab.Modules()
87+
for _, modName := range modNames {
88+
catMod, err := cfg.Cat_dab.GetModule(wfapi.CatalogRef{modName, "", ""})
89+
if err != nil {
90+
return err
91+
}
92+
if err := cfg.CatalogModuleAndChildrenToHtml(*catMod); err != nil {
93+
return err
94+
}
95+
}
96+
return nil
97+
}
98+
99+
// doTemplate does the common bits of making files, processing the template,
100+
// and getting the output where it needs to go.
101+
//
102+
// Errors:
103+
//
104+
// - warpforge-error-io -- in case of errors writing out the new html content.
105+
// - warpforge-error-internal -- in case of templating errors.
106+
func (cfg SiteConfig) doTemplate(outputPath string, tmpl string, data interface{}) error {
107+
if err := os.MkdirAll(filepath.Dir(outputPath), 0775); err != nil {
108+
return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err)
109+
}
110+
f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)
111+
if err != nil {
112+
return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err)
113+
}
114+
defer f.Close()
115+
116+
t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(tmpl))
117+
if err := t.Execute(f, data); err != nil {
118+
return wfapi.ErrorInternal("templating failed", err)
119+
}
120+
return nil
121+
}
122+
123+
// CatalogToHtml generates a root page that links to all the modules.
124+
//
125+
// This function has no parameters because it uses the DAB in the SiteConfig entirely.
126+
//
127+
// Errors:
128+
//
129+
// - warpforge-error-io -- in case of errors writing out the new html content.
130+
// - warpforge-error-internal -- in case of templating errors.
131+
func (cfg SiteConfig) CatalogToHtml() error {
132+
// Future: It's perhaps a bit odd that this uses the workspace.Catalog object instead of the API object. We probably haven't hammered out appropriate data access helpers yet.
133+
return cfg.doTemplate(
134+
filepath.Join(cfg.OutputPath, "index.html"),
135+
catalogIndexTemplate,
136+
cfg.Cat_dab.Modules(),
137+
)
138+
}
139+
140+
// CatalogModuleAndChildrenToHtml performs CatalogModuleToHtml, and also
141+
// procedes to invoke the html'ing of all releases within.
142+
//
143+
// Errors:
144+
//
145+
// - warpforge-error-io -- in case of errors writing out the new html content.
146+
// - warpforge-error-internal -- in case of templating errors.
147+
// - warpforge-error-catalog-invalid -- in case the catalog data is invalid.
148+
// - warpforge-error-catalog-parse -- in case the catalog data failed to parse entirely.
149+
func (cfg SiteConfig) CatalogModuleAndChildrenToHtml(catMod wfapi.CatalogModule) error {
150+
if err := cfg.CatalogModuleToHtml(catMod); err != nil {
151+
return err
152+
}
153+
for _, releaseName := range catMod.Releases.Keys {
154+
rel, err := cfg.Cat_dab.GetRelease(wfapi.CatalogRef{catMod.Name, releaseName, ""})
155+
if err != nil {
156+
return err
157+
}
158+
if err := cfg.ReleaseToHtml(catMod, *rel); err != nil {
159+
return err
160+
}
161+
}
162+
return nil
163+
}
164+
165+
// CatalogModuleToHtml generates a page for a module which enumerates
166+
// and links to all the releases within it,
167+
// as well as enumerates all the metadata attached to the catalog module.
168+
//
169+
// Errors:
170+
//
171+
// - warpforge-error-io -- in case of errors writing out the new html content.
172+
// - warpforge-error-internal -- in case of templating errors.
173+
func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error {
174+
return cfg.doTemplate(
175+
filepath.Join(cfg.OutputPath, string(catMod.Name), "_module.html"),
176+
catalogModuleTemplate,
177+
catMod,
178+
)
179+
}
180+
181+
// CatalogModuleToHtml generates a page for a release within a catalog module
182+
// which enumerates all the items within it,
183+
// as well as enumerates all the metadata attached to the release.
184+
//
185+
// Possible but not-yet-implemented future features of this output might include:
186+
// linking better to metadata that references other documents (such as Replays);
187+
// links to neighboring (e.g. forward and previous) releases; etc.
188+
//
189+
// Errors:
190+
//
191+
// - warpforge-error-io -- in case of errors writing out the new html content.
192+
// - warpforge-error-internal -- in case of templating errors.
193+
func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.CatalogRelease) error {
194+
return cfg.doTemplate(
195+
filepath.Join(cfg.OutputPath, string(catMod.Name), "_releases", string(rel.ReleaseName)+".html"),
196+
catalogReleaseTemplate,
197+
map[string]interface{}{
198+
"Module": catMod,
199+
"Release": rel,
200+
},
201+
)
202+
}

pkg/cataloghtml/css.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
body {
3+
color: #F00;
4+
}
5+
*/

pkg/cataloghtml/demo_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package cataloghtml
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/warpfork/warpforge/pkg/workspace"
10+
)
11+
12+
func TestWhee(t *testing.T) {
13+
cwd, err := os.Getwd()
14+
if err != nil {
15+
panic(err)
16+
}
17+
cat_dab, err := workspace.OpenCatalog(os.DirFS("/"), filepath.Join(cwd, "../../.warpforge/catalog")[1:])
18+
if err != nil {
19+
panic(err)
20+
}
21+
// Output paths are currently hardcoded and can be seen in the config object below.
22+
// No actual assertions take place on this; the "test" is manually looking at that output.
23+
cfg := SiteConfig{
24+
Ctx: context.Background(),
25+
Cat_dab: cat_dab,
26+
OutputPath: "/tmp/wf-test-cathtml/",
27+
URLPrefix: "/tmp/wf-test-cathtml/",
28+
}
29+
os.RemoveAll(cfg.OutputPath)
30+
if err := cfg.CatalogAndChildrenToHtml(); err != nil {
31+
panic(err)
32+
}
33+
}

0 commit comments

Comments
 (0)