Source File: go-coreutils/tr/main.go

package main

import (
  "flag"
  "os"
  "bufio"
  "strings"
  "fmt"
  "log"
)

type Opts struct {
  Compliment bool
  Delete bool
  Squeeze bool
  Truncate bool
}

type Translator struct {
  opts Opts
  TwoArgs bool
  RuneMap map[rune]rune
}

func (tr *Translator) ParseOpts() {
  var from []rune
  var to []rune
  tr.RuneMap = make(map[rune]rune)
  tr.TwoArgs = true

  flag.BoolVar(&tr.opts.Compliment, "c", false, "Use compliment of array1")
  flag.BoolVar(&tr.opts.Delete, "d", false, "Delete chars in array1")
  flag.BoolVar(&tr.opts.Squeeze, "s", false, "Squeeze chars in array2")
  flag.BoolVar(&tr.opts.Truncate, "t", false, "Truncate array1 to array2 length")
  flag.Parse()

  args := flag.Args()

  if flag.NArg() == 1 {
    from, to = []rune(args[0]), []rune(args[0])
    tr.TwoArgs = false
  } else if flag.NArg() == 2 {
    from, to = []rune(args[0]), []rune(args[1])
  } else {
    log.Fatal("USAGE: tr [-c] [-d] [-s] [-t] <array1> [array2]")
  }

  for i, ch := range from {
    tr.RuneMap[ch] = to[i]
  }
}

func (tr *Translator) Translate(from rune) rune {
  if !tr.TwoArgs { log.Fatal("translate requires two args") }

  to, ok := tr.RuneMap[from]

  if ok {
    return to
  } else {
    return from
  }
}

func (tr *Translator) Delete(line string) string {
  result := make([]rune, 0, len(line))

  for _, ch := range []rune(line) {
    _, ok := tr.RuneMap[ch]

    if !ok {
      result = append(result, ch)
    }
  }

  return string(result)
}

func (tr *Translator) Process(line string) {
  if tr.opts.Delete {
    line = tr.Delete(line)
  } else {
    line = strings.Map(tr.Translate, line)
  }

  fmt.Print(line)
}

func main() {
  tr := new(Translator)
  tr.ParseOpts()

  scan := bufio.NewScanner(os.Stdin)

  for scan.Scan() {
    line := scan.Text() + "\n"

    tr.Process(line)
  }
}