|
| 1 | +package cataloghtml |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "html/template" |
| 6 | + "os" |
| 7 | + "path" |
| 8 | + "path/filepath" |
| 9 | + "reflect" |
| 10 | + |
| 11 | + "github.com/warpfork/warpforge/pkg/workspace" |
| 12 | + "github.com/warpfork/warpforge/wfapi" |
| 13 | +) |
| 14 | + |
| 15 | +type SiteConfig struct { |
| 16 | + Ctx context.Context |
| 17 | + |
| 18 | + // Data Access Broker for getting Catalog info. |
| 19 | + // Some functions pass around data in memory, |
| 20 | + // but sometimes those objects just contain CIDs, which we'll need to go load. |
| 21 | + // This has helper functions that do the loading. |
| 22 | + // Arguably should be a parameter, but would end up in almost every single function, so, eh. |
| 23 | + Cat_dab workspace.Catalog |
| 24 | + |
| 25 | + // A plain string for output path prefix is used because golang still lacks |
| 26 | + // an interface for filesystem *writing* -- io/fs is only reading. Sigh. |
| 27 | + OutputPath string |
| 28 | + |
| 29 | + // Set to "/" if you'll be publishing at the root of a subdomain. |
| 30 | + URLPrefix string |
| 31 | +} |
| 32 | + |
| 33 | +func (cfg SiteConfig) tfuncs() map[string]interface{} { |
| 34 | + return map[string]interface{}{ |
| 35 | + "string": func(x interface{}) string { // golang would you please shut the fuck up and let me be productive, honestly |
| 36 | + // this is for things that are literally typedefs of string but the template package isn't smart enough to be calm about unboxing it. |
| 37 | + return reflect.ValueOf(x).String() |
| 38 | + }, |
| 39 | + "url": func(parts ...string) string { |
| 40 | + return path.Join(append([]string{cfg.URLPrefix}, parts...)...) |
| 41 | + }, |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +func (cfg SiteConfig) CatalogAndChildrenToHtml() error { |
| 46 | + if err := cfg.CatalogToHtml(); err != nil { |
| 47 | + return err |
| 48 | + } |
| 49 | + modNames := cfg.Cat_dab.Modules() |
| 50 | + for _, modName := range modNames { |
| 51 | + catMod, err := cfg.Cat_dab.GetModule(wfapi.CatalogRef{modName, "", ""}) |
| 52 | + if err != nil { |
| 53 | + return err |
| 54 | + } |
| 55 | + if err := cfg.CatalogModuleAndChildrenToHtml(*catMod); err != nil { |
| 56 | + return err |
| 57 | + } |
| 58 | + } |
| 59 | + return nil |
| 60 | +} |
| 61 | + |
| 62 | +// CatalogToHtml generates a root page that links to all the modules. |
| 63 | +// |
| 64 | +// This function has no parameters because it uses the DAB in the SiteConfig entirely. |
| 65 | +func (cfg SiteConfig) CatalogToHtml() error { |
| 66 | + // 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. |
| 67 | + if err := os.MkdirAll(cfg.OutputPath, 0775); err != nil { |
| 68 | + return err |
| 69 | + } |
| 70 | + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) |
| 71 | + if err != nil { |
| 72 | + return err |
| 73 | + } |
| 74 | + defer f.Close() |
| 75 | + |
| 76 | + // TODO: it's completely bork that we don't have access to the CIDs here. workspace.Catalog is Not Good right now. |
| 77 | + // TODO: this probably needs sorting to be stable. |
| 78 | + // Future: we should have a CID of the entire catalog tree root snapshot somewhere, too. (It should probably use prolly trees or something, though, which is not available as a convenient library yet.) |
| 79 | + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` |
| 80 | + <html> |
| 81 | + <div style="border: 1px solid; padding 0.5em;"> |
| 82 | + <h1 style="display:inline">catalog</h1> |
| 83 | + </div> |
| 84 | + <h2>modules</h2> |
| 85 | + <ul> |
| 86 | + {{- range $moduleName := . }} |
| 87 | + <li><a href="{{ (url (string $moduleName) "index.html") }}">{{ $moduleName }}</a></li> |
| 88 | + {{- end }} |
| 89 | + </ul> |
| 90 | + </html> |
| 91 | + `)) |
| 92 | + return t.Execute(f, cfg.Cat_dab.Modules()) |
| 93 | +} |
| 94 | + |
| 95 | +func (cfg SiteConfig) CatalogModuleAndChildrenToHtml(catMod wfapi.CatalogModule) error { |
| 96 | + if err := cfg.CatalogModuleToHtml(catMod); err != nil { |
| 97 | + return err |
| 98 | + } |
| 99 | + for _, releaseName := range catMod.Releases.Keys { |
| 100 | + rel, err := cfg.Cat_dab.GetRelease(wfapi.CatalogRef{catMod.Name, releaseName, ""}) |
| 101 | + if err != nil { |
| 102 | + return err |
| 103 | + } |
| 104 | + if err := cfg.ReleaseToHtml(catMod, *rel); err != nil { |
| 105 | + return err |
| 106 | + } |
| 107 | + } |
| 108 | + return nil |
| 109 | +} |
| 110 | + |
| 111 | +func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { |
| 112 | + if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name)), 0775); err != nil { |
| 113 | + return err |
| 114 | + } |
| 115 | + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) |
| 116 | + if err != nil { |
| 117 | + return err |
| 118 | + } |
| 119 | + defer f.Close() |
| 120 | + |
| 121 | + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` |
| 122 | + <html> |
| 123 | + <div style="border: 1px solid; padding 0.5em;"> |
| 124 | + <i>module:</i> |
| 125 | + <h1 style="display:inline">{{ .Name }}</h1> |
| 126 | + </div> |
| 127 | + (<a href="{{ (url "index.html") }}">back to root</a>) |
| 128 | + <h2>releases</h2> |
| 129 | + <ul> |
| 130 | + {{- $dot := . -}} |
| 131 | + {{- range $releaseKey := .Releases.Keys }} |
| 132 | + <li><a href="{{ (url (string $dot.Name) (string $releaseKey) "index.html") }}">{{ $releaseKey }}</a> <small>(cid: {{ index $dot.Releases.Values $releaseKey }})</small></li> |
| 133 | + {{- end }} |
| 134 | + </ul> |
| 135 | + <h2>metadata</h2> |
| 136 | + {{- range $metadataKey := .Metadata.Keys }} |
| 137 | + <dt>{{ $metadataKey }}</dt><dd>{{ index $dot.Metadata.Values $metadataKey }}</dd> |
| 138 | + {{- end }} |
| 139 | + </html> |
| 140 | + `)) |
| 141 | + return t.Execute(f, catMod) |
| 142 | +} |
| 143 | + |
| 144 | +func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.CatalogRelease) error { |
| 145 | + if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName)), 0775); err != nil { |
| 146 | + return err |
| 147 | + } |
| 148 | + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) |
| 149 | + if err != nil { |
| 150 | + return err |
| 151 | + } |
| 152 | + defer f.Close() |
| 153 | + |
| 154 | + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` |
| 155 | + <html> |
| 156 | + <div style="border: 1px solid; padding 0.5em;"> |
| 157 | + <i>module:</i> |
| 158 | + <h1 style="display:inline">{{ .Module.Name }}</h1> |
| 159 | + <i>release:</i> |
| 160 | + <h1 style="display:inline">{{ .Release.ReleaseName }}</h1> |
| 161 | + </div> |
| 162 | + (<a href="{{ (url "index.html") }}">back to root</a>; <a href="{{ (url (string .Module.Name) "index.html") }}">back to module index</a>) |
| 163 | + <h2>items</h2> |
| 164 | + <ul> |
| 165 | + {{- $dot := .Release -}} |
| 166 | + {{- range $itemKey := .Release.Items.Keys }} |
| 167 | + <li>{{ $itemKey }} : {{ index $dot.Items.Values $itemKey }}</li> |
| 168 | + {{- end }} |
| 169 | + </ul> |
| 170 | + <h2>metadata</h2> |
| 171 | + {{- range $metadataKey := .Release.Metadata.Keys }} |
| 172 | + <dt>{{ $metadataKey }}</dt><dd>{{ index $dot.Metadata.Values $metadataKey }}</dd> |
| 173 | + {{- end }} |
| 174 | + </html> |
| 175 | + `)) |
| 176 | + return t.Execute(f, map[string]interface{}{ |
| 177 | + "Module": catMod, |
| 178 | + "Release": rel, |
| 179 | + }) |
| 180 | +} |
0 commit comments