diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-01-10 10:55:03 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-02-04 11:37:25 +0700 |
commit | c71e1b106e6011d148cac899f83c4685dee33a22 (patch) | |
tree | c5c7090f0c2398c7771e4908ebcc97aa7714ffd2 /hugolib | |
parent | 0ada40591216572b0e4c6a8ab986b0aa4fb13c13 (diff) |
all: Refactor to nonglobal file systems
Updates #2701
Fixes #2951
Diffstat (limited to 'hugolib')
35 files changed, 1290 insertions, 1127 deletions
diff --git a/hugolib/alias_test.go b/hugolib/alias_test.go index 87bc9b130..22803d22e 100644 --- a/hugolib/alias_test.go +++ b/hugolib/alias_test.go @@ -16,6 +16,10 @@ package hugolib import ( "path/filepath" "testing" + + "github.com/spf13/hugo/deps" + "github.com/spf13/hugo/hugofs" + "github.com/stretchr/testify/require" ) const pageWithAlias = `--- @@ -30,31 +34,37 @@ const aliasTemplate = "<html><body>ALIASTEMPLATE</body></html>" func TestAlias(t *testing.T) { testCommonResetState() - writeSource(t, filepath.Join("content", "page.md"), pageWithAlias) - writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate) - if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil { - t.Fatalf("Failed to build site: %s", err) - } + fs := hugofs.NewMem() + + writeSource(t, fs, filepath.Join("content", "page.md"), pageWithAlias) + writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), basicTemplate) + + buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{}) // the real page - assertFileContent(t, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") + assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") // the alias redirector - assertFileContent(t, filepath.Join("public", "foo", "bar", "index.html"), false, "<meta http-equiv=\"refresh\" content=\"0; ") + assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "<meta http-equiv=\"refresh\" content=\"0; ") } func TestAliasTemplate(t *testing.T) { testCommonResetState() - writeSource(t, filepath.Join("content", "page.md"), pageWithAlias) - writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate) - writeSource(t, filepath.Join("layouts", "alias.html"), aliasTemplate) - if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil { - t.Fatalf("Failed to build site: %s", err) - } + fs := hugofs.NewMem() + + writeSource(t, fs, filepath.Join("content", "page.md"), pageWithAlias) + writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), basicTemplate) + writeSource(t, fs, filepath.Join("layouts", "alias.html"), aliasTemplate) + + sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs}) + + require.NoError(t, err) + + require.NoError(t, sites.Build(BuildCfg{})) // the real page - assertFileContent(t, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") + assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man") // the alias redirector - assertFileContent(t, filepath.Join("public", "foo", "bar", "index.html"), false, "ALIASTEMPLATE") + assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "ALIASTEMPLATE") } diff --git a/hugolib/case_insensitive_test.go b/hugolib/case_insensitive_test.go index ad8351337..eefde1727 100644 --- a/hugolib/case_insensitive_test.go +++ b/hugolib/case_insensitive_test.go @@ -18,6 +18,11 @@ import ( "path/filepath" "strings" "testing" + + "github.com/spf13/viper" + + "github.com/spf13/hugo/deps" + "github.com/spf13/hugo/hugofs" ) var ( @@ -106,22 +111,22 @@ ColorS: ` ) -func caseMixingTestsWriteCommonSources(t *testing.T) { - writeSource(t, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1) - writeSource(t, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2) - writeSource(t, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En) +func caseMixingTestsWriteCommonSources(t *testing.T, fs *hugofs.Fs) { + writeSource(t, fs, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1) + writeSource(t, fs, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2) + writeSource(t, fs, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En) - writeSource(t, "layouts/shortcodes/shortcode.html", ` + writeSource(t, fs, "layouts/shortcodes/shortcode.html", ` Shortcode Page: {{ .Page.Params.COLOR }}|{{ .Page.Params.Colors.Blue }} Shortcode Site: {{ .Page.Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} `) - writeSource(t, "layouts/partials/partial.html", ` + writeSource(t, fs, "layouts/partials/partial.html", ` Partial Page: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }} Partial Site: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} `) - writeSource(t, "config.toml", caseMixingSiteConfigTOML) + writeSource(t, fs, "config.toml", caseMixingSiteConfigTOML) } @@ -139,13 +144,21 @@ func TestCaseInsensitiveConfigurationVariations(t *testing.T) { // page frontmatter: regular fields, blackfriday config, param with nested map testCommonResetState() - caseMixingTestsWriteCommonSources(t) - writeSource(t, filepath.Join("layouts", "_default", "baseof.html"), ` + depsCfg := newTestDepsConfig() + viper.SetFs(depsCfg.Fs.Source) + + caseMixingTestsWriteCommonSources(t, depsCfg.Fs) + + if err := LoadGlobalConfig("", "config.toml"); err != nil { + t.Fatalf("Failed to load config: %s", err) + } + + writeSource(t, depsCfg.Fs, filepath.Join("layouts", "_default", "baseof.html"), ` Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }} {{ block "main" . }}default{{end}}`) - writeSource(t, filepath.Join("layouts", "sect2", "single.html"), ` + writeSource(t, depsCfg.Fs, filepath.Join("layouts", "sect2", "single.html"), ` {{ define "main"}} Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }} Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }} @@ -154,7 +167,7 @@ Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }} {{ end }} `) - writeSource(t, filepath.Join("layouts", "_default", "single.html"), ` + writeSource(t, depsCfg.Fs, filepath.Join("layouts", "_default", "single.html"), ` Page Title: {{ .Title }} Site Title: {{ .Site.Title }} Site Lang Mood: {{ .Site.Language.Params.MOoD }} @@ -164,11 +177,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} {{ partial "partial.html" . }} `) - if err := LoadGlobalConfig("", "config.toml"); err != nil { - t.Fatalf("Failed to load config: %s", err) - } - - sites, err := NewHugoSitesFromConfiguration(DepsCfg{}) + sites, err := NewHugoSitesFromConfiguration(depsCfg) if err != nil { t.Fatalf("Failed to create sites: %s", err) @@ -180,7 +189,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} t.Fatalf("Failed to build sites: %s", err) } - assertFileContent(t, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, + assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, "Page Colors: red|heavenly", "Site Colors: green|yellow", "Site Lang Mood: Happy", @@ -193,7 +202,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} "«Hi»", // angled quotes ) - assertFileContent(t, filepath.Join("public", "en", "sect1", "page1", "index.html"), true, + assertFileContent(t, sites.Fs, filepath.Join("public", "en", "sect1", "page1", "index.html"), true, "Site Colors: Pink|golden", "Page Colors: black|bluesy", "Site Lang Mood: Thoughtful", @@ -202,7 +211,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} "“Hi”", ) - assertFileContent(t, filepath.Join("public", "nn", "sect2", "page2", "index.html"), true, + assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect2", "page2", "index.html"), true, "Page Colors: black|sky", "Site Colors: green|yellow", "Shortcode Page: black|sky", @@ -244,7 +253,15 @@ func TestCaseInsensitiveConfigurationForAllTemplateEngines(t *testing.T) { func doTestCaseInsensitiveConfigurationForTemplateEngine(t *testing.T, suffix string, templateFixer func(s string) string) { testCommonResetState() - caseMixingTestsWriteCommonSources(t) + + fs := hugofs.NewMem() + viper.SetFs(fs.Source) + + caseMixingTestsWriteCommonSources(t, fs) + + if err := LoadGlobalConfig("", "config.toml"); err != nil { + t.Fatalf("Failed to load config: %s", err) + } t.Log("Testing", suffix) @@ -261,13 +278,9 @@ p t.Log(templ) - writeSource(t, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ) - - if err := LoadGlobalConfig("", "config.toml"); err != nil { - t.Fatalf("Failed to load config: %s", err) - } + writeSource(t, fs, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ) - sites, err := NewHugoSitesFromConfiguration(DepsCfg{}) + sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs}) if err != nil { t.Fatalf("Failed to create sites: %s", err) @@ -279,7 +292,7 @@ p t.Fatalf("Failed to build sites: %s", err) } - assertFileContent(t, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, + assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true, "Page Colors: red|heavenly", "Site Colors: green|yellow", "Shortcode Page: red|heavenly", diff --git a/hugolib/config_test.go b/hugolib/config_test.go index 6a2325fc9..cbfc71a22 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -18,6 +18,7 @@ import ( "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/hugofs" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,7 +31,10 @@ func TestLoadGlobalConfig(t *testing.T) { PaginatePath = "side" ` - writeSource(t, "hugo.toml", configContent) + fs := hugofs.NewMem() + viper.SetFs(fs.Source) + + writeSource(t, fs, "hugo.toml", configContent) require.NoError(t, LoadGlobalConfig("", "hugo.toml")) assert.Equal(t, "side", helpers.Config().GetString("paginatePath")) diff --git a/hugolib/datafiles_test.go b/hugolib/datafiles_test.go index dd330b0a1..0f848594a 100644 --- a/hugolib/datafiles_test.go +++ b/hugolib/datafiles_test.go @@ -89,19 +89,16 @@ func TestDataDirUnknownFormat(t *testing.T) { sources := []source.ByteSource{ {Name: filepath.FromSlash("test.roml"), Content: []byte("boo")}, } - s := NewSiteDefaultLang() - err := s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}}) - if err != nil { - t.Fatalf("Should not return an error") - } + s, err := NewSiteDefaultLang() + require.NoError(t, err) + require.NoError(t, s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}})) } func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) { - s := NewSiteDefaultLang() - err := s.loadData(sources) - if err != nil { - t.Fatalf("Error loading data: %s", err) - } + s, err := NewSiteDefaultLang() + require.NoError(t, err) + require.NoError(t, s.loadData(sources)) + if !reflect.DeepEqual(expected, s.Data) { t.Errorf("Expected structure\n%#v got\n%#v", expected, s.Data) } @@ -109,21 +106,22 @@ func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) { func TestDataFromShortcode(t *testing.T) { testCommonResetState() - writeSource(t, "data/hugo.toml", "slogan = \"Hugo Rocks!\"") - writeSource(t, "layouts/_default/single.html", ` + + cfg := newTestDepsConfig() + + writeSource(t, cfg.Fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"") + writeSource(t, cfg.Fs, "layouts/_default/single.html", ` * Slogan from template: {{ .Site.Data.hugo.slogan }} * {{ .Content }}`) - writeSource(t, "layouts/shortcodes/d.html", `{{ .Page.Site.Data.hugo.slogan }}`) - writeSource(t, "content/c.md", `--- + writeSource(t, cfg.Fs, "layouts/shortcodes/d.html", `{{ .Page.Site.Data.hugo.slogan }}`) + writeSource(t, cfg.Fs, "content/c.md", `--- --- Slogan from shortcode: {{< d >}} `) - h, err := newHugoSitesDefaultLanguage() - require.NoError(t, err) - require.NoError(t, h.Build(BuildCfg{})) + buildSingleSite(t, cfg, BuildCfg{}) - content := readSource(t, "public/c/index.html") + content := readSource(t, cfg.Fs, "public/c/index.html") require.True(t, strings.Contains(content, "Slogan from template: Hugo Rocks!"), content) require.True(t, strings.Contains(content, "Slogan from shortcode: Hugo Rocks!"), content) diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go index 61c40cf01..64a92247b 100644 --- a/hugolib/embedded_shortcodes_test.go +++ b/hugolib/embedded_shortcodes_test.go @@ -27,9 +27,11 @@ import ( "log" "path/filepath" - "github.com/spf13/hugo/tpl" + "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/hugofs" + "github.com/spf13/hugo/tplapi" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -65,17 +67,17 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) { path := filepath.FromSlash("blog/post.md") in := fmt.Sprintf(`{{< %s "%s" >}}`, refShortcode, path) - writeSource(t, "content/"+path, simplePageWithURL+": "+in) + fs := hugofs.NewMem() + + writeSource(t, fs, "content/"+path, simplePageWithURL+": "+in) expected := fmt.Sprintf(`%s/simple/url/`, expectedBase) - sites, err := newHugoSitesDefaultLanguage() - require.NoError(t, err) + s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{}) - require.NoError(t, sites.Build(BuildCfg{})) - require.Len(t, sites.Sites[0].RegularPages, 1) + require.Len(t, s.RegularPages, 1) - output := string(sites.Sites[0].RegularPages[0].Content) + output := string(s.RegularPages[0].Content) if !strings.Contains(output, expected) { t.Errorf("Got\n%q\nExpected\n%q", output, expected) @@ -308,7 +310,7 @@ func TestShortcodeTweet(t *testing.T) { }, } - p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error { + p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error { templ.Funcs(tweetFuncMap) return nil }) @@ -361,7 +363,7 @@ func TestShortcodeInstagram(t *testing.T) { }, } - p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error { + p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error { templ.Funcs(instagramFuncMap) return nil }) diff --git a/hugolib/gitinfo.go b/hugolib/gitinfo.go index 2893db06f..82baa3250 100644 --- a/hugolib/gitinfo.go +++ b/hugolib/gitinfo.go @@ -20,7 +20,6 @@ import ( "github.com/bep/gitmap" "github.com/spf13/hugo/helpers" - jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" ) @@ -36,7 +35,7 @@ func (h *HugoSites) assembleGitInfo() { gitRepo, err := gitmap.Map(workingDir, "") if err != nil { - jww.ERROR.Printf("Got error reading Git log: %s", err) + h.Log.ERROR.Printf("Got error reading Git log: %s", err) return } @@ -60,7 +59,7 @@ func (h *HugoSites) assembleGitInfo() { filename := path.Join(filepath.ToSlash(contentRoot), contentDir, filepath.ToSlash(p.Path())) g, ok := gitMap[filename] if !ok { - jww.ERROR.Printf("Failed to find GitInfo for %q", filename) + h.Log.ERROR.Printf("Failed to find GitInfo for %q", filename) return } diff --git a/hugolib/handler_page.go b/hugolib/handler_page.go index 2026f2bbf..6b6b17173 100644 --- a/hugolib/handler_page.go +++ b/hugolib/handler_page.go @@ -65,7 +65,6 @@ type htmlHandler struct { func (h htmlHandler) Extensions() []string { return []string{"html", "htm"} } -// TODO(bep) globals use p.s.t func (h htmlHandler) PageConvert(p *Page) HandledResult { if p.rendered { panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName())) diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go index ba5daa8c2..01e6793a6 100644 --- a/hugolib/handler_test.go +++ b/hugolib/handler_test.go @@ -17,44 +17,36 @@ import ( "path/filepath" "testing" + "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" "github.com/spf13/hugo/hugofs" - "github.com/spf13/hugo/source" - "github.com/spf13/hugo/target" "github.com/spf13/viper" ) func TestDefaultHandler(t *testing.T) { testCommonResetState() - hugofs.InitMemFs() - sources := []source.ByteSource{ - {Name: filepath.FromSlash("sect/doc1.html"), Content: []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")}, - {Name: filepath.FromSlash("sect/doc2.html"), Content: []byte("<!doctype html><html><body>more content</body></html>")}, - {Name: filepath.FromSlash("sect/doc3.md"), Content: []byte("# doc3\n*some* content")}, - {Name: filepath.FromSlash("sect/doc4.md"), Content: []byte("---\ntitle: doc4\n---\n# doc4\n*some content*")}, - {Name: filepath.FromSlash("sect/doc3/img1.png"), Content: []byte("‰PNG ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚")}, - {Name: filepath.FromSlash("sect/img2.gif"), Content: []byte("GIF89a��€��ÿÿÿ���,�������D�;")}, - {Name: filepath.FromSlash("sect/img2.spf"), Content: []byte("****FAKE-FILETYPE****")}, - {Name: filepath.FromSlash("doc7.html"), Content: []byte("<html><body>doc7 content</body></html>")}, - {Name: filepath.FromSlash("sect/doc8.html"), Content: []byte("---\nmarkup: md\n---\n# title\nsome *content*")}, - } - viper.Set("defaultExtension", "html") viper.Set("verbose", true) + viper.Set("uglyURLs", true) - s := &Site{ - Source: &source.InMemorySource{ByteSource: sources}, - targets: targetList{page: &target.PagePub{UglyURLs: true, PublishDir: "public"}}, - Language: helpers.NewLanguage("en"), - } + fs := hugofs.NewMem() - if err := buildAndRenderSite(s, - "_default/single.html", "{{.Content}}", - "head", "<head><script src=\"script.js\"></script></head>", - "head_abs", "<head><script src=\"/script.js\"></script></head>"); err != nil { - t.Fatalf("Failed to render site: %s", err) - } + writeSource(t, fs, filepath.FromSlash("content/sect/doc1.html"), "---\nmarkup: markdown\n---\n# title\nsome *content*") + writeSource(t, fs, filepath.FromSlash("content/sect/doc2.html"), "<!doctype html><html><body>more content</body></html>") + writeSource(t, fs, filepath.FromSlash("content/sect/doc3.md"), "# doc3\n*some* content") + writeSource(t, fs, filepath.FromSlash("content/sect/doc4.md"), "---\ntitle: doc4\n---\n# doc4\n*some content*") + writeSource(t, fs, filepath.FromSlash("content/sect/doc3/img1.png"), "‰PNG ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚") + writeSource(t, fs, filepath.FromSlash("content/sect/img2.gif"), "GIF89a��€��ÿÿÿ���,�������D�;") + writeSource(t, fs, filepath.FromSlash("content/sect/img2.spf"), "****FAKE-FILETYPE****") + writeSource(t, fs, filepath.FromSlash("content/doc7.html"), "<html><body>doc7 content</body></html>") + writeSource(t, fs, filepath.FromSlash("content/sect/doc8.html"), "---\nmarkup: md\n---\n# title\nsome *content*") + + writeSource(t, fs, filepath.FromSlash("layouts/_default/single.html"), "{{.Content}}") + writeSource(t, fs, filepath.FromSlash("head"), "<head><script src=\"script.js\"></script></head>") + writeSource(t, fs, filepath.FromSlash("head_abs"), "<head><script src=\"/script.js\"></script></head") + + buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{}) tests := []struct { doc string @@ -71,7 +63,7 @@ func TestDefaultHandler(t *testing.T) { } for _, test := range tests { - file, err := hugofs.Destination().Open(test.doc) + file, err := fs.Destination.Open(test.doc) if err != nil { t.Fatalf("Did not find %s in target.", test.doc) } diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 0d9105ef6..c8ae51d0a 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -14,20 +14,19 @@ package hugolib import ( + "errors" "fmt" - "io/ioutil" - "log" - "os" "strings" "sync" + "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" "github.com/spf13/viper" "github.com/spf13/hugo/source" "github.com/spf13/hugo/tpl" - jww "github.com/spf13/jwalterweatherman" + "github.com/spf13/hugo/tplapi" ) // HugoSites represents the sites to build. Each site represents a language. @@ -38,74 +37,77 @@ type HugoSites struct { multilingual *Multilingual - *deps + *deps.Deps } -// deps holds dependencies used by many. -// TODO(bep) globals a better name. -// There will be normally be only one instance of deps in play -// at a given time. -type deps struct { - // The logger to use. - log *jww.Notepad - - tmpl *tpl.GoHTMLTemplate - - // TODO(bep) next in line: Viper, hugofs -} - -func (d *deps) refreshTemplates(withTemplate ...func(templ tpl.Template) error) { - d.tmpl = tpl.New(d.log, withTemplate...) - d.tmpl.PrintErrors() // TODO(bep) globals error handling -} |