mirror of
https://github.com/tmedwards/tweego.git
synced 2025-07-05 06:00:26 -04:00
ADD: Add JSON output (-j, --json
).
This commit is contained in:
parent
33a6da381c
commit
3a8dfb2344
8 changed files with 175 additions and 92 deletions
|
@ -22,6 +22,7 @@ type outputMode int
|
|||
|
||||
const (
|
||||
outModeHTML outputMode = iota
|
||||
outModeJSON
|
||||
outModeTwee3
|
||||
outModeTwee1
|
||||
outModeTwine2Archive
|
||||
|
@ -132,6 +133,7 @@ func newConfig() *config {
|
|||
options.Add("format", "-f=s|--format=s")
|
||||
options.Add("head", "--head=s")
|
||||
options.Add("help", "-h|--help")
|
||||
options.Add("json", "-j|--json")
|
||||
options.Add("listcharsets", "--list-charsets")
|
||||
options.Add("listformats", "--list-formats")
|
||||
options.Add("logfiles", "--log-files")
|
||||
|
@ -164,6 +166,8 @@ func newConfig() *config {
|
|||
c.headFile = val.(string)
|
||||
case "help":
|
||||
usage()
|
||||
case "json":
|
||||
c.outMode = outModeJSON
|
||||
case "listcharsets":
|
||||
usageCharsets()
|
||||
case "listformats":
|
||||
|
|
92
passage.go
92
passage.go
|
@ -8,7 +8,6 @@ package main
|
|||
|
||||
import (
|
||||
// standard packages
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
|
@ -35,7 +34,7 @@ var infoPassages = []string{
|
|||
// Compilers: Twine/Twee 1.4+, Twee2, & Tweego.
|
||||
"StorySettings",
|
||||
|
||||
// Compilers: Tweego & (whatever Dan Cox's compiler is called).
|
||||
// Compilers: Tweego, Extwee, & others.
|
||||
"StoryData",
|
||||
|
||||
// Compilers: Twine/Twee 1.4+ & Twee2.
|
||||
|
@ -143,95 +142,6 @@ func (p *passage) isStoryPassage() bool {
|
|||
return !p.hasInfoName() && !p.hasInfoTags()
|
||||
}
|
||||
|
||||
func (p *passage) toTwee(outMode outputMode) string {
|
||||
var output string
|
||||
if outMode == outModeTwee3 {
|
||||
output = ":: " + tweeEscapeString(p.name)
|
||||
if len(p.tags) > 0 {
|
||||
output += " [" + tweeEscapeString(strings.Join(p.tags, " ")) + "]"
|
||||
}
|
||||
if p.hasAnyMetadata() {
|
||||
output += " " + string(p.marshalMetadata())
|
||||
}
|
||||
} else {
|
||||
output = ":: " + p.name
|
||||
if len(p.tags) > 0 {
|
||||
output += " [" + strings.Join(p.tags, " ") + "]"
|
||||
}
|
||||
}
|
||||
output += "\n"
|
||||
if len(p.text) > 0 {
|
||||
output += p.text + "\n"
|
||||
}
|
||||
output += "\n\n"
|
||||
return output
|
||||
}
|
||||
|
||||
func (p *passage) toPassagedata(pid uint) string {
|
||||
var (
|
||||
position string
|
||||
size string
|
||||
)
|
||||
if p.hasMetadataPosition() {
|
||||
position = p.metadata.position
|
||||
} else {
|
||||
// No position metadata, so generate something sensible on the fly.
|
||||
x := pid % 10
|
||||
y := pid / 10
|
||||
if x == 0 {
|
||||
x = 10
|
||||
} else {
|
||||
y++
|
||||
}
|
||||
position = fmt.Sprintf("%d,%d", x*125-25, y*125-25)
|
||||
}
|
||||
if p.hasMetadataSize() {
|
||||
size = p.metadata.size
|
||||
} else {
|
||||
// No size metadata, so default to the normal size.
|
||||
size = "100,100"
|
||||
}
|
||||
|
||||
/*
|
||||
<tw-passagedata pid="…" name="…" tags="…" position="…" size="…">…</tw-passagedata>
|
||||
*/
|
||||
return fmt.Sprintf(`<tw-passagedata pid="%d" name=%q tags=%q position=%q size=%q>%s</tw-passagedata>`,
|
||||
pid,
|
||||
attrEscapeString(p.name),
|
||||
attrEscapeString(strings.Join(p.tags, " ")),
|
||||
attrEscapeString(position),
|
||||
attrEscapeString(size),
|
||||
htmlEscapeString(p.text),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *passage) toTiddler(pid uint) string {
|
||||
var position string
|
||||
if p.hasMetadataPosition() {
|
||||
position = p.metadata.position
|
||||
} else {
|
||||
// No position metadata, so generate something sensible on the fly.
|
||||
x := pid % 10
|
||||
y := pid / 10
|
||||
if x == 0 {
|
||||
x = 10
|
||||
} else {
|
||||
y++
|
||||
}
|
||||
position = fmt.Sprintf("%d,%d", x*140-130, y*140-130)
|
||||
}
|
||||
|
||||
/*
|
||||
<div tiddler="…" tags="…" created="…" modifier="…" twine-position="…">…</div>
|
||||
*/
|
||||
return fmt.Sprintf(`<div tiddler=%q tags=%q twine-position=%q>%s</div>`,
|
||||
attrEscapeString(p.name),
|
||||
attrEscapeString(strings.Join(p.tags, " ")),
|
||||
attrEscapeString(position),
|
||||
tiddlerEscapeString(p.text),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *passage) countWords() uint64 {
|
||||
text := p.text
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@ import (
|
|||
"encoding/json"
|
||||
)
|
||||
|
||||
type passageJSON struct {
|
||||
Name string `json:"name"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type passageMetadataJSON struct {
|
||||
Position string `json:"position,omitempty"` // Twine 2 (`position`) & Twine 1 (`twine-position`).
|
||||
Size string `json:"size,omitempty"` // Twine 2 (`size`).
|
||||
|
|
110
passageout.go
Normal file
110
passageout.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright © 2014–2021 Thomas Michael Edwards. All rights reserved.
|
||||
Use of this source code is governed by a Simplified BSD License which
|
||||
can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
// standard packages
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (p *passage) toJSON() *passageJSON {
|
||||
return &passageJSON{
|
||||
Name: p.name,
|
||||
Tags: p.tags,
|
||||
Text: p.text,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *passage) toTwee(outMode outputMode) string {
|
||||
var output string
|
||||
if outMode == outModeTwee3 {
|
||||
output = ":: " + tweeEscapeString(p.name)
|
||||
if len(p.tags) > 0 {
|
||||
output += " [" + tweeEscapeString(strings.Join(p.tags, " ")) + "]"
|
||||
}
|
||||
if p.hasAnyMetadata() {
|
||||
output += " " + string(p.marshalMetadata())
|
||||
}
|
||||
} else {
|
||||
output = ":: " + p.name
|
||||
if len(p.tags) > 0 {
|
||||
output += " [" + strings.Join(p.tags, " ") + "]"
|
||||
}
|
||||
}
|
||||
output += "\n"
|
||||
if len(p.text) > 0 {
|
||||
output += p.text + "\n"
|
||||
}
|
||||
output += "\n\n"
|
||||
return output
|
||||
}
|
||||
|
||||
func (p *passage) toPassagedata(pid uint) string {
|
||||
var (
|
||||
position string
|
||||
size string
|
||||
)
|
||||
if p.hasMetadataPosition() {
|
||||
position = p.metadata.position
|
||||
} else {
|
||||
// No position metadata, so generate something sensible on the fly.
|
||||
x := pid % 10
|
||||
y := pid / 10
|
||||
if x == 0 {
|
||||
x = 10
|
||||
} else {
|
||||
y++
|
||||
}
|
||||
position = fmt.Sprintf("%d,%d", x*125-25, y*125-25)
|
||||
}
|
||||
if p.hasMetadataSize() {
|
||||
size = p.metadata.size
|
||||
} else {
|
||||
// No size metadata, so default to the normal size.
|
||||
size = "100,100"
|
||||
}
|
||||
|
||||
/*
|
||||
<tw-passagedata pid="…" name="…" tags="…" position="…" size="…">…</tw-passagedata>
|
||||
*/
|
||||
return fmt.Sprintf(`<tw-passagedata pid="%d" name=%q tags=%q position=%q size=%q>%s</tw-passagedata>`,
|
||||
pid,
|
||||
attrEscapeString(p.name),
|
||||
attrEscapeString(strings.Join(p.tags, " ")),
|
||||
attrEscapeString(position),
|
||||
attrEscapeString(size),
|
||||
htmlEscapeString(p.text),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *passage) toTiddler(pid uint) string {
|
||||
var position string
|
||||
if p.hasMetadataPosition() {
|
||||
position = p.metadata.position
|
||||
} else {
|
||||
// No position metadata, so generate something sensible on the fly.
|
||||
x := pid % 10
|
||||
y := pid / 10
|
||||
if x == 0 {
|
||||
x = 10
|
||||
} else {
|
||||
y++
|
||||
}
|
||||
position = fmt.Sprintf("%d,%d", x*140-130, y*140-130)
|
||||
}
|
||||
|
||||
/*
|
||||
<div tiddler="…" tags="…" created="…" modifier="…" twine-position="…">…</div>
|
||||
*/
|
||||
return fmt.Sprintf(`<div tiddler=%q tags=%q twine-position=%q>%s</div>`,
|
||||
attrEscapeString(p.name),
|
||||
attrEscapeString(strings.Join(p.tags, " ")),
|
||||
attrEscapeString(position),
|
||||
tiddlerEscapeString(p.text),
|
||||
)
|
||||
}
|
12
storydata.go
12
storydata.go
|
@ -13,6 +13,18 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type storyJSON struct {
|
||||
Name string `json:"name"`
|
||||
Ifid string `json:"ifid,omitempty"`
|
||||
Start string `json:"start,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
FormatVersion string `json:"format-version,omitempty"`
|
||||
Creator string `json:"creator,omitempty"`
|
||||
CreatorVersion string `json:"creator-version,omitempty"`
|
||||
Passages []*passageJSON `json:"passages"`
|
||||
}
|
||||
|
||||
type storyDataJSON struct {
|
||||
Ifid string `json:"ifid,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
|
|
35
storyout.go
35
storyout.go
|
@ -8,6 +8,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -17,6 +18,40 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func (s *story) toJSON(startName string) []byte {
|
||||
var passages = make([]*passageJSON, 0, 32)
|
||||
for _, p := range s.passages {
|
||||
if p.name == "StoryTitle" || p.name == "StoryData" {
|
||||
continue
|
||||
}
|
||||
|
||||
passages = append(passages, p.toJSON())
|
||||
}
|
||||
|
||||
marshaled, err := json.MarshalIndent(
|
||||
&storyJSON{
|
||||
s.name,
|
||||
s.ifid,
|
||||
startName,
|
||||
twine2OptionsMapToSlice(s.twine2.options),
|
||||
s.twine2.format,
|
||||
s.twine2.formatVersion,
|
||||
strings.Title(tweegoName),
|
||||
tweegoVersion.Version(),
|
||||
passages,
|
||||
},
|
||||
"",
|
||||
"\t",
|
||||
)
|
||||
if err != nil {
|
||||
// NOTE: We should never be able to see an error here. If we do,
|
||||
// then something truly exceptional—in a bad way—has happened, so
|
||||
// we get our panic on.
|
||||
panic(err)
|
||||
}
|
||||
return marshaled
|
||||
}
|
||||
|
||||
func (s *story) toTwee(outMode outputMode) []byte {
|
||||
var data []byte
|
||||
for _, p := range s.passages {
|
||||
|
|
|
@ -64,6 +64,11 @@ func buildOutput(c *config) *story {
|
|||
|
||||
// Write the output.
|
||||
switch c.outMode {
|
||||
case outModeJSON:
|
||||
// Write out the project as JSON.
|
||||
if _, err := fileWriteAll(c.outFile, alignRecordSeparators(s.toJSON(c.startName))); err != nil {
|
||||
log.Fatalf(`error: %s`, err.Error())
|
||||
}
|
||||
case outModeTwee3, outModeTwee1:
|
||||
// Write out the project as Twee source.
|
||||
if _, err := fileWriteAll(c.outFile, alignRecordSeparators(s.toTwee(c.outMode))); err != nil {
|
||||
|
|
3
usage.go
3
usage.go
|
@ -42,6 +42,7 @@ Options:
|
|||
-h, --help Print this help, then exit.
|
||||
--head=FILE Name of the file whose contents will be appended
|
||||
as-is to the <head> element of the compiled HTML.
|
||||
-j, --json Output JSON, instead of compiled HTML.
|
||||
--list-charsets List the supported input character sets, then exit.
|
||||
--list-formats List the available story formats, then exit.
|
||||
--log-files Log the processed input files.
|
||||
|
@ -121,7 +122,7 @@ func usageVersion() {
|
|||
fmt.Fprintf(os.Stderr, "\n%s, %s\n", tweegoName, tweegoVersion)
|
||||
fmt.Fprint(os.Stderr, `
|
||||
Tweego (a Twee compiler in Go) [http://www.motoslave.net/tweego/]
|
||||
Copyright (c) 2014-2020 Thomas Michael Edwards. All rights reserved.
|
||||
Copyright (c) 2014-2021 Thomas Michael Edwards. All rights reserved.
|
||||
|
||||
`)
|
||||
os.Exit(1)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue