diff --git a/README.md b/README.md index b48ee11..6bc49d9 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,4 @@ __DONE__ * about * migrate to gRPC * routing, not found +* template engine diff --git a/formatter.go b/formatter.go index 0e408e3..c217c73 100644 --- a/formatter.go +++ b/formatter.go @@ -1,9 +1,43 @@ package main import ( + "errors" + "net/url" "sort" + "strings" ) +func (file *TreeFile) CategoriesAsUrl() string { + escapedCategories := []string{} + for _, c := range file.Category { + escapedCategories = append(escapedCategories, url.QueryEscape(c)) + } + + return strings.Join(escapedCategories, "/") +} + +func (file *TreeFile) GmiName() string { + return strings.Replace(file.Name, ".md", ".gmi", 1) +} + +func (tree *Tree) GetIndexFile() (*TreeFile, error) { + for _, file := range tree.RootFiles { + if file.Name == "index.md" { + return file, nil + } + } + + return nil, errors.New("index file not found") +} +func (tree *Tree) HasIndexFile() bool { + _, err := tree.GetIndexFile() + if err != nil { + return false + } + + return true +} + func GetLastFiles(files []*TreeFile) []*TreeFile { sortingFiles := make(map[string]*TreeFile) for _, f := range files { diff --git a/go.mod b/go.mod index 88d69d6..f30c5de 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( git.sr.ht/~adnano/go-gemini v0.2.3 + github.com/flosch/pongo2/v6 v6.0.0 google.golang.org/grpc v1.48.0 google.golang.org/protobuf v1.28.1 ) diff --git a/go.sum b/go.sum index c4c9f75..a4a5144 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU= +github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -47,6 +49,8 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -126,6 +130,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 64381d0..e03250e 100644 --- a/main.go +++ b/main.go @@ -6,13 +6,13 @@ import ( "fmt" "google.golang.org/grpc/credentials/insecure" "log" - "os" "regexp" "strings" "time" "git.sr.ht/~adnano/go-gemini" "git.sr.ht/~adnano/go-gemini/certificate" + "github.com/flosch/pongo2/v6" "google.golang.org/grpc" ) @@ -62,7 +62,10 @@ func main() { func processFavicon(_ context.Context, w gemini.ResponseWriter, _ *gemini.Request) { w.SetMediaType("text/plain") - w.Write([]byte("\U0001F31B")) + _, err := w.Write([]byte("\U0001F31B")) + if err != nil { + w.WriteHeader(gemini.StatusNotFound, "Out of space") + } } func process(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) { @@ -93,12 +96,175 @@ func process(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) { renderSearch(lang, w, r, client) case regexp.MustCompile(`^/(sgs|en)/f/?$`).MatchString(r.URL.Path): renderAllFiles(lang, w, client) + case regexp.MustCompile(`^/(sgs|en)/f/([\p{L}\d_+.]+/)+[\d\w]+/[\w_]+.gmi$`).MatchString(r.URL.Path): + renderFile(lang, w, r, client) + case regexp.MustCompile(`^/(sgs|en)/f/[\p{L}\d_+.]+(/[\p{L}\d_+.]+)*/?$`).MatchString(r.URL.Path): + renderCategory(lang, w, r, client) default: w.WriteHeader(gemini.StatusNotFound, "Out of space") } } +func renderIndex(lang string, w gemini.ResponseWriter, client TreeManagerClient) { + langFilter := TreeRequest_Filter{Key: "lang", Value: lang} + filters := []*TreeRequest_Filter{&langFilter} + + path := "" + tr := TreeRequest{Path: &path, Filter: filters} + + tree, err := client.GetSummery(context.Background(), &tr) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } + + w.SetMediaType("text/gemini") + tpl := pongo2.Must(pongo2.FromFile(fmt.Sprintf("templates/%s/index.gmi", lang))) + page, err := tpl.Execute(pongo2.Context{"tree": tree, "lang": lang, "lastFiles": GetLastFiles(tree.Files)}) + if err != nil { + log.Fatalf("template failed: %v", err) + return + } + + _, err = w.Write([]byte(page)) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } +} + +func renderAbout(lang string, w gemini.ResponseWriter) { + w.SetMediaType("text/gemini") + tpl := pongo2.Must(pongo2.FromFile(fmt.Sprintf("templates/%s/about.gmi", lang))) + page, err := tpl.Execute(pongo2.Context{}) + if err != nil { + log.Fatalf("template failed: %v", err) + return + } + + _, err = w.Write([]byte(page)) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + } +} + +func renderSearch(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) { + q, err := gemini.QueryUnescape(r.URL.RawQuery) + if err != nil || q == "" { + searchStr := "Input searching tag, created date or word in description" + if lang == "sgs" { + searchStr = "Ivesk ėiškuoma žīma, sokūrėma data arba žuodi ėš aprašīma" + } + w.WriteHeader(gemini.StatusInput, searchStr) + return + } + + descFilter := TreeRequest_Filter{Key: "description", Value: q} + createdFilter := TreeRequest_Filter{Key: "created", Value: q} + tagFilter := TreeRequest_Filter{Key: "tag", Value: q} + filters := []*TreeRequest_Filter{&descFilter, &createdFilter, &tagFilter} + + path := "" + tr := TreeRequest{Path: &path, Filter: filters} + + tree, err := client.GetSummery(context.Background(), &tr) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } + + w.SetMediaType("text/gemini") + tpl := pongo2.Must(pongo2.FromFile("templates/search.gmi")) + page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree, "q": q}) + if err != nil { + log.Fatalf("template failed: %v", err) + return + } + + _, err = w.Write([]byte(page)) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } +} + +func renderCategory(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) { + urlParts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") + path := "/" + strings.Join(urlParts[2:], "/") + + tr := TreeRequest{Path: &path, Filter: []*TreeRequest_Filter{}} + + tree, err := client.GetSummery(context.Background(), &tr) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } + + indexFile, err := tree.GetIndexFile() + var file *FileContent + if err == nil { + fr := FileRequest{Id: indexFile.Id} + file, err = client.GetFile(context.Background(), &fr) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } + } + + w.SetMediaType("text/gemini") + tpl := pongo2.Must(pongo2.FromFile("templates/category.gmi")) + fmt.Println(file, &file) + page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree, "path": path, "indexFile": file}) + if err != nil { + log.Fatalf("template failed: %v", err) + return + } + + _, err = w.Write([]byte(page)) + if err != nil { + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } +} + +func renderFile(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) { + urlParts := strings.Split(r.URL.Path, "/") + id := urlParts[len(urlParts)-2] + + fr := FileRequest{Id: id} + file, err := client.GetFile(context.Background(), &fr) + if err != nil { + log.Fatalf("client.GetSummery failed: %v", err) + w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") + return + } + + category := "" + for i, cat := range file.File.Category { + category = category + fmt.Sprintf("=> /%s/f/%s .%s└─ %s\n", + lang, + strings.Join(file.File.Category[:i+1], "/"), + strings.Repeat(" ", i), + cat, + ) + } + content := fmt.Sprintf( + "# %s\n\n"+ + "%s\n\n"+ + "%s\n%s\n\n"+ + "%s\n\n[ %s ]", + file.File.Description, + file.Content, + file.File.Created, + file.File.Copyright, + category, + strings.Join(file.File.Tags, " "), + ) + + w.Write([]byte(content)) +} + func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClient) { langFilter := TreeRequest_Filter{Key: "lang", Value: lang} filters := []*TreeRequest_Filter{&langFilter} @@ -121,15 +287,7 @@ func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClie } for _, f := range tree.Files { - content = content + fmt.Sprintf( - "=> /%s/f/%s/%s/%s %s (%s)\n", - lang, - strings.Join(f.Category, "/"), - f.Id, - strings.Replace(f.Name, ".md", ".gmi", 1), - f.Description, - f.Created, - ) + content = appendFileLink(lang, content, f) } if lang == "sgs" { @@ -141,98 +299,15 @@ func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClie w.Write([]byte(content)) } -func renderSearch(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) { - q, err := gemini.QueryUnescape(r.URL.RawQuery) - if err != nil || q == "" { - searchStr := "Input searching tag, created date or word in description" - if lang == "sgs" { - searchStr = "Ivesk ėiškuoma žīma, sokūrėma data arba žuodi ėš aprašīma" - } - w.WriteHeader(gemini.StatusInput, searchStr) - return - } - - langFilter := TreeRequest_Filter{Key: "lang", Value: lang} - descFilter := TreeRequest_Filter{Key: "description", Value: lang} - createdFilter := TreeRequest_Filter{Key: "created", Value: lang} - tagFilter := TreeRequest_Filter{Key: "tag", Value: lang} - filters := []*TreeRequest_Filter{&langFilter, &descFilter, &createdFilter, &tagFilter} - - path := "" - tr := TreeRequest{Path: &path, Filter: filters} - - tree, err := client.GetSummery(context.Background(), &tr) - if err != nil { - log.Fatalf("client.GetSummery failed: %v", err) - w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") - return - } - fmt.Println(tree) - w.Write([]byte("Nieka narada dā\n" + - "=> / ← back")) -} - -func renderAbout(lang string, w gemini.ResponseWriter) { - w.SetMediaType("text/gemini") - content, err := os.ReadFile(fmt.Sprintf("templates/%s/about.gmi", lang)) - if err != nil { - w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") - return - } - - _, err = w.Write(content) - if err != nil { - w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") - return - } -} - -func renderIndex(lang string, w gemini.ResponseWriter, client TreeManagerClient) { - w.SetMediaType("text/gemini") - content, err := os.ReadFile(fmt.Sprintf("templates/%s/index.gmi", lang)) - if err != nil { - w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") - return - } - page := string(content) - - langFilter := TreeRequest_Filter{Key: "lang", Value: lang} - filters := []*TreeRequest_Filter{&langFilter} - - path := "" - tr := TreeRequest{Path: &path, Filter: filters} - - tree, err := client.GetSummery(context.Background(), &tr) - if err != nil { - log.Fatalf("client.GetSummery failed: %v", err) - w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") - return - } - - categories := "" - for c, count := range tree.Categories { - categories = categories + fmt.Sprintf("=> /%s/f/%s %s (%d)\n", lang, c, c, count) - } - page = strings.Replace(page, "{{categories}}", categories, 1) - - tags := "" - for t, count := range tree.Tags { - tags = tags + fmt.Sprintf("=> /%s/t/%s %s (%d)\n", lang, t, t, count) - } - page = strings.Replace(page, "{{tags}}", tags, 1) - - lastFiles := "" - for _, f := range GetLastFiles(tree.Files) { - lastFiles = lastFiles + fmt.Sprintf( - "=> /%s/f/%s/%s/%s %s (%s)\n", - lang, strings.Join(f.Category, "/"), f.Id, strings.Replace(f.Name, ".md", ".gmi", 1), f.Description, f.Created, - ) - } - page = strings.Replace(page, "{{last_posts}}", lastFiles, 1) - - _, err = w.Write([]byte(page)) - if err != nil { - w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") - return - } +func appendFileLink(lang string, content string, f *TreeFile) string { + content = content + fmt.Sprintf( + "=> /%s/f/%s/%s/%s %s (%s)\n", + lang, + strings.Join(f.Category, "/"), + f.Id, + strings.Replace(f.Name, ".md", ".gmi", 1), + f.Description, + f.Created, + ) + return content } diff --git a/templates/category.gmi b/templates/category.gmi new file mode 100644 index 0000000..761ce5c --- /dev/null +++ b/templates/category.gmi @@ -0,0 +1,10 @@ +{% import "macros.tpl" category_url, text_url %} +# Arna alkierios :: {{ path }} + +{% if indexFile %}{{ indexFile.Content }}{% endif %} + +{% for file in tree.RootFiles %}{{ text_url(lang, file) }} {% endfor %} +{% if tree.Categories %}### {% if lang == "sgs" %}Kateguorėjės{% else %}Categories{% endif %} +{% for cat, count in tree.Categories %}{{ category_url(lang, cat, cat, count) }} +{% endfor %} {% endif %} +=> {% if lang == "sgs" %}/sgs 🏠 nomėi{% else %}/en 🏠 home{% endif %} diff --git a/templates/en/about.gmi b/templates/en/about.gmi index 015d1ee..040614a 100644 --- a/templates/en/about.gmi +++ b/templates/en/about.gmi @@ -27,7 +27,7 @@ Articles I wrote simply in files and put them in my self-hosted cloud by created From this service it is served to gemini service what is written in Go, too, or to http service (backend on Go, frontend on Vue.js). For communications I use gRPC. -=> /en/f/gemini/arns-lt more... +=> /en/f/gemini/arns_lt more... => https://g.arns.lt/zordsdavini/zord-tree zord-tree library to build tree from files => https://g.arns.lt/zordsdavini/arns-lt-tree-push-service service written on top of zord-tree to serve files => https://g.arns.lt/zordsdavini/arns-lt-gemini gemini service diff --git a/templates/en/index.gmi b/templates/en/index.gmi index 5bdbfc0..eca5d13 100644 --- a/templates/en/index.gmi +++ b/templates/en/index.gmi @@ -1,3 +1,4 @@ +{% import "../macros.tpl" category_url, tag_url, text_url %} # Arna alkierios Welcome to my capsule in Gemini space. @@ -12,16 +13,16 @@ Main themes are spreaded by categories. Each file may have tags what can filter => /en/s search => /en/a about -### Categories +{% if tree.Categories %}### Categories +{% for cat, count in tree.Categories %} +{{ category_url(lang, cat, cat, count) }} {% endfor %} {% endif %} -{{categories}} +{% if tree.Tags %}### Tags +{% for tag, count in tree.Tags %} +{{ tag_url(lang, tag, count) }} {% endfor %} {% endif %} -### Tags - -{{tags}} - -### Last posts - -{{last_posts}} +{% if lastFiles %}### Last texts +{% for file in lastFiles %} +{{ text_url(lang, file) }} {% endfor %} {% endif %} => /en/f all texts diff --git a/templates/macros.tpl b/templates/macros.tpl new file mode 100644 index 0000000..fcafb98 --- /dev/null +++ b/templates/macros.tpl @@ -0,0 +1,5 @@ +{% macro category_url(lang, path, category, count) export %}=> /{{ lang }}/f/{{ path }} {{ category }} ({{ count }}) {% endmacro %} + +{% macro tag_url(lang, tag, count) export %}=> /{{ lang }}/t/{{ tag }} {{ tag }} ({{ count }}) {% endmacro %} + +{% macro text_url(lang, file) export %}=> /{{ lang }}/f/{{ file.CategoriesAsUrl() }}/{{ file.Id }}/{{ file.GmiName() }} {{ file.Description }} ({{ file.Created }}) {% endmacro %} diff --git a/templates/search.gmi b/templates/search.gmi new file mode 100644 index 0000000..9acc9dd --- /dev/null +++ b/templates/search.gmi @@ -0,0 +1,9 @@ +{% import "macros.tpl" text_url %} +# Arna alkierios :: {% if lang == "sgs" %}Paėiška{% else %}Search{% endif %} + +{% if lang == "sgs" %}Ėiškuota vagol{% else %}Searched by{% endif %}: {{ q }} +{% if tree.Files|length == 0 %}{% if lang == "sgs" %}Nieka narada{% else %}Nothing found{% endif %}...{% else %} +{% for file in tree.Files %} +{{ text_url(lang, file) }} {% endfor %} {% endif %} + +=> {% if lang == "sgs" %}/sgs ← grīžtė{% else %}/en ← back{% endif %} diff --git a/templates/sgs/about.gmi b/templates/sgs/about.gmi index e4e3e82..b32ef35 100644 --- a/templates/sgs/about.gmi +++ b/templates/sgs/about.gmi @@ -27,7 +27,7 @@ Tekstus rašau i paprastiausius failiokus ėr anus talpėno sava „cloud'ė“ Jau ėš makliavuonės servėsa pajemo i gemini servėsa, katras parašīts ėrgė so Go, arba i http servėsa (backend's ont Go REST, frontend's ont Vue.js). Ruodā nauduojo gRPC pruotuokuola. -=> /en/f/gemini/arns-lt platiau (onglėškā) +=> /en/f/gemini/arns_lt platiau (onglėškā) => https://g.arns.lt/zordsdavini/zord-tree zord-tree bėbliuoteka sokortė miedi ėš failu => https://g.arns.lt/zordsdavini/arns-lt-tree-push-service servėsos ont zord-tree somakliavuotė failus ėr anus padoutė tuoliau => https://g.arns.lt/zordsdavini/arns-lt-gemini gemini servėsos diff --git a/templates/sgs/index.gmi b/templates/sgs/index.gmi index 08f1cab..ea8fb7e 100644 --- a/templates/sgs/index.gmi +++ b/templates/sgs/index.gmi @@ -1,3 +1,4 @@ +{% import "../macros.tpl" category_url, tag_url, text_url %} # Arna alkierios Sveikė atvīkėn i mona kapsolė Gemini ertie. @@ -12,16 +13,16 @@ Pagrindėnės temas paskėrstītas par kateguorėjės. Kuožnos fails gal turie => /sgs/s ėiškuok => /sgs/a aple -### Kateguorėjės +{% if tree.Categories %}### Kateguorėjės +{% for cat, count in tree.Categories %} +{{ category_url(lang, cat, cat, count) }} {% endfor %} {% endif %} -{{categories}} +{% if tree.Tags %}### Žīmas +{% for tag, count in tree.Tags %} +{{ tag_url(lang, tag, count) }} {% endfor %} {% endif %} -### Žīmas - -{{tags}} - -### Paskotėnē īrašā - -{{last_posts}} +{% if lastFiles %}### Paskotėnē īrašā +{% for file in lastFiles %} +{{ text_url(lang, file) }} {% endfor %} {% endif %} => /sgs/f vėsė straipsnē diff --git a/tree.pb.go b/tree.pb.go index aa4bf5d..d20e8b9 100644 --- a/tree.pb.go +++ b/tree.pb.go @@ -231,6 +231,7 @@ type Tree struct { unknownFields protoimpl.UnknownFields Files []*TreeFile `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"` + RootFiles []*TreeFile `protobuf:"bytes,4,rep,name=rootFiles,proto3" json:"rootFiles,omitempty"` Tags map[string]int32 `protobuf:"bytes,2,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` Categories map[string]int32 `protobuf:"bytes,3,rep,name=categories,proto3" json:"categories,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` } @@ -274,6 +275,13 @@ func (x *Tree) GetFiles() []*TreeFile { return nil } +func (x *Tree) GetRootFiles() []*TreeFile { + if x != nil { + return x.RootFiles + } + return nil +} + func (x *Tree) GetTags() map[string]int32 { if x != nil { return x.Tags @@ -426,36 +434,39 @@ var file_tree_proto_rawDesc = []byte{ 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xb8, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e, - 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, - 0x3a, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e, - 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54, - 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x32, 0x6f, 0x0a, 0x0b, 0x54, 0x72, 0x65, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, - 0x2d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e, - 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0a, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x31, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x69, 0x6e, - 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6d, - 0x61, 0x69, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, - 0x00, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, + 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e, 0x54, 0x61, + 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x3a, 0x0a, + 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, + 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x4b, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x12, 0x22, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, + 0x66, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x32, 0x6f, + 0x0a, 0x0b, 0x54, 0x72, 0x65, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x2d, 0x0a, + 0x0a, 0x47, 0x65, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, + 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0a, + 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x69, + 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x42, + 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -484,18 +495,19 @@ var file_tree_proto_goTypes = []interface{}{ var file_tree_proto_depIdxs = []int32{ 5, // 0: main.TreeRequest.filter:type_name -> main.TreeRequest.Filter 2, // 1: main.Tree.files:type_name -> main.TreeFile - 6, // 2: main.Tree.tags:type_name -> main.Tree.TagsEntry - 7, // 3: main.Tree.categories:type_name -> main.Tree.CategoriesEntry - 2, // 4: main.FileContent.file:type_name -> main.TreeFile - 1, // 5: main.TreeManager.GetSummery:input_type -> main.TreeRequest - 0, // 6: main.TreeManager.GetFile:input_type -> main.FileRequest - 3, // 7: main.TreeManager.GetSummery:output_type -> main.Tree - 4, // 8: main.TreeManager.GetFile:output_type -> main.FileContent - 7, // [7:9] is the sub-list for method output_type - 5, // [5:7] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 2, // 2: main.Tree.rootFiles:type_name -> main.TreeFile + 6, // 3: main.Tree.tags:type_name -> main.Tree.TagsEntry + 7, // 4: main.Tree.categories:type_name -> main.Tree.CategoriesEntry + 2, // 5: main.FileContent.file:type_name -> main.TreeFile + 1, // 6: main.TreeManager.GetSummery:input_type -> main.TreeRequest + 0, // 7: main.TreeManager.GetFile:input_type -> main.FileRequest + 3, // 8: main.TreeManager.GetSummery:output_type -> main.Tree + 4, // 9: main.TreeManager.GetFile:output_type -> main.FileContent + 8, // [8:10] is the sub-list for method output_type + 6, // [6:8] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_tree_proto_init() } diff --git a/tree.proto b/tree.proto index 8c33958..9e91889 100644 --- a/tree.proto +++ b/tree.proto @@ -39,6 +39,7 @@ message TreeFile { message Tree { repeated TreeFile files = 1; + repeated TreeFile rootFiles = 4; map tags = 2; map categories = 3; }