<commit_before>package main

import(
    "image"
    "fmt"
    "image/png"
    "image/color"
    "os"
    "imgProcessor/data"
    "log"
    "project-x/scanner"
)

var filenameIn, filenameOut, identifier, separator string
var accuracy int

//set default settings and start main menu
func main(){
    fmt.Println("-------------------------------------------------------------------------")
    fmt.Println("----------------------- Welcome to ImageProcessor -----------------------")
    fmt.Println("-----------------------   (C)2017 Max Obermeier   -----------------------")
    fmt.Println("-------------------------------------------------------------------------")
    filenameIn = "output.txt"
    identifier = "$Data"
    separator = "/"
    accuracy = 0
    menu()
}

//main menu
func menu(){
    for {
        fmt.Println()
        fmt.Println("Enter help to get a list of options or type in any other command.")
        input := scanner.GetS("==","help","license","colors","settings","process","exit")
        if input == "help" {
            help()
        }else if input == "license" {
            license()
        }else if input == "exit" {
            os.Exit(0)
        }else if input == "colors"{
            listColors()
        }else if input == "settings" {
            filenameIn, filenameOut, identifier, separator, accuracy = getParameters()
        }else if input == "process" {
            createImg(filenameIn, filenameOut, identifier, separator, accuracy)
        }
    }
}

//print license
func license(){
    fmt.Println("MIT License")
    fmt.Println("Copyright (c) 2017 Max Obermeier")
    fmt.Println("")
    fmt.Println(`Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:"`)
    fmt.Println("")
    fmt.Println("The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.")
    fmt.Println("")
    fmt.Println(`THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`)
    fmt.Println("")
}

//list colors and according values
func listColors(){
    fmt.Println("The third parameter of each highlighted line is portraied as a color.")
    fmt.Println("Here is a list of the colors, with its according value.")
    fmt.Println("  - 0 \t \t=> white")
    fmt.Println("  - 1 \t \t=> red")
    fmt.Println("  - 2 \t \t=> blue")
    fmt.Println("  - 3 \t \t=> green")
    fmt.Println("  - 4 \t \t=> turquoise")
    fmt.Println("  - 5 \t \t=> purple")
    fmt.Println("  - 6 \t \t=> yellow")
    fmt.Println("  - 7 \t \t=> black")
    fmt.Println("  - > 7 \t=> white")

}

//list all commands
func help(){
    fmt.Println("List of options:")
    fmt.Println("  - help \t \t=> Show list of options")
    fmt.Println("  - license \t \t=> Show license")
    fmt.Println("  - settings \t \t=> Set processing parameters")
    fmt.Println("  - process \t \t=> Start image creating process")
    fmt.Println("  - colors \t \t=> Show list of colors")
    fmt.Println("  - exit \t \t=> Exit program")
}

//change settings
func getParameters() (filenameIn, filenameOut, identifier, separator string, accuracy int){
    fmt.Println("Enter the filename of the input file (ending with .txt):")
    filenameIn = scanner.GetString()
    fmt.Println("Enter the identifier, the lines containing data start with:")
    identifier = scanner.GetString()
    fmt.Println("Enter the separator, the values are separated with:")
    separator = scanner.GetString()
    fmt.Println("Enter the number of decimal places the coordinates are cut off after:")
    accuracy = scanner.GetI("><",0,10)
    fmt.Println("Enter the filename of the output file (ending with .png):")
    filenameOut = scanner.GetString()
    return
}

//create image
func createImg(filenameIn, filenameOut, identifier, separator string, accuracy int){
    //build color slice
    var colors []color.RGBA
    colors = append(colors, color.RGBA{255,255,255,255})
    colors = append(colors, color.RGBA{255,0,0,255})
    colors = append(colors, color.RGBA{0,0,255,255})
    colors = append(colors, color.RGBA{0,255,0,255})
    colors = append(colors, color.RGBA{0,255,255,255})
    colors = append(colors, color.RGBA{255,0,255,255})
    colors = append(colors, color.RGBA{255,255,0,255})
    colors = append(colors, color.RGBA{0,0,0,255})
    //create data from file
    d := data.NewData()
    err := d.CreateFromFile(filenameIn, identifier, separator, accuracy)
    //check for any errors
    if err != nil {
        log.Println(err)
        return
    }
    //create rectangle with fitting dimensions (rectangle is used to build a rgbaImage)
    rect := image.Rectangle{image.Point{0, 0}, image.Point{d.X, d.Y}}
    //build image form rectangle
    img := image.NewRGBA(rect)

    //range over x and y coordinates of data.Img
    for x := range d.Img {
        for y := range d.Img[x]{
            //if there is a specific color for this value in the colors slice use it
            if d.Img[x][y] < len(colors) {
                //invert y - coordinates, because (0|0) of a image/png is at the top left corner and not at the bottom left as in a coordinate system
                img.SetRGBA(x, d.Y - y, colors[d.Img[x][y]])
            //if there is no color specified just use white
            }else {
                //invert y - coordinates, because (0|0) of a image/png is at the top left corner and not at the bottom left as in a coordinate system
                img.SetRGBA(x, d.Y - y, colors[0])
            }
        }
    }

    //finally save the image with the given name
    f, _ := os.Create(filenameOut)
    defer f.Close()
    err = png.Encode(f, img)
    if err != nil {
        log.Println(err)
        return
    }
}
<commit_msg>added settings type<commit_after>package main

import(
    "image"
    "fmt"
    "image/png"
    "image/color"
    "os"
    "imgProcessor/data"
    "log"
    "project-x/scanner"
)

type Settings struct {
    FilenameIn, FilenameOut, Identifier, Separator string
    Accuracy int
}

var settings Settings

//set default settings and start main menu
func main(){
    fmt.Println("-------------------------------------------------------------------------")
    fmt.Println("----------------------- Welcome to ImageProcessor -----------------------")
    fmt.Println("-----------------------   (C)2017 Max Obermeier   -----------------------")
    fmt.Println("-------------------------------------------------------------------------")
    settings.FilenameIn = "output.txt"
    settings.Identifier = "$Data"
    settings.Separator = "/"
    settings.Accuracy = 0
    menu()
}

//main menu
func menu(){
    for {
        fmt.Println()
        fmt.Println("Enter help to get a list of options or type in any other command.")
        input := scanner.GetS("==","help","license","colors","settings","process","exit")
        if input == "help" {
            help()
        }else if input == "license" {
            license()
        }else if input == "exit" {
            os.Exit(0)
        }else if input == "colors"{
            listColors()
        }else if input == "settings" {
            settings.FilenameIn, settings.FilenameOut, settings.Identifier, settings.Separator, settings.Accuracy = getParameters()
        }else if input == "process" {
            createImg(settings.FilenameIn, settings.FilenameOut, settings.Identifier, settings.Separator, settings.Accuracy)
        }
    }
}

//print license
func license(){
    fmt.Println("MIT License")
    fmt.Println("Copyright (c) 2017 Max Obermeier")
    fmt.Println("")
    fmt.Println(`Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:"`)
    fmt.Println("")
    fmt.Println("The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.")
    fmt.Println("")
    fmt.Println(`THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`)
    fmt.Println("")
}

//list colors and according values
func listColors(){
    fmt.Println("The third parameter of each highlighted line is portraied as a color.")
    fmt.Println("Here is a list of the colors, with its according value.")
    fmt.Println("  - 0 \t \t=> white")
    fmt.Println("  - 1 \t \t=> red")
    fmt.Println("  - 2 \t \t=> blue")
    fmt.Println("  - 3 \t \t=> green")
    fmt.Println("  - 4 \t \t=> turquoise")
    fmt.Println("  - 5 \t \t=> purple")
    fmt.Println("  - 6 \t \t=> yellow")
    fmt.Println("  - 7 \t \t=> black")
    fmt.Println("  - > 7 \t=> white")

}

//list all commands
func help(){
    fmt.Println("List of options:")
    fmt.Println("  - help \t \t=> Show list of options")
    fmt.Println("  - license \t \t=> Show license")
    fmt.Println("  - settings \t \t=> Set processing parameters")
    fmt.Println("  - process \t \t=> Start image creating process")
    fmt.Println("  - colors \t \t=> Show list of colors")
    fmt.Println("  - exit \t \t=> Exit program")
}

//change settings
func getParameters() (settings.FilenameIn, settings.FilenameOut, settings.Identifier, settings.Separator string, settings.Accuracy int){
    fmt.Println("Enter the filename of the input file (ending with .txt):")
    settings.FilenameIn = scanner.GetString()
    fmt.Println("Enter the settings.Identifier, the lines containing data start with:")
    settings.Identifier = scanner.GetString()
    fmt.Println("Enter the settings.Separator, the values are separated with:")
    settings.Separator = scanner.GetString()
    fmt.Println("Enter the number of decimal places the coordinates are cut off after:")
    settings.Accuracy = scanner.GetI("><",0,10)
    fmt.Println("Enter the filename of the output file (ending with .png):")
    settings.FilenameOut = scanner.GetString()
    return
}

//create image
func createImg(settings.FilenameIn, settings.FilenameOut, settings.Identifier, settings.Separator string, settings.Accuracy int){
    //build color slice
    var colors []color.RGBA
    colors = append(colors, color.RGBA{255,255,255,255})
    colors = append(colors, color.RGBA{255,0,0,255})
    colors = append(colors, color.RGBA{0,0,255,255})
    colors = append(colors, color.RGBA{0,255,0,255})
    colors = append(colors, color.RGBA{0,255,255,255})
    colors = append(colors, color.RGBA{255,0,255,255})
    colors = append(colors, color.RGBA{255,255,0,255})
    colors = append(colors, color.RGBA{0,0,0,255})
    //create data from file
    d := data.NewData()
    err := d.CreateFromFile(settings.FilenameIn, settings.Identifier, settings.Separator, settings.Accuracy)
    //check for any errors
    if err != nil {
        log.Println(err)
        return
    }
    //create rectangle with fitting dimensions (rectangle is used to build a rgbaImage)
    rect := image.Rectangle{image.Point{0, 0}, image.Point{d.X, d.Y}}
    //build image form rectangle
    img := image.NewRGBA(rect)

    //range over x and y coordinates of data.Img
    for x := range d.Img {
        for y := range d.Img[x]{
            //if there is a specific color for this value in the colors slice use it
            if d.Img[x][y] < len(colors) {
                //invert y - coordinates, because (0|0) of a image/png is at the top left corner and not at the bottom left as in a coordinate system
                img.SetRGBA(x, d.Y - y, colors[d.Img[x][y]])
            //if there is no color specified just use white
            }else {
                //invert y - coordinates, because (0|0) of a image/png is at the top left corner and not at the bottom left as in a coordinate system
                img.SetRGBA(x, d.Y - y, colors[0])
            }
        }
    }

    //finally save the image with the given name
    f, _ := os.Create(settings.FilenameOut)
    defer f.Close()
    err = png.Encode(f, img)
    if err != nil {
        log.Println(err)
        return
    }
}
<|endoftext|>


<commit_before>//
// nntp.go
//
package main

import (
  "bufio"
  "bytes"
  "io/ioutil"
  "log"
  "net"
  "strings"
)

type ConnectionInfo struct {
  mode string
  newsgroup string
  allowsPosting bool
  supportsStream bool
}

type NNTPConnection struct {
  conn net.Conn
  reader *bufio.Reader
  inbound bool
  debug bool
  info *ConnectionInfo
  policy *FeedPolicy
  send chan *NNTPMessage
}

