mirror of
https://github.com/tmedwards/tweego.git
synced 2025-07-05 14:10:27 -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 (
|
const (
|
||||||
outModeHTML outputMode = iota
|
outModeHTML outputMode = iota
|
||||||
|
outModeJSON
|
||||||
outModeTwee3
|
outModeTwee3
|
||||||
outModeTwee1
|
outModeTwee1
|
||||||
outModeTwine2Archive
|
outModeTwine2Archive
|
||||||
|
@ -132,6 +133,7 @@ func newConfig() *config {
|
||||||
options.Add("format", "-f=s|--format=s")
|
options.Add("format", "-f=s|--format=s")
|
||||||
options.Add("head", "--head=s")
|
options.Add("head", "--head=s")
|
||||||
options.Add("help", "-h|--help")
|
options.Add("help", "-h|--help")
|
||||||
|
options.Add("json", "-j|--json")
|
||||||
options.Add("listcharsets", "--list-charsets")
|
options.Add("listcharsets", "--list-charsets")
|
||||||
options.Add("listformats", "--list-formats")
|
options.Add("listformats", "--list-formats")
|
||||||
options.Add("logfiles", "--log-files")
|
options.Add("logfiles", "--log-files")
|
||||||
|
@ -164,6 +166,8 @@ func newConfig() *config {
|
||||||
c.headFile = val.(string)
|
c.headFile = val.(string)
|
||||||
case "help":
|
case "help":
|
||||||
usage()
|
usage()
|
||||||
|
case "json":
|
||||||
|
c.outMode = outModeJSON
|
||||||
case "listcharsets":
|
case "listcharsets":
|
||||||
usageCharsets()
|
usageCharsets()
|
||||||
case "listformats":
|
case "listformats":
|
||||||
|
|
92
passage.go
92
passage.go
|
@ -8,7 +8,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// standard packages
|
// standard packages
|
||||||
"fmt"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ var infoPassages = []string{
|
||||||
// Compilers: Twine/Twee 1.4+, Twee2, & Tweego.
|
// Compilers: Twine/Twee 1.4+, Twee2, & Tweego.
|
||||||
"StorySettings",
|
"StorySettings",
|
||||||
|
|
||||||
// Compilers: Tweego & (whatever Dan Cox's compiler is called).
|
// Compilers: Tweego, Extwee, & others.
|
||||||
"StoryData",
|
"StoryData",
|
||||||
|
|
||||||
// Compilers: Twine/Twee 1.4+ & Twee2.
|
// Compilers: Twine/Twee 1.4+ & Twee2.
|
||||||
|
@ -143,95 +142,6 @@ func (p *passage) isStoryPassage() bool {
|
||||||
return !p.hasInfoName() && !p.hasInfoTags()
|
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 {
|
func (p *passage) countWords() uint64 {
|
||||||
text := p.text
|
text := p.text
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,12 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type passageJSON struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
type passageMetadataJSON struct {
|
type passageMetadataJSON struct {
|
||||||
Position string `json:"position,omitempty"` // Twine 2 (`position`) & Twine 1 (`twine-position`).
|
Position string `json:"position,omitempty"` // Twine 2 (`position`) & Twine 1 (`twine-position`).
|
||||||
Size string `json:"size,omitempty"` // Twine 2 (`size`).
|
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"
|
"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 {
|
type storyDataJSON struct {
|
||||||
Ifid string `json:"ifid,omitempty"`
|
Ifid string `json:"ifid,omitempty"`
|
||||||
Format string `json:"format,omitempty"`
|
Format string `json:"format,omitempty"`
|
||||||
|
|
35
storyout.go
35
storyout.go
|
@ -8,6 +8,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,6 +18,40 @@ import (
|
||||||
"time"
|
"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 {
|
func (s *story) toTwee(outMode outputMode) []byte {
|
||||||
var data []byte
|
var data []byte
|
||||||
for _, p := range s.passages {
|
for _, p := range s.passages {
|
||||||
|
|
|
@ -64,6 +64,11 @@ func buildOutput(c *config) *story {
|
||||||
|
|
||||||
// Write the output.
|
// Write the output.
|
||||||
switch c.outMode {
|
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:
|
case outModeTwee3, outModeTwee1:
|
||||||
// Write out the project as Twee source.
|
// Write out the project as Twee source.
|
||||||
if _, err := fileWriteAll(c.outFile, alignRecordSeparators(s.toTwee(c.outMode))); err != nil {
|
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.
|
-h, --help Print this help, then exit.
|
||||||
--head=FILE Name of the file whose contents will be appended
|
--head=FILE Name of the file whose contents will be appended
|
||||||
as-is to the <head> element of the compiled HTML.
|
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-charsets List the supported input character sets, then exit.
|
||||||
--list-formats List the available story formats, then exit.
|
--list-formats List the available story formats, then exit.
|
||||||
--log-files Log the processed input files.
|
--log-files Log the processed input files.
|
||||||
|
@ -121,7 +122,7 @@ func usageVersion() {
|
||||||
fmt.Fprintf(os.Stderr, "\n%s, %s\n", tweegoName, tweegoVersion)
|
fmt.Fprintf(os.Stderr, "\n%s, %s\n", tweegoName, tweegoVersion)
|
||||||
fmt.Fprint(os.Stderr, `
|
fmt.Fprint(os.Stderr, `
|
||||||
Tweego (a Twee compiler in Go) [http://www.motoslave.net/tweego/]
|
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)
|
os.Exit(1)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue