devtools/hackdoc: init
This is hackdoc, a documentation rendering tool for monorepos.
This is the first code iteration, that can only serve from a local git
checkout.
The code is incomplete, and is WIP.
Change-Id: I68ef7a991191c1bb1b0fdd2a8d8353aba642e28f
diff --git a/devtools/hackdoc/source/BUILD.bazel b/devtools/hackdoc/source/BUILD.bazel
new file mode 100644
index 0000000..b896159
--- /dev/null
+++ b/devtools/hackdoc/source/BUILD.bazel
@@ -0,0 +1,12 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "source.go",
+ "source_local.go",
+ ],
+ importpath = "code.hackerspace.pl/hscloud/devtools/hackdoc/source",
+ visibility = ["//visibility:public"],
+ deps = ["@com_github_golang_glog//:go_default_library"],
+)
diff --git a/devtools/hackdoc/source/source.go b/devtools/hackdoc/source/source.go
new file mode 100644
index 0000000..a79a920
--- /dev/null
+++ b/devtools/hackdoc/source/source.go
@@ -0,0 +1,10 @@
+package source
+
+type Source interface {
+ IsFile(path string) (bool, error)
+ ReadFile(path string) ([]byte, error)
+ IsDirectory(path string) (bool, error)
+
+ CacheSet(dependencies []string, key string, value interface{})
+ CacheGet(key string) interface{}
+}
diff --git a/devtools/hackdoc/source/source_local.go b/devtools/hackdoc/source/source_local.go
new file mode 100644
index 0000000..35ad172
--- /dev/null
+++ b/devtools/hackdoc/source/source_local.go
@@ -0,0 +1,78 @@
+package source
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+)
+
+type LocalSource struct {
+ root string
+}
+
+func NewLocal(root string) Source {
+ return &LocalSource{
+ root: strings.TrimRight(root, "/"),
+ }
+}
+
+func (s *LocalSource) resolve(path string) (string, error) {
+ if !strings.HasPrefix(path, "//") {
+ return "", fmt.Errorf("invalid path %q, expected // prefix", path)
+ }
+ path = path[2:]
+ if strings.HasPrefix(path, "/") {
+ return "", fmt.Errorf("invalid path %q, expected // prefix", path)
+ }
+
+ return s.root + "/" + path, nil
+}
+
+func (s *LocalSource) IsFile(path string) (bool, error) {
+ path, err := s.resolve(path)
+ if err != nil {
+ return false, err
+ }
+ stat, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, fmt.Errorf("os.Stat(%q): %w", path, err)
+ }
+ return !stat.IsDir(), nil
+}
+
+func (s *LocalSource) ReadFile(path string) ([]byte, error) {
+ path, err := s.resolve(path)
+ if err != nil {
+ return nil, err
+ }
+ // TODO(q3k): limit size
+ return ioutil.ReadFile(path)
+}
+
+func (s *LocalSource) IsDirectory(path string) (bool, error) {
+ path, err := s.resolve(path)
+ if err != nil {
+ return false, err
+ }
+ stat, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, fmt.Errorf("os.Stat(%q): %w", path, err)
+ }
+ return stat.IsDir(), nil
+}
+
+func (s *LocalSource) CacheSet(dependencies []string, key string, value interface{}) {
+ // Swallow writes. The local filesystem can always change underneath us and
+ // we're not tracking anything, so we cannot ever keep caches.
+}
+
+func (s *LocalSource) CacheGet(key string) interface{} {
+ return nil
+}