~chrisppy/beagles

ee6782d362db154aa2963cca7e37982ba4dcf048 — Chris Palmer 28 days ago 910f804
add fmsg package
7 files changed, 253 insertions(+), 13 deletions(-)

M Makefile
A fmsg/fmsg.go
A fmsg/fmsg_test.go
A fmsg/schema.bare
M go.mod
M go.sum
M opml/opml_test.go
M Makefile => Makefile +10 -10
@@ 73,17 73,17 @@ check: clean
	$(GO) get -u gitlab.com/opennota/check/cmd/varcheck
	$(GO) get -u github.com/kisielk/errcheck
	$(GO) get -u github.com/psampaz/go-mod-outdated
	$(GO) fmt -x `$(GO) list ./... | grep -v fmsg`
	$(GO) vet `$(GO) list ./... | grep -v fmsg`
	golint -set_exit_status `$(GO) list ./... | grep -v fmsg`
	staticcheck `$(GO) list ./... | grep -v fmsg`
	aligncheck `$(GO) list ./... | grep -v fmsg`
	structcheck `$(GO) list ./... | grep -v fmsg`
	varcheck `$(GO) list ./... | grep -v fmsg`
	errcheck `$(GO) list ./... | grep -v fmsg`
	gosec -exclude=G107,G204 -exclude-dir=fmsg ./... 
	$(GO) fmt -x `$(GO) list ./...`
	$(GO) vet `$(GO) list ./...`
	golint -set_exit_status `$(GO) list ./...`
	staticcheck `$(GO) list ./...`
	aligncheck `$(GO) list ./...`
	structcheck `$(GO) list ./...`
	varcheck `$(GO) list ./...`
	errcheck `$(GO) list ./...`
	gosec -exclude=G107,G204 ./... 
	$(GO) list -u -m -json all | go-mod-outdated -direct -ci
	$(GO) test -cover `$(GO) list ./... | grep -v vendor | grep -v fmsg`
	$(GO) test -cover `$(GO) list ./... | grep -v vendor`
	make clean

package: check

A fmsg/fmsg.go => fmsg/fmsg.go +126 -0
@@ 0,0 1,126 @@
// This file is part of beagles.
//
// Copyright © 2020 Chris Palmer <chris@red-oxide.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package fmsg

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"time"

	"git.sr.ht/~sircmpwn/go-bare"
)

// Time string which should conform to RFC3339
type Time string

// Item contains all info needed for a RSS Item
type Item struct {
	Link     string `bare:"link"`
	Title    string `bare:"title"`
	Content  string `bare:"content"`
	Location string `bare:"location"`
	Mimetype string `bare:"mimetype"`
	Length   string `bare:"length"`
	Read     bool   `bare:"read"`
	Favorite bool   `bare:"favorite"`
	Date     Time   `bare:"date"`
}

// Feed contains all info needed for a RSS Feed
type Feed struct {
	Feed        string `bare:"feed"`
	Title       string `bare:"title"`
	Description string `bare:"description"`
	Link        string `bare:"link"`
	Items       []Item `bare:"items"`
}

// MessageV1 is the first version of a fmsg Message
type MessageV1 struct {
	Created   Time   `bare:"created"`
	Generator string `bare:"generator"`
	Feeds     []Feed `bare:"feeds"`
}

// Message is the core structure for fmsg
type Message MessageV1

// FromFile will read the fmsg file
func FromFile(path string) (*Message, error) {
	b, err := ioutil.ReadFile(filepath.Clean(path))
	if err != nil {
		return nil, err
	}

	return Read(b)
}

// Read the fmsg from a reader
func Read(b []byte) (*Message, error) {
	var m Message
	if err := bare.Unmarshal(b, &m); err != nil {
		return nil, err
	}

	errs := m.validate()
	if len(errs) > 0 {
		e := ""
		for err := range errs {
			e = fmt.Sprintf("%s\n%s", e, err.Error())
		}
		return nil, fmt.Errorf(e)
	}

	return &m, nil
}

func (m *Message) validate() map[error]bool {
	errors := make(map[error]bool)

	c := string(m.Created)
	if _, err := time.Parse(time.RFC3339, c); err != nil {
		errors[fmt.Errorf("%s does not conform to RFC3339", c)] = false
	}

	for _, f := range m.Feeds {
		for _, i := range f.Items {
			t := string(i.Date)
			if _, err := time.Parse(time.RFC3339, t); err != nil {
				errors[fmt.Errorf("%s does not conform to RFC3339", t)] = false
			}
		}
	}

	return errors
}

// Message will convert the structure to Fmsg format
func (m *Message) Message() ([]byte, error) {
	return bare.Marshal(m)
}

// Write the structure to a file as Fmsg
func (m *Message) Write(path string) error {
	b, err := m.Message()
	if err != nil {
		return err
	}

	return ioutil.WriteFile(filepath.Clean(path), b, 0600)
}

A fmsg/fmsg_test.go => fmsg/fmsg_test.go +66 -0
@@ 0,0 1,66 @@
// This file is part of beagles.
//
// Copyright © 2020 Chris Palmer <chris@red-oxide.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package fmsg

import (
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestWriteRead(t *testing.T) {
	i := Item{
		Link:     "a",
		Title:    "b",
		Content:  "c",
		Location: "d",
		Mimetype: "e",
		Length:   "f",
		Read:     true,
		Favorite: true,
		Date:     Time(time.Now().Format(time.RFC3339)),
	}

	f := Feed{
		Feed:        "https://example.com/feed.xml",
		Title:       "Test file",
		Description: "This is a test",
		Link:        "example.com",
		Items:       []Item{i},
	}

	m := Message{
		Created:   Time(time.Now().Format(time.RFC3339)),
		Generator: "generated",
		Feeds:     []Feed{f},
	}

	b, err := m.Message()
	assert.NoError(t, err)
	assert.NotNil(t, b)

	fmt.Println(string(b))

	m2, err := Read(b)
	assert.NoError(t, err)
	assert.NotNil(t, b)

	assert.Equal(t, m, *m2)
}

A fmsg/schema.bare => fmsg/schema.bare +28 -0
@@ 0,0 1,28 @@
type Time string # ISO 8601
type Message MessageV1

type MessageV1 {
	created: Time
	generator: string
	feeds: []Feed
}

type Feed {
	feed: string
	title: string
	description: string
	link: string
	items: []Item
}

type Item {
	link: string
	title: string
	content: string
	location: string
	mimetype: string
	length: string
	read: bool
	favorite: bool
	date: Time
}

M go.mod => go.mod +2 -1
@@ 6,6 6,7 @@ require (
	git.sr.ht/~adnano/go-gemini v0.1.3
	git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc
	git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3
	git.sr.ht/~sircmpwn/go-bare v0.0.0-20201009180350-cb0704728ca1
	github.com/DataDrake/waterlog v1.0.5
	github.com/gdamore/tcell/v2 v2.0.0
	github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7


@@ 17,6 18,6 @@ require (
	gitlab.com/tslocum/cbind v0.1.3
	gitlab.com/tslocum/cview v1.5.1-0.20201009235145-c33ce9563344
	go.etcd.io/bbolt v1.3.5
	golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d
	golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)

M go.sum => go.sum +4 -2
@@ 4,6 4,8 @@ git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc h1:51BD67xFX+bozd
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc/go.mod h1:t+Ww6SR24yYnXzEWiNlOY0AFo5E9B73X++10lrSpp4U=
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3 h1:4wDp4BKF7NQqoh73VXpZsB/t1OEhDpz/zEpmdQfbjDk=
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
git.sr.ht/~sircmpwn/go-bare v0.0.0-20201009180350-cb0704728ca1 h1:UQ3n+JCY5i1QX0GOiPz+fV3IvqTf93hJdVqCdcp02lM=
git.sr.ht/~sircmpwn/go-bare v0.0.0-20201009180350-cb0704728ca1/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDrake/waterlog v1.0.5 h1:+c506dboTQh4MoHHwdVtNa9E8K/3qAM/lieke0mH/mE=


@@ 81,8 83,8 @@ golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM=
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

M opml/opml_test.go => opml/opml_test.go +17 -0
@@ 1,3 1,20 @@
// This file is part of beagles.
//
// Copyright © 2020 Chris Palmer <chris@red-oxide.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package opml

import (