start implementing metadata parsing support
Signed-off-by: Christine Dodrill <me@christine.website>
This commit is contained in:
parent
12d9012c08
commit
539a943576
105
gempub.go
105
gempub.go
|
@ -1 +1,106 @@
|
|||
// Package gempub implements the gempub 1.0.0 specification as described here: https://codeberg.org/oppenlab/gempub
|
||||
package gempub
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
Title string // mandatory title of the work
|
||||
GpubVersion string // mandatory Gempub format version: 1.0.0
|
||||
Index string // path to index.gmi, if none is specified it should be assumed to be in the root
|
||||
Author string // author of the work
|
||||
Language string // BCP 47 language code
|
||||
Charset string // if not set, assume UTF-8
|
||||
Description string // human-readable description
|
||||
Published time.Time // YYYY when date is unknown
|
||||
PublishDate time.Time // YYYY-MM-DD eg. 2006-01-02
|
||||
RevisionDate time.Time // YYYY-MM-DD eg. 2006-01-02
|
||||
Copyright string // copyright of the book
|
||||
License string // license of the book Version string a // human readable only, not meant to be parsed
|
||||
Cover string // a JPG or PNG image which can be anywhere in the directory structure
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNoTitle = errors.New("gempub: no title in document")
|
||||
)
|
||||
|
||||
func loadDateOrYear(when string) (t time.Time, err error) {
|
||||
if len(when) == 4 {
|
||||
t, err = time.Parse("2006", when)
|
||||
return
|
||||
}
|
||||
|
||||
t, err = time.Parse("2006-01-02", when)
|
||||
return
|
||||
}
|
||||
|
||||
func (m Metadata) Valid() error {
|
||||
switch {
|
||||
case m.Title == "":
|
||||
return ErrNoTitle
|
||||
case m.GpubVersion != "1.0.0":
|
||||
return fmt.Errorf("gempub: wrong gempub version: %v", m.GpubVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadMetadata(r io.Reader) (*Metadata, error) {
|
||||
var result Metadata
|
||||
|
||||
sc := bufio.NewScanner(r)
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
sp := strings.SplitN(line, ":", 2)
|
||||
key, val := sp[0], sp[1]
|
||||
val = strings.TrimSpace(val)
|
||||
switch key {
|
||||
case "title":
|
||||
result.Title = val
|
||||
case "gpubVersion":
|
||||
result.GpubVersion = val
|
||||
case "index":
|
||||
result.Index = val
|
||||
case "author":
|
||||
result.Author = val
|
||||
case "language":
|
||||
result.Language = val
|
||||
case "charset":
|
||||
result.Charset = val
|
||||
case "description":
|
||||
result.Description = val
|
||||
case "published":
|
||||
when, err := loadDateOrYear(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gempub: can't understand date %q: %v", val, err)
|
||||
}
|
||||
result.Published = when
|
||||
case "publishDate":
|
||||
when, err := loadDateOrYear(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gempub: can't understand date %q: %v", val, err)
|
||||
}
|
||||
result.PublishDate = when
|
||||
case "revisionDate":
|
||||
when, err := loadDateOrYear(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gempub: can't understand date %q: %v", val, err)
|
||||
}
|
||||
result.RevisionDate = when
|
||||
case "copyright":
|
||||
result.Copyright = val
|
||||
case "license":
|
||||
result.License = val
|
||||
case "cover":
|
||||
result.Cover = val
|
||||
}
|
||||
}
|
||||
|
||||
return &result, result.Valid()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package gempub
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:embed testdata/*
|
||||
var testdata embed.FS
|
||||
|
||||
func TestGoodMetadata(t *testing.T) {
|
||||
fnames, err := fs.Glob(testdata, "testdata/metadata/good/*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, fname := range fnames {
|
||||
t.Run(fname, func(t *testing.T) {
|
||||
fin, err := testdata.Open(fname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fin.Close()
|
||||
|
||||
md, err := ReadMetadata(fin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadMetadata(t *testing.T) {
|
||||
fnames, err := fs.Glob(testdata, "testdata/metadata/bad/*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, fname := range fnames {
|
||||
t.Run(fname, func(t *testing.T) {
|
||||
fin, err := testdata.Open(fname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fin.Close()
|
||||
|
||||
_, err = ReadMetadata(fin)
|
||||
if err == nil {
|
||||
t.Fatal("wanted error, but got none")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
title: Spellblade
|
||||
gpubVersion: 1.0.0
|
||||
index: book/index.gmi
|
||||
author: Xe
|
||||
language: en-US
|
||||
charset: UTF-8
|
||||
description: A woman realizes the gap between two opposites and their place in the middle.
|
||||
published: 2021
|
||||
publishDate: 2021-12-31
|
||||
revisionDate: 2022-01-01
|
||||
copyright: Christine Dodrill
|
||||
license: All Rights Reserved
|
||||
version: 1.0.0
|
||||
cover: cover.jpg
|
|
@ -0,0 +1,4 @@
|
|||
title: Star Maker
|
||||
author: Olaf Stapledon
|
||||
index: ./capsule/index.gmi
|
||||
gpubVersion: 1.0.0
|
Loading…
Reference in New Issue