1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 05:20:26 +00:00

Hypha search works ok

This commit is contained in:
Timur Ismagilov 2020-06-12 21:22:02 +05:00
commit 79991a3e64
19 changed files with 414 additions and 0 deletions

16
genealogy.go Normal file
View File

@ -0,0 +1,16 @@
/* Genealogy is all about relationships between hyphae. For now, the only goal of this file is to help find children of hyphae as they are not marked during the hypha search phase.
*/
package main
type Genealogy struct {
parent string
child string
}
func setRelations(hyphae map[string]*Hypha) {
for name, h := range hyphae {
if _, ok := hyphae[h.ParentName]; ok && h.ParentName != "." {
hyphae[h.ParentName].ChildrenNames = append(hyphae[h.ParentName].ChildrenNames, name)
}
}
}

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module github.com/bouncepaw/mycorrhiza
go 1.14
require (
github.com/gorilla/mux v1.7.4
github.com/sirupsen/logrus v1.6.0 // indirect
gopkg.in/ini.v1 v1.57.0
)

12
go.sum Normal file
View File

@ -0,0 +1,12 @@
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

BIN
hypha Executable file

Binary file not shown.

74
hypha.go Normal file
View File

@ -0,0 +1,74 @@
package main
import (
"fmt"
)
type Hypha struct {
// Hypha is physically located here. Most fields below are stored in <path>/mm.ini (mm for metametadata). Its revisions are physically located in <path>/<n>/ subfolders. <n> ∈ [0;∞] with 0 being latest revision, 1 the first.
Path string
// Every hypha was created at some point
CreationTime int `json:"creationTime"`
// Hypha has name but it can be changed
Name string `json:"name"`
// Hypha can be deleted. If it is deleted, it is not indexed by most of the software but still can be recovered at some point.
Deleted bool `json:"deleted"`
// Fields below are not part of m.ini and are created when traversing the file tree.
// Hypha can be a child of any other hypha except its children. The parent hypha is stored in <path>/..
ParentName string
// Hypha can have any number of children which are stored as subfolders in <path>.
ChildrenNames []string
Revisions []Revision
}
func (h Hypha) String() string {
var revbuf string
for _, r := range h.Revisions {
revbuf += r.String() + "\n"
}
return fmt.Sprintf("Hypha %v {\n\t"+
"path %v\n\t"+
"created at %v\n\t"+
"child of %v\n\t"+
"parent of %v\n\t"+
"Having these revisions:\n%v"+
"}\n", h.Name, h.Path, h.CreationTime, h.ParentName, h.ChildrenNames,
revbuf)
}
type Revision struct {
// Revision is hypha's state at some point in time. Future revisions are not really supported. Most data here is stored in m.ini.
Id int
// Name used at this revision
Name string `json:"name"`
// Present in every hypha. Stored in t.txt.
TextPath string
// In at least one markup. Supported ones are "myco", "html", "md", "plain"
Markup string `json:"markup"`
// Some hyphæ have binary contents such as images. Their presence change hypha's behavior in a lot of ways (see methods' implementations). If stored, it is stored in b (filename "b")
BinaryPath string
// To tell what is meaning of binary content, mimeType for them is stored. If the hypha has no binary content, this field must be "application/x-hypha"
MimeType string `json:"mimeType"`
// Every revision was created at some point. This field stores the creation time of the latest revision
RevisionTime int `json:"createdAt"`
// Every hypha has any number of tags
Tags []string `json:"tags"`
// Current revision is authored by someone
RevisionAuthor string `json:"author"`
// and has a comment in plain text
RevisionComment string `json:"comment"`
// Rest of fields are ignored
}
func (h Revision) String() string {
return fmt.Sprintf(`Revision %v created at %v {
name: %v
textPath: %v
markup: %v
binaryPath: %v
mimeType: %v
tags: %v
revisionAuthor: %v
revisionComment: %v
}`, h.Id, h.RevisionTime, h.Name, h.TextPath, h.Markup, h.BinaryPath, h.MimeType, h.Tags, h.RevisionAuthor, h.RevisionComment)
}

46
main.gg Normal file
View File

@ -0,0 +1,46 @@
package main
import (
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
"time"
)
func RegexForHypha(s string) string {
return s + ":[^\\s\\d:/?&\\\\][^:?&\\\\]*}"
}
func EditFillHandler(w http.ResponseWriter, r *http.Request) {
}
func HyphaHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
fmt.Fprintf(w, "Accessing hypha %v\n", vars["hypha"])
fmt.Fprintf(w, "Request at %v", r)
}
func main() {
r := mux.NewRouter()
r.Queries(
"action", "edit",
"fill", "{templateName}").
Path(RegexForHypha("/{hypha")).
HandlerFunc(EditFillHandler)
r.HandleFunc(RegexForHypha("/{hypha"), HyphaHandler)
r.HandleFunc("/kek", HyphaHandler)
http.Handle("/", r)
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}

34
main.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
"os"
"path/filepath"
)
var rootWikiDir string
func hyphaeAsMap(hyphae []*Hypha) map[string]*Hypha {
mh := make(map[string]*Hypha)
for _, h := range hyphae {
mh[h.Name] = h
}
return mh
}
func main() {
if len(os.Args) == 1 {
panic("Expected a root wiki pages directory")
}
// Required so the rootWikiDir hereinbefore does not get redefined.
var err error
rootWikiDir, err = filepath.Abs(os.Args[1])
if err != nil {
panic(err)
}
hyphae := hyphaeAsMap(recurFindHyphae(rootWikiDir))
setRelations(hyphae)
fmt.Println(hyphae)
}

1
w/README.md Normal file
View File

@ -0,0 +1 @@
This is root wiki directory.

9
w/m/Fruit/0/m.json Normal file
View File

@ -0,0 +1,9 @@
{
"createdAt": 1591636222,
"comment": "update Fruit",
"tags": ["fetus", "tasty"],
"mimeType": "application/x-hypha",
"markup": "md",
"name": "Fruit",
"author": "fungimaster"
}

3
w/m/Fruit/0/t.txt Normal file
View File

@ -0,0 +1,3 @@
# Fruit
Many people ask the question: what is fruit exactly?
Fruit is a type of fetus. Is tasty so cool coo l ha i love fwriotwsn

8
w/m/Fruit/1/m.json Normal file
View File

@ -0,0 +1,8 @@
{
"createdAt":1591635559,
"comment":"create Fruit",
"tags":["fetus", "tasty"],
"mimeType":"application/x-hypha",
"markup":"md",
"name":"Fruit",
"author":"bouncepaw"}

1
w/m/Fruit/1/t.txt Normal file
View File

@ -0,0 +1 @@
Fruit is a type of fetus. Is tasty so cool coo l ha i love fwriotwsn

BIN
w/m/Fruit/Apple/0/b Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

7
w/m/Fruit/Apple/0/m.json Normal file
View File

@ -0,0 +1,7 @@
{"createdAt":1591639464,
"comment":"add apple pic hehehe",
"tags":["img"],
"mimeType":"image/jpeg",
"markup":"plain",
"name":"Apple",
"author":"bouncepaw"}

3
w/m/Fruit/Apple/0/t.txt Normal file
View File

@ -0,0 +1,3 @@
A honeycrisp apple from an organic food farm co-op.
Source: https://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Honeycrisp-Apple.jpg

1
w/m/Fruit/Apple/mm.json Normal file
View File

@ -0,0 +1 @@
{"creationTime": 1591639284}

1
w/m/Fruit/mm.json Normal file
View File

@ -0,0 +1 @@
{"creationTime":1591635559}

1
w/m/README.md Normal file
View File

@ -0,0 +1 @@
This is directory with hyphae

188
walk.go Normal file
View File

@ -0,0 +1,188 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"regexp"
"strconv"
)
func scanHyphaDir(fullPath string) (structureMet bool, possibleRevisionPaths []string, possibleHyphaPaths []string, reterr error) {
nodes, err := ioutil.ReadDir(fullPath)
if err != nil {
reterr = err
return // implicit return values
}
var (
mmJsonPresent bool
zeroDirPresent bool
)
for _, node := range nodes {
matchedHypha, _ := regexp.MatchString(hyphaPattern, node.Name())
matchedRev, _ := regexp.MatchString(revisionPattern, node.Name())
switch {
case matchedRev && node.IsDir():
if node.Name() == "0" {
zeroDirPresent = true
}
possibleRevisionPaths = append(
possibleRevisionPaths,
filepath.Join(fullPath, node.Name()),
)
case (node.Name() == "mm.json") && !node.IsDir():
mmJsonPresent = true
case matchedHypha && node.IsDir():
possibleHyphaPaths = append(
possibleHyphaPaths,
filepath.Join(fullPath, node.Name()),
)
// Other nodes are ignored. It is not promised they will be ignored in future versions
}
}
if mmJsonPresent && zeroDirPresent {
structureMet = true
}
return // implicit return values
}
func check(e error) {
if e != nil {
panic(e)
}
}
// Hypha name is rootWikiDir/{here}
func hyphaName(fullPath string) string {
return fullPath[len(rootWikiDir)+1:]
}
const (
hyphaPattern = `[^\s\d:/?&\\][^:?&\\]*`
revisionPattern = `[\d]+`
)
// Sends found hyphae to the `ch`. `fullPath` is tested for hyphaness, then its subdirs with hyphaesque names are tested too using goroutines for each subdir. The function is recursive.
func recurFindHyphae(fullPath string) (hyphae []*Hypha) {
structureMet, possibleRevisionPaths, possibleHyphaPaths, err := scanHyphaDir(fullPath)
if err != nil {
return hyphae
}
// First, let's process inner hyphae
for _, possibleHyphaPath := range possibleHyphaPaths {
hyphae = append(hyphae, recurFindHyphae(possibleHyphaPath)...)
}
// This folder is not a hypha itself, nothing to do here
if !structureMet {
return hyphae
}
// Template hypha struct. Other fields are default jsont values.
h := Hypha{
Path: fullPath,
Name: hyphaName(fullPath),
ParentName: filepath.Dir(hyphaName(fullPath)),
// Children names are unknown now
}
// Fill in every revision
for _, possibleRevisionPath := range possibleRevisionPaths {
rev, err := makeRevision(possibleRevisionPath)
if err == nil {
h.Revisions = append(h.Revisions, rev)
}
}
mmJsonPath := filepath.Join(fullPath, "mm.json")
mmJsonContents, err := ioutil.ReadFile(mmJsonPath)
if err != nil {
fmt.Println(fullPath, ">\tError:", err)
return hyphae
}
err = json.Unmarshal(mmJsonContents, &h)
if err != nil {
fmt.Println(fullPath, ">\tError:", err)
return hyphae
}
// Now the hypha should be ok, gotta send structs
hyphae = append(hyphae, &h)
return hyphae
}
func makeRevision(fullPath string) (r Revision, err error) {
// fullPath is expected to be a path to a dir.
// Revision directory must have at least `m.json` and `t.txt` files.
var (
mJsonPresent bool
tTxtPresent bool
bPresent bool
)
nodes, err := ioutil.ReadDir(fullPath)
if err != nil {
return r, err
}
for _, node := range nodes {
if node.IsDir() {
continue
}
switch node.Name() {
case "m.json":
mJsonPresent = true
case "t.txt":
tTxtPresent = true
case "b":
bPresent = true
}
}
if !(mJsonPresent && tTxtPresent) {
return r, errors.New("makeRevision: m.json and t.txt files are not found")
}
// If all the flags are true, this directory is assumed to be a revision. Gotta check further. This is template Revision struct. Other fields fall back to default init values.
mJsonPath := filepath.Join(fullPath, "m.json")
mJsonContents, err := ioutil.ReadFile(mJsonPath)
if err != nil {
fmt.Println(fullPath, ">\tError:", err)
return r, err
}
r = Revision{}
err = json.Unmarshal(mJsonContents, &r)
if err != nil {
fmt.Println(fullPath, ">\tError:", err)
return r, err
}
// Now, let's fill in t.txt path
r.TextPath = filepath.Join(fullPath, "t.txt")
// There's sense in reading binary file only if the hypha is marked as such
if r.MimeType != "application/x-hypha" {
// Do not check for binary file presence, attempt to read it will fail anyway
if bPresent {
r.BinaryPath = filepath.Join(fullPath, "b")
} else {
return r, errors.New("makeRevision: b file not present")
}
}
// So far, so good. Let's fill in id. It is guaranteed to be correct, so no error checking
id, _ := strconv.Atoi(filepath.Base(fullPath))
r.Id = id
// It is safe now to return, I guess
return r, nil
}