[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnunet-go-plugins] 03/04: eample-go: initial revision.
From: |
gnunet |
Subject: |
[gnunet-go-plugins] 03/04: eample-go: initial revision. |
Date: |
Sun, 30 Oct 2022 18:22:43 +0100 |
This is an automated email from the git hooks/post-receive script.
bernd-fix pushed a commit to branch master
in repository gnunet-go-plugins.
commit e69bd785b5b04e22bab59b73e030e98523818233
Author: Bernd Fix <brf@hoi-polloi.org>
AuthorDate: Sun Oct 30 18:15:07 2022 +0100
eample-go: initial revision.
---
example-go/Makefile | 30 ++++++++
example-go/gui.htpl | 111 +++++++++++++++++++++++++++
example-go/main.go | 189 ++++++++++++++++++++++++++++++++++++++++++++++
example-go/plugin_test.go | 109 ++++++++++++++++++++++++++
example-go/records.go | 96 +++++++++++++++++++++++
5 files changed, 535 insertions(+)
diff --git a/example-go/Makefile b/example-go/Makefile
new file mode 100644
index 0000000..f4fa800
--- /dev/null
+++ b/example-go/Makefile
@@ -0,0 +1,30 @@
+# This file is part of gnunet-go-plugins, a collection of plugins for
+# the GNUnet-implementation in Golang (gnunet.go). Plugins are used to
+# implement type-specific processing of resource records (e.g. GUI).
+# Copyright (C) 2022 by the authors:
+#
+# * Bernd Fix <brf@hoi-polloi.org> >Y<
+#
+# gnunet-go-plugins is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License,
+# or (at your option) any later version.
+#
+# gnunet-go is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# SPDX-License-Identifier: AGPL3.0-or-later
+
+
+all: ${GOPATH}/bin/plugin_example_go.so
+
+clean: ${GOPATH}/bin/plugin_example_go.so
+ rm -f $^
+
+${GOPATH}/bin/plugin_example_go.so: main.go gui.htpl
+ go build -buildmode=plugin -trimpath -gcflags="-N -l" -o $@ ./...
diff --git a/example-go/gui.htpl b/example-go/gui.htpl
new file mode 100644
index 0000000..8317a72
--- /dev/null
+++ b/example-go/gui.htpl
@@ -0,0 +1,111 @@
+{{define "new_myrecords"}}
+ <!-- DEBUG: show template parameters -->
+ <div>
+ <h3>Template data:</h3>
+ <p>Ref = {{.Ref}}</p>
+ <p>Action = {{.Action}}</p>
+ <p>Button = {{.Button}}</p>
+ <p>Names: {{range $i,$v := .Names}}'{{$v}}',{{end}}</p>
+ <p>RRSpecs:</p>
+ <ul>
+ {{range $i,$s := .RRspecs}}
+ <li>Type = {{$s.Type}}, Flags = {{$s.Flags}}</li>
+ {{end}}
+ </ul>
+ <p>Params:</p>
+ <ul>
+ {{range $k,$v := .Params}}
+ <li>'{{$k}}' = '{{$v}}'</li>
+ {{end}}
+ </ul>
+ </div>
+
+ {{$data := .}}
+ <div>
+ <h3>Creating a new custom resource record for label "{{index .Params
"label"}}":</h3>
+ <div class="tabset">
+ {{range $i, $type := .RRspecs}}
+ <input type="radio" name="tabset" id="tab{{$i}}"
aria-controls="tab{{$i}}" {{if eq $i 0}}checked{{end}}>
+ <label for="tab{{$i}}">{{rrtype $type.Type}}</label>
+ {{end}}
+ <div class="tab-panels">
+ {{range $i, $spec := .RRspecs}}
+ <section id="tab{{$i}}" class="tab-panel">
+ {{$t := rrtype $spec.Type}}
+ {{$pf := setspecs $data.Params $spec}}
+ {{if eq $t "MYRECTYPE1"}}{{template "MYRECTYPE1"
$data}}{{end}}
+ {{if eq $t "MYRECTYPE2"}}{{template "MYRECTYPE2"
$data}}{{end}}
+ </section>
+ {{end}}
+ </div>
+ </div>
+ <a href="/"><button>Leave</button></a>
+ </div>
+{{end}}
+
+{{define "edit_myrecords"}}
+ <!-- DEBUG: show template parameters -->
+ <div>
+ <h3>Template data:</h3>
+ <p>Ref = {{.Ref}}</p>
+ <p>Action = {{.Action}}</p>
+ <p>Button = {{.Button}}</p>
+ <p>Names: {{range $i,$v := .Names}}'{{$v}}',{{end}}</p>
+ <ul>
+ {{range $k,$v := .Params}}
+ <li>'{{$k}}' = '{{$v}}'</li>
+ {{end}}
+ </ul>
+ </div>
+
+ <div>
+ <h3>Edit a resource record for label {{index .Params "label"}}:</h3>
+ <p><small>(Created: {{index .Params "created"}}, Last edited: {{index
.Params "modified"}})</small></p>
+ {{$t := rritype (index .Params "type")}}
+ {{if eq $t "MYRECTYPE1"}}{{template "MYRECTYPE1" .}}{{end}}
+ {{if eq $t "MYRECTYPE2"}}{{template "MYRECTYPE2" .}}{{end}}
+ </div>
+ <a href="/"><button>Leave</button></a>
+{{end}}
+
+{{define "MYRECTYPE1"}}
+ <h3>RECTYPE1</h3>
+ <form action="/action/{{.Action}}/rr/{{.Ref}}" {{if eq .Action
"upd"}}method="post"{{end}}>
+ <input type="hidden" name="type" value="{{index .Params "type"}}">
+ <table>
+ <tr>
+ <td align="right"><b>Attribute1:</b></td>
+ <td>
+ <input type="text" name="myrec1_text"
+ maxlength="63" size="63"
+ autofocus required
+ value="{{index .Params "myrec1_text"}}"
+ >
+ </td>
+ </tr>
+ {{template "RRCommon" .}}
+ <tr><td/><td><button id="submit">{{.Button}}
record</button></td></tr>
+ </table>
+ </form>
+{{end}}
+
+{{define "MYRECTYPE2"}}
+ <h3>RECTYPE2</h3>
+ <form action="/action/{{.Action}}/rr/{{.Ref}}" {{if eq .Action
"upd"}}method="post"{{end}}>
+ <input type="hidden" name="type" value="{{index .Params "type"}}">
+ <table>
+ <tr>
+ <td align="right"><b>Attribute1:</b></td>
+ <td>
+ <input type="text" name="myrec2_text"
+ maxlength="63" size="63"
+ autofocus required
+ value="{{index .Params "myrec2_text"}}"
+ >
+ </td>
+ </tr>
+ {{template "RRCommon" .}}
+ <tr><td/><td><button id="submit">{{.Button}}
record</button></td></tr>
+ </table>
+ </form>
+{{end}}
diff --git a/example-go/main.go b/example-go/main.go
new file mode 100644
index 0000000..7a6f66a
--- /dev/null
+++ b/example-go/main.go
@@ -0,0 +1,189 @@
+// This file is part of gnunet-go-plugins, a collection of plugins for
+// the GNUnet-implementation in Golang (gnunet.go). Plugins are used to
+// implement type-specific processing of resource records (e.g. GUI).
+// Copyright (C) 2022 by the authors:
+//
+// * Bernd Fix <brf@hoi-polloi.org> >Y<
+//
+// gnunet-go-plugins is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package main
+
+import (
+ _ "embed"
+ "fmt"
+ "strconv"
+
+ "github.com/bfix/gospel/data"
+)
+
+//======================================================================
+// C U S T O M I Z A T I O N
+//======================================================================
+
+// list of custom record types
+//
+// TODO: list record types handled by the plugin
+const (
+ RECTYPE1 = 90666
+ RECTYPE2 = 90667
+)
+
+//----------------------------------------------------------------------
+// Interface implementation
+//----------------------------------------------------------------------
+
+// Name returns the plugin name (sub-system name)
+//
+// TODO: customize name of the plugin
+func (p *CustomPlugin) Name() string {
+ return "Example-Go"
+}
+
+// CanHandle returns a list of resource record types that are handled
+// by the plugin.
+//
+// TODO: customize record type list
+func (p *CustomPlugin) CanHandle() (list []uint32) {
+ return []uint32{
+ RECTYPE1,
+ RECTYPE2,
+ }
+}
+
+// Compute a set of record specs allowed under a label with existing records
+func (p *CustomPlugin) Compatible(label string, rrSpecs [][2]uint32)
[][2]uint32 {
+ // simple case: no restrictions (all types, all flags)
+ list := [][2]uint32{
+ {RECTYPE1, 0},
+ {RECTYPE2, 0},
+ }
+ return list
+}
+
+// TemplateNames returns the names of the new / edit template
+// TODO: customize template names (see gui.htpl)
+func (p *CustomPlugin) TemplateNames() (string, string) {
+ return "new_myrecords", "edit_myrecords"
+}
+
+// Prefix returns the prefix for record attributes in map
+func (p *CustomPlugin) Prefix(t uint32) string {
+ switch t {
+ case RECTYPE1:
+ return "rectype1"
+ case RECTYPE2:
+ return "rectype2"
+ }
+ return ""
+}
+
+//======================================================================
+// No need to customize code beond this point...
+//======================================================================
+
+// Plugin is the instance of a custom implementation accessed by the
+// gnunet-go framework
+var Plugin = NewCustomPlugin()
+
+// ExamplePlugin is an example plugin for custom records that implements
+// the zonemaster.Plugin interface.
+type CustomPlugin struct{}
+
+// NewCustomPlugin creates an initialized plugin instance
+func NewCustomPlugin() CustomPlugin {
+ return CustomPlugin{}
+}
+
+//go:embed gui.htpl
+var tpl []byte
+
+// Template returns the new / edit template for resource record types
+// handled by the plugin.
+func (p *CustomPlugin) Template() string {
+ return string(tpl)
+}
+
+// Value returns a human-readable description of RR data
+func (p *CustomPlugin) Value(t uint32, rr []byte) (string, error) {
+ rec, err := p.GetInstance(t, rr)
+ if err != nil {
+ return "", err
+ }
+ return rec.Value()
+}
+
+// ToMap converts resource record data into GUI template variables
+func (p *CustomPlugin) ToMap(t uint32, rr []byte) (map[string]string, error) {
+ rec, err := p.GetInstance(t, rr)
+ if err != nil {
+ return nil, err
+ }
+ return rec.ToMap()
+}
+
+// FromMap converts a GUI template variables into resource record data
+func (p *CustomPlugin) FromMap(t uint32, vars map[string]string) ([]byte,
error) {
+ rec, err := p.GetInstance(t, nil)
+ if err != nil {
+ return nil, err
+ }
+ if err = rec.FromMap(vars); err != nil {
+ return nil, err
+ }
+ return data.Marshal(rec)
+}
+
+// CustomRecord interface impemented by custom types.
+type CustomRecord interface {
+ Value() (string, error)
+ ToMap() (map[string]string, error)
+ FromMap(map[string]string) error
+}
+
+// Get record instance for given type and data
+// TODO: customize to own needs
+func (p *CustomPlugin) GetInstance(t uint32, buf []byte) (rec CustomRecord,
err error) {
+ switch t {
+ case RECTYPE1:
+ rec = new(MyRecord1)
+ case RECTYPE2:
+ rec = new(MyRecord2)
+ }
+ if buf != nil {
+ err = data.Unmarshal(rec, buf)
+ }
+ return
+}
+
+// Numbers convertable from parameter value (string)
+type Number interface {
+ uint16 | uint32 | int | int16 | int32 | float32 | float64
+}
+
+// convert parameter value (string) to given number type
+func asInt[T Number](params map[string]string, key string) (val T, err error) {
+ vs, ok := params[key]
+ if !ok {
+ err = fmt.Errorf("%s missing", key)
+ return
+ }
+ var v int
+ if v, err = strconv.Atoi(vs); err != nil {
+ return
+ }
+ return T(v), nil
+}
diff --git a/example-go/plugin_test.go b/example-go/plugin_test.go
new file mode 100644
index 0000000..d002789
--- /dev/null
+++ b/example-go/plugin_test.go
@@ -0,0 +1,109 @@
+// This file is part of gnunet-go-plugins, a collection of plugins for
+// the GNUnet-implementation in Golang (gnunet.go). Plugins are used to
+// implement type-specific processing of resource records (e.g. GUI).
+// Copyright (C) 2022 by the authors:
+//
+// * Bernd Fix <brf@hoi-polloi.org> >Y<
+//
+// gnunet-go-plugins is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package main
+
+import (
+ "bytes"
+ "encoding/hex"
+ "testing"
+)
+
+func TestPluginCanHandle(t *testing.T) {
+ list := Plugin.CanHandle()
+ if len(list) == 0 {
+ t.Fatal("no type list received from plugin")
+ }
+ for _, rr := range list {
+ t.Logf("Can handle %d\n", rr)
+ }
+}
+
+func TestPluginCompatible(t *testing.T) {
+ in := make([][2]uint32, 0)
+ out := Plugin.Compatible("@", in)
+ for _, s := range out {
+ t.Logf("Compatible: type=%d, flags=%d\n", s[0], s[1])
+ }
+}
+
+func TestPluginPrefix(t *testing.T) {
+ t.Logf("Prefix: '%s' (%d)\n", Plugin.Prefix(90666), 90666)
+ t.Logf("Prefix: '%s' (%d)\n", Plugin.Prefix(90667), 90667)
+}
+
+var (
+ rec1 = []byte{0x00, 0x03, 0x66, 0x6f, 0x6f}
+ rec2 = []byte{0x00, 0x00, 0x00, 0x23, 0x00, 0x42}
+)
+
+func TestPluginValue(t *testing.T) {
+ s1, err := Plugin.Value(90666, rec1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("rec1: text='%s'\n", s1)
+
+ s2, err := Plugin.Value(90667, rec2)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("rec2: '%s'\n", s2)
+}
+
+func TestPluginToMap(t *testing.T) {
+ params, err := Plugin.ToMap(90666, rec1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("ToMap: rec1 -> %v\n", params)
+
+ if params, err = Plugin.ToMap(90667, rec2); err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("ToMap: rec2 -> %v\n", params)
+}
+
+func TestPluginFromMap(t *testing.T) {
+ params := map[string]string{
+ "rectype1_text": "foo",
+ }
+ rr, err := Plugin.FromMap(90666, params)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("FromMap: rr=%s\n", hex.EncodeToString(rr))
+ if !bytes.Equal(rr, rec1) {
+ t.Fatal("FromMap for rectype1 failed")
+ }
+ params = map[string]string{
+ "rectype2_var1": "35",
+ "rectype2_var2": "66",
+ }
+ if rr, err = Plugin.FromMap(90667, params); err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("FromMap: rr=%s\n", hex.EncodeToString(rr))
+ if !bytes.Equal(rr, rec2) {
+ t.Fatal("FromMap for rectype2 failed")
+ }
+}
diff --git a/example-go/records.go b/example-go/records.go
new file mode 100644
index 0000000..9053a4c
--- /dev/null
+++ b/example-go/records.go
@@ -0,0 +1,96 @@
+// This file is part of gnunet-go-plugins, a collection of plugins for
+// the GNUnet-implementation in Golang (gnunet.go). Plugins are used to
+// implement type-specific processing of resource records (e.g. GUI).
+// Copyright (C) 2022 by the authors:
+//
+// * Bernd Fix <brf@hoi-polloi.org> >Y<
+//
+// gnunet-go-plugins is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+)
+
+//----------------------------------------------------------------------
+// MyRecord1 (example custom record #1)
+//----------------------------------------------------------------------
+
+// MyRecord1 is an example record, where the record data is a simple
+// text string.
+type MyRecord1 struct {
+ TxtLen uint16 `order:"big"` // length of text in NBO
+ Text []byte `size:"TxtLen"` // text data (not terminated by \0)
+}
+
+// Value returns a human-readable representation of the record
+func (rec *MyRecord1) Value() (string, error) {
+ return string(rec.Text), nil
+}
+
+// ToMap returns the parameter set for the record
+func (rec *MyRecord1) ToMap() (params map[string]string, err error) {
+ params = make(map[string]string)
+ params["rectype1_text"] = string(rec.Text)
+ return
+}
+
+// FromMap reconstructs the record attributes from parameter list
+func (rec *MyRecord1) FromMap(params map[string]string) error {
+ text, ok := params["rectype1_text"]
+ if !ok {
+ return errors.New("rectype1_text missing")
+ }
+ rec.TxtLen = uint16(len(text))
+ rec.Text = []byte(text)
+ return nil
+}
+
+//----------------------------------------------------------------------
+// MyRecord2 (example custom record #2)
+//----------------------------------------------------------------------
+
+// MyRecord2 is an example record, where the record data contains two
+// integer values
+type MyRecord2 struct {
+ Var1 uint32 `order:"big"` // variable #1 in NBO
+ Var2 uint16 `order:"big"` // variable #2 in NBO
+}
+
+// Value returns a human-readable representation of the record
+func (rec *MyRecord2) Value() (string, error) {
+ return fmt.Sprintf("var1=%d,<br>var2=%d", rec.Var1, rec.Var2), nil
+}
+
+// ToMap returns the parameter set for the record
+func (rec *MyRecord2) ToMap() (params map[string]string, err error) {
+ params = make(map[string]string)
+ params["rectype2_var1"] = strconv.Itoa(int(rec.Var1))
+ params["rectype2_var2"] = strconv.Itoa(int(rec.Var2))
+ return
+}
+
+// FromMap reconstructs the record attributes from parameter list
+func (rec *MyRecord2) FromMap(params map[string]string) (err error) {
+ if rec.Var1, err = asInt[uint32](params, "rectype2_var1"); err != nil {
+ return
+ }
+ rec.Var2, err = asInt[uint16](params, "rectype2_var2")
+ return
+}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.