func (self *NNTPConnection) HandleOutbound(d *NNTPDaemon) {
  var err error
  line := self.ReadLine()
  self.info.allowsPosting = strings.HasPrefix(line, "200 ")
  // they allow posting
  // send capabilities command
  err = self.SendLine("CAPABILITIES")

  // get capabilites
  for {
    line = strings.ToLower(self.ReadLine())
    if line == ".\r\n" {
      // done reading capabilities
      break
    }
    if line == "streaming\r\n" {
      self.info.supportsStream = true
    } else if line == "postihavestreaming\r\n" {
      self.info.supportsStream = true
    }
  }

  // if they support streaming and allow posting continue
  // otherwise quit
  if ! self.info.supportsStream || ! self.info.allowsPosting {
    if self.debug {
      log.Println(self.info.supportsStream, self.info.allowsPosting)
    }

    self.Quit()
    return
  }
  err = self.SendLine("MODE STREAM")
  if err != nil {
    return
  }
  line = self.ReadLine()
  if strings.HasPrefix(line, "203 ") {
    self.info.mode = "stream"
    log.Println("streaming mode activated")
  } else {
    self.Quit()
    return
  }

  if d.sync_on_start {
    d.store.IterateAllArticles(func(messageID string) bool {
      msg := d.store.GetMessage(messageID, false)
      if msg != nil {
        err = self.sendMessage(msg, d)
      }
      return err != nil
    })
  }

  // mainloop
  for  {
    if err != nil {
      // error from previous
      break
    }
    // poll for new message
    message := <- self.send
    err = self.sendMessage(message, d)
    if err != nil {
      log.Println(err)
      break
    }
  }
}

func (self *NNTPConnection) sendMessage(message *NNTPMessage, d *NNTPDaemon) error {
  var err error
  var line string
  // check if we allow it
  if self.policy == nil {
    // we have no policy so reject
    return nil
  }
  if ! self.policy.AllowsNewsgroup(message.Newsgroup) {
    log.Println("not federating article", message.MessageID, "beause it's in", message.Newsgroup)
    return nil
  }
  // send check
  err = self.SendLine("CHECK "+message.MessageID)
  line = self.ReadLine()
  if strings.HasPrefix(line, "238 ") {
    // accepted
    // send it
    err = self.SendLine("TAKETHIS "+message.MessageID)
    if err != nil {
      log.Println("error in outfeed", err)
      return  err
    }
    // load file
    data, err := ioutil.ReadFile(d.store.GetFilename(message.MessageID))
    if err != nil {
      log.Fatal("failed to read article", message.MessageID)
      self.Quit()
      return err
    }
    // split into lines
    parts := bytes.Split(data,[]byte{'\n'})
    // for each line send it
    for idx := range parts {
      ba := parts[idx]
      err = self.SendBytes(ba)
      err = self.Send("\r\n")
    }
    // send delimiter
    err = self.SendLine(".")
    if err != nil {
      log.Println("failed to send")
      self.Quit()
      return err
    }
    // check for success / fail
    line := self.ReadLine()
    if strings.HasPrefix(line, "239 ") {
      log.Println("Article", message.MessageID, "sent")
    } else {
      log.Println("Article", message.MessageID, "failed to send", line)
    }
    // done
    return nil
  } else if strings.HasPrefix(line, "435 ") {
    // already have it
    if self.debug {
      log.Println(message.MessageID, "already owned")
    }
  } else if strings.HasPrefix(line, "437 ") {
    // article banned
    log.Println(message.MessageID, "was banned")
  }
  if err != nil {
    self.Quit()
    log.Println("failure in outfeed", err)
    return err
  }
  return nil
}

// handle inbound connection
func (self *NNTPConnection) HandleInbound(d *NNTPDaemon) {
  var err error
  self.info.mode = "STREAM"
  log.Println("Incoming nntp connection from", self.conn.RemoteAddr())
  // send welcome
  self.SendLine("200 ayy lmao we are SRNd2, posting allowed")
  for {
    if err != nil {
      log.Println("failure in infeed", err)
      self.Quit()
      return
    }
    line := self.ReadLine()
      if len(line) == 0 {
      break
    }
    // parse line

    _line := strings.Replace(line, "\n", "", -1)
    _line = strings.Replace(_line, "\r", "", -1)
    commands := strings.Split(_line, " ")
    cmd := strings.ToUpper(commands[0])

    // capabilities command
    if cmd == "CAPABILITIES" {
      self.sendCapabilities()
    } else if cmd == "MODE" { // mode switch
      if len(commands) == 2 {
        mode := strings.ToUpper(commands[1])
        if mode == "READER" {
          self.SendLine("501 no reader mode")
        } else if mode == "STREAM" {
          self.info.mode = mode
          self.SendLine("203 stream as desired")
        } else {
          self.SendLine("501 unknown mode")
        }
      } else {
        self.SendLine("500 syntax error")
      }
    } else if self.info.mode == "STREAM" { // we are in stream mode
      if cmd == "TAKETHIS" {
        if len(commands) == 2 {
          article := commands[1]
          if ValidMessageID(article) {
            file := d.store.OpenFile(article)
            for {
              line := self.ReadLine()
              // unexpected close
              if len(line) == 0 {
                log.Fatal(self.conn.RemoteAddr(), "unexpectedly closed connection")
              }
              if strings.HasPrefix(line, "Path: ") {
                line = "Path: " + d.instance_name + "!" + line[6:]
              }
              // done reading
              if line == ".\r\n" {
                break
              } else {
                line = strings.Replace(line, "\r", "", -1)
                file.Write([]byte(line))
              }
            }
            file.Close()
            // the send was good
            // tell them
            self.SendLine("239 "+article)
            log.Println(self.conn.RemoteAddr(), "got article", article)
            d.infeed <- article
          }
        }
      }
      if cmd == "CHECK" {
        if len(commands) == 2 {
          if ! ValidMessageID(commands[1]) {
            self.SendLine("501 bad message id")
            continue
          }
          article := commands[1]
          if d.store.HasArticle(article) {
            self.SendLine("435 "+commands[1]+" we have this article")
          } else {
            self.SendLine("238 "+commands[1]+" we want this article please give it")
          }
        }
      }
    }
  }
  self.Close()
}

func (self *NNTPConnection) sendCapabilities() {
  self.SendLine("101 we can do stuff")
  self.SendLine("VERSION 2")
  self.SendLine("IMPLEMENTATION srndv2 better than SRNd")
  self.SendLine("STREAMING")
  self.SendLine(".")
}

func (self *NNTPConnection) Quit() {
  if ! self.inbound {
    self.SendLine("QUIT")
    _ = self.ReadLine()
  }
  self.Close()
}

func (self *NNTPConnection) ReadLine() string {
  line, err := self.reader.ReadString('\n')
  if err != nil {
    return ""
  }
  //line = strings.Replace(line, "\n", "", -1)
  //line = strings.Replace(line, "\r", "", -1)
  if self.debug {
    log.Println(self.conn.RemoteAddr(), "recv line", line)
  }
  return line
}

// send a line
func (self *NNTPConnection) SendLine(line string) error {
  if self.debug {
    log.Println(self.conn.RemoteAddr(), "send line", line)
  }
  return self.Send(line+"\r\n")
}

// send data
func (self *NNTPConnection) Send(data string) error {
  _, err := self.conn.Write([]byte(data))
  return err
}

// send data
func (self *NNTPConnection) SendBytes(data []byte) error {
  _ , err := self.conn.Write(data)
  return err
}

// close the connection
func (self *NNTPConnection) Close() {
  err := self.conn.Close()
  if err != nil {
    log.Println(self.conn.RemoteAddr(), err)
  }
  log.Println(self.conn.RemoteAddr(), "Closed Connection")
}
<commit_msg>only rewrite path header once<commit_after>//
// nntp.go
//
package main

import (
  "bufio"
  "bytes"
  "io/ioutil"
  "log"
  "net"
  "strings"
)

type ConnectionInfo struct {
  mode string
  newsgroup string
  allowsPosting bool
  supportsStream bool
}

type NNTPConnection struct {
  conn net.Conn
  reader *bufio.Reader
  inbound bool
  debug bool
  info *ConnectionInfo
  policy *FeedPolicy
  send chan *NNTPMessage
}

func (self *NNTPConnection) HandleOutbound(d *NNTPDaemon) {
  var err error
  line := self.ReadLine()
  self.info.allowsPosting = strings.HasPrefix(line, "200 ")
  // they allow posting
  // send capabilities command
  err = self.SendLine("CAPABILITIES")

  // get capabilites
  for {
    line = strings.ToLower(self.ReadLine())
    if line == ".\r\n" {
      // done reading capabilities
      break
    }
    if line == "streaming\r\n" {
      self.info.supportsStream = true
    } else if line == "postihavestreaming\r\n" {
      self.info.supportsStream = true
    }
  }

  // if they support streaming and allow posting continue
  // otherwise quit
  if ! self.info.supportsStream || ! self.info.allowsPosting {
    if self.debug {
      log.Println(self.info.supportsStream, self.info.allowsPosting)
    }

    self.Quit()
    return
  }
  err = self.SendLine("MODE STREAM")
  if err != nil {
    return
  }
  line = self.ReadLine()
  if strings.HasPrefix(line, "203 ") {
    self.info.mode = "stream"
    log.Println("streaming mode activated")
  } else {
    self.Quit()
    return
  }

  if d.sync_on_start {
    d.store.IterateAllArticles(func(messageID string) bool {
      msg := d.store.GetMessage(messageID, false)
      if msg != nil {
        err = self.sendMessage(msg, d)
      }
      return err != nil
    })
  }

  // mainloop
  for  {
    if err != nil {
      // error from previous
      break
    }
    // poll for new message
    message := <- self.send
    err = self.sendMessage(message, d)
    if err != nil {
      log.Println(err)
      break
    }
  }
}

func (self *NNTPConnection) sendMessage(message *NNTPMessage, d *NNTPDaemon) error {
  var err error
  var line string
  // check if we allow it
  if self.policy == nil {
    // we have no policy so reject
    return nil
  }
  if ! self.policy.AllowsNewsgroup(message.Newsgroup) {
    log.Println("not federating article", message.MessageID, "beause it's in", message.Newsgroup)
    return nil
  }
  // send check
  err = self.SendLine("CHECK "+message.MessageID)
  line = self.ReadLine()
  if strings.HasPrefix(line, "238 ") {
    // accepted
    // send it
    err = self.SendLine("TAKETHIS "+message.MessageID)
    if err != nil {
      log.Println("error in outfeed", err)
      return  err
    }
    // load file
    data, err := ioutil.ReadFile(d.store.GetFilename(message.MessageID))
    if err != nil {
      log.Fatal("failed to read article", message.MessageID)
      self.Quit()
      return err
    }
    // split into lines
    parts := bytes.Split(data,[]byte{'\n'})
    // for each line send it
    for idx := range parts {
      ba := parts[idx]
      err = self.SendBytes(ba)
      err = self.Send("\r\n")
    }
    // send delimiter
    err = self.SendLine(".")
    if err != nil {
      log.Println("failed to send")
      self.Quit()
      return err
    }
    // check for success / fail
    line := self.ReadLine()
    if strings.HasPrefix(line, "239 ") {
      log.Println("Article", message.MessageID, "sent")
    } else {
      log.Println("Article", message.MessageID, "failed to send", line)
    }
    // done
    return nil
  } else if strings.HasPrefix(line, "435 ") {
    // already have it
    if self.debug {
      log.Println(message.MessageID, "already owned")
    }
  } else if strings.HasPrefix(line, "437 ") {
    // article banned
    log.Println(message.MessageID, "was banned")
  }
  if err != nil {
    self.Quit()
    log.Println("failure in outfeed", err)
    return err
  }
  return nil
}

// handle inbound connection
func (self *NNTPConnection) HandleInbound(d *NNTPDaemon) {
  var err error
  self.info.mode = "STREAM"
  log.Println("Incoming nntp connection from", self.conn.RemoteAddr())
  // send welcome
  self.SendLine("200 ayy lmao we are SRNd2, posting allowed")
  for {
    if err != nil {
      log.Println("failure in infeed", err)
      self.Quit()
      return
    }
    line := self.ReadLine()
      if len(line) == 0 {
      break
    }
    // parse line

    _line := strings.Replace(line, "\n", "", -1)
    _line = strings.Replace(_line, "\r", "", -1)
    commands := strings.Split(_line, " ")
    cmd := strings.ToUpper(commands[0])

    // capabilities command
    if cmd == "CAPABILITIES" {
      self.sendCapabilities()
    } else if cmd == "MODE" { // mode switch
      if len(commands) == 2 {
        mode := strings.ToUpper(commands[1])
        if mode == "READER" {
          self.SendLine("501 no reader mode")
        } else if mode == "STREAM" {
          self.info.mode = mode
          self.SendLine("203 stream as desired")
        } else {
          self.SendLine("501 unknown mode")
        }
      } else {
        self.SendLine("500 syntax error")
      }
    } else if self.info.mode == "STREAM" { // we are in stream mode
      if cmd == "TAKETHIS" {
        if len(commands) == 2 {
          article := commands[1]
          if ValidMessageID(article) {
            file := d.store.OpenFile(article)
            var rewrote_path bool
            for {
              line := self.ReadLine()
              // unexpected close
              if len(line) == 0 {
                log.Fatal(self.conn.RemoteAddr(), "unexpectedly closed connection")
              }
              if ! rewrote_path && strings.HasPrefix(line, "Path: ") {
                line = "Path: " + d.instance_name + "!" + line[6:]
              }
              // done reading
              if line == ".\r\n" {
                break
              } else {
                line = strings.Replace(line, "\r", "", -1)
                file.Write([]byte(line))
              }
            }
            file.Close()
            // the send was good
            // tell them
            self.SendLine("239 "+article)
            log.Println(self.conn.RemoteAddr(), "got article", article)
            d.infeed <- article
          }
        }
      }
      if cmd == "CHECK" {
        if len(commands) == 2 {
          if ! ValidMessageID(commands[1]) {
            self.SendLine("501 bad message id")
            continue
          }
          article := commands[1]
          if d.store.HasArticle(article) {
            self.SendLine("435 "+commands[1]+" we have this article")
          } else {
            self.SendLine("238 "+commands[1]+" we want this article please give it")
          }
        }
      }
    }
  }
  self.Close()
}

func (self *NNTPConnection) sendCapabilities() {
  self.SendLine("101 we can do stuff")
  self.SendLine("VERSION 2")
  self.SendLine("IMPLEMENTATION srndv2 better than SRNd")
  self.SendLine("STREAMING")
  self.SendLine(".")
}

func (self *NNTPConnection) Quit() {
  if ! self.inbound {
    self.SendLine("QUIT")
    _ = self.ReadLine()
  }
  self.Close()
}

func (self *NNTPConnection) ReadLine() string {
  line, err := self.reader.ReadString('\n')
  if err != nil {
    return ""
  }
  //line = strings.Replace(line, "\n", "", -1)
  //line = strings.Replace(line, "\r", "", -1)
  if self.debug {
    log.Println(self.conn.RemoteAddr(), "recv line", line)
  }
  return line
}

// send a line
func (self *NNTPConnection) SendLine(line string) error {
  if self.debug {
    log.Println(self.conn.RemoteAddr(), "send line", line)
  }
  return self.Send(line+"\r\n")
}

// send data
func (self *NNTPConnection) Send(data string) error {
  _, err := self.conn.Write([]byte(data))
  return err
}

// send data
func (self *NNTPConnection) SendBytes(data []byte) error {
  _ , err := self.conn.Write(data)
  return err
}

// close the connection
func (self *NNTPConnection) Close() {
  err := self.conn.Close()
  if err != nil {
    log.Println(self.conn.RemoteAddr(), err)
  }
  log.Println(self.conn.RemoteAddr(), "Closed Connection")
}
<|endoftext|>


<commit_before>// Copyright 2013 Google, Inc.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package yaml

import (
	"bytes"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// A File represents the top-level YAML node found in a file.  It is intended
// for use as a configuration file.
type File struct {
	Root Node

	// TODO(kevlar): Add a cache?
}

// ReadFile reads a YAML configuration file from the given filename.
func ReadFile(filename string) (*File, error) {
	fin, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer fin.Close()

	f := new(File)
	f.Root, err = Parse(fin)
	if err != nil {
		return nil, err
	}

	return f, nil
}

// Config reads a YAML configuration from a static string.  If an error is
// found, it will panic.  This is a utility function and is intended for use in
// initializers.
func Config(yamlconf string) *File {
	var err error
	buf := bytes.NewBufferString(yamlconf)

	f := new(File)
	f.Root, err = Parse(buf)
	if err != nil {
		panic(err)
	}

	return f
}

// ConfigFile reads a YAML configuration file from the given filename and
// panics if an error is found.  This is a utility function and is intended for
// use in initializers.
func ConfigFile(filename string) *File {
	f, err := ReadFile(filename)
	if err != nil {
		panic(err)
	}
	return f
}

// Get retrieves a scalar from the file specified by a string of the same
// format as that expected by Child.  If the final node is not a Scalar, Get
// will return an error.
func (f *File) Get(spec string) (string, error) {
	node, err := Child(f.Root, spec)
	if err != nil {
		return "", err
	}

	if node == nil {
		return "", &NodeNotFound{
			Full: spec,
			Spec: spec,
		}
	}

	scalar, ok := node.(Scalar)
	if !ok {
		return "", &NodeTypeMismatch{
			Full:     spec,
			Spec:     spec,
			Token:    "$",
			Expected: "yaml.Scalar",
			Node:     node,
		}
	}
	return scalar.String(), nil
}

func (f *File) GetInt(spec string) (int64, error) {
	s, err := f.Get(spec)
	if err != nil {
		return 0, err
	}

	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return 0, err
	}

	return i, nil
}

func (f *File) GetBool(spec string) (bool, error) {
	s, err := f.Get(spec)
	if err != nil {
		return false, err
	}

	b, err := strconv.ParseBool(s)
	if err != nil {
		return false, err
	}

	return b, nil
}

// Count retrieves a the number of elements in the specified list from the file
// using the same format as that expected by Child.  If the final node is not a
// List, Count will return an error.
func (f *File) Count(spec string) (int, error) {
	node, err := Child(f.Root, spec)
	if err != nil {
		return -1, err
	}

	if node == nil {
		return -1, &NodeNotFound{
			Full: spec,
			Spec: spec,
		}
	}

	lst, ok := node.(List)
	if !ok {
		return -1, &NodeTypeMismatch{
			Full:     spec,
			Spec:     spec,
			Token:    "$",
			Expected: "yaml.List",
			Node:     node,
		}
	}
	return lst.Len(), nil
}

// Require retrieves a scalar from the file specified by a string of the same
// format as that expected by Child.  If the final node is not a Scalar, String
// will panic.  This is a convenience function for use in initializers.
func (f *File) Require(spec string) string {
	str, err := f.Get(spec)
	if err != nil {
		panic(err)
	}
	return str
}

// Child retrieves a child node from the specified node as follows:
//   .mapkey   - Get the key 'mapkey' of the Node, which must be a Map
//   [idx]     - Choose the index from the current Node, which must be a List
//
// The above selectors may be applied recursively, and each successive selector
// applies to the result of the previous selector.  For convenience, a "." is
// implied as the first character if the first character is not a "." or "[".
// The node tree is walked from the given node, considering each token of the
// above format.  If a node along the evaluation path is not found, an error is
// returned. If a node is not the proper type, an error is returned.  If the
// final node is not a Scalar, an error is returned.
func Child(root Node, spec string) (Node, error) {
	if len(spec) == 0 {
		return root, nil
	}

	if first := spec[0]; first != '.' && first != '[' {
		spec = "." + spec
	}

	var recur func(Node, string, string) (Node, error)
	recur = func(n Node, last, s string) (Node, error) {

		if len(s) == 0 {
			return n, nil
		}

		if n == nil {
			return nil, &NodeNotFound{
				Full: spec,
				Spec: last,
			}
		}

		// Extract the next token
		delim := 1 + strings.IndexAny(s[1:], ".[")
		if delim <= 0 {
			delim = len(s)
		}
		tok := s[:delim]
		remain := s[delim:]

		switch s[0] {
		case '[':
			s, ok := n.(List)
			if !ok {
				return nil, &NodeTypeMismatch{
					Node:     n,
					Expected: "yaml.List",
					Full:     spec,
					Spec:     last,
					Token:    tok,
				}
			}

			if tok[0] == '[' && tok[len(tok)-1] == ']' {
				if num, err := strconv.Atoi(tok[1 : len(tok)-1]); err == nil {
					if num >= 0 && num < len(s) {
						return recur(s[num], last+tok, remain)
					}
				}
			}
			return nil, &NodeNotFound{
				Full: spec,
				Spec: last + tok,
			}
		default:
			m, ok := n.(Map)
			if !ok {
				return nil, &NodeTypeMismatch{
					Node:     n,
					Expected: "yaml.Map",
					Full:     spec,
					Spec:     last,
					Token:    tok,
				}
			}

			n, ok = m[tok[1:]]
			return recur(n, last+tok, remain)
		}
		panic("unreachable")
	}
	return recur(root, "", spec)
}

type NodeNotFound struct {
	Full string
	Spec string
}

func (e *NodeNotFound) Error() string {
	return fmt.Sprintf("yaml: %s: %q not found", e.Full, e.Spec)
}

type NodeTypeMismatch struct {
	Full     string
	Spec     string
	Token    string
	Node     Node
	Expected string
}

func (e *NodeTypeMismatch) Error() string {
	return fmt.Sprintf("yaml: %s: type mismatch: %q is %T, want %s (at %q)",
		e.Full, e.Spec, e.Node, e.Expected, e.Token)
}
<commit_msg>Return a NodeNotFound error when yaml.Child map keys don't exist.<commit_after>// Copyright 2013 Google, Inc.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package yaml

import (
	"bytes"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// A File represents the top-level YAML node found in a file.  It is intended
// for use as a configuration file.
type File struct {
	Root Node

	// TODO(kevlar): Add a cache?
}

// ReadFile reads a YAML configuration file from the given filename.
func ReadFile(filename string) (*File, error) {
	fin, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer fin.Close()

	f := new(File)
	f.Root, err = Parse(fin)
	if err != nil {
		return nil, err
	}

	return f, nil
}

// Config reads a YAML configuration from a static string.  If an error is
// found, it will panic.  This is a utility function and is intended for use in
// initializers.
func Config(yamlconf string) *File {
	var err error
	buf := bytes.NewBufferString(yamlconf)

	f := new(File)
	f.Root, err = Parse(buf)
	if err != nil {
		panic(err)
	}

	return f
}

// ConfigFile reads a YAML configuration file from the given filename and
// panics if an error is found.  This is a utility function and is intended for
// use in initializers.
func ConfigFile(filename string) *File {
	f, err := ReadFile(filename)
	if err != nil {
		panic(err)
	}
	return f
}

// Get retrieves a scalar from the file specified by a string of the same
// format as that expected by Child.  If the final node is not a Scalar, Get
// will return an error.
func (f *File) Get(spec string) (string, error) {
	node, err := Child(f.Root, spec)
	if err != nil {
		return "", err
	}

	if node == nil {
		return "", &NodeNotFound{
			Full: spec,
			Spec: spec,
		}
	}

	scalar, ok := node.(Scalar)
	if !ok {
		return "", &NodeTypeMismatch{
			Full:     spec,
			Spec:     spec,
			Token:    "$",
			Expected: "yaml.Scalar",
			Node:     node,
		}
	}
	return scalar.String(), nil
}

func (f *File) GetInt(spec string) (int64, error) {
	s, err := f.Get(spec)
	if err != nil {
		return 0, err
	}

	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return 0, err
	}

	return i, nil
}

func (f *File) GetBool(spec string) (bool, error) {
	s, err := f.Get(spec)
	if err != nil {
		return false, err
	}

	b, err := strconv.ParseBool(s)
	if err != nil {
		return false, err
	}

	return b, nil
}

// Count retrieves a the number of elements in the specified list from the file
// using the same format as that expected by Child.  If the final node is not a
// List, Count will return an error.
func (f *File) Count(spec string) (int, error) {
	node, err := Child(f.Root, spec)
	if err != nil {
		return -1, err
	}

	if node == nil {
		return -1, &NodeNotFound{
			Full: spec,
			Spec: spec,
		}
	}

	lst, ok := node.(List)
	if !ok {
		return -1, &NodeTypeMismatch{
			Full:     spec,
			Spec:     spec,
			Token:    "$",
			Expected: "yaml.List",
			Node:     node,
		}
	}
	return lst.Len(), nil
}

// Require retrieves a scalar from the file specified by a string of the same
// format as that expected by Child.  If the final node is not a Scalar, String
// will panic.  This is a convenience function for use in initializers.
func (f *File) Require(spec string) string {
	str, err := f.Get(spec)
	if err != nil {
		panic(err)
	}
	return str
}

// Child retrieves a child node from the specified node as follows:
//   .mapkey   - Get the key 'mapkey' of the Node, which must be a Map
//   [idx]     - Choose the index from the current Node, which must be a List
//
// The above selectors may be applied recursively, and each successive selector
// applies to the result of the previous selector.  For convenience, a "." is
// implied as the first character if the first character is not a "." or "[".
// The node tree is walked from the given node, considering each token of the
// above format.  If a node along the evaluation path is not found, an error is
// returned. If a node is not the proper type, an error is returned.  If the
// final node is not a Scalar, an error is returned.
func Child(root Node, spec string) (Node, error) {
	if len(spec) == 0 {
		return root, nil
	}

	if first := spec[0]; first != '.' && first != '[' {
		spec = "." + spec
	}

	var recur func(Node, string, string) (Node, error)
	recur = func(n Node, last, s string) (Node, error) {

		if len(s) == 0 {
			return n, nil
		}

		if n == nil {
			return nil, &NodeNotFound{
				Full: spec,
				Spec: last,
			}
		}

		// Extract the next token
		delim := 1 + strings.IndexAny(s[1:], ".[")
		if delim <= 0 {
			delim = len(s)
		}
		tok := s[:delim]
		remain := s[delim:]

		switch s[0] {
		case '[':
			s, ok := n.(List)
			if !ok {
				return nil, &NodeTypeMismatch{
					Node:     n,
					Expected: "yaml.List",
					Full:     spec,
					Spec:     last,
					Token:    tok,
				}
			}

			if tok[0] == '[' && tok[len(tok)-1] == ']' {
				if num, err := strconv.Atoi(tok[1 : len(tok)-1]); err == nil {
					if num >= 0 && num < len(s) {
						return recur(s[num], last+tok, remain)
					}
				}
			}
			return nil, &NodeNotFound{
				Full: spec,
				Spec: last + tok,
			}
		default:
			m, ok := n.(Map)
			if !ok {
				return nil, &NodeTypeMismatch{
					Node:     n,
					Expected: "yaml.Map",
					Full:     spec,
					Spec:     last,
					Token:    tok,
				}
			}

			n, ok = m[tok[1:]]
			if !ok {
				return nil, &NodeNotFound{
					Full: spec,
					Spec: last + tok,
				}
			}
			return recur(n, last+tok, remain)
		}
		panic("unreachable")
	}
	return recur(root, "", spec)
}

type NodeNotFound struct {
	Full string
	Spec string
}

func (e *NodeNotFound) Error() string {
	return fmt.Sprintf("yaml: %s: %q not found", e.Full, e.Spec)
}

type NodeTypeMismatch struct {
	Full     string
	Spec     string
	Token    string
	Node     Node
	Expected string
}

func (e *NodeTypeMismatch) Error() string {
	return fmt.Sprintf("yaml: %s: type mismatch: %q is %T, want %s (at %q)",
		e.Full, e.Spec, e.Node, e.Expected, e.Token)
}
<|endoftext|>


<commit_before>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package flatpack

import (
	"reflect"
	"testing"

	"CodeCity/server/testutil"
)

// Some user-declared types for testing:
type myBool bool
type myInt int
type myUint64 uint64
type myFloat64 float64
type myComplex64 complex64
type myString string
type loop *loop

// All these should not be changed by flatten() or unflatten(), and in
// the former case nothing should be added to the flatpack.
var simpleCases = []interface{}{
	false,
	int(1),
	int8(2),
	int16(3),
	int32(4),
	int64(5),
	uint(6),
	uint8(7),
	uint16(8),
	uint32(9),
	uint64(10),
	uintptr(11),
	float32(12.0),
	float64(13.0),
	complex64(14 + 15i),
	complex128(16 + 17i),
	string("Eighteen"),

	myBool(false),
	myInt(19),
	myUint64(20),
	myFloat64(21.0),
	myComplex64(22 + 23i),
	myString("twenty-four"),

	[3]int{24, 25, 26},
	[]int{27, 28, 29},
	map[myString]myInt{
		"cpcallen": 2365779,
		"fraser":   7499832,
	},
}

func TestSimple(t *testing.T) {
	var f = New()
	for _, c := range simpleCases {
		typ := reflect.TypeOf(c)
		v := reflect.ValueOf(c)
		if r := f.flatten(v); !testutil.RecEqual(r.Interface(), c, true) {
			t.Errorf("f.flatten(reflect.ValueOf(%#v)).Interface() == %#v (expected %#v)", c, r, c)
		}
		if r := f.unflatten(typ, v); !testutil.RecEqual(r.Interface(), c, true) {
			t.Errorf("f.unflatten(%s, reflect.ValueOf(%#v)).Interface() == %#v (expected %#v)", typ, c, r, c)
		}
	}
	if len(f.Values) != 0 {
		t.Errorf("f.Values == %#v (expected empty slice)", f.Values)
	}
}

type complexCase struct {
	name       string      // Testcase name
	pre        interface{} // Value to flatten before test (as setup)
	orig, flat interface{} // Original and flattened value
	newVals    int         // How many items will be added to .Values()?
	skip       bool        // Should this test be skipped?
}

// Data used (by pointer) in some complex cases:

var ints = [...]int{42, 69, 105}
var spam = "spam"
var loop1, loop2 loop
var hhg = map[string]int{"answer": 42}

// complexCases are the cases checked by TestComplex.
//
// N.B. TestComplex runs extra checks for certain named testcases.  Be
// careful if renaming testcases to make sure new name matches!
var complexCases = []complexCase{
	{
		name:    "ArrayPtr",
		orig:    [...]*string{nil, &spam, &spam, &spam, &spam},
		flat:    [...]ref{-1, 0, 0, 0, 0},
		newVals: 1,
	}, {
		name:    "PtrLoop",
		orig:    (loop)(&loop1),
		flat:    ref(0),
		newVals: 2,
	}, {
		// FIXME: broken because flatten gets confused about loop vs *loop
		name:    "PtrLoopBroken",
		orig:    &loop1,
		flat:    ref(0),
		newVals: 2,
		skip:    true,
	}, {
		name: "StringMap",
		pre:  [...]*int{&ints[0], &ints[1]}, // Force ref order
		orig: map[myString]*int{
			"answer":  &ints[0],
			"naughty": &ints[1],
			"random":  &ints[2],
		},
		flat: map[myString]ref{
			"answer":  ref(0),
			"naughty": ref(1),
			"random":  ref(2),
		},
		newVals: 3,
	}, {
		name: "NonStringMap",
		orig: map[[2]int]string{
			[2]int{1914, 1918}: "WW I",
			[2]int{1939, 1945}: "WW II",
			[2]int{2026, 2053}: "WW III", // Citation: http://memory-alpha.wikia.com/wiki/World_War_III
		},
	}, {
		name: "NilMap",
		orig: map[int]string(nil),
	}, {
		name: "EmptyMap",
		orig: map[int]string{},
	}, {
		name: "NonStringMapShareSubstructure",
		pre:  [...]*int{&ints[0], &ints[1]}, // Force ref order
		orig: map[interface{}]*int{
			nil:    &ints[0],
			"one":  &ints[1],
			2:      &ints[2],
			3 + 0i: &ints[2], // Check shared substructure
		},
		newVals: 3,
	}, {
		name: "NilSlice",
		orig: []int(nil),
	}, {
		name: "EmptySlice",
		orig: []int{},
	}, {
		name: "Struct",
		orig: (struct {
			i  int
			sl []interface{}
		}{42, []interface{}{nil, 69, "Hello", true}}),
	}, {
		// FIXME: broken because flatten fails to check for
		// map-pointer equality.
		name: "SharedMapSubstructure",
		orig: struct{ m1, m2 map[string]int }{hhg, hhg},
		skip: true,
	}, {
		// FIXME: code (probably) works but test broken because of
		// limitations of RecEqual: it only compares map keys with ==
		name: "MapKeysShareSubstructure",
		orig: struct {
			s *string
			m map[*string]int
		}{
			s: &spam,
			m: map[*string]int{&spam: 42},
		},
		skip: true,
	},
}

// TestComplex does round-trip testing of more complex cases.
//
// N.B.: there are some extra checks for certain named testcases in
// the bottom section of the function.
func TestComplex(t *testing.T) {
	for _, c := range complexCases {
		t.Run(c.name, func(t *testing.T) {
			if c.skip {
				t.Skipf("Skipping %s", c.name)
			}
			var f = New()
			if c.pre != nil {
				_ = f.flatten(reflect.ValueOf(c.pre))
			}
			typ := reflect.TypeOf(c.orig)
			v := reflect.ValueOf(c.orig)
			flat := f.flatten(v)
			unflat := f.unflatten(typ, flat)

			if ftyp := flat.Type(); ftyp != flatType(typ) {
				t.Errorf("f.Flatten(reflect.ValueOf(%#v)).Type() == %s (expected %s)", c.orig, ftyp, flatType(typ))
			}
			if c.flat != nil && !testutil.RecEqual(flat.Interface(), c.flat, true) {
				t.Errorf("f.flatten(reflect.ValueOf(%#v)).Interface() == %#v (expected %#v)", c.orig, flat, c.flat)
			}
			if !testutil.RecEqual(unflat.Interface(), c.orig, true) {
				t.Errorf("f.unflatten(%s, f.flatten(reflect.ValueOf(%#v))).Interface() == %#v (expected %#[2]v)", typ, c.orig, unflat)
			}
			if len(f.Values) != c.newVals {
				t.Errorf("len(f.Values) == %d (expected %d)", len(f.Values), c.newVals)
				t.Logf("f.Values == %#v", f.Values)
			}

			// Extra checks:
			switch c.name {
			case "StringMap":
				if ft := flat.Type().Kind(); ft != reflect.Map {
					t.Errorf("f.flatten(reflect.ValueOf(%#v)).Type() == %s (expected a map type)", c.orig, ft)
				}
			case "NonStringMap", "NonStringMapShareSubstructure":
				if ft := flat.Type().Kind(); ft == reflect.Map {
					t.Errorf("f.flatten(reflect.ValueOf(%#v)).Type() == %s (expected a non-map type)", c.orig, ft)

				}
			}
		})
	}
}

// init registers types for testing, plus sets up loops:
func init() {
	var examples = []interface{}{
		myBool(false),
		myInt(0),
		myUint64(0),
		myFloat64(0),
		myComplex64(0 + 0i),
		myString(""),
		loop(nil),
	}
	for _, val := range examples {
		RegisterTypeOf(val)
	}

	loop1 = &loop2
	loop2 = &loop1
}
<commit_msg>Remove unnecessary type decoration<commit_after>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package flatpack

import (
	"reflect"
	"testing"

	"CodeCity/server/testutil"
)

// Some user-declared types for testing:
type myBool bool
type myInt int
type myUint64 uint64
type myFloat64 float64
type myComplex64 complex64
type myString string
type loop *loop

// All these should not be changed by flatten() or unflatten(), and in
// the former case nothing should be added to the flatpack.
var simpleCases = []interface{}{
	false,
	int(1),
	int8(2),
	int16(3),
	int32(4),
	int64(5),
	uint(6),
	uint8(7),
	uint16(8),
	uint32(9),
	uint64(10),
	uintptr(11),
	float32(12.0),
	float64(13.0),
	complex64(14 + 15i),
	complex128(16 + 17i),
	string("Eighteen"),

	myBool(false),
	myInt(19),
	myUint64(20),
	myFloat64(21.0),
	myComplex64(22 + 23i),
	myString("twenty-four"),

	[3]int{24, 25, 26},
	[]int{27, 28, 29},
	map[myString]myInt{
		"cpcallen": 2365779,
		"fraser":   7499832,
	},
}

func TestSimple(t *testing.T) {
	var f = New()
	for _, c := range simpleCases {
		typ := reflect.TypeOf(c)
		v := reflect.ValueOf(c)
		if r := f.flatten(v); !testutil.RecEqual(r.Interface(), c, true) {
			t.Errorf("f.flatten(reflect.ValueOf(%#v)).Interface() == %#v (expected %#v)", c, r, c)
		}
		if r := f.unflatten(typ, v); !testutil.RecEqual(r.Interface(), c, true) {
			t.Errorf("f.unflatten(%s, reflect.ValueOf(%#v)).Interface() == %#v (expected %#v)", typ, c, r, c)
		}
	}
	if len(f.Values) != 0 {
		t.Errorf("f.Values == %#v (expected empty slice)", f.Values)
	}
}

type complexCase struct {
	name       string      // Testcase name
	pre        interface{} // Value to flatten before test (as setup)
	orig, flat interface{} // Original and flattened value
	newVals    int         // How many items will be added to .Values()?
	skip       bool        // Should this test be skipped?
}

// Data used (by pointer) in some complex cases:

var ints = [...]int{42, 69, 105}
var spam = "spam"
var loop1, loop2 loop
var hhg = map[string]int{"answer": 42}

// complexCases are the cases checked by TestComplex.
//
// N.B. TestComplex runs extra checks for certain named testcases.  Be
// careful if renaming testcases to make sure new name matches!
var complexCases = []complexCase{
	{
		name:    "ArrayPtr",
		orig:    [...]*string{nil, &spam, &spam, &spam, &spam},
		flat:    [...]ref{-1, 0, 0, 0, 0},
		newVals: 1,
	}, {
		name:    "PtrLoop",
		orig:    (loop)(&loop1),
		flat:    ref(0),
		newVals: 2,
	}, {
		// FIXME: broken because flatten gets confused about loop vs *loop
		name:    "PtrLoopBroken",
		orig:    &loop1,
		flat:    ref(0),
		newVals: 2,
		skip:    true,
	}, {
		name: "StringMap",
		pre:  [...]*int{&ints[0], &ints[1]}, // Force ref order
		orig: map[myString]*int{
			"answer":  &ints[0],
			"naughty": &ints[1],
			"random":  &ints[2],
		},
		flat: map[myString]ref{
			"answer":  ref(0),
			"naughty": ref(1),
			"random":  ref(2),
		},
		newVals: 3,
	}, {
		name: "NonStringMap",
		orig: map[[2]int]string{
			{1914, 1918}: "WW I",
			{1939, 1945}: "WW II",
			{2026, 2053}: "WW III", // Citation: http://memory-alpha.wikia.com/wiki/World_War_III
		},
	}, {
		name: "NilMap",
		orig: map[int]string(nil),
	}, {
		name: "EmptyMap",
		orig: map[int]string{},
	}, {
		name: "NonStringMapShareSubstructure",
		pre:  [...]*int{&ints[0], &ints[1]}, // Force ref order
		orig: map[interface{}]*int{
			nil:    &ints[0],
			"one":  &ints[1],
			2:      &ints[2],
			3 + 0i: &ints[2], // Check shared substructure
		},
		newVals: 3,
	}, {
		name: "NilSlice",
		orig: []int(nil),
	}, {
		name: "EmptySlice",
		orig: []int{},
	}, {
		name: "Struct",
		orig: (struct {
			i  int
			sl []interface{}
		}{42, []interface{}{nil, 69, "Hello", true}}),
	}, {
		// FIXME: broken because flatten fails to check for
		// map-pointer equality.
		name: "SharedMapSubstructure",
		orig: struct{ m1, m2 map[string]int }{hhg, hhg},
		skip: true,
	}, {
		// FIXME: code (probably) works but test broken because of
		// limitations of RecEqual: it only compares map keys with ==
		name: "MapKeysShareSubstructure",
		orig: struct {
			s *string
			m map[*string]int
		}{
			s: &spam,
			m: map[*string]int{&spam: 42},
		},
		skip: true,
	},
}

// TestComplex does round-trip testing of more complex cases.
//
// N.B.: there are some extra checks for certain named testcases in
// the bottom section of the function.
func TestComplex(t *testing.T) {
	for _, c := range complexCases {
		t.Run(c.name, func(t *testing.T) {
			if c.skip {
				t.Skipf("Skipping %s", c.name)
			}
			var f = New()
			if c.pre != nil {
				_ = f.flatten(reflect.ValueOf(c.pre))
			}
			typ := reflect.TypeOf(c.orig)
			v := reflect.ValueOf(c.orig)
			flat := f.flatten(v)
			unflat := f.unflatten(typ, flat)

			if ftyp := flat.Type(); ftyp != flatType(typ) {
				t.Errorf("f.Flatten(reflect.ValueOf(%#v)).Type() == %s (expected %s)", c.orig, ftyp, flatType(typ))
			}
			if c.flat != nil && !testutil.RecEqual(flat.Interface(), c.flat, true) {
				t.Errorf("f.flatten(reflect.ValueOf(%#v)).Interface() == %#v (expected %#v)", c.orig, flat, c.flat)
			}
			if !testutil.RecEqual(unflat.Interface(), c.orig, true) {
				t.Errorf("f.unflatten(%s, f.flatten(reflect.ValueOf(%#v))).Interface() == %#v (expected %#[2]v)", typ, c.orig, unflat)
			}
			if len(f.Values) != c.newVals {
				t.Errorf("len(f.Values) == %d (expected %d)", len(f.Values), c.newVals)
				t.Logf("f.Values == %#v", f.Values)
			}

			// Extra checks:
			switch c.name {
			case "StringMap":
				if ft := flat.Type().Kind(); ft != reflect.Map {
					t.Errorf("f.flatten(reflect.ValueOf(%#v)).Type() == %s (expected a map type)", c.orig, ft)
				}
			case "NonStringMap", "NonStringMapShareSubstructure":
				if ft := flat.Type().Kind(); ft == reflect.Map {
					t.Errorf("f.flatten(reflect.ValueOf(%#v)).Type() == %s (expected a non-map type)", c.orig, ft)

				}
			}
		})
	}
}

// init registers types for testing, plus sets up loops:
func init() {
	var examples = []interface{}{
		myBool(false),
		myInt(0),
		myUint64(0),
		myFloat64(0),
		myComplex64(0 + 0i),
		myString(""),
		loop(nil),
	}
	for _, val := range examples {
		RegisterTypeOf(val)
	}

	loop1 = &loop2
	loop2 = &loop1
}
<|endoftext|>


<commit_before>package qshell

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
)

type Account struct {
	AccessKey string `json:"access_key"`
	SecretKey string `json:"secret_key"`
	Zone      string `json:"zone,omitempty"`
}

func (this *Account) ToJson() (jsonStr string, err error) {
	jsonData, mErr := json.Marshal(this)
	if mErr != nil {
		err = errors.New(fmt.Sprintf("Marshal account data failed, %s", mErr))
		return
	}
	jsonStr = string(jsonData)
	return
}

func (this *Account) String() string {
	return fmt.Sprintf("AccessKey: %s SecretKey: %s Zone: %s", this.AccessKey, this.SecretKey, this.Zone)
}

func (this *Account) Set(accessKey string, secretKey string, zone string) (err error) {
	currentUser, uErr := user.Current()
	if uErr != nil {
		err = errors.New(fmt.Sprintf("Get current user failed, %s", uErr.Error()))
		return
	}
	qAccountFolder := filepath.Join(currentUser.HomeDir, ".qshell")
	if _, sErr := os.Stat(qAccountFolder); sErr != nil {
		if mErr := os.MkdirAll(qAccountFolder, 0775); mErr != nil {
			err = errors.New(fmt.Sprintf("Mkdir `%s' failed, %s", qAccountFolder, mErr.Error()))
			return
		}
	}
	qAccountFile := filepath.Join(qAccountFolder, "account.json")

	fp, openErr := os.Create(qAccountFile)
	if openErr != nil {
		err = errors.New(fmt.Sprintf("Open account file failed, %s, please use `account` to set AccessKey and SecretKey first", openErr.Error()))
		return
	}
	defer fp.Close()

	this.AccessKey = accessKey
	this.SecretKey = secretKey

	//default to nb
	if !IsValidZone(zone) {
		zone = ZoneNB
	}

	this.Zone = zone

	jsonStr, mErr := this.ToJson()
	if mErr != nil {
		err = mErr
		return
	}

	_, wErr := fp.WriteString(jsonStr)
	if wErr != nil {
		err = errors.New(fmt.Sprintf("Write account info failed, %s", wErr.Error()))
		return
	}

	return
}

func (this *Account) Get() (err error) {
	currentUser, uErr := user.Current()
	if uErr != nil {
		err = errors.New(fmt.Sprintf("Get current user failed, %s", uErr.Error()))
		return
	}
	qAccountFile := filepath.Join(currentUser.HomeDir, ".qshell", "account.json")
	fp, openErr := os.Open(qAccountFile)
	if openErr != nil {
		err = errors.New(fmt.Sprintf("Open account file failed, %s", openErr.Error()))
		return
	}
	defer fp.Close()
	accountBytes, readErr := ioutil.ReadAll(fp)
	if readErr != nil {
		err = errors.New(fmt.Sprintf("Read account file error, %s", readErr.Error()))
		return
	}
	if umError := json.Unmarshal(accountBytes, this); umError != nil {
		err = errors.New(fmt.Sprintf("Parse account file error, %s", umError.Error()))
		return
	}

	if this.Zone == "" {
		this.Zone = ZoneNB
	}

	//set default hosts
	switch this.Zone {
	case ZoneAWS:
		SetZone(ZoneAWSConfig)
	case ZoneBC:
		SetZone(ZoneBCConfig)
	default:
		SetZone(ZoneNBConfig)
	}

	return
}
<commit_msg>fix the silly output<commit_after>package qshell

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
)

type Account struct {
	AccessKey string `json:"access_key"`
	SecretKey string `json:"secret_key"`
	Zone      string `json:"zone,omitempty"`
}

func (this *Account) ToJson() (jsonStr string, err error) {
	jsonData, mErr := json.Marshal(this)
	if mErr != nil {
		err = errors.New(fmt.Sprintf("Marshal account data failed, %s", mErr))
		return
	}
	jsonStr = string(jsonData)
	return
}

func (this *Account) String() string {
	return fmt.Sprintf("AccessKey: %s SecretKey: %s Zone: %s", this.AccessKey, this.SecretKey, this.Zone)
}

func (this *Account) Set(accessKey string, secretKey string, zone string) (err error) {
	currentUser, uErr := user.Current()
	if uErr != nil {
		err = errors.New(fmt.Sprintf("Get current user failed, %s", uErr.Error()))
		return
	}
	qAccountFolder := filepath.Join(currentUser.HomeDir, ".qshell")
	if _, sErr := os.Stat(qAccountFolder); sErr != nil {
		if mErr := os.MkdirAll(qAccountFolder, 0775); mErr != nil {
			err = errors.New(fmt.Sprintf("Mkdir `%s' failed, %s", qAccountFolder, mErr.Error()))
			return
		}
	}
	qAccountFile := filepath.Join(qAccountFolder, "account.json")

	fp, openErr := os.Create(qAccountFile)
	if openErr != nil {
		err = errors.New(fmt.Sprintf("Open account file failed, %s", openErr.Error()))
		return
	}
	defer fp.Close()

	this.AccessKey = accessKey
	this.SecretKey = secretKey

	//default to nb
	if !IsValidZone(zone) {
		zone = ZoneNB
	}

	this.Zone = zone

	jsonStr, mErr := this.ToJson()
	if mErr != nil {
		err = mErr
		return
	}

	_, wErr := fp.WriteString(jsonStr)
	if wErr != nil {
		err = errors.New(fmt.Sprintf("Write account info failed, %s", wErr.Error()))
		return
	}

	return
}

func (this *Account) Get() (err error) {
	currentUser, uErr := user.Current()
	if uErr != nil {
		err = errors.New(fmt.Sprintf("Get current user failed, %s", uErr.Error()))
		return
	}
	qAccountFile := filepath.Join(currentUser.HomeDir, ".qshell", "account.json")
	fp, openErr := os.Open(qAccountFile)
	if openErr != nil {
		err = errors.New(fmt.Sprintf("Open account file failed, %s ,please use `account` to set AccessKey and SecretKey first", openErr.Error()))
		return
	}
	defer fp.Close()
	accountBytes, readErr := ioutil.ReadAll(fp)
	if readErr != nil {
		err = errors.New(fmt.Sprintf("Read account file error, %s", readErr.Error()))
		return
	}
	if umError := json.Unmarshal(accountBytes, this); umError != nil {
		err = errors.New(fmt.Sprintf("Parse account file error, %s", umError.Error()))
		return
	}

	if this.Zone == "" {
		this.Zone = ZoneNB
	}

	//set default hosts
	switch this.Zone {
	case ZoneAWS:
		SetZone(ZoneAWSConfig)
	case ZoneBC:
		SetZone(ZoneBCConfig)
	default:
		SetZone(ZoneNBConfig)
	}

	return
}
<|endoftext|>


<commit_before>package makex

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"

	"code.google.com/p/rog-go/parallel"
)

// TargetsNeedingBuild returns an ordered list of target sets
func (c *Config) NewMaker(mf *Makefile, goals ...string) *Maker {
	m := &Maker{
		mf:     mf,
		goals:  goals,
		Config: c,
	}
	m.buildDAG()
	return m
}

type Maker struct {
	mf     *Makefile
	goals  []string
	dag    map[string][]string
	topo   [][]string
	cycles map[string][]string

	*Config
}

func (m *Maker) buildDAG() {
	// topological sort taken from
	// http://rosettacode.org/wiki/Topological_sort#Go.

	if m.dag == nil || m.cycles == nil {
		m.dag = make(map[string][]string)
		m.cycles = make(map[string][]string)
	}

	seen := make(map[string]struct{})
	queue := append([]string{}, m.goals...)
	for {
		if len(queue) == 0 {
			break
		}
		origLen := len(queue)
		for _, target := range queue {
			if _, seen := seen[target]; seen {
				continue
			}
			seen[target] = struct{}{}

			rule := m.mf.Rule(target)
			if rule == nil {
				continue
			}
			m.dag[target] = rule.Prereqs()
			for _, dep := range rule.Prereqs() {
				// make a node for the prereq target even if it isn't defined
				queue = append(queue, dep)
				m.dag[dep] = m.dag[dep]
			}
		}
		queue = queue[origLen:]
	}

	// topological sort on the DAG
	for len(m.dag) > 0 {

		// collect targets with no dependencies
		var zero []string
		for target, prereqs := range m.dag {
			if len(prereqs) == 0 {
				zero = append(zero, target)
				delete(m.dag, target)
			}
		}

		// cycle detection
		if len(zero) == 0 {
			// collect un-orderable dependencies
			cycle := make(map[string]bool)
			for _, prereqs := range m.dag {
				for _, dep := range prereqs {
					cycle[dep] = true
				}
			}

			// mark targets with un-orderable dependencies
			for target, prereqs := range m.dag {
				if cycle[target] {
					m.cycles[target] = prereqs
				}
			}
			return
		}

		// output a set that can be processed concurrently
		m.topo = append(m.topo, zero)

		// remove edges (dependencies) from dg
		for _, remove := range zero {
			for target, prereqs := range m.dag {
				for i, dep := range prereqs {
					if dep == remove {
						copy(prereqs[i:], prereqs[i+1:])
						m.dag[target] = prereqs[:len(prereqs)-1]
						break
					}
				}
			}
		}
	}
}

func (m *Maker) TargetSets() [][]string {
	return m.topo
}

func (m *Maker) TargetSetsNeedingBuild() ([][]string, error) {
	for _, goal := range m.goals {
		if rule := m.mf.Rule(goal); rule == nil {
			return nil, errNoRuleToMakeTarget(goal)
		}
		if deps, isCycle := m.cycles[goal]; isCycle {
			return nil, errCircularDependency(goal, deps)
		}
	}

	targetSets := make([][]string, 0)
	for _, targetSet := range m.topo {
		var targetsNeedingBuild []string
		for _, target := range targetSet {
			exists, err := m.pathExists(target)
			if err != nil {
				return nil, err
			}
			if !exists {
				rule := m.mf.Rule(target)
				if rule == nil {
					return nil, errNoRuleToMakeTarget(target)
				}
				targetsNeedingBuild = append(targetsNeedingBuild, target)
			}
		}
		if len(targetsNeedingBuild) > 0 {
			targetSets = append(targetSets, targetsNeedingBuild)
		}
	}
	return targetSets, nil
}

// DryRun prints information about what targets *would* be built if Run() was
// called.
func (m *Maker) DryRun(w io.Writer) error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}
	if len(targetSets) == 0 {
		fmt.Fprintln(w, "No target sets need building.")
	}
	for i, targetSet := range targetSets {
		if i != 0 {
			fmt.Fprintln(w)
		}
		fmt.Fprintf(w, "========= TARGET SET %d (%d targets)\n", i, len(targetSet))
		for _, target := range targetSet {
			fmt.Fprintln(w, " - ", target)
		}
	}
	return nil
}

func (m *Maker) Run() error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}

	for _, targetSet := range targetSets {
		par := parallel.NewRun(m.ParallelJobs)
		for _, target := range targetSet {
			rule := m.mf.Rule(target)
			par.Do(func() error {
				for _, recipe := range rule.Recipes() {
					recipe = ExpandAutoVars(rule, recipe)
					if m.Verbose {
						m.Log.Printf("[%s] %s", rule.Target(), recipe)
					}
					cmd := exec.Command("sh", "-c", recipe)

					var buf bytes.Buffer
					var out io.Writer
					if m.Verbose {
						out = io.MultiWriter(os.Stderr)
					} else {
						out = &buf
					}

					cmd.Stdout, cmd.Stderr = out, out
					err := cmd.Run()
					if err != nil {
						// remove files if failed
						if exists, _ := m.pathExists(rule.Target()); exists {
							err2 := m.FS.Remove(rule.Target())
							if err2 != nil {
								m.Log.Printf("[%s] failed removing target after error: %s", rule.Target(), err)
							}
						}

						m.Log.Printf(`command failed:
============================================================
FAIL: %s
------------------------------------------------------------
%s
============================================================
`, recipe, buf.String())
						return fmt.Errorf("[%s] command %q failed: %s", rule.Target(), recipe, err)
					}
				}
				return nil
			})
		}
		err := par.Wait()
		if err != nil {
			return Errors(err.(parallel.Errors))
		}
	}

	return nil
}

func errNoRuleToMakeTarget(target string) error {
	return fmt.Errorf("no rule to make target %q", target)
}

func errCircularDependency(target string, deps []string) error {
	return fmt.Errorf("circular dependency for target %q: %v", target, deps)
}
<commit_msg>use .fs()<commit_after>package makex

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"

	"code.google.com/p/rog-go/parallel"
)

// TargetsNeedingBuild returns an ordered list of target sets
func (c *Config) NewMaker(mf *Makefile, goals ...string) *Maker {
	m := &Maker{
		mf:     mf,
		goals:  goals,
		Config: c,
	}
	m.buildDAG()
	return m
}

type Maker struct {
	mf     *Makefile
	goals  []string
	dag    map[string][]string
	topo   [][]string
	cycles map[string][]string

	*Config
}

func (m *Maker) buildDAG() {
	// topological sort taken from
	// http://rosettacode.org/wiki/Topological_sort#Go.

	if m.dag == nil || m.cycles == nil {
		m.dag = make(map[string][]string)
		m.cycles = make(map[string][]string)
	}

	seen := make(map[string]struct{})
	queue := append([]string{}, m.goals...)
	for {
		if len(queue) == 0 {
			break
		}
		origLen := len(queue)
		for _, target := range queue {
			if _, seen := seen[target]; seen {
				continue
			}
			seen[target] = struct{}{}

			rule := m.mf.Rule(target)
			if rule == nil {
				continue
			}
			m.dag[target] = rule.Prereqs()
			for _, dep := range rule.Prereqs() {
				// make a node for the prereq target even if it isn't defined
				queue = append(queue, dep)
				m.dag[dep] = m.dag[dep]
			}
		}
		queue = queue[origLen:]
	}

	// topological sort on the DAG
	for len(m.dag) > 0 {

		// collect targets with no dependencies
		var zero []string
		for target, prereqs := range m.dag {
			if len(prereqs) == 0 {
				zero = append(zero, target)
				delete(m.dag, target)
			}
		}

		// cycle detection
		if len(zero) == 0 {
			// collect un-orderable dependencies
			cycle := make(map[string]bool)
			for _, prereqs := range m.dag {
				for _, dep := range prereqs {
					cycle[dep] = true
				}
			}

			// mark targets with un-orderable dependencies
			for target, prereqs := range m.dag {
				if cycle[target] {
					m.cycles[target] = prereqs
				}
			}
			return
		}

		// output a set that can be processed concurrently
		m.topo = append(m.topo, zero)

		// remove edges (dependencies) from dg
		for _, remove := range zero {
			for target, prereqs := range m.dag {
				for i, dep := range prereqs {
					if dep == remove {
						copy(prereqs[i:], prereqs[i+1:])
						m.dag[target] = prereqs[:len(prereqs)-1]
						break
					}
				}
			}
		}
	}
}

func (m *Maker) TargetSets() [][]string {
	return m.topo
}

func (m *Maker) TargetSetsNeedingBuild() ([][]string, error) {
	for _, goal := range m.goals {
		if rule := m.mf.Rule(goal); rule == nil {
			return nil, errNoRuleToMakeTarget(goal)
		}
		if deps, isCycle := m.cycles[goal]; isCycle {
			return nil, errCircularDependency(goal, deps)
		}
	}

	targetSets := make([][]string, 0)
	for _, targetSet := range m.topo {
		var targetsNeedingBuild []string
		for _, target := range targetSet {
			exists, err := m.pathExists(target)
			if err != nil {
				return nil, err
			}
			if !exists {
				rule := m.mf.Rule(target)
				if rule == nil {
					return nil, errNoRuleToMakeTarget(target)
				}
				targetsNeedingBuild = append(targetsNeedingBuild, target)
			}
		}
		if len(targetsNeedingBuild) > 0 {
			targetSets = append(targetSets, targetsNeedingBuild)
		}
	}
	return targetSets, nil
}

// DryRun prints information about what targets *would* be built if Run() was
// called.
func (m *Maker) DryRun(w io.Writer) error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}
	if len(targetSets) == 0 {
		fmt.Fprintln(w, "No target sets need building.")
	}
	for i, targetSet := range targetSets {
		if i != 0 {
			fmt.Fprintln(w)
		}
		fmt.Fprintf(w, "========= TARGET SET %d (%d targets)\n", i, len(targetSet))
		for _, target := range targetSet {
			fmt.Fprintln(w, " - ", target)
		}
	}
	return nil
}

func (m *Maker) Run() error {
	targetSets, err := m.TargetSetsNeedingBuild()
	if err != nil {
		return err
	}

	for _, targetSet := range targetSets {
		par := parallel.NewRun(m.ParallelJobs)
		for _, target := range targetSet {
			rule := m.mf.Rule(target)
			par.Do(func() error {
				for _, recipe := range rule.Recipes() {
					recipe = ExpandAutoVars(rule, recipe)
					if m.Verbose {
						m.Log.Printf("[%s] %s", rule.Target(), recipe)
					}
					cmd := exec.Command("sh", "-c", recipe)

					var buf bytes.Buffer
					var out io.Writer
					if m.Verbose {
						out = io.MultiWriter(os.Stderr)
					} else {
						out = &buf
					}

					cmd.Stdout, cmd.Stderr = out, out
					err := cmd.Run()
					if err != nil {
						// remove files if failed
						if exists, _ := m.pathExists(rule.Target()); exists {
							err2 := m.fs().Remove(rule.Target())
							if err2 != nil {
								m.Log.Printf("[%s] failed removing target after error: %s", rule.Target(), err)
							}
						}

						m.Log.Printf(`command failed:
============================================================
FAIL: %s
------------------------------------------------------------
%s
============================================================
`, recipe, buf.String())
						return fmt.Errorf("[%s] command %q failed: %s", rule.Target(), recipe, err)
					}
				}
				return nil
			})
		}
		err := par.Wait()
		if err != nil {
			return Errors(err.(parallel.Errors))
		}
	}

	return nil
}

func errNoRuleToMakeTarget(target string) error {
	return fmt.Errorf("no rule to make target %q", target)
}

func errCircularDependency(target string, deps []string) error {
	return fmt.Errorf("circular dependency for target %q: %v", target, deps)
}
<|endoftext|>


<commit_before>// +build ignore

// SILVER - Service Wrapper
//
// Copyright (c) 2014 PaperCut Software http://www.papercut.com/
// Use of this source code is governed by an MIT or GPL Version 2 license.
// See the project's LICENSE file for more information.
//

// This Go make file builds Silver directly from a code checkout, bypassing
// the need to configure/setup a Go workspace.
//
// Run on the command line with:
//     $ go run make.go
//
// Other options:
//   Run tests:
//     $ go run make.go test
//   Setup environment (install godep):
//     $ go run make.go setup
//
// Alternatively you may use "go get" to load the code into your default
// workspace and play from there.
//
// Concepts loosly based on concepts in Camlistore
//     https://github.com/bradfitz/camlistore
//
package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

const (
	rootNamespace = "github.com/papercutsoftware/silver"
)

var (
	// The project root where this file is located
	projectRoot string
	//
	buildGoPath    string
	buildOutputDir string
)

func main() {

	var err error
	projectRoot, err = os.Getwd()
	if err != nil {
		panic(fmt.Sprintf("Failed to get current directory: %v\n", err))
	}
	buildGoPath = filepath.Join(projectRoot, ".gopath")
	buildOutputDir = filepath.Join(projectRoot, "build", runtime.GOOS)

	action := "all"
	if len(os.Args) > 1 {
		action = os.Args[1]
	}

	switch action {
	case "test":
		testAll()
	case "setup":
		setupEnv()
	default:
		buildAll()
	}
}

func verifyEnv() {
	if _, err := exec.LookPath("godep"); err != nil {
		fmt.Println("Please install godep. Run:")
		fmt.Println("   go make.go setup")
	}

}

func setupEnv() {
	fmt.Println("Installing godep...")
	runCmd("go", "get", "github.com/tools/godep")
	if _, err := exec.LookPath("godep"); err != nil {
		fmt.Println("ERROR: godep does note seem to be on the path")
	}
}

func buildAll() {
	verifyEnv()
	setupBuildGoPath()
	makeDir(buildOutputDir)

	fmt.Printf("Building binaries for %s...\n", runtime.GOOS)
	runCmd("godep", "go", "build", "-o", makeOutputPath(buildOutputDir, "updater"), rootNamespace+"/updater")
	runCmd("godep", "go", "build", "-o", makeOutputPath(buildOutputDir, "service"), rootNamespace+"/service")
	runCmd("godep", "go", "build", "-tags", "nohttp", "-o", makeOutputPath(buildOutputDir, "service-no-http"), rootNamespace+"/service")

	fmt.Printf("\nBuild done. Files in '%s'\n", buildOutputDir)

}

func testAll() {
	verifyEnv()
	setupBuildGoPath()
	runCmd("godep", "go", "test", rootNamespace+"/...")
}

func runCmd(cmd string, arg ...string) error {
	c := exec.Command(cmd, arg...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	if err := c.Run(); err != nil {
		return fmt.Errorf("Error running command %s: %v", cmd, err)
	}
	return nil
}

func makeDir(dir string) {
	if err := os.MkdirAll(dir, 0755); err != nil {
		panic(err)
	}
}

func makeOutputPath(dir, name string) string {
	if runtime.GOOS == "windows" {
		if !strings.HasSuffix(name, ".exe") {
			name = name + ".exe"
		}
	}
	return filepath.Join(dir, name)
}

func setupBuildGoPath() {

	// .gopath/src/github.com/papercut/silver
	goPathSrcDir := filepath.Join(buildGoPath, "src", rootNamespace)
	makeDir(goPathSrcDir)

	goDirs := []string{
		"lib",
		"service",
		"updater",
	}

	for _, dir := range goDirs {
		orig := filepath.Join(projectRoot, filepath.FromSlash(dir))
		dest := filepath.Join(goPathSrcDir, filepath.FromSlash(dir))
		if _, err := mirrorDir(orig, dest); err != nil {
			panic(fmt.Sprintf("Error while mirroring %s to %s: %v", orig, dest, err))
		}
	}

	os.Setenv("GOPATH", buildGoPath)
}

func mirrorDir(src, dst string) (maxMod time.Time, err error) {
	err = filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return nil
		}
		suffix, err := filepath.Rel(src, path)
		if err != nil {
			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, path, err)
		}
		if t := fi.ModTime(); t.After(maxMod) {
			maxMod = t
		}
		return mirrorFile(path, filepath.Join(dst, suffix))
	})
	return
}

func isExecMode(mode os.FileMode) bool {
	return (mode & 0111) != 0
}

func mirrorFile(src, dst string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	if sfi.Mode()&os.ModeType != 0 {
		panic(fmt.Sprintf("mirrorFile can't deal with non-regular file %s", src))
	}
	dfi, err := os.Stat(dst)
	if err == nil &&
		isExecMode(sfi.Mode()) == isExecMode(dfi.Mode()) &&
		(dfi.Mode()&os.ModeType == 0) &&
		dfi.Size() == sfi.Size() &&
		dfi.ModTime().Unix() == sfi.ModTime().Unix() {
		// Seems to not be modified.
		return nil
	}

	dstDir := filepath.Dir(dst)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return err
	}

	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	if err == nil {
		err = os.Chmod(dst, sfi.Mode())
	}
	if err == nil {
		err = os.Chtimes(dst, sfi.ModTime(), sfi.ModTime())
	}
	return err
}
<commit_msg>Minor updated to make.go script<commit_after>// +build ignore

// SILVER - Service Wrapper
//
// Copyright (c) 2014 PaperCut Software http://www.papercut.com/
// Use of this source code is governed by an MIT or GPL Version 2 license.
// See the project's LICENSE file for more information.
//

// This Go make file builds Silver directly from a code checkout, bypassing
// the need to configure/setup a Go workspace.
//
// Run on the command line with:
//     $ go run make.go
//
// Other options:
//   Run tests:
//     $ go run make.go test
//   Setup environment (install godep):
//     $ go run make.go setup
//
// Alternatively you may use "go get" to load the code into your default
// workspace and play from there.
//
// Concepts loosly based on concepts in Camlistore
//     https://github.com/bradfitz/camlistore
//
package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

const (
	rootNamespace = "github.com/papercutsoftware/silver"
)

var (
	// The project root where this file is located
	projectRoot string
	//
	buildGoPath    string
	buildOutputDir string
)

func usage() {
	fmt.Println("Usage: go run make.go [command]")
	fmt.Println("    all")
	fmt.Println("    test")
	fmt.Println("    setup\n")
	os.Exit(1)
}

func main() {

	var err error
	projectRoot, err = os.Getwd()
	if err != nil {
		panic(fmt.Sprintf("Failed to get current directory: %v\n", err))
	}
	buildGoPath = filepath.Join(projectRoot, ".gopath")
	buildOutputDir = filepath.Join(projectRoot, "build", runtime.GOOS)

	action := "all"
	if len(os.Args) > 1 {
		action = os.Args[1]
	}

	switch action {
	case "all":
		buildAll()
	case "test":
		testAll()
	case "setup":
		setupEnv()
	default:
		usage()
	}
}

func verifyEnv() {
	if _, err := exec.LookPath("godep"); err != nil {
		fmt.Println("Please install godep. Run:")
		fmt.Println("   go make.go setup")
	}
}

func setupEnv() {
	fmt.Println("Installing godep...")
	runCmd("go", "get", "github.com/tools/godep")
	if _, err := exec.LookPath("godep"); err != nil {
		fmt.Println("ERROR: godep does note seem to be on the path")
	}
}

func buildAll() {
	verifyEnv()
	setupBuildGoPath()
	makeDir(buildOutputDir)

	fmt.Printf("Building binaries for %s...\n", runtime.GOOS)
	runCmd("godep", "go", "build", "-o", makeOutputPath(buildOutputDir, "updater"), rootNamespace+"/updater")
	runCmd("godep", "go", "build", "-o", makeOutputPath(buildOutputDir, "service"), rootNamespace+"/service")
	runCmd("godep", "go", "build", "-tags", "nohttp", "-o", makeOutputPath(buildOutputDir, "service-no-http"), rootNamespace+"/service")

	fmt.Printf("\nCOMPLETE. You'll find the files in:\n    '%s'\n", buildOutputDir)

}

func testAll() {
	verifyEnv()
	setupBuildGoPath()
	runCmd("godep", "go", "test", rootNamespace+"/...")
}

func runCmd(cmd string, arg ...string) error {
	c := exec.Command(cmd, arg...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	if err := c.Run(); err != nil {
		return fmt.Errorf("Error running command %s: %v", cmd, err)
	}
	return nil
}

func makeDir(dir string) {
	if err := os.MkdirAll(dir, 0755); err != nil {
		panic(err)
	}
}

func makeOutputPath(dir, name string) string {
	if runtime.GOOS == "windows" {
		if !strings.HasSuffix(name, ".exe") {
			name = name + ".exe"
		}
	}
	return filepath.Join(dir, name)
}

func setupBuildGoPath() {

	// .gopath/src/github.com/papercut/silver
	goPathSrcDir := filepath.Join(buildGoPath, "src", rootNamespace)
	makeDir(goPathSrcDir)

	goDirs := []string{
		"lib",
		"service",
		"updater",
	}

	for _, dir := range goDirs {
		orig := filepath.Join(projectRoot, filepath.FromSlash(dir))
		dest := filepath.Join(goPathSrcDir, filepath.FromSlash(dir))
		if _, err := mirrorDir(orig, dest); err != nil {
			panic(fmt.Sprintf("Error while mirroring %s to %s: %v", orig, dest, err))
		}
	}

	os.Setenv("GOPATH", buildGoPath)
}

func mirrorDir(src, dst string) (maxMod time.Time, err error) {
	err = filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return nil
		}
		suffix, err := filepath.Rel(src, path)
		if err != nil {
			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, path, err)
		}
		if t := fi.ModTime(); t.After(maxMod) {
			maxMod = t
		}
		return mirrorFile(path, filepath.Join(dst, suffix))
	})
	return
}

func isExecMode(mode os.FileMode) bool {
	return (mode & 0111) != 0
}

func mirrorFile(src, dst string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	if sfi.Mode()&os.ModeType != 0 {
		panic(fmt.Sprintf("mirrorFile can't deal with non-regular file %s", src))
	}
	dfi, err := os.Stat(dst)
	if err == nil &&
		isExecMode(sfi.Mode()) == isExecMode(dfi.Mode()) &&
		(dfi.Mode()&os.ModeType == 0) &&
		dfi.Size() == sfi.Size() &&
		dfi.ModTime().Unix() == sfi.ModTime().Unix() {
		// Seems to not be modified.
		return nil
	}

	dstDir := filepath.Dir(dst)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return err
	}

	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	if err == nil {
		err = os.Chmod(dst, sfi.Mode())
	}
	if err == nil {
		err = os.Chtimes(dst, sfi.ModTime(), sfi.ModTime())
	}
	return err
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2019 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package pb

import (
	"fmt"
	"strings"
	"sync"

	"github.com/fatih/color"
	"github.com/sirupsen/logrus"
)

const defaultWidth = 40
const defaultBarColor = color.Faint

// Status of the progress bar
type Status string

// Progress bar status symbols
const (
	Running     Status = " "
	Waiting     Status = "•"
	Stopping    Status = "↓"
	Interrupted Status = "✗"
	Done        Status = "✓"
)

//nolint:gochecknoglobals
var statusColors = map[Status]*color.Color{
	Interrupted: color.New(color.FgRed),
	Done:        color.New(color.FgGreen),
	Waiting:     color.New(defaultBarColor),
}

// ProgressBar is a simple thread-safe progressbar implementation with
// callbacks.
type ProgressBar struct {
	mutex  sync.RWMutex
	width  int
	color  *color.Color
	logger *logrus.Entry
	status Status

	left     func() string
	progress func() (progress float64, right []string)
	hijack   func() string
}

// ProgressBarOption is used for helper functions that modify the progressbar
// parameters, either in the constructor or via the Modify() method.
type ProgressBarOption func(*ProgressBar)

// WithLeft modifies the function that returns the left progressbar value.
func WithLeft(left func() string) ProgressBarOption {
	return func(pb *ProgressBar) { pb.left = left }
}

// WithConstLeft sets the left progressbar value to the supplied const.
func WithConstLeft(left string) ProgressBarOption {
	return func(pb *ProgressBar) {
		pb.left = func() string { return left }
	}
}

// WithLogger modifies the logger instance
func WithLogger(logger *logrus.Entry) ProgressBarOption {
	return func(pb *ProgressBar) { pb.logger = logger }
}

// WithProgress modifies the progress calculation function.
func WithProgress(progress func() (float64, []string)) ProgressBarOption {
	return func(pb *ProgressBar) { pb.progress = progress }
}

// WithStatus modifies the progressbar status
func WithStatus(status Status) ProgressBarOption {
	return func(pb *ProgressBar) { pb.status = status }
}

// WithConstProgress sets the progress and right values to the supplied consts.
func WithConstProgress(progress float64, right ...string) ProgressBarOption {
	return func(pb *ProgressBar) {
		pb.progress = func() (float64, []string) { return progress, right }
	}
}

// WithHijack replaces the progressbar Render function with the argument.
func WithHijack(hijack func() string) ProgressBarOption {
	return func(pb *ProgressBar) { pb.hijack = hijack }
}

// New creates and initializes a new ProgressBar struct, calling all of the
// supplied options
func New(options ...ProgressBarOption) *ProgressBar {
	pb := &ProgressBar{
		mutex: sync.RWMutex{},
		width: defaultWidth,
		color: color.New(defaultBarColor),
	}
	pb.Modify(options...)
	return pb
}

// Left returns the left part of the progressbar in a thread-safe way.
func (pb *ProgressBar) Left() string {
	pb.mutex.RLock()
	defer pb.mutex.RUnlock()

	return pb.renderLeft(0)
}

// renderLeft renders the left part of the progressbar, replacing text
// exceeding maxLen with an ellipsis.
func (pb *ProgressBar) renderLeft(maxLen int) string {
	var left string
	if pb.left != nil {
		l := pb.left()
		if maxLen > 0 && len(l) > maxLen {
			l = l[:maxLen-3] + "..."
		}
		left = l
	}
	return left
}

// Modify changes the progressbar options in a thread-safe way.
func (pb *ProgressBar) Modify(options ...ProgressBarOption) {
	pb.mutex.Lock()
	defer pb.mutex.Unlock()
	for _, option := range options {
		option(pb)
	}
}

// ProgressBarRender stores the different rendered parts of the
// progress bar UI.
type ProgressBarRender struct {
	Left, Status, Progress, Hijack string
	Right                          []string
}

func (pbr ProgressBarRender) String() string {
	if pbr.Hijack != "" {
		return pbr.Hijack
	}
	var right string
	if len(pbr.Right) > 0 {
		right = " " + strings.Join(pbr.Right, "  ")
	}
	return fmt.Sprintf("%s %-1s %s%s",
		pbr.Left, pbr.Status, pbr.Progress, right)
}

// Render locks the progressbar struct for reading and calls all of
// its methods to return the final output. A struct is returned over a
// plain string to allow dynamic padding and positioning of elements
// depending on other elements on the screen.
// - leftMax defines the maximum character length of the left-side
//   text. Characters exceeding this length will be replaced with a
//   single ellipsis. Passing <=0 disables this.
func (pb *ProgressBar) Render(leftMax int) ProgressBarRender {
	pb.mutex.RLock()
	defer pb.mutex.RUnlock()

	var out ProgressBarRender
	if pb.hijack != nil {
		out.Hijack = pb.hijack()
		return out
	}

	var progress float64
	if pb.progress != nil {
		progress, out.Right = pb.progress()
		progressClamped := Clampf(progress, 0, 1)
		if progress != progressClamped {
			progress = progressClamped
			if pb.logger != nil {
				pb.logger.Warnf("progress value %.2f exceeds valid range, clamped between 0 and 1", progress)
			}
		}
	}

	space := pb.width - 2
	filled := int(float64(space) * progress)

	filling := ""
	caret := ""
	if filled > 0 {
		if filled < space {
			filling = strings.Repeat("=", filled-1)
			caret = ">"
		} else {
			filling = strings.Repeat("=", filled)
		}
	}

	padding := ""
	if space > filled {
		padding = pb.color.Sprint(strings.Repeat("-", space-filled))
	}

	out.Left = pb.renderLeft(leftMax)
	status := string(pb.status)
	if c, ok := statusColors[pb.status]; ok {
		status = c.Sprint(pb.status)
	}
	out.Status = fmt.Sprintf("%-1s", status)
	out.Progress = fmt.Sprintf("[%s%s%s]", filling, caret, padding)

	return out
}
<commit_msg>Make pb.Status a rune instead of a string<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2019 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package pb

import (
	"fmt"
	"strings"
	"sync"

	"github.com/fatih/color"
	"github.com/sirupsen/logrus"
)

const defaultWidth = 40
const defaultBarColor = color.Faint

// Status of the progress bar
type Status rune

// Progress bar status symbols
const (
	Running     Status = ' '
	Waiting     Status = '•'
	Stopping    Status = '↓'
	Interrupted Status = '✗'
	Done        Status = '✓'
)

//nolint:gochecknoglobals
var statusColors = map[Status]*color.Color{
	Interrupted: color.New(color.FgRed),
	Done:        color.New(color.FgGreen),
	Waiting:     color.New(defaultBarColor),
}

// ProgressBar is a simple thread-safe progressbar implementation with
// callbacks.
type ProgressBar struct {
	mutex  sync.RWMutex
	width  int
	color  *color.Color
	logger *logrus.Entry
	status Status

	left     func() string
	progress func() (progress float64, right []string)
	hijack   func() string
}

// ProgressBarOption is used for helper functions that modify the progressbar
// parameters, either in the constructor or via the Modify() method.
type ProgressBarOption func(*ProgressBar)

// WithLeft modifies the function that returns the left progressbar value.
func WithLeft(left func() string) ProgressBarOption {
	return func(pb *ProgressBar) { pb.left = left }
}

// WithConstLeft sets the left progressbar value to the supplied const.
func WithConstLeft(left string) ProgressBarOption {
	return func(pb *ProgressBar) {
		pb.left = func() string { return left }
	}
}

// WithLogger modifies the logger instance
func WithLogger(logger *logrus.Entry) ProgressBarOption {
	return func(pb *ProgressBar) { pb.logger = logger }
}

// WithProgress modifies the progress calculation function.
func WithProgress(progress func() (float64, []string)) ProgressBarOption {
	return func(pb *ProgressBar) { pb.progress = progress }
}

// WithStatus modifies the progressbar status
func WithStatus(status Status) ProgressBarOption {
	return func(pb *ProgressBar) { pb.status = status }
}

// WithConstProgress sets the progress and right values to the supplied consts.
func WithConstProgress(progress float64, right ...string) ProgressBarOption {
	return func(pb *ProgressBar) {
		pb.progress = func() (float64, []string) { return progress, right }
	}
}

// WithHijack replaces the progressbar Render function with the argument.
func WithHijack(hijack func() string) ProgressBarOption {
	return func(pb *ProgressBar) { pb.hijack = hijack }
}

// New creates and initializes a new ProgressBar struct, calling all of the
// supplied options
func New(options ...ProgressBarOption) *ProgressBar {
	pb := &ProgressBar{
		mutex: sync.RWMutex{},
		width: defaultWidth,
		color: color.New(defaultBarColor),
	}
	pb.Modify(options...)
	return pb
}

// Left returns the left part of the progressbar in a thread-safe way.
func (pb *ProgressBar) Left() string {
	pb.mutex.RLock()
	defer pb.mutex.RUnlock()

	return pb.renderLeft(0)
}

// renderLeft renders the left part of the progressbar, replacing text
// exceeding maxLen with an ellipsis.
func (pb *ProgressBar) renderLeft(maxLen int) string {
	var left string
	if pb.left != nil {
		l := pb.left()
		if maxLen > 0 && len(l) > maxLen {
			l = l[:maxLen-3] + "..."
		}
		left = l
	}
	return left
}

// Modify changes the progressbar options in a thread-safe way.
func (pb *ProgressBar) Modify(options ...ProgressBarOption) {
	pb.mutex.Lock()
	defer pb.mutex.Unlock()
	for _, option := range options {
		option(pb)
	}
}

// ProgressBarRender stores the different rendered parts of the
// progress bar UI.
type ProgressBarRender struct {
	Left, Status, Progress, Hijack string
	Right                          []string
}

func (pbr ProgressBarRender) String() string {
	if pbr.Hijack != "" {
		return pbr.Hijack
	}
	var right string
	if len(pbr.Right) > 0 {
		right = " " + strings.Join(pbr.Right, "  ")
	}
	return fmt.Sprintf("%s %-1s %s%s",
		pbr.Left, pbr.Status, pbr.Progress, right)
}

// Render locks the progressbar struct for reading and calls all of
// its methods to return the final output. A struct is returned over a
// plain string to allow dynamic padding and positioning of elements
// depending on other elements on the screen.
// - leftMax defines the maximum character length of the left-side
//   text. Characters exceeding this length will be replaced with a
//   single ellipsis. Passing <=0 disables this.
func (pb *ProgressBar) Render(leftMax int) ProgressBarRender {
	pb.mutex.RLock()
	defer pb.mutex.RUnlock()

	var out ProgressBarRender
	if pb.hijack != nil {
		out.Hijack = pb.hijack()
		return out
	}

	var progress float64
	if pb.progress != nil {
		progress, out.Right = pb.progress()
		progressClamped := Clampf(progress, 0, 1)
		if progress != progressClamped {
			progress = progressClamped
			if pb.logger != nil {
				pb.logger.Warnf("progress value %.2f exceeds valid range, clamped between 0 and 1", progress)
			}
		}
	}

	space := pb.width - 2
	filled := int(float64(space) * progress)

	filling := ""
	caret := ""
	if filled > 0 {
		if filled < space {
			filling = strings.Repeat("=", filled-1)
			caret = ">"
		} else {
			filling = strings.Repeat("=", filled)
		}
	}

	padding := ""
	if space > filled {
		padding = pb.color.Sprint(strings.Repeat("-", space-filled))
	}

	out.Left = pb.renderLeft(leftMax)

	switch c, ok := statusColors[pb.status]; {
	case ok:
		out.Status = c.Sprint(string(pb.status))
	case pb.status > 0:
		out.Status = string(pb.status)
	default:
		out.Status = " "
	}

	out.Progress = fmt.Sprintf("[%s%s%s]", filling, caret, padding)

	return out
}
<|endoftext|>