diff --git a/code/golang/go.mod b/code/golang/go.mod new file mode 100644 index 0000000..2a1b1f0 --- /dev/null +++ b/code/golang/go.mod @@ -0,0 +1,15 @@ +module ads + +go 1.17 + +require ( + github.com/akamensky/argparse v1.3.1 + github.com/davecgh/go-spew v1.1.1 + github.com/lithammer/dedent v1.1.0 + github.com/pmezard/go-difflib v1.0.0 + github.com/slongfield/pyfmt v0.0.0-20180124071345-020a7cb18bca + github.com/stretchr/objx v0.1.0 + github.com/stretchr/testify v1.7.0 + golang.org/x/tools v0.1.7 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +) diff --git a/code/golang/internal/core/logging/logging.go b/code/golang/internal/core/logging/logging.go new file mode 100644 index 0000000..f34b9ef --- /dev/null +++ b/code/golang/internal/core/logging/logging.go @@ -0,0 +1,116 @@ +package logging + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "fmt" + "os" + + "ads/internal/core/utils" +) + +/* ---------------------------------------------------------------- * + * GLOBAL VARIABLES + * ---------------------------------------------------------------- */ + +var quietmode bool = false +var debugmode bool = false +var ansimode bool = true +var loggingPrefix string = "" +var force bool = false +var tagAll bool = false + +func GetQuietMode() bool { + return quietmode +} + +func SetQuietMode(mode bool) { + quietmode = mode +} + +func GetDebugMode() bool { + return debugmode +} + +func SetDebugMode(mode bool) { + debugmode = mode +} + +func GetAnsiMode() bool { + return ansimode +} + +func SetAnsiMode(mode bool) { + ansimode = mode +} + +func GetForce() bool { + return force +} + +func SetForce(mode bool) { + force = mode +} + +func SetTagAll(mode bool) { + tagAll = mode +} + +/* ---------------------------------------------------------------- * + * METHOD logging + * ---------------------------------------------------------------- */ + +func logGeneric(tag string, lines ...interface{}) { + if !force && quietmode { + return + } + if !(tag == "") { + tag = tag + " " + } + for _, line := range lines { + _line := fmt.Sprintf("%[1]s%[2]s%[3]v", loggingPrefix, tag, line) + if !ansimode { + _line = utils.StripAnsi(_line) + } + fmt.Println(_line) + if !tagAll { + tag = "" + } + } +} + +func LogPlain(lines ...interface{}) { + SetTagAll(false) + logGeneric("", lines...) +} + +func LogInfo(lines ...interface{}) { + SetTagAll(true) + logGeneric("[\033[94;1mINFO\033[0m]", lines...) +} + +func LogDebug(lines ...interface{}) { + if !debugmode { + return + } + SetTagAll(true) + logGeneric("[\033[96;1mDEBUG\033[0m]", lines...) +} + +func LogWarn(lines ...interface{}) { + SetTagAll(false) + logGeneric("[\033[93;1mWARNING\033[0m]", lines...) +} + +func LogError(lines ...interface{}) { + SetTagAll(false) + logGeneric("[\033[91;1mERROR\033[0m]", lines...) +} + +func LogFatal(lines ...interface{}) { + SetTagAll(false) + logGeneric("[\033[91;1mFATAL\033[0m]", lines...) + os.Exit(1) +} diff --git a/code/golang/internal/core/utils/utils_arrays.go b/code/golang/internal/core/utils/utils_arrays.go new file mode 100644 index 0000000..b7c2ce2 --- /dev/null +++ b/code/golang/internal/core/utils/utils_arrays.go @@ -0,0 +1,85 @@ +package utils + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "reflect" +) + +/* ---------------------------------------------------------------- * + * METHOD array contains + * ---------------------------------------------------------------- */ + +func ArrayContains(x interface{}, elem interface{}) bool { + xAsArray := reflect.ValueOf(x) + if xAsArray.Kind() == reflect.Slice { + for i := 0; i < xAsArray.Len(); i++ { + if xAsArray.Index(i).Interface() == elem { + return true + } + } + } + return false +} + +// func ListComprehension(x interface{}, cond (interface{}) bool) bool { +// xAsArray := reflect.ValueOf(x) +// if xAsArray.Kind() == reflect.Slice { +// for i := 0; i < xAsArray.Len(); i++ { +// if xAsArray.Index(i).Interface() == elem { +// return true +// } +// } +// } +// return false +// } + +/* ---------------------------------------------------------------- * + * METHOD array contains + * ---------------------------------------------------------------- */ + +func UniqueListOfStrings(X []string) []string { + var ok bool + m := map[string]bool{} + X_unique := []string{} + for _, x := range X { + if _, ok = m[x]; !ok { + X_unique = append(X_unique, x) + } + } + return X_unique +} + +/* ---------------------------------------------------------------- * + * METHOD get value from array of unknown length + * ---------------------------------------------------------------- */ + +func GetArrayStringValue(arr *[]string, index int, Default string) string { + if arr != nil && len(*arr) > index { + return (*arr)[index] + } + return Default +} + +func GetArrayBoolValue(arr *[]bool, index int, Default bool) bool { + if arr != nil && len(*arr) > index { + return (*arr)[index] + } + return Default +} + +func GetArrayIntValue(arr *[]int, index int, Default int) int { + if arr != nil && len(*arr) > index { + return (*arr)[index] + } + return Default +} + +func GetArrayInterfaceValue(arr *[](interface{}), index int, Default interface{}) interface{} { + if arr != nil && len(*arr) > index { + return (*arr)[index] + } + return Default +} diff --git a/code/golang/internal/core/utils/utils_strings.go b/code/golang/internal/core/utils/utils_strings.go new file mode 100644 index 0000000..0593705 --- /dev/null +++ b/code/golang/internal/core/utils/utils_strings.go @@ -0,0 +1,96 @@ +package utils + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "fmt" + "log" + "reflect" + "strconv" + "strings" + + "github.com/lithammer/dedent" + "github.com/slongfield/pyfmt" + + "ads/pkg/re" +) + +/* ---------------------------------------------------------------- * + * METHOD format strings with dictionary substitution + * ---------------------------------------------------------------- */ + +func FormatPythonString(text string, arguments map[string]interface{}) string { + var ( + err error + key string + value interface{} + kind reflect.Kind + refValue reflect.Value + ) + // force compatibility of expressions with python + for key, value = range arguments { + kind = reflect.TypeOf(value).Kind() + switch kind { + case reflect.Ptr: + refValue = reflect.ValueOf(value) + if refValue.IsNil() { + arguments[key] = "None" + } + case reflect.Bool: + arguments[key] = strings.Title(fmt.Sprintf(`%v`, value)) + } + } + text, err = pyfmt.Fmt(text, arguments) + if err != nil { + log.Fatal(err) + } + return text +} + +/* ---------------------------------------------------------------- * + * METHOD dedent textblock and expand escaped symbols + * ---------------------------------------------------------------- */ + +func DedentIgnoreEmptyLines(text string) string { + return dedent.Dedent(text) +} + +func DedentIgnoreFirstAndLast(text string) string { + text = re.Sub(`^\s*[\n\r]|[\n\r]\s*$`, ``, text) + return DedentIgnoreEmptyLines(text) +} + +func DedentAndExpand(text string) string { + var err error + var result []string + result = []string{} + text = dedent.Dedent(text) + lines := strings.Split(text, "\n") + for _, line := range lines { + line = fmt.Sprintf(`"%s"`, line) + line, err = strconv.Unquote(line) + if err != nil { + log.Fatal(err) + } + result = append(result, line) + } + return strings.Join(result, "\n") +} + +func FormatTextBlockAsList(text string, options ...bool) []string { + var unindent bool = GetArrayBoolValue(&options, 0, true) + if unindent { + text = DedentIgnoreFirstAndLast(text) + } + return re.Split(`\n`, text) +} + +/* ---------------------------------------------------------------- * + * METHODS ansi + * ---------------------------------------------------------------- */ + +func StripAnsi(text string) string { + return re.Sub(`\x1b[^m]*m`, ``, text) +} diff --git a/code/golang/internal/endpoints/endpoints_print.go b/code/golang/internal/endpoints/endpoints_print.go new file mode 100644 index 0000000..8bbd20d --- /dev/null +++ b/code/golang/internal/endpoints/endpoints_print.go @@ -0,0 +1,34 @@ +package endpoints + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "ads/internal/core/logging" + "ads/internal/setup" +) + +/* ---------------------------------------------------------------- * + * ENDPOINT version + * ---------------------------------------------------------------- */ + +func Version() { + logging.SetForce(true) + logging.LogPlain(setup.Version()) +} + +/* ---------------------------------------------------------------- * + * ENDPOINT help + * ---------------------------------------------------------------- */ + +func Help() { + logging.SetForce(true) + logging.LogPlain( + "", + setup.Logo(), + // cli.Parser.Usage(nil), + setup.Help(), + "", + ) +} diff --git a/code/golang/internal/endpoints/endpoints_run.go b/code/golang/internal/endpoints/endpoints_run.go new file mode 100644 index 0000000..2cacf62 --- /dev/null +++ b/code/golang/internal/endpoints/endpoints_run.go @@ -0,0 +1,20 @@ +package endpoints + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "ads/internal/core/logging" + "ads/internal/setup" +) + +/* ---------------------------------------------------------------- * + * ENDPOINT run + * ---------------------------------------------------------------- */ + +func Run(fnameConfig string) error { + logging.LogPlain(setup.Logo()) + logging.LogWarn("Die Go-Implementierung ist noch unter Arbeit.") + return nil +} diff --git a/code/golang/internal/setup/cli/cli.go b/code/golang/internal/setup/cli/cli.go new file mode 100644 index 0000000..577c816 --- /dev/null +++ b/code/golang/internal/setup/cli/cli.go @@ -0,0 +1,66 @@ +package cli + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "github.com/akamensky/argparse" + + "ads/internal/types" +) + +/* ---------------------------------------------------------------- * + * GLOBAL VARIABLES + * ---------------------------------------------------------------- */ + +var Parser *argparse.Parser + +/* ---------------------------------------------------------------- * + * LOCAL VARIABLES / CONSTANTS + * ---------------------------------------------------------------- */ + +var optionsQuiet = argparse.Options{ + Help: "Blendet alle Konsole-Messages aus.", + Required: false, + Default: false, +} + +var optionsDebug = argparse.Options{ + Help: "Blendet die Debugging-Befehle ein.", + Required: false, + Default: false, +} + +var optionsColour = argparse.Options{ + Help: "Ob Logging färblich angezeigt wird (default=true).", + Required: false, + // NOTE: no `Boolean` option available! + Default: "true", +} + +var optionsConfigFile = argparse.Options{ + Help: "Pfad zur Configdatei (nur für run Endpunkt).", + Required: false, + Default: "code/config.yml", +} + +/* ---------------------------------------------------------------- * + * METHODS parse cli + * ---------------------------------------------------------------- */ + +func ParseCli(args []string) (*types.CliArguments, error) { + var err error + Parser = argparse.NewParser("cli parser", "Liest Optionen + Flags von Kommandozeile.") + arguments := types.CliArguments{ + Help: Parser.NewCommand("help", ""), + Version: Parser.NewCommand("version", "Ruft Endpunkt auf, der die Version anzeigt."), + Run: Parser.NewCommand("run", "Ruft Endpunkt auf, der die Algorithmen laufen lässt."), + Quiet: Parser.Flag("q", "quiet", &optionsQuiet), + Debug: Parser.Flag("", "debug", &optionsDebug), + Colour: Parser.String("", "colour", &optionsColour), + ConfigFile: Parser.String("", "config", &optionsConfigFile), + } + err = Parser.Parse(args) + return &arguments, err +} diff --git a/code/golang/internal/setup/setup_assets.go b/code/golang/internal/setup/setup_assets.go new file mode 100644 index 0000000..c161f8a --- /dev/null +++ b/code/golang/internal/setup/setup_assets.go @@ -0,0 +1,56 @@ +package setup + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "embed" + "fmt" + "log" + "strings" + + "ads/internal/core/utils" +) + +/* ---------------------------------------------------------------- * + * GLOBAL VARIABLES + * ---------------------------------------------------------------- */ + +var Res embed.FS +var Assets map[string]string + +/* ---------------------------------------------------------------- * + * METHOD read assets + * ---------------------------------------------------------------- */ + +func ReadAsset(key string) string { + var found bool + if _, found = Assets[key]; !found { + log.Fatal(fmt.Sprintf("Key \033[1m%s\033[0m not found in dictionary!", key)) + } + data, err := Res.ReadFile(Assets[key]) + if err != nil { + log.Fatal(err) + } + text := string(data) + return text +} + +/* ---------------------------------------------------------------- * + * METHODS templates + * ---------------------------------------------------------------- */ + +func Help() string { + contents := ReadAsset("help") + return utils.DedentAndExpand(contents) +} + +func Logo() string { + contents := ReadAsset("logo") + return utils.DedentAndExpand(contents) +} + +func Version() string { + return strings.Trim(ReadAsset("version"), "\n") +} diff --git a/code/golang/internal/types/types_cli.go b/code/golang/internal/types/types_cli.go new file mode 100644 index 0000000..c12ed37 --- /dev/null +++ b/code/golang/internal/types/types_cli.go @@ -0,0 +1,45 @@ +package types + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "strings" + + "github.com/akamensky/argparse" + + "ads/pkg/re" +) + +/* ---------------------------------------------------------------- * + * TYPES + * ---------------------------------------------------------------- */ + +type CliArguments struct { + Help *argparse.Command + Version *argparse.Command + Run *argparse.Command + Quiet *bool + Debug *bool + Colour *string + ConfigFile *string +} + +/* ---------------------------------------------------------------- * + * METHODS convert string option to boolean + * ---------------------------------------------------------------- */ + +func IsTrue(text string) bool { + text = strings.TrimSpace(text) + return re.Matches(`(?i)(^(true|t|yes|y|1|\+|\+1)$)`, text) +} + +func IsFalse(text string) bool { + text = strings.TrimSpace(text) + return re.Matches(`(?i)(^(false|f|no|n|0|-|-1)$)`, text) +} + +func (arguments *CliArguments) ShowColour() bool { + return !IsFalse(*arguments.Colour) +} diff --git a/code/golang/main.go b/code/golang/main.go new file mode 100644 index 0000000..45d7544 --- /dev/null +++ b/code/golang/main.go @@ -0,0 +1,68 @@ +package main + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "embed" + "os" + + "ads/internal/core/logging" + "ads/internal/endpoints" + "ads/internal/setup" + "ads/internal/setup/cli" + "ads/internal/types" +) + +/* ---------------------------------------------------------------- * + * GLOBAL VARIABLES + * ---------------------------------------------------------------- */ + +var ( + // !!! NOTE: do not remove the following "comment", as it is a preprocessing instruction !!! + //go:embed assets/* + res embed.FS + assets = map[string]string{ + "version": "assets/VERSION", + "logo": "assets/LOGO", + "help": "assets/HELP", + } +) + +/* ---------------------------------------------------------------- * + * METHOD main + * ---------------------------------------------------------------- */ + +func main() { + var err error + var arguments *types.CliArguments + + // set assets + setup.Res = res + setup.Assets = assets + + // parse cli arguments + arguments, err = cli.ParseCli(os.Args) + + // initialise logging options + if err == nil { + logging.SetQuietMode(*arguments.Quiet) + logging.SetDebugMode(*arguments.Debug) + logging.SetAnsiMode(arguments.ShowColour()) + } + + if err == nil { + if arguments.Version.Happened() { + endpoints.Version() + } else if arguments.Run.Happened() { + err = endpoints.Run(*arguments.ConfigFile) + } else { // } else if arguments.Help.Happened() { + endpoints.Help() + } + } + + if err != nil { + logging.LogFatal(err) + } +} diff --git a/code/golang/pkg/re/re.go b/code/golang/pkg/re/re.go new file mode 100644 index 0000000..de5bf4d --- /dev/null +++ b/code/golang/pkg/re/re.go @@ -0,0 +1,62 @@ +package re + +/* ---------------------------------------------------------------- * + * IMPORTS + * ---------------------------------------------------------------- */ + +import ( + "regexp" +) + +/* ---------------------------------------------------------------- * + * GLOBAL VARIABLES + * ---------------------------------------------------------------- */ + +type Reader struct { + regex *regexp.Regexp + lastpattern *string +} + +var defaultReader Reader = Reader{} + +/* ---------------------------------------------------------------- * + * METHODS + * ---------------------------------------------------------------- */ + +func Matches(pattern string, text string) bool { + return defaultReader.Matches(pattern, text) +} + +func Sub(pattern string, substitute string, text string) string { + return defaultReader.Sub(pattern, substitute, text) +} + +func Split(pattern string, text string) []string { + return defaultReader.Split(pattern, text) +} + +func (r *Reader) Matches(pattern string, text string) bool { + r.setReader(pattern) + return r.regex.MatchString(text) +} + +func (r *Reader) Sub(pattern string, substitute string, text string) string { + r.setReader(pattern) + return r.regex.ReplaceAllString(text, substitute) +} + +func (r *Reader) Split(pattern string, text string) []string { + r.setReader(pattern) + return r.regex.Split(text, -1) +} + +/* ---------------------------------------------------------------- * + * PRIVATE MEHODS + * ---------------------------------------------------------------- */ + +func (r *Reader) setReader(pattern string) { + if r.regex == nil || r.lastpattern == nil || *r.lastpattern != pattern { + r.lastpattern = &pattern + r.regex = regexp.MustCompile(pattern) + } +} diff --git a/code/golang/requirements b/code/golang/requirements new file mode 100644 index 0000000..86c8677 --- /dev/null +++ b/code/golang/requirements @@ -0,0 +1,6 @@ +github.com/akamensky/argparse@v1.3.1 +github.com/lithammer/dedent@v1.1.0 +github.com/slongfield/pyfmt@v0.0.0-20180124071345-020a7cb18bca +github.com/stretchr/testify@v1.7.0 +golang.org/x/tools +gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b