diff --git a/cmd/mankidown/main.go b/cmd/mankidown/main.go index 7996643..77450ca 100644 --- a/cmd/mankidown/main.go +++ b/cmd/mankidown/main.go @@ -8,14 +8,11 @@ import ( "io" "log" "os" - "path/filepath" "strings" "github.com/revelaction/mankidown" ) -const outFileExt = ".txt" - const usage = `Usage: mankidown [-d DECK] [-n NOTE-TYPE] [-n GUID-PREFIX] [-t TAG] [-o OUTPUT] INPUT @@ -129,32 +126,11 @@ func main() { in = f } - var outFile string - - if outFlag != "" { - outFile = outFlag - } else { - baseName := filepath.Base(inFile) - outFile = strings.TrimSuffix(baseName, filepath.Ext(baseName)) + outFileExt - } - if guidPrefix != "" { conf.GuidPrefix = guidPrefix - } else { - conf.GuidPrefix = outFile } - var out io.Writer - f, err := os.OpenFile(outFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - errorf("failed to open output file %q: %v", outFlag, err) - } - defer func() { - if err := f.Close(); err != nil { - errorf("failed to close output file %q: %v", outFlag, err) - } - }() - out = f + conf.InFile = inFile markdown, err := io.ReadAll(in) if err != nil { @@ -167,8 +143,11 @@ func main() { errorf("failed to parse input file %q: %v", inFile, err) } - ex := mankidown.NewExporter(out, conf) - ex.Export(notes) + ex := mankidown.NewExporter(conf) + err = ex.Export(notes) + if err != nil { + errorf("failed to export file %q: %v", inFile, err) + } } // l is a logger with no prefixes. diff --git a/export.go b/export.go index 20a5c27..187cba7 100644 --- a/export.go +++ b/export.go @@ -3,7 +3,10 @@ package mankidown import ( "fmt" "io" + "os" + "path/filepath" "strings" + "unicode" ) // Anki header fields. @@ -19,58 +22,100 @@ const ( separatorChar = `|` - guidColumnName = "Mankidown-Guid" + guidColumnName = "mankidown-Guid" tagsColumnName = "Tags" + + outFileExt = ".txt" ) type Config struct { GuidPrefix string - InFileName string + InFile string Deck string NoteType string Tags []string } type Exporter struct { - out io.Writer config *Config } -func NewExporter(out io.Writer, config *Config) *Exporter { - return &Exporter{out: out, config: config} +func NewExporter(config *Config) *Exporter { + return &Exporter{config: config} +} + +func (ex *Exporter) Export(notes *Notes) error { + + var err error + inFileBaseName := filepath.Base(ex.config.InFile) + inFile := strings.TrimSuffix(inFileBaseName, filepath.Ext(inFileBaseName)) + outFile := inFile + outFileExt + + var out io.Writer + f, err := os.OpenFile(outFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return fmt.Errorf("failed to open output file %q: %v", f, err) + } + + defer func() { + if err = f.Close(); err != nil { + err = fmt.Errorf("failed to close output file %q: %v", f, err) + } + }() + + out = f + + ex.appendHeaders(out) + ex.appendHeaderColumns(out, notes.FieldNames()) + ex.appendHeaderTags(out, inFile) + + for _, note := range notes.Notes { + + // 1 field) id + fields := []string{} + fields = append(fields, ex.buildIdField(note, outFile)) + + // 2 field) tags + fields = append(fields, buildTagsField(note)) + + for _, field := range note.Fields() { + fields = append(fields, buildFieldAsLine(field.Html)) + } + + noteline := strings.Join(fields, separatorChar) + fmt.Fprintf(out, "%s\n", noteline) + + } + + return nil } -func (ex *Exporter) AppendHeaders() { +func (ex *Exporter) appendHeaders(out io.Writer) { // separator - fmt.Fprintln(ex.out, HeaderSeparator) + fmt.Fprintln(out, HeaderSeparator) // html - fmt.Fprintln(ex.out, HeaderHtml) + fmt.Fprintln(out, HeaderHtml) // guid column - fmt.Fprintln(ex.out, HeaderGuidColumn) + fmt.Fprintln(out, HeaderGuidColumn) // tag column - fmt.Fprintln(ex.out, HeaderTagColumn) + fmt.Fprintln(out, HeaderTagColumn) // deck if ex.config.Deck != "" { - fmt.Fprintf(ex.out, HeaderDeck, ex.config.Deck) + fmt.Fprintf(out, HeaderDeck, ex.config.Deck) } // notetype if ex.config.NoteType != "" { - fmt.Fprintf(ex.out, HeaderNoteType, ex.config.NoteType) + fmt.Fprintf(out, HeaderNoteType, ex.config.NoteType) } - // Tags - if len(ex.config.Tags) > 0 { - tagsStr := strings.Join(ex.config.Tags, " ") - fmt.Fprintf(ex.out, HeaderTags, tagsStr) - } } -func (ex *Exporter) AppendHeaderColumns(columns []string) { +func (ex *Exporter) appendHeaderColumns(out io.Writer, columns []string) { cols := []string{} // prepend id as first (anki duplication) @@ -80,17 +125,33 @@ func (ex *Exporter) AppendHeaderColumns(columns []string) { cols = append(cols, columns...) c := strings.Join(cols, separatorChar) - fmt.Fprintf(ex.out, "#columns:%s\n", c) + fmt.Fprintf(out, "#columns:%s\n", c) } -func buildIdField(n *Note, config *Config) string { +func (ex *Exporter) appendHeaderTags(out io.Writer, inFile string) { + + tags := ex.config.Tags - if config.GuidPrefix != "" { - return config.GuidPrefix + n.Id() + // Split the inFile words + f := func(c rune) bool { + return !unicode.IsLetter(c) && !unicode.IsNumber(c) } - return n.Id() + + tagsFromInFile := strings.FieldsFunc(inFile, f) + tags = append(tags, tagsFromInFile...) + fmt.Fprintf(out, HeaderTags, strings.Join(tags, " ")) } +func (ex *Exporter) buildIdField(n *Note, outFile string) string { + + if ex.config.GuidPrefix != "" { + return ex.config.GuidPrefix + n.Id() + } + + return outFile + n.Id() +} + +// buildTagsField builds the Tags string for a note func buildTagsField(n *Note) string { return strings.Join(n.Tags(), " ") @@ -109,26 +170,3 @@ func buildFieldAsLine(html string) string { fieldLine = strings.Replace(fieldLine, ">
<", "><", -1) return fieldLine } - -func (ex *Exporter) Export(notes *Notes) { - ex.AppendHeaders() - ex.AppendHeaderColumns(notes.FieldNames()) - - for _, note := range notes.Notes { - - // 1 field) id - fields := []string{} - fields = append(fields, buildIdField(note, ex.config)) - - // 2 field) tags - fields = append(fields, buildTagsField(note)) - - for _, field := range note.Fields() { - fields = append(fields, buildFieldAsLine(field.Html)) - } - - noteline := strings.Join(fields, separatorChar) - fmt.Fprintf(ex.out, "%s\n", noteline) - - } -} diff --git a/parse.go b/parse.go index d58f035..930b093 100644 --- a/parse.go +++ b/parse.go @@ -59,7 +59,7 @@ func (n *Note) addTags(tags string) { words := strings.Fields(tags) - n.tags = append(n.tags, words...) + n.tags = append(n.tags, words...) } func (n *Note) addId(id string) { @@ -240,7 +240,7 @@ func isNoteStart(n ast.Node) bool { return false } - if h.Level == 1{ + if h.Level == 1 { return true } @@ -279,7 +279,7 @@ func isFieldStart(n ast.Node) bool { return false } - if h.Level == 2{ + if h.Level == 2 { return true } @@ -311,7 +311,7 @@ func isFieldEnd(n ast.Node, inside bool) bool { if v.Level == 2 { return true } - if v.Level == 1{ + if v.Level == 1 { return true } }