miki

Miki: personal wiki with the fmd markup format
git clone https://wtenison.com/repo/miki.git
Log | Files | Refs | README | LICENSE

commit fe630405277c63b0be494a89cda68f0e2d168be3
parent 070bf5debe4a9bfc0bc63d90ccad03ca2476d695
Author: weetat <wtenison@protonmail.com>
Date:   Wed, 26 Feb 2025 22:18:40 +0000

Split filesystem.go into directories.go and files.go

Also unexport the fmd token types.

Diffstat:
Asrc/filesystem/directories.go | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/filesystem/files.go | 25+++++++++++++++++++++++++
Msrc/filesystem/filesystem.go | 150++++++++-----------------------------------------------------------------------
Msrc/fmd/interpreter.go | 2+-
Msrc/fmd/lexer.go | 42+++++++++++++++++++++---------------------
Msrc/fmd/parser.go | 30+++++++++++++++---------------
Msrc/network/network.go | 8++++----
7 files changed, 209 insertions(+), 177 deletions(-)

diff --git a/src/filesystem/directories.go b/src/filesystem/directories.go @@ -0,0 +1,129 @@ +package filesystem + +import ( + "fmt" + "log" + "miki/src/fmd" + "os" + "path/filepath" + "regexp" + "strings" +) + +type SearchDepth int + +const ( + // NOTE: Should I have an INF option? + SEARCH_DEPTH_INF SearchDepth = -1 + SEARCH_DEPTH_ZERO SearchDepth = 0 +) + +type DirectorySignature struct { + Root string // The root directoy of the resource. + DependencyDepth SearchDepth // The folder search depth of the resource. -1=inf, 0=single, >0=n-depth + DependencyPattern *regexp.Regexp // The pattern of valid resource dependency files. +} + +type DirectoryPackage struct { + Root string // The root directory of the resource. + DependencyFiles []string // The set of valid resource files. + DependencyData interface{} // Any relevant resource data. +} + +func MakeDirectorySig(directory string, depth SearchDepth, pattern *regexp.Regexp) (DirectorySignature, error) { + + if directory == "" || depth < -1 || pattern == nil { + _retErr := fmt.Errorf("MakeDirectorySig:invalid directory signature (%s, %d, %s)", directory, depth, pattern) + return DirectorySignature{}, _retErr + } + + if _, _err := os.Stat(directory); _err != nil { + _retErr := fmt.Errorf("MakeDirectorySig:%s", _err) + return DirectorySignature{}, _retErr + } + + return DirectorySignature{directory, depth, pattern}, nil +} + +func MakeDirectoryPkg(resSig DirectorySignature) (DirectoryPackage, error) { + + // Check and collect resource signature directory files. + _files, _err := os.ReadDir(resSig.Root) + if _err != nil { + _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) + return DirectoryPackage{}, _retErr + } + + _resPkg := DirectoryPackage{ + Root: resSig.Root, + DependencyFiles: []string{}, + DependencyData: nil, + } + + _resFileDirDeps := []string{} + + // Collete all valid files and/or sub-directory files. + for _, _f := range _files { + + // Recurse into valid sub-directory files. + if _f.IsDir() && (resSig.DependencyDepth > SEARCH_DEPTH_ZERO || resSig.DependencyDepth == SEARCH_DEPTH_INF) { + + // Decrement depth if not infinite. + _depDepth := resSig.DependencyDepth + if _depDepth != SEARCH_DEPTH_INF { + _depDepth = _depDepth - 1 + } + + // Make directory signature for each sub-file/dir + _resDirSig, _err := MakeDirectorySig(_f.Name(), _depDepth, resSig.DependencyPattern) + if _err != nil { + _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) + return DirectoryPackage{}, _retErr + } + + // Make the respective package. + _resDirPkg, _err := MakeDirectoryPkg(_resDirSig) + if _err != nil { + _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) + return DirectoryPackage{}, _retErr + } + + // Add to package dependencies list. + _resFileDirDeps = append(_resFileDirDeps, _resDirPkg.DependencyFiles...) + + } else if resSig.DependencyPattern != nil && resSig.DependencyPattern.MatchString(_f.Name()) { + // Add files that match pattern to dependency list. + _resPkg.DependencyFiles = append(_resPkg.DependencyFiles, filepath.Join(_resPkg.Root, _f.Name())) + } + } + + // Include any sub-directory file dependencies. + _resPkg.DependencyFiles = append(_resPkg.DependencyFiles, _resFileDirDeps...) + + // Check for atricle entry. + // TODO: Move to secure and refined interface. + _dataFiles, _err := os.ReadDir(Dirs["data"]) + if _err != nil { + _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) + return DirectoryPackage{}, _retErr + } + + for _, _df := range _dataFiles { + _rootFile := strings.Split(resSig.Root, "/") + _rootDataFile := _rootFile[len(_rootFile)-1] + ".fmd" + + if _df.Name() == _rootDataFile { + log.Printf("INFO\tMakeDirectoryPkg:df:%s, rootDataFile:%s", _df.Name(), _rootDataFile) + + var _DataContainer struct { + PageContent string + } + + _DataContainer.PageContent = fmd.WritFMDToHTMLString(filepath.Join(Dirs["data"], _rootDataFile)) + + _resPkg.DependencyData = _DataContainer + } + } + + return _resPkg, nil +} diff --git a/src/filesystem/files.go b/src/filesystem/files.go @@ -0,0 +1,25 @@ +package filesystem + +import ( + "encoding/json" + "fmt" + "os" + "regexp" +) + +var FilePatternHTML = regexp.MustCompile("^([a-zA-Z0-9.]+).html$") +var FilePatternJSON = regexp.MustCompile("^([a-zA-Z0-9.]+).json$") + +func ReadFileJSON(filePath string, outputDataContainer interface{}) error { + + _fsData, _err := os.ReadFile("sys/config.json") + if _err != nil { + return fmt.Errorf("_ERROR\tfilesystem:ReadFileJSON:os.ReadFile:%s:%s", filePath, _err.Error()) + } + + if _err = json.Unmarshal(_fsData, &outputDataContainer); _err != nil { + return fmt.Errorf("_ERROR\tfilesystem:ReadFileJSON:json.Unmarshal:%s:%s", filePath, _err.Error()) + } + + return nil +} diff --git a/src/filesystem/filesystem.go b/src/filesystem/filesystem.go @@ -1,166 +1,44 @@ package filesystem import ( - "encoding/json" - "fmt" "log" - "miki/src/fmd" - "os" "path/filepath" - "regexp" - "strings" ) -var FilePatternHTML = regexp.MustCompile("^([a-zA-Z0-9.]+).html$") +// var Dirs map[string]string +var Dirs = make(map[string]string) -var Dirs struct { - DataDirectory string `json:"data-directory"` - WebDirectory string `json:"web-directory"` -} - -var GlobalPkg DirectoryPackage - -type DirectorySignature struct { - Root string // The root directoy of the resource. - DependencyDepth int // The folder search depth of the resource. -1=inf, 0=single, >0=n-depth - DependencyPattern *regexp.Regexp // The pattern of valid resource dependency files. -} - -type DirectoryPackage struct { - Root string // The root directory of the resource. - DependencyFiles []string // The set of valid resource files. - DependencyData interface{} // Any relevant resource data. -} - -type IDirectoryData interface { -} - -func MakeDirectorySig(directory string, depth int, pattern *regexp.Regexp) (DirectorySignature, error) { - - if directory == "" || depth < -1 || pattern == nil { - _retErr := fmt.Errorf("MakeDirectorySig:invalid directory signature (%s, %d, %s)", directory, depth, pattern) - return DirectorySignature{}, _retErr - } - - if _, _err := os.Stat(directory); _err != nil { - _retErr := fmt.Errorf("MakeDirectorySig:%s", _err) - return DirectorySignature{}, _retErr - } - - return DirectorySignature{directory, depth, pattern}, nil -} - -func MakeDirectoryPkg(resSig DirectorySignature) (DirectoryPackage, error) { - - // Check and collect resource signature directory files. - _files, _err := os.ReadDir(resSig.Root) - if _err != nil { - _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) - return DirectoryPackage{}, _retErr - } - - _resPkg := DirectoryPackage{ - Root: resSig.Root, - DependencyFiles: []string{}, - DependencyData: nil, - } - - _resFileDirDeps := []string{} - - // Collete all valid files and/or sub-directory files. - for _, _f := range _files { - - if _f.IsDir() && (resSig.DependencyDepth > 0 || resSig.DependencyDepth == -1) { - // Recurse into valid sub-directory files. - - // Make directory signature for each sub-file/dir - _resDirSig, _err := MakeDirectorySig(_f.Name(), resSig.DependencyDepth, resSig.DependencyPattern) - if _err != nil { - _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) - return DirectoryPackage{}, _retErr - } - - // Make the respective package. - _resDirPkg, _err := MakeDirectoryPkg(_resDirSig) - if _err != nil { - _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) - return DirectoryPackage{}, _retErr - } - - // Add to package dependencies list. - _resFileDirDeps = append(_resFileDirDeps, _resDirPkg.DependencyFiles...) - - } else if resSig.DependencyPattern != nil && resSig.DependencyPattern.MatchString(_f.Name()) { - // Add files that match pattern to dependency list. - _resPkg.DependencyFiles = append(_resPkg.DependencyFiles, filepath.Join(_resPkg.Root, _f.Name())) - } - } - - // Include any sub-directory file dependencies. - _resPkg.DependencyFiles = append(_resPkg.DependencyFiles, _resFileDirDeps...) - - // Check for atricle entry. - // TODO: Move to secure and refined interface. - _dataFiles, _err := os.ReadDir(Dirs.DataDirectory) - if _err != nil { - _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err) - return DirectoryPackage{}, _retErr - } - - for _, _df := range _dataFiles { - _rootFile := strings.Split(resSig.Root, "/") - _rootDataFile := _rootFile[len(_rootFile)-1] + ".fmd" - - if _df.Name() == _rootDataFile { - log.Printf("INFO\tMakeDirectoryPkg:df:%s, rootDataFile:%s", _df.Name(), _rootDataFile) - - var _DataContainer struct { - PageContent string - } - - _DataContainer.PageContent = fmd.WritFMDToHTMLString(filepath.Join(Dirs.DataDirectory, _rootDataFile)) - - _resPkg.DependencyData = _DataContainer - } - } - - return _resPkg, nil -} +var WebGlobalPkg DirectoryPackage func Init() { //// PULL SYSTEM INFO FROM JSON - _fsData, _err := os.ReadFile("sys/config.json") - if _err != nil { - log.Panicln("__PANIC\tfilesystem:Init:os.ReadFile:config.json:" + _err.Error()) - return - } - var _fsDataStruct struct { DataDirectory string `json:"data-directory"` WebDirectory string `json:"web-directory"` } - if _err = json.Unmarshal(_fsData, &_fsDataStruct); _err != nil { - log.Panicln("__PANIC\tjson.Unmarshal:config.json:" + _err.Error()) - return + if _err := ReadFileJSON("sys/config.json", &_fsDataStruct); _err != nil { + log.Panicf("__PANIC\tfilesystem:%s", _err) } - Dirs.DataDirectory = _fsDataStruct.DataDirectory - Dirs.WebDirectory = _fsDataStruct.WebDirectory + Dirs["data"] = _fsDataStruct.DataDirectory + Dirs["web"] = _fsDataStruct.WebDirectory log.Printf("INFO\tfilesystem:Init:Dirs:%v\n", Dirs) - //// GET GLOBAL DIRECTORY RESOURCES + //// GET GLOBAL WEB RESOURCES - _globalSig, _err := MakeDirectorySig(filepath.Join(Dirs.WebDirectory, "/global"), 0, FilePatternHTML) + _globalSig, _err := MakeDirectorySig(filepath.Join(Dirs["web"], "/global"), 0, FilePatternHTML) if _err != nil { - log.Panicf("__PANIC\tfilesystem:Init:MakeDirectorySig:%s", _err) + log.Panicf("__PANIC\tfilesystem:Init:%s", _err) } - GlobalPkg, _err = MakeDirectoryPkg(_globalSig) + WebGlobalPkg, _err = MakeDirectoryPkg(_globalSig) if _err != nil { - log.Panicf("__PANIC\tfilesystem:Init:MakeDirectoryPkg:%s", _err) + log.Panicf("__PANIC\tfilesystem:Init:%s", _err) } + + log.Printf("INFO\tfilesystem:Init:WebGlobals:%v\n", Dirs) } diff --git a/src/fmd/interpreter.go b/src/fmd/interpreter.go @@ -45,7 +45,7 @@ func interpret(n node) string { return "<div>" + interpret(n.children[0]) + "</div>" } case nodeBlock: - _res.WriteString("<div>") + _res.WriteString("<div class=\"__block\">") for _, _child := range n.children { _res.WriteString(interpret(_child)) } diff --git a/src/fmd/lexer.go b/src/fmd/lexer.go @@ -11,28 +11,28 @@ type tokenType int const ( // WORDS - TokenLiteral tokenType = iota // 0. (letters, numbers, symbols)+ - TokenKeyword // 1. (valid keywords) + tokenLiteral tokenType = iota // 0. (letters, numbers, symbols)+ + tokenKeyword // 1. (valid keywords) // SPACING - TokenSpace // 2. " " - TokenNewline // 3. \n - TokenTab // 4. \t - TokenEscape // 5. \ + tokenSpace // 2. " " + tokenNewline // 3. \n + tokenTab // 4. \t + tokenEscape // 5. \ // OPERATORS - TokenColon // 6. : - TokenLBrace // 7. { - TokenRBrace // 8. } + tokenColon // 6. : + tokenLBrace // 7. { + tokenRBrace // 8. } ) type token struct { - Type tokenType + tType tokenType Value string } var symbols = []string{" ", "\n", "\t", "\\", ":", "{", "}"} -var opKeywords = []string{ +var keywords = []string{ "b", "i", "h1", "h2", "h3", "h4", "h5", "h6", "@", "#", "col", "img", "_"} type lexer struct { @@ -52,25 +52,25 @@ func (l *lexer) Exec() []token { switch { case _currentSymbol == ' ': - l.tokens = append(l.tokens, token{TokenSpace, " "}) + l.tokens = append(l.tokens, token{tokenSpace, " "}) l.pos++ case _currentSymbol == '\n': - l.tokens = append(l.tokens, token{TokenNewline, "\n"}) + l.tokens = append(l.tokens, token{tokenNewline, "\n"}) l.pos++ case _currentSymbol == '\t': - l.tokens = append(l.tokens, token{TokenTab, "\t"}) + l.tokens = append(l.tokens, token{tokenTab, "\t"}) l.pos++ case _currentSymbol == '\\': - l.tokens = append(l.tokens, token{TokenEscape, "\\"}) + l.tokens = append(l.tokens, token{tokenEscape, "\\"}) l.pos++ case _currentSymbol == ':': - l.tokens = append(l.tokens, token{TokenColon, ":"}) + l.tokens = append(l.tokens, token{tokenColon, ":"}) l.pos++ case _currentSymbol == '{': - l.tokens = append(l.tokens, token{TokenLBrace, "{"}) + l.tokens = append(l.tokens, token{tokenLBrace, "{"}) l.pos++ case _currentSymbol == '}': - l.tokens = append(l.tokens, token{TokenRBrace, "}"}) + l.tokens = append(l.tokens, token{tokenRBrace, "}"}) l.pos++ default: { // NOTE: inlined check until good reason to abstract arrives. @@ -83,10 +83,10 @@ func (l *lexer) Exec() []token { _tokenString = string(l.input[_start:l.pos]) - if slices.Contains(opKeywords, _tokenString) { - l.tokens = append(l.tokens, token{TokenKeyword, _tokenString}) + if slices.Contains(keywords, _tokenString) { + l.tokens = append(l.tokens, token{tokenKeyword, _tokenString}) } else if _tokenString != "" { - l.tokens = append(l.tokens, token{TokenLiteral, _tokenString}) + l.tokens = append(l.tokens, token{tokenLiteral, _tokenString}) } } } diff --git a/src/fmd/parser.go b/src/fmd/parser.go @@ -46,18 +46,18 @@ func (p *parser) Exec() node { func (p *parser) parseCommand() node { _command := node{nType: nodeCommand} - _keyword := p.consume(TokenKeyword) + _keyword := p.consume(tokenKeyword) _command.value = _keyword.Value - p.consume(TokenColon) + p.consume(tokenColon) // keyword:{block} - if p.peekNextN(TokenLBrace, 0) { - p.consume(TokenLBrace) + if p.peekNextN(tokenLBrace, 0) { + p.consume(tokenLBrace) _command.children = []node{p.parseBlock()} - p.consume(TokenRBrace) + p.consume(tokenRBrace) } else { // keyword:"literal" _command.children = []node{p.parseLiteral()} } @@ -68,17 +68,17 @@ func (p *parser) parseCommand() node { func (p *parser) parseBlock() node { _block := node{nType: nodeBlock} - for p.pos < len(p.tokens) && p.current().Type != TokenRBrace { + for p.pos < len(p.tokens) && p.current().tType != tokenRBrace { if p.isNextCommand() { // keyword:? _block.children = append(_block.children, p.parseCommand()) - } else if p.peekNextN(TokenSpace, 0) { // " " + } else if p.peekNextN(tokenSpace, 0) { // " " _block.children = append(_block.children, node{nType: nodeSpace, value: " "}) p.pos++ - } else if p.peekNextN(TokenNewline, 0) { // "\n" + } else if p.peekNextN(tokenNewline, 0) { // "\n" _block.children = append(_block.children, node{nType: nodeNewline, value: "\n"}) p.pos++ - } else if p.peekNextN(TokenTab, 0) { // "\t" + } else if p.peekNextN(tokenTab, 0) { // "\t" // _block.children = append(_block.children, node{nType: nodeSpace, value: "\\t"}) p.pos++ } else { // "literal" @@ -91,19 +91,19 @@ func (p *parser) parseBlock() node { } func (p *parser) parseLiteral() node { - _literal := node{nType: nodeLiteral, value: p.consume(TokenLiteral).Value} + _literal := node{nType: nodeLiteral, value: p.consume(tokenLiteral).Value} return _literal } func (p *parser) isNextCommand() bool { - return p.peekNextN(TokenKeyword, 0) && - p.peekNextN(TokenColon, 1) && - p.peekNextN(TokenLiteral, 2) || - p.peekNextN(TokenLBrace, 2) + return p.peekNextN(tokenKeyword, 0) && + p.peekNextN(tokenColon, 1) && + p.peekNextN(tokenLiteral, 2) || + p.peekNextN(tokenLBrace, 2) } func (p *parser) peekNextN(t tokenType, n int) bool { - _res := p.pos+n < len(p.tokens) && p.tokens[p.pos+n].Type == t + _res := p.pos+n < len(p.tokens) && p.tokens[p.pos+n].tType == t return _res } diff --git a/src/network/network.go b/src/network/network.go @@ -24,7 +24,7 @@ var mux *http.ServeMux func handlePageDefault(w http.ResponseWriter, r *http.Request) { // Produce URL path to directory relation. - _r, _err := mfs.MakeDirectorySig(filepath.Join(mfs.Dirs.WebDirectory, r.URL.String()), 0, mfs.FilePatternHTML) + _r, _err := mfs.MakeDirectorySig(filepath.Join(mfs.Dirs["web"], r.URL.String()), 0, mfs.FilePatternHTML) if _err != nil { log.Printf("_ERROR\tnetwork:handlePageDefault(\"/\"):%s\n", _err) http.NotFound(w, r) @@ -40,7 +40,7 @@ func handlePageDefault(w http.ResponseWriter, r *http.Request) { } // Add 'global' file dependencies. - _rPkg.DependencyFiles = append(_rPkg.DependencyFiles, mfs.GlobalPkg.DependencyFiles...) + _rPkg.DependencyFiles = append(_rPkg.DependencyFiles, mfs.WebGlobalPkg.DependencyFiles...) // Create template from package. _tmpl, _err := template.ParseFiles(_rPkg.DependencyFiles...) @@ -108,10 +108,10 @@ func Init() { mux = http.NewServeMux() - mux.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir(mfs.Dirs.WebDirectory)))) + mux.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir(mfs.Dirs["web"])))) mux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, filepath.Join(mfs.Dirs.WebDirectory, "/global/favicon.ico")) + http.ServeFile(w, r, filepath.Join(mfs.Dirs["web"], "/global/favicon.ico")) }) mux.HandleFunc("/home", handlePageDefault)