<commit_before>package raft

import (
  "fmt"
  "time"
)

// Global warmup state
var INIT string = "init"

// Global State Object, main core of Raft
const STATE *State = &State{
}

// Internal struct
type State struct {
  // Status of raft:
  // INIT: raft is in warmup state, usually to make sure services are started
  // FOLLOWER: store logs, follow leader, vote for others
  // CANDIDATE: vote for themselves
  // LEADER: send logs, send heartbeats
  Status string

  // Channel to sync everyting
  C chan string
}

// Safely switch from a state to another
// This should only be called by the Raft package
func (s *State) Switch(state string) error {
  // If the state does not change, return
  if state == STATE.Status {
    return nil
  }

  // Raft cannot be at any state other than FOLLOWER when
  // state is at INIT
  if STATE.Status == INIT && state != FOLLOWER {
    return fmt.Errorf("State must be follower when STATE == INIT")
  }

  // Checking correct state has argument
  switch state {
    case FOLLOWER, CANDIDATE, LEADER:
      s.C <- state
    default:
      return fmt.Errorf("Only supported operations: FOLLOWER, CANDIDATE or LEADER.")
  }

  // Make the switch
  // Call the function in the cases
  switch state {
    case FOLLOWER:
      <- time.Tick(100 * time.Millisecond)
    case CANDIDATE:
      <- time.Tick(300 * time.Millisecond)
    case LEADER:
      <- time.Tick(500 * time.Millisecond)
  }

  // Apply the status if the case bellow has been
  // correctly executed
  s.Status = <- s.C

  return nil
}

type InitFunc func() error

type StateMachine struct {
  Init []InitFunc
  Store *Storage
  Rpc *RPC
  Initialized bool
}

// Initialize Init functions.
// Each function are executed into go routines.
// If some funcs are blocking Initialize will block,
// otherwise, goroutines are destroyed.
// If any errors are returned, Initialize will return,
// the error and by that, it will cause all goroutines
// to stop acting.
func (s *StateMachine) Initialize() error {
  errC := make(chan error)

  // Initialize status to INIT to prevent Raft of
  // acting before every service has stated
  STATE.Status = INIT
  STATE.C = make(chan string, 1)

  for _, initFunc := range s.Init {
    go func() {
      err := initFunc()
      if err != nil {
        errC <- err
      }

      errC <- nil
    }()
  }

  s.Initialized = true

  for _, _ = range s.Init {
    err := <- errC
    if err != nil {
      s.Initialized = false
      return err
    }
  }

  return nil
}

// Create a state machine object that stores every
// component of Raft, use this object to start Raft.
// You also can overide fields before initialize the
// state machine.
// The functions of the state machine are stored in
// an array, and it's synchronized with channels to
// pass references.
func NewStateMachine() *StateMachine  {
  s.Initialized = false

  s := &StateMachine{
    Init: make([]InitFunc, 0, 0),
  }

  s.Init = append(s.Init, func() error {
    storage, err := NewStorage()
    s.Store = storage

    return err
  })

  s.Init = append(s.Init, func() error {
    rpc, err := NewRPC()
    if err != nil {
      return err
    }

    s.Rpc = rpc

    err = s.Rpc.Start()
    if err != nil {
      return err
    }

    return nil
  })

  return s
}
<commit_msg>Added state machine initialization safety<commit_after>package raft

import (
  "fmt"
  "time"
)

// Global warmup state
var INIT string = "init"

// Global State Object, main core of Raft
const STATE *State = &State{
}

// Internal struct
type State struct {
  // Status of raft:
  // INIT: raft is in warmup state, usually to make sure services are started
  // FOLLOWER: store logs, follow leader, vote for others
  // CANDIDATE: vote for themselves
  // LEADER: send logs, send heartbeats
  Status string

  // Channel to sync everyting
  C chan string
}

// Safely switch from a state to another
// This should only be called by the Raft package
func (s *State) Switch(state string) error {
  // If the state does not change, return
  if state == STATE.Status {
    return nil
  }

  // Raft cannot be at any state other than FOLLOWER when
  // state is at INIT
  if STATE.Status == INIT && state != FOLLOWER {
    return fmt.Errorf("State must be follower when STATE == INIT")
  }

  // Checking correct state has argument
  switch state {
    case FOLLOWER, CANDIDATE, LEADER:
      s.C <- state
    default:
      return fmt.Errorf("Only supported operations: FOLLOWER, CANDIDATE or LEADER.")
  }

  // Make the switch
  // Call the function in the cases
  switch state {
    case FOLLOWER:
      <- time.Tick(100 * time.Millisecond)
    case CANDIDATE:
      <- time.Tick(300 * time.Millisecond)
    case LEADER:
      <- time.Tick(500 * time.Millisecond)
  }

  // Apply the status if the case bellow has been
  // correctly executed
  s.Status = <- s.C

  return nil
}

type InitFunc func() error

type StateMachine struct {
  Init []InitFunc
  Store *Storage
  Rpc *RPC
  Initialized bool
}

// Initialize Init functions.
// Each function are executed into go routines.
// If some funcs are blocking Initialize will block,
// otherwise, goroutines are destroyed.
// If any errors are returned, Initialize will return,
// the error and by that, it will cause all goroutines
// to stop acting.
func (s *StateMachine) Initialize() error {
  if s.Initialized == true {
    err := fmt.Errorf("StateMachine is already initialized.")

    return err
  }

  errC := make(chan error)

  // Initialize status to INIT to prevent Raft of
  // acting before every service has stated
  STATE.Status = INIT
  STATE.C = make(chan string, 1)

  for _, initFunc := range s.Init {
    go func() {
      err := initFunc()
      if err != nil {
        errC <- err
      }

      errC <- nil
    }()
  }

  s.Initialized = true

  for _, _ = range s.Init {
    err := <- errC
    if err != nil {
      s.Initialized = false
      return err
    }
  }

  return nil
}

// Create a state machine object that stores every
// component of Raft, use this object to start Raft.
// You also can overide fields before initialize the
// state machine.
// The functions of the state machine are stored in
// an array, and it's synchronized with channels to
// pass references.
func NewStateMachine() *StateMachine  {
  s.Initialized = false

  s := &StateMachine{
    Init: make([]InitFunc, 0, 0),
  }

  s.Init = append(s.Init, func() error {
    storage, err := NewStorage()
    s.Store = storage

    return err
  })

  s.Init = append(s.Init, func() error {
    rpc, err := NewRPC()
    if err != nil {
      return err
    }

    s.Rpc = rpc

    err = s.Rpc.Start()
    if err != nil {
      return err
    }

    return nil
  })

  return s
}
<|endoftext|>


<commit_before>package lnwallet

import (
	"github.com/roasbeef/btcutil"
)

// FeeEstimator provides the ability to estimate on-chain transaction fees for
// various combinations of transaction sizes and desired confirmation time
// (measured by number of blocks).
type FeeEstimator interface {
	// EstimateFeePerByte takes in a target for the number of blocks until
	// an initial confirmation and returns the estimated fee expressed in
	// satoshis/byte.
	EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error)

	// EstimateFeePerWeight takes in a target for the number of blocks
	// until an initial confirmation and returns the estimated fee
	// expressed in satoshis/weight.
	EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error)
}

// StaticFeeEstimator will return a static value for all fee calculation
// requests. It is designed to be replaced by a proper fee calculation
// implementation.
type StaticFeeEstimator struct {
	// FeeRate is the static fee rate in satoshis-per-byte that will be
	// returned by this fee estimator. Queries for the fee rate in weight
	// units will be scaled accordingly.
	FeeRate uint64
}

// EstimateFeePerByte will return a static value for fee calculations.
func (e StaticFeeEstimator) EstimateFeePerByte(numBlocks uint32) uint64 {
	return e.FeeRate
}

// EstimateFeePerWeight will return a static value for fee calculations.
func (e StaticFeeEstimator) EstimateFeePerWeight(numBlocks uint32) uint64 {
	return e.FeeRate / 4
}

// A compile-time assertion to ensure that StaticFeeEstimator implements the
// FeeEstimator interface.
var _ FeeEstimator = (*StaticFeeEstimator)(nil)
<commit_msg>lnwallet: add Start() and Stop() method to the FeeEstimator interface<commit_after>package lnwallet

import (
	"github.com/roasbeef/btcd/blockchain"
	"github.com/roasbeef/btcd/rpcclient"
	"github.com/roasbeef/btcutil"
)

// FeeEstimator provides the ability to estimate on-chain transaction fees for
// various combinations of transaction sizes and desired confirmation time
// (measured by number of blocks).
type FeeEstimator interface {
	// EstimateFeePerByte takes in a target for the number of blocks until
	// an initial confirmation and returns the estimated fee expressed in
	// satoshis/byte.
	EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error)

	// EstimateFeePerWeight takes in a target for the number of blocks
	// until an initial confirmation and returns the estimated fee
	// expressed in satoshis/weight.
	EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error)

	// Start signals the FeeEstimator to start any processes or goroutines
	// it needs to perform its duty.
	Start() error

	// Stop stops any spawned goroutines and cleans up the resources used
	// by the fee estimator.
	Stop() error
}

// StaticFeeEstimator will return a static value for all fee calculation
// requests. It is designed to be replaced by a proper fee calculation
// implementation.
type StaticFeeEstimator struct {
	// FeeRate is the static fee rate in satoshis-per-byte that will be
	// returned by this fee estimator. Queries for the fee rate in weight
	// units will be scaled accordingly.
	FeeRate uint64
}

// EstimateFeePerByte will return a static value for fee calculations.
func (e StaticFeeEstimator) EstimateFeePerByte(numBlocks uint32) uint64 {
	return e.FeeRate
}

// EstimateFeePerWeight will return a static value for fee calculations.
func (e StaticFeeEstimator) EstimateFeePerWeight(numBlocks uint32) uint64 {
	return e.FeeRate / 4
}

// A compile-time assertion to ensure that StaticFeeEstimator implements the
// FeeEstimator interface.
var _ FeeEstimator = (*StaticFeeEstimator)(nil)
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
	"unicode"

	"github.com/spf13/cobra"
)

type conftabSection struct {
	options    map[string]bool
	definition string
}

type conftabReader struct {
	filename   string
	scanner    *bufio.Scanner
	lineNumber int
	optRegexp  *regexp.Regexp
}

func (reader *conftabReader) Err(message string) error {
	return errors.New(fmt.Sprintf("%s:%d: %s", reader.filename,
		reader.lineNumber, message))
}

func (reader *conftabReader) readSection() (*conftabSection,
	string, error) {
	section := conftabSection{make(map[string]bool), ""}

	for reader.scanner.Scan() {
		reader.lineNumber++
		line := strings.TrimSpace(reader.scanner.Text())

		if line == "" {
			section.definition += "\n"
			continue
		}

		if line[0] == '[' {
			if line[len(line)-1] != ']' {
				return nil, "", reader.Err(
					"invalid section caption format")
			}
			return &section, line[1 : len(line)-1], nil
		}

		section.definition += line + "\n"

		if line[0] == '#' {
			line = strings.TrimLeftFunc(
				strings.TrimLeft(line, "#"),
				unicode.IsSpace)
		} else if line[0] != '-' {
			return nil, "", reader.Err("invalid option format " +
				"(must start with a dash)")
		}

		matches := reader.optRegexp.FindStringSubmatch(line)
		if len(matches) > 1 {
			section.options[matches[1]] = true
		}
	}

	return &section, "", nil
}

func (section *conftabSection) printSection(stream *os.File) {
	writer := bufio.NewWriter(stream)

	writer.WriteString(section.definition)

	writer.Flush()
}

type conftab struct {
	globalSection   *conftabSection
	packageSections []conftabSection
}

func readConftab(filename string) (*conftab, error) {
	conftabFile, err := os.Open(filename)

	if err != nil {
		return nil, err
	}

	conftabScanner := bufio.NewScanner(conftabFile)

	reader := conftabReader{filename, conftabScanner, 0,
		regexp.MustCompile(`^--([^\s\[=]+)`)}

	globalSection, nextSectionCaption, err := reader.readSection()

	if err != nil {
		return nil, err
	}

	if nextSectionCaption == "" {
		if err = conftabFile.Close(); err != nil {
			return nil, err
		}
	}

	return &conftab{globalSection, nil}, err
}

var dumpconftabCmd = &cobra.Command{
	Use:   "dumpconftab",
	Short: "Dump the conftab.ini file",
	Args:  cobra.MaximumNArgs(0),
	Run: func(_ *cobra.Command, _ []string) {
		conftab, err := readConftab("conftab")

		if err != nil {
			log.Fatal(err)
		}

		conftab.globalSection.printSection(os.Stdout)
	},
}

func init() {
	RootCmd.AddCommand(dumpconftabCmd)

	dumpconftabCmd.Flags().SortFlags = false
	addWorkspaceDirFlag(dumpconftabCmd)
}
<commit_msg>Implement conftab parser<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
	"unicode"

	"github.com/spf13/cobra"
)

type conftabSection struct {
	caption    string
	options    map[string]bool
	definition string
}

type conftabReader struct {
	filename   string
	scanner    *bufio.Scanner
	lineNumber int
	optRegexp  *regexp.Regexp
}

func (reader *conftabReader) Err(message string) error {
	return errors.New(fmt.Sprintf("%s:%d: %s", reader.filename,
		reader.lineNumber, message))
}

func (reader *conftabReader) readSection(sectionName string) (*conftabSection,
	string, error) {
	section := conftabSection{sectionName, make(map[string]bool), ""}

	for reader.scanner.Scan() {
		reader.lineNumber++
		line := strings.TrimSpace(reader.scanner.Text())

		if line == "" {
			section.definition += "\n"
			continue
		}

		if line[0] == '[' {
			if line[len(line)-1] != ']' {
				return nil, "", reader.Err(
					"invalid section caption format")
			}
			return &section, line + "\n", nil
		}

		section.definition += line + "\n"

		if line[0] == '#' {
			line = strings.TrimLeftFunc(
				strings.TrimLeft(line, "#"),
				unicode.IsSpace)
		} else if line[0] != '-' {
			return nil, "", reader.Err("invalid option format " +
				"(must start with a dash)")
		}

		matches := reader.optRegexp.FindStringSubmatch(line)
		if len(matches) > 1 {
			section.options[matches[1]] = true
		}
	}

	return &section, "", nil
}

type conftabStruct struct {
	globalSection        *conftabSection
	packageSections      []*conftabSection
	sectionByPackageName map[string]*conftabSection
}

func (conftab *conftabStruct) print(writer *bufio.Writer) {
	writer.WriteString(conftab.globalSection.definition)

	for _, section := range conftab.packageSections {
		writer.WriteString(section.caption)
		writer.WriteString(section.definition)
	}
}

func readConftab(filename string) (*conftabStruct, error) {
	conftabFile, err := os.Open(filename)

	if err != nil {
		return nil, err
	}

	conftabScanner := bufio.NewScanner(conftabFile)

	reader := conftabReader{filename, conftabScanner, 0,
		regexp.MustCompile(`^--([^\s\[=]+)`)}

	section, nextSectionCaption, err := reader.readSection("")

	if err != nil {
		return nil, err
	}

	conftab := conftabStruct{section, nil,
		make(map[string]*conftabSection)}

	for nextSectionCaption != "" {
		packageName :=
			nextSectionCaption[1 : len(nextSectionCaption)-2]
		section, nextSectionCaption, err =
			reader.readSection(nextSectionCaption)
		if err != nil {
			return nil, err
		}
		conftab.packageSections = append(conftab.packageSections,
			section)
		conftab.sectionByPackageName[packageName] = section
	}

	if err = conftabFile.Close(); err != nil {
		return nil, err
	}

	return &conftab, nil
}

var dumpconftabCmd = &cobra.Command{
	Use:   "dumpconftab",
	Short: "Dump the conftab.ini file",
	Args:  cobra.MaximumNArgs(0),
	Run: func(_ *cobra.Command, _ []string) {
		conftab, err := readConftab("conftab")

		if err != nil {
			log.Fatal(err)
		}

		writer := bufio.NewWriter(os.Stdout)

		conftab.print(writer)

		writer.Flush()
	},
}

func init() {
	RootCmd.AddCommand(dumpconftabCmd)

	dumpconftabCmd.Flags().SortFlags = false
	addWorkspaceDirFlag(dumpconftabCmd)
}
<|endoftext|>


<commit_before>// Copyright 2013 The beego Authors.
// Copyright 2014 The Gogs Authors.
//
// 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 logs

import (
	"encoding/json"
	"log"
	"os"
	"runtime"
)

type Brush func(string) string

func NewBrush(color string) Brush {
	pre := "\033["
	reset := "\033[0m"
	return func(text string) string {
		return pre + color + "m" + text + reset
	}
}

var colors = []Brush{
	NewBrush("1;36"), // Trace      cyan
	NewBrush("1;34"), // Debug      blue
	NewBrush("1;32"), // Info       green
	NewBrush("1;33"), // Warn       yellow
	NewBrush("1;31"), // Error      red
	NewBrush("1;35"), // Critical   purple
}

// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type ConsoleWriter struct {
	lg    *log.Logger
	Level int `json:"level"`
}

// create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface {
	cw := new(ConsoleWriter)
	cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	cw.Level = LevelTrace
	return cw
}

// init console logger.
// jsonconfig like '{"level":0}'.
func (c *ConsoleWriter) Init(jsonconfig string) error {
	err := json.Unmarshal([]byte(jsonconfig), c)
	if err != nil {
		return err
	}
	return nil
}

// write message in console.
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
	if level < c.Level {
		return nil
	}
	if goos := runtime.GOOS; goos == "windows" {
		c.lg.Println(msg)
	} else {
		c.lg.Println(colors[level](msg))
	}
	return nil
}

// implementing method. empty.
func (c *ConsoleWriter) Destroy() {

}

// implementing method. empty.
func (c *ConsoleWriter) Flush() {

}

func init() {
	Register("console", NewConsole)
}
<commit_msg>Fix #3<commit_after>// Copyright 2013 The beego Authors.
// Copyright 2014 The Gogs Authors.
//
// 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 logs

import (
	"encoding/json"
	"log"
	"os"
	"runtime"
)

type Brush func(string) string

func NewBrush(color string) Brush {
	pre := "\033["
	reset := "\033[0m"
	return func(text string) string {
		return pre + color + "m" + text + reset
	}
}

var colors = []Brush{
	NewBrush("1;36"), // Trace      cyan
	NewBrush("1;34"), // Debug      blue
	NewBrush("1;32"), // Info       green
	NewBrush("1;33"), // Warn       yellow
	NewBrush("1;31"), // Error      red
	NewBrush("1;35"), // Critical   purple
	NewBrush("1;31"), // Fatal      red
}

// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type ConsoleWriter struct {
	lg    *log.Logger
	Level int `json:"level"`
}

// create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface {
	cw := new(ConsoleWriter)
	cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	cw.Level = LevelTrace
	return cw
}

// init console logger.
// jsonconfig like '{"level":0}'.
func (c *ConsoleWriter) Init(jsonconfig string) error {
	err := json.Unmarshal([]byte(jsonconfig), c)
	if err != nil {
		return err
	}
	return nil
}

// write message in console.
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
	if level < c.Level {
		return nil
	}
	if goos := runtime.GOOS; goos == "windows" {
		c.lg.Println(msg)
	} else {
		c.lg.Println(colors[level](msg))
	}
	return nil
}

// implementing method. empty.
func (c *ConsoleWriter) Destroy() {

}

// implementing method. empty.
func (c *ConsoleWriter) Flush() {

}

func init() {
	Register("console", NewConsole)
}
<|endoftext|>


<commit_before>package bla

import (
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

const IndexNumber = 20

type Doc struct {
	RealPath   string // as id
	Path       string
	Title      string
	headerNode *goquery.Selection
	Time       time.Time
	Tags       []string
	Parsed     *goquery.Document
	Related    []*Doc
}

func santiSpace(s string) string {
	ss := strings.Replace(s, " ", "-", -1)
	return strings.Replace(ss, "/", "-", -1)
}

func (d *Doc) String() string {
	return fmt.Sprintf("Path:%s Title:%s Time:%s Tags:%s", d.Path, d.Title, d.Time, d.Tags)
}

func (s *Server) LoadDoc(path string, info os.FileInfo, e error) (err error) {

	if info == nil || !info.Mode().IsRegular() {
		return
	}

	if !strings.HasSuffix(filepath.Base(path), ".html") {
		return
	}

	//Log(path)

	f, err := os.Open(path)
	if err != nil {
		log.Print(err)
		return
	}
	defer f.Close()

	doc := &Doc{RealPath: path,
		Related: make([]*Doc, 0)}

	parsed, err := goquery.NewDocumentFromReader(f)
	if err != nil {
		log.Printf("Parse failed:%v", err)
		return
	}

	header := parsed.Find("h1").First()
	doc.Title = header.Text()
	doc.headerNode = header
	doc.Path = Cfg.BasePath + fmt.Sprintf(Cfg.DocURLFormat, santiSpace(doc.Title))

	if t := parsed.Find(".date").First().Text(); t != "" {
		doc.Time, err = time.Parse("2006-01-02", t)
		if err != nil {
			log.Print(err)
			return
		}
	} else {
		doc.Time = info.ModTime()
	}

	parsed.Find(".tag").Each(func(i int, sel *goquery.Selection) {
		t := sel.Text()
		doc.Tags = append(doc.Tags, t)
		if _, ok := s.Tags[t]; ok {
			s.Tags[t] = append(s.Tags[t], doc)
		} else {
			s.Tags[t] = []*Doc{doc}
		}
	})
	// Make header links
	header.ReplaceWithHtml(fmt.Sprintf(`<h1 class="title"><a href="%s">%s</a></h1>`, doc.Path, doc.Title))

	//Log(doc)
	doc.Parsed = parsed

	s.DocLock.Lock()
	defer s.DocLock.Unlock()

	s.Docs[doc.RealPath] = doc
	return
}

func (s *Server) makeRelated() {

	// clean all docs
	s.sortedDocs = s.sortedDocs[:0]

	for _, d := range s.Docs {
		// reset related docs
		d.Related = d.Related[:0]
		s.sortedDocs = append(s.sortedDocs, d)
		for _, t := range d.Tags {
			for _, rd := range s.Tags[t] {
				if !docInDocs(rd, d.Related) && rd != d {
					d.Related = append(d.Related, rd)
				}
			}
		}
	}

	sort.Sort(docsByTime(s.sortedDocs))

}

func (s *Server) LoadAllDocs() (err error) {

	start := time.Now()
	s.Tags = make(map[string][]*Doc)
	s.Docs = make(map[string]*Doc)

	filepath.Walk(Cfg.ContentPath, s.LoadDoc)
	// make related doc
	s.makeRelated()
	log.Printf("Load %d docs in %s", len(s.sortedDocs), time.Since(start))

	return
}

type rootData struct {
	Cfg        *Config
	Server     *Server
	Docs       []*Doc
	IndexCount int
}

func (s *Server) newRootData() *rootData {
	return &rootData{Server: s, Cfg: Cfg, Docs: make([]*Doc, 0)}
}

func docInDocs(doc *Doc, docs []*Doc) bool {

	for _, d := range docs {
		if d.Title == doc.Title {
			return true
		}
	}
	return false
}

func (s *Server) SaveAllDocs() (err error) {

	r := s.newRootData()

	err = os.MkdirAll(path.Join(Cfg.PublicPath, Cfg.BasePath), 0755)

	if err != nil {
		log.Fatal(err)
	}
	for _, d := range s.Docs {

		r.Docs = []*Doc{d}
		docPubPath := path.Join(Cfg.PublicPath, d.Path)
		if docPubPath == path.Dir(docPubPath) {
			continue
		}

		f, err := os.Create(docPubPath)
		if err != nil {
			log.Print(err)
		}

		if err = s.template.doc.ExecuteTemplate(f, "root", r); err != nil {
			log.Print(err)
		}
		f.Close()
		os.Chtimes(docPubPath, d.Time, d.Time)
	}

	return
}

func (s *Server) MakeHome() (err error) {

	log.Print("making home of ", len(s.sortedDocs))
	n := Cfg.HomeArticles
	if len(s.sortedDocs) < Cfg.HomeArticles {
		n = len(s.sortedDocs)
	}
	r := s.newRootData()

	r.Docs = s.sortedDocs[0:n]

	f, err := os.Create(path.Join(Cfg.PublicPath, Cfg.BasePath, "index.html"))
	if err != nil {
		log.Print(err)
	}
	if err = s.template.home.ExecuteTemplate(f, "root", r); err != nil {
		log.Print(err)
	}

	j := 1
	for i := 0; i < len(s.Docs); i += IndexNumber {
		f, err := os.Create(path.Join(Cfg.PublicPath, Cfg.BasePath, fmt.Sprintf("index-%d", j)))
		if err != nil {
			log.Print(err)
		}

		n := i + IndexNumber
		if i+IndexNumber > len(s.sortedDocs) {
			n = len(s.sortedDocs) - 1
		}

		r.Docs = s.sortedDocs[i:n]

		if err = s.template.index.ExecuteTemplate(f, "root", r); err != nil {
			log.Print(err)
		}
		j++
	}

	return
}

func (s *Server) MakeSitemap() (err error) {

	f, err := os.Create(path.Join(Cfg.PublicPath, Cfg.BasePath, "sitemap.txt"))
	defer f.Close()
	for i := 0; i < len(s.Docs); i += 1 {
		_, err = f.WriteString(path.Join(Cfg.BaseURL, s.sortedDocs[i].Path))
		f.WriteString("\n")
	}
	return
}

type docsByTime []*Doc

func (s docsByTime) Len() int           { return len(s) }
func (s docsByTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
<commit_msg>change sitemap join<commit_after>package bla

import (
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

const IndexNumber = 20

type Doc struct {
	RealPath   string // as id
	Path       string
	Title      string
	headerNode *goquery.Selection
	Time       time.Time
	Tags       []string
	Parsed     *goquery.Document
	Related    []*Doc
}

func santiSpace(s string) string {
	ss := strings.Replace(s, " ", "-", -1)
	return strings.Replace(ss, "/", "-", -1)
}

func (d *Doc) String() string {
	return fmt.Sprintf("Path:%s Title:%s Time:%s Tags:%s", d.Path, d.Title, d.Time, d.Tags)
}

func (s *Server) LoadDoc(path string, info os.FileInfo, e error) (err error) {

	if info == nil || !info.Mode().IsRegular() {
		return
	}

	if !strings.HasSuffix(filepath.Base(path), ".html") {
		return
	}

	//Log(path)

	f, err := os.Open(path)
	if err != nil {
		log.Print(err)
		return
	}
	defer f.Close()

	doc := &Doc{RealPath: path,
		Related: make([]*Doc, 0)}

	parsed, err := goquery.NewDocumentFromReader(f)
	if err != nil {
		log.Printf("Parse failed:%v", err)
		return
	}

	header := parsed.Find("h1").First()
	doc.Title = header.Text()
	doc.headerNode = header
	doc.Path = Cfg.BasePath + fmt.Sprintf(Cfg.DocURLFormat, santiSpace(doc.Title))

	if t := parsed.Find(".date").First().Text(); t != "" {
		doc.Time, err = time.Parse("2006-01-02", t)
		if err != nil {
			log.Print(err)
			return
		}
	} else {
		doc.Time = info.ModTime()
	}

	parsed.Find(".tag").Each(func(i int, sel *goquery.Selection) {
		t := sel.Text()
		doc.Tags = append(doc.Tags, t)
		if _, ok := s.Tags[t]; ok {
			s.Tags[t] = append(s.Tags[t], doc)
		} else {
			s.Tags[t] = []*Doc{doc}
		}
	})
	// Make header links
	header.ReplaceWithHtml(fmt.Sprintf(`<h1 class="title"><a href="%s">%s</a></h1>`, doc.Path, doc.Title))

	//Log(doc)
	doc.Parsed = parsed

	s.DocLock.Lock()
	defer s.DocLock.Unlock()

	s.Docs[doc.RealPath] = doc
	return
}

func (s *Server) makeRelated() {

	// clean all docs
	s.sortedDocs = s.sortedDocs[:0]

	for _, d := range s.Docs {
		// reset related docs
		d.Related = d.Related[:0]
		s.sortedDocs = append(s.sortedDocs, d)
		for _, t := range d.Tags {
			for _, rd := range s.Tags[t] {
				if !docInDocs(rd, d.Related) && rd != d {
					d.Related = append(d.Related, rd)
				}
			}
		}
	}

	sort.Sort(docsByTime(s.sortedDocs))

}

func (s *Server) LoadAllDocs() (err error) {

	start := time.Now()
	s.Tags = make(map[string][]*Doc)
	s.Docs = make(map[string]*Doc)

	filepath.Walk(Cfg.ContentPath, s.LoadDoc)
	// make related doc
	s.makeRelated()
	log.Printf("Load %d docs in %s", len(s.sortedDocs), time.Since(start))

	return
}

type rootData struct {
	Cfg        *Config
	Server     *Server
	Docs       []*Doc
	IndexCount int
}

func (s *Server) newRootData() *rootData {
	return &rootData{Server: s, Cfg: Cfg, Docs: make([]*Doc, 0)}
}

func docInDocs(doc *Doc, docs []*Doc) bool {

	for _, d := range docs {
		if d.Title == doc.Title {
			return true
		}
	}
	return false
}

func (s *Server) SaveAllDocs() (err error) {

	r := s.newRootData()

	err = os.MkdirAll(path.Join(Cfg.PublicPath, Cfg.BasePath), 0755)

	if err != nil {
		log.Fatal(err)
	}
	for _, d := range s.Docs {

		r.Docs = []*Doc{d}
		docPubPath := path.Join(Cfg.PublicPath, d.Path)
		if docPubPath == path.Dir(docPubPath) {
			continue
		}

		f, err := os.Create(docPubPath)
		if err != nil {
			log.Print(err)
		}

		if err = s.template.doc.ExecuteTemplate(f, "root", r); err != nil {
			log.Print(err)
		}
		f.Close()
		os.Chtimes(docPubPath, d.Time, d.Time)
	}

	return
}

func (s *Server) MakeHome() (err error) {

	log.Print("making home of ", len(s.sortedDocs))
	n := Cfg.HomeArticles
	if len(s.sortedDocs) < Cfg.HomeArticles {
		n = len(s.sortedDocs)
	}
	r := s.newRootData()

	r.Docs = s.sortedDocs[0:n]

	f, err := os.Create(path.Join(Cfg.PublicPath, Cfg.BasePath, "index.html"))
	if err != nil {
		log.Print(err)
	}
	if err = s.template.home.ExecuteTemplate(f, "root", r); err != nil {
		log.Print(err)
	}

	j := 1
	for i := 0; i < len(s.Docs); i += IndexNumber {
		f, err := os.Create(path.Join(Cfg.PublicPath, Cfg.BasePath, fmt.Sprintf("index-%d", j)))
		if err != nil {
			log.Print(err)
		}

		n := i + IndexNumber
		if i+IndexNumber > len(s.sortedDocs) {
			n = len(s.sortedDocs) - 1
		}

		r.Docs = s.sortedDocs[i:n]

		if err = s.template.index.ExecuteTemplate(f, "root", r); err != nil {
			log.Print(err)
		}
		j++
	}

	return
}

func (s *Server) MakeSitemap() (err error) {

	f, err := os.Create(path.Join(Cfg.PublicPath, Cfg.BasePath, "sitemap.txt"))
	defer f.Close()
	for i := 0; i < len(s.Docs); i += 1 {
		_, err = f.WriteString(Cfg.BaseURL + s.sortedDocs[i].Path)
		f.WriteString("\n")
	}
	return
}

type docsByTime []*Doc

func (s docsByTime) Len() int           { return len(s) }
func (s docsByTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"math/rand"
	"net/mail"
	"strconv"
	"time"

	"log"

	"github.com/extemporalgenome/slug"
	"github.com/joshsoftware/curem/config"
	"labix.org/v2/mgo/bson"
)

// contact type holds the fields related to a particular contact.
// omitempty tag will make sure the database doesn't contain content like:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//    Phone:
//    Skype:
//    Country:
//   }
// Instead, it will store the above data as:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//   }
type contact struct {
	Id        bson.ObjectId `bson:"_id"                  json:"id"`
	Company   string        `bson:"company,omitempty"    json:"company,omitempty"`
	Person    string        `bson:"person,omitempty"     json:"person,omitempty"`
	Slug      string        `bson:"slug,omitempty"       json:"slug,omitempty"`
	Email     string        `bson:"email,omitempty"      json:"email,omitempty"`
	Phone     string        `bson:"phone,omitempty"      json:"phone,omitempty"`
	SkypeId   string        `bson:"skypeId,omitempty"    json:"skypeId,omitempty"`
	Country   string        `bson:"country,omitempty"    json:"country,omitempty"`
	CreatedAt time.Time     `bson:"createdAt,omitempty"  json:"createdAt,omitempty"`
	UpdatedAt time.Time     `bson:"updatedAt,omitempty"  json:"updatedAt,omitempty"`
}

// incomingContact type is used for handling PATCH requests.
// To understand why we are using pointer types for fields,
// refer to http://blog.golang.org/json-and-go .
// Using pointer types, we can differentiate intentional nil value fields
// and empty fields.
// This type is used *only* for decoding json obtained from a PATCH request.
type incomingContact struct {
	Id      *bson.ObjectId `json:"id"`
	Company *string        `json:"company,omitempty"`
	Person  *string        `json:"person,omitempty"`
	Slug    *string        `json:"slug,omitempty"`
	Email   *string        `json:"email,omitempty"`
	Phone   *string        `json:"phone,omitempty"`
	SkypeId *string        `json:"skypeId,omitempty"`
	Country *string        `json:"country,omitempty"`
}

// copyIncomingFields copies the fields from an incomingContact into a
// contact object.
// Using multiple if statements provides more granularity, while allowing
// update of only some specific fields.
// TODO(Hari): Use reflect package instead of multiple if statements
func (c *contact) copyIncomingFields(i *incomingContact) error {
	if i.Id != nil {
		if *i.Id != c.Id {
			return errors.New("Id doesn't match")
		}
	}
	if i.Slug != nil {
		if *i.Slug != c.Slug {
			return errors.New("Slug can't be updated")
		}
	}
	if i.Company != nil {
		c.Company = *i.Company
	}
	if i.Person != nil {
		c.Person = *i.Person
	}
	if i.Slug != nil {
		c.Slug = *i.Slug
	}
	if i.Email != nil {
		c.Email = *i.Email
	}
	if i.Phone != nil {
		c.Phone = *i.Phone
	}
	if i.SkypeId != nil {
		c.SkypeId = *i.SkypeId
	}
	if i.Country != nil {
		c.Country = *i.Country
	}
	return nil
}

func validateContact(c *contact) error {
	if c.Person == "" {
		err := errors.New("person can't be empty")
		return err
	}
	if c.Email == "" {
		err := errors.New("email can't be empty")
		return err
	}
	if _, err := mail.ParseAddress(c.Email); err != nil {
		return err
	}

	return nil
}

// NewContact takes the fields of a contact, initializes a struct of contact type and returns
// the pointer to that struct.
func NewContact(company, person, email, phone, skypeid, country string) (*contact, error) {
	doc := contact{
		Id:      bson.NewObjectId(),
		Company: company,
		Person:  person,
		Email:   email,
		Phone:   phone,
		SkypeId: skypeid,
		Country: country,
	}
	if err := validateContact(&doc); err != nil {
		return &contact{}, err
	}
	slugifyContact(&doc)

	doc.CreatedAt = doc.Id.Time()
	doc.UpdatedAt = doc.CreatedAt

	err := config.ContactsCollection.Insert(doc)
	if err != nil {
		return &contact{}, err
	}
	return &doc, nil
}

// GetContactById takes the contact Id as an argument and returns a pointer to the contact object.
func GetContactById(i bson.ObjectId) (*contact, error) {
	var c contact
	err := config.ContactsCollection.FindId(i).One(&c)
	if err != nil {
		return &contact{}, err
	}
	return &c, nil
}

// GetContactBySlug takes the contact slug as an argument and returns a pointer to the contact object.
func GetContactBySlug(slug string) (*contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		return &contact{}, err
	}

	if len(c) == 0 {
		return &contact{}, errors.New("no contact")
	}

	if len(c) > 1 {
		return &contact{}, errors.New("more than 1 contact found")
	}
	return &c[0], nil
}

// GetAllContacts fetches all the contacts from the database.
func GetAllContacts() ([]contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(nil).All(&c)
	if err != nil {
		return []contact{}, err
	}
	return c, nil
}

// Update updates the contact in the database.
// First, fetch a contact from the database and change the necessary fields.
// Then call the Update method on that contact object.
func (c *contact) Update() error {
	if err := validateContact(c); err != nil {
		return err
	}
	c.UpdatedAt = bson.Now()
	err := config.ContactsCollection.UpdateId(c.Id, c)
	return err
}

// Delete deletes the contact from the database.
func (c *contact) Delete() error {
	return config.ContactsCollection.RemoveId(c.Id)
}

// slugifyContact takes a pointer to a contact as the argument and
// creates a slug and populates the slug field of the contact.
func slugifyContact(c *contact) {
	base := slug.SlugAscii(c.Person)
	temp := base
	rand.Seed(time.Now().UnixNano()) // takes the current time in nanoseconds as the seed
	i := rand.Intn(10000)
	for {
		if contactSlugExists(temp) {
			temp = base + "-" + strconv.Itoa(i)
			i = rand.Intn(10000)
		} else {
			c.Slug = temp
			return
		}

	}
}

// contactSlugExists returns true if a slug for a contact already exists in the database
// else returns false.
func contactSlugExists(slug string) bool {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		log.Println(err)
		return true // returns true just to be safe.
	}
	if len(c) == 0 {
		return false
	}
	return true
}
<commit_msg>Change validateContact function into a Validate method<commit_after>package main

import (
	"errors"
	"math/rand"
	"net/mail"
	"strconv"
	"time"

	"log"

	"github.com/extemporalgenome/slug"
	"github.com/joshsoftware/curem/config"
	"labix.org/v2/mgo/bson"
)

// contact type holds the fields related to a particular contact.
// omitempty tag will make sure the database doesn't contain content like:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//    Phone:
//    Skype:
//    Country:
//   }
// Instead, it will store the above data as:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//   }
type contact struct {
	Id        bson.ObjectId `bson:"_id"                  json:"id"`
	Company   string        `bson:"company,omitempty"    json:"company,omitempty"`
	Person    string        `bson:"person,omitempty"     json:"person,omitempty"`
	Slug      string        `bson:"slug,omitempty"       json:"slug,omitempty"`
	Email     string        `bson:"email,omitempty"      json:"email,omitempty"`
	Phone     string        `bson:"phone,omitempty"      json:"phone,omitempty"`
	SkypeId   string        `bson:"skypeId,omitempty"    json:"skypeId,omitempty"`
	Country   string        `bson:"country,omitempty"    json:"country,omitempty"`
	CreatedAt time.Time     `bson:"createdAt,omitempty"  json:"createdAt,omitempty"`
	UpdatedAt time.Time     `bson:"updatedAt,omitempty"  json:"updatedAt,omitempty"`
}

// incomingContact type is used for handling PATCH requests.
// To understand why we are using pointer types for fields,
// refer to http://blog.golang.org/json-and-go .
// Using pointer types, we can differentiate intentional nil value fields
// and empty fields.
// This type is used *only* for decoding json obtained from a PATCH request.
type incomingContact struct {
	Id      *bson.ObjectId `json:"id"`
	Company *string        `json:"company,omitempty"`
	Person  *string        `json:"person,omitempty"`
	Slug    *string        `json:"slug,omitempty"`
	Email   *string        `json:"email,omitempty"`
	Phone   *string        `json:"phone,omitempty"`
	SkypeId *string        `json:"skypeId,omitempty"`
	Country *string        `json:"country,omitempty"`
}

// copyIncomingFields copies the fields from an incomingContact into a
// contact object.
// Using multiple if statements provides more granularity, while allowing
// update of only some specific fields.
// TODO(Hari): Use reflect package instead of multiple if statements
func (c *contact) copyIncomingFields(i *incomingContact) error {
	if i.Id != nil {
		if *i.Id != c.Id {
			return errors.New("Id doesn't match")
		}
	}
	if i.Slug != nil {
		if *i.Slug != c.Slug {
			return errors.New("Slug can't be updated")
		}
	}
	if i.Company != nil {
		c.Company = *i.Company
	}
	if i.Person != nil {
		c.Person = *i.Person
	}
	if i.Slug != nil {
		c.Slug = *i.Slug
	}
	if i.Email != nil {
		c.Email = *i.Email
	}
	if i.Phone != nil {
		c.Phone = *i.Phone
	}
	if i.SkypeId != nil {
		c.SkypeId = *i.SkypeId
	}
	if i.Country != nil {
		c.Country = *i.Country
	}
	return nil
}

func (c *contact) Validate() error {
	if c.Person == "" {
		err := errors.New("person can't be empty")
		return err
	}
	if c.Email == "" {
		err := errors.New("email can't be empty")
		return err
	}
	if _, err := mail.ParseAddress(c.Email); err != nil {
		return err
	}

	return nil
}

// NewContact takes the fields of a contact, initializes a struct of contact type and returns
// the pointer to that struct.
func NewContact(company, person, email, phone, skypeid, country string) (*contact, error) {
	doc := contact{
		Id:      bson.NewObjectId(),
		Company: company,
		Person:  person,
		Email:   email,
		Phone:   phone,
		SkypeId: skypeid,
		Country: country,
	}
	if err := (&doc).Validate(); err != nil {
		return &contact{}, err
	}
	slugifyContact(&doc)

	doc.CreatedAt = doc.Id.Time()
	doc.UpdatedAt = doc.CreatedAt

	err := config.ContactsCollection.Insert(doc)
	if err != nil {
		return &contact{}, err
	}
	return &doc, nil
}

// GetContactById takes the contact Id as an argument and returns a pointer to the contact object.
func GetContactById(i bson.ObjectId) (*contact, error) {
	var c contact
	err := config.ContactsCollection.FindId(i).One(&c)
	if err != nil {
		return &contact{}, err
	}
	return &c, nil
}

// GetContactBySlug takes the contact slug as an argument and returns a pointer to the contact object.
func GetContactBySlug(slug string) (*contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		return &contact{}, err
	}

	if len(c) == 0 {
		return &contact{}, errors.New("no contact")
	}

	if len(c) > 1 {
		return &contact{}, errors.New("more than 1 contact found")
	}
	return &c[0], nil
}

// GetAllContacts fetches all the contacts from the database.
func GetAllContacts() ([]contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(nil).All(&c)
	if err != nil {
		return []contact{}, err
	}
	return c, nil
}

// Update updates the contact in the database.
// First, fetch a contact from the database and change the necessary fields.
// Then call the Update method on that contact object.
func (c *contact) Update() error {
	if err := c.Validate(); err != nil {
		return err
	}
	c.UpdatedAt = bson.Now()
	err := config.ContactsCollection.UpdateId(c.Id, c)
	return err
}

// Delete deletes the contact from the database.
func (c *contact) Delete() error {
	return config.ContactsCollection.RemoveId(c.Id)
}

// slugifyContact takes a pointer to a contact as the argument and
// creates a slug and populates the slug field of the contact.
func slugifyContact(c *contact) {
	base := slug.SlugAscii(c.Person)
	temp := base
	rand.Seed(time.Now().UnixNano()) // takes the current time in nanoseconds as the seed
	i := rand.Intn(10000)
	for {
		if contactSlugExists(temp) {
			temp = base + "-" + strconv.Itoa(i)
			i = rand.Intn(10000)
		} else {
			c.Slug = temp
			return
		}

	}
}

// contactSlugExists returns true if a slug for a contact already exists in the database
// else returns false.
func contactSlugExists(slug string) bool {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		log.Println(err)
		return true // returns true just to be safe.
	}
	if len(c) == 0 {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package fakku

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strconv"
	"time"
)

const (
	ErrorContentDoesntExist = "Content doesn't exist"
	ErrorUnknownJsonData    = "Got unknown json data back from content request. API Change?"
	ErrorUnknownJsonLayout  = "Got an unknown layout back from content request. API Change?"
)

type AttributeList []Attribute
type Attribute struct {
	Attribute     string `json:"attribute"`
	AttributeLink string `json:"attribute_link"`
}

func (this *Attribute) populate(c map[string]interface{}) {
	this.Attribute = c["attribute"].(string)
	this.AttributeLink = c["attribute_link"].(string)
}

func (this *Attribute) String() string {
	return this.Attribute
}

func constructAttributeFields(c map[string]interface{}, field string) AttributeList {
	try, ok := c[field]
	if !ok {
		return nil
	}
	tmp := try.([]interface{})
	attrs := make(AttributeList, len(tmp))
	for i := 0; i < len(tmp); i++ {
		attrs[i].populate(tmp[i].(map[string]interface{}))
	}
	return attrs
}

type Content struct {
	Name         string
	RawUrl       string
	Description  string
	Language     string
	Category     string
	RawDate      float64
	FileSize     float64
	Favorites    float64
	CommentCount float64
	Pages        float64
	Poster       string
	RawPosterUrl string
	Tags         AttributeList `json:"content_tags"`
	Translators  AttributeList `json:"content_translators"`
	Series       AttributeList `json:"content_series"`
	Artists      AttributeList `json:"content_artists"`
	Images       struct {
		Cover  string
		Sample string
	}
	rawName string
}
type ContentList []Content

func (this *Content) Url() (*url.URL, error) {
	return url.Parse(https + this.RawUrl)
}
func (this *Content) PosterUrl() (*url.URL, error) {
	return url.Parse(https + this.RawPosterUrl)
}
func (this *Content) CoverUrl() (*url.URL, error) {
	return url.Parse(https + this.Images.Cover)
}
func (this *Content) SampleUrl() (*url.URL, error) {
	return url.Parse(https + this.Images.Sample)
}
func (this *Content) Date() time.Time {
	return time.Unix(int64(this.RawDate), 0)
}
func (c *Content) UnmarshalJSON(b []byte) error {
	var f interface{}
	json.Unmarshal(b, &f)
	switch f.(type) {
	case map[string]interface{}:
		m := f.(map[string]interface{})
		contents := m["content"]
		switch contents.(type) {
		case map[string]interface{}:
			v := contents.(map[string]interface{})
			c.populate(v)
			return nil
		case []interface{}:
			q := contents.([]interface{})
			if len(q) == 0 {
				// doesn't exist
				return fmt.Errorf(ErrorContentDoesntExist)
			} else {
				return fmt.Errorf(ErrorUnknownJsonData)
			}
		default:
			return fmt.Errorf(ErrorUnknownJsonLayout)
		}
	case []interface{}:
		q := f.([]interface{})
		if len(q) == 0 {
			// doesn't exist
			return fmt.Errorf(ErrorContentDoesntExist)
		} else {
			return fmt.Errorf(ErrorUnknownJsonData)
		}
	default:
		return fmt.Errorf(ErrorUnknownJsonLayout)
	}
}

func (c *Content) populate(v map[string]interface{}) {
	c.Name = v["content_name"].(string)
	c.RawUrl = v["content_url"].(string)
	c.Description = v["content_description"].(string)
	c.Language = v["content_language"].(string)
	c.Category = v["content_category"].(string)
	c.RawDate = v["content_date"].(float64)
	c.FileSize = v["content_filesize"].(float64)
	c.Favorites = v["content_favorites"].(float64)
	c.CommentCount = v["content_comments"].(float64)
	c.Pages = v["content_pages"].(float64)
	c.Poster = v["content_poster"].(string)
	c.RawPosterUrl = v["content_poster_url"].(string)
	c.Tags = constructAttributeFields(v, "content_tags")
	c.Translators = constructAttributeFields(v, "content_translators")
	c.Series = constructAttributeFields(v, "content_series")
	c.Artists = constructAttributeFields(v, "content_artists")

	tmp := v["content_images"]
	z := tmp.(map[string]interface{})
	c.Images.Cover = z["cover"].(string)
	c.Images.Sample = z["sample"].(string)
}

func GetContent(category, name string) (*Content, error) {
	var c Content
	url := contentApiFunction{Category: category, Name: name}
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		c.rawName = name
		return &c, nil
	}
}

type contentApiFunction struct {
	Category string
	Name     string
}

func (a contentApiFunction) Construct() string {
	return fmt.Sprintf("%s/%s/%s", apiHeader, a.Category, a.Name)
}

type contentCommentApiFunction struct {
	contentApiFunction
	TopComments bool
	supportsPagination
}

func (a contentCommentApiFunction) Construct() string {
	base := fmt.Sprintf("%s/comments", a.contentApiFunction.Construct())
	log.Printf("url: %s", base)
	if a.TopComments {
		return fmt.Sprintf("%s/top", base)
	} else {
		return paginateString(base, a.Page)
	}
}

func getContentCommentsGeneric(url apiFunction) (*Comments, error) {
	var c Comments
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		return &c, nil
	}
}
func ContentComments(category, name string) (*Comments, error) {
	url := contentCommentApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
	}
	return getContentCommentsGeneric(url)
}

func ContentCommentsPage(category, name string, page uint) (*Comments, error) {
	url := contentCommentApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
		supportsPagination: supportsPagination{Page: page},
	}
	return getContentCommentsGeneric(url)
}

func ContentTopComments(category, name string) (*Comments, error) {
	url := contentCommentApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
		TopComments: true,
	}
	return getContentCommentsGeneric(url)
}

func (this *Content) TopComments() (*Comments, error) {
	return ContentTopComments(this.Category, this.rawName)
}
func (this *Content) CommentsPage(page uint) (*Comments, error) {
	return ContentCommentsPage(this.Category, this.rawName, page)
}
func (this *Content) Comments() (*Comments, error) {
	return ContentComments(this.Category, this.rawName)
}

type Comment struct {
	Id           int     `json:"comment_id"`
	AttachedId   int     `json:"comment_attached_id"`
	Poster       string  `json:"comment_poster"`
	RawPosterUrl string  `json:"comment_poster_url"`
	Reputation   float64 `json:"comment_reputation"`
	Text         string  `json:"comment_text"`
	RawDate      float64 `json:"comment_date"`
}

func (this *Comment) PosterUrl() (*url.URL, error) {
	return url.Parse(this.RawPosterUrl)
}
func (this *Comment) Date() time.Time {
	return time.Unix(int64(this.RawDate), 0)
}

type Comments struct {
	Comments   []Comment `json:"comments"`
	PageNumber int       `json:"page"`
	Total      int       `json:"total"`
	Pages      int       `json:"pages"`
}
type PageList []Page
type ReadOnlineContent struct {
	Content Content  `json:"content"`
	Pages   PageList `json:"pages"`
}

func (r *ReadOnlineContent) UnmarshalJSON(b []byte) error {
	var f interface{}
	if err := json.Unmarshal(b, &r.Content); err != nil {
		return err
	}
	json.Unmarshal(b, &f)
	// need to check and make sure that the content exists
	switch f.(type) {
	case map[string]interface{}:
		m := f.(map[string]interface{})
		pages := m["pages"]
		v := pages.(map[string]interface{})
		r.Pages = make([]Page, len(v))
		for i := 0; i < len(v); i++ {
			ind := strconv.Itoa(i + 1)
			r.Pages[i].populate(v[ind].(map[string]interface{}))
		}
		return nil
	case []interface{}:
		q := f.([]interface{})
		if len(q) == 0 {
			// doesn't exist
			return fmt.Errorf("Content doesn't exist")
		} else {
			return fmt.Errorf("Got unknown json data back from content request. API Change?")
		}
	default:
		return fmt.Errorf("Got an unknown layout back from content request. API Change?")
	}
}

type Page struct {
	Thumb string
	Image string
}

func (this *Page) populate(c map[string]interface{}) {
	this.Thumb = c["thumb"].(string)
	this.Image = c["image"].(string)
}

func (this *Page) ThumbUrl() (*url.URL, error) {
	return url.Parse(this.Thumb)
}
func (this *Page) ImageUrl() (*url.URL, error) {
	return url.Parse(this.Image)
}

type contentReadOnlineApiFunction struct {
	contentApiFunction
}

func (a contentReadOnlineApiFunction) Construct() string {
	return fmt.Sprintf("%s/read", a.contentApiFunction.Construct())
}

func ReadOnline(category, name string) (*ReadOnlineContent, error) {
	var c ReadOnlineContent
	url := contentReadOnlineApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
	}
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		return &c, nil
	}
}

func (this *Content) ReadOnline() (PageList, error) {
	element, err := ReadOnline(this.Category, this.rawName)
	if err != nil {
		return nil, err
	} else {
		return element.Pages, nil
	}
}

type contentRelatedApiFunction struct {
	contentApiFunction
	supportsPagination
}

func (a contentRelatedApiFunction) Construct() string {
	base := fmt.Sprintf("%s/related", a.contentApiFunction.Construct())
	return paginateString(base, a.Page)
}

type RelatedContentList struct {
	Related ContentList `json:"related"`
	Total   uint        `json:"total"`
	Pages   uint        `json:"pages"`
}

func RelatedContent(category, name string) (*RelatedContentList, error) {
	return RelatedContentPage(category, name, 0)
}

func RelatedContentPage(category, name string, page uint) (*RelatedContentList, error) {
	var c RelatedContentList
	url := contentRelatedApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
		supportsPagination: supportsPagination{Page: page},
	}
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		return &c, nil
	}
}
func (this *Content) RelatedContent() (*RelatedContentList, error) {
	return RelatedContent(this.Category, this.rawName)
}
func (this *Content) RelatedContentPage(page uint) (*RelatedContentList, error) {
	return RelatedContentPage(this.Category, this.rawName, page)
}

func (c *RelatedContentList) UnmarshalJSON(b []byte) error {
	// slightly different
	var f interface{}
	json.Unmarshal(b, &f)
	m := f.(map[string]interface{})
	related := m["related"]
	v := related.([]interface{})
	c.Related = make(ContentList, len(v))
	for i := 0; i < len(v); i++ {
		c.Related[i].populate(v[i].(map[string]interface{}))
	}
	c.Total = uint(m["total"].(float64))
	c.Pages = uint(m["pages"].(float64))
	return nil
}
<commit_msg>Started cleaning up content.go<commit_after>package fakku

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strconv"
	"time"
)

const (
	ErrorContentDoesntExist = "Content doesn't exist"
	ErrorUnknownJsonData    = "Got unknown json data back from content request. API Change?"
	ErrorUnknownJsonLayout  = "Got an unknown layout back from content request. API Change?"
)

type AttributeList []Attribute
type Attribute struct {
	Attribute     string `json:"attribute"`
	AttributeLink string `json:"attribute_link"`
}

func (this *Attribute) populate(c map[string]interface{}) {
	this.Attribute = c["attribute"].(string)
	this.AttributeLink = c["attribute_link"].(string)
}

func (this *Attribute) String() string {
	return this.Attribute
}

func constructAttributeFields(c map[string]interface{}, field string) AttributeList {
	try, ok := c[field]
	if !ok {
		return nil
	}
	tmp := try.([]interface{})
	attrs := make(AttributeList, len(tmp))
	for i := 0; i < len(tmp); i++ {
		attrs[i].populate(tmp[i].(map[string]interface{}))
	}
	return attrs
}

type Content struct {
	Name         string
	RawUrl       string
	Description  string
	Language     string
	Category     string
	RawDate      float64
	FileSize     float64
	Favorites    float64
	CommentCount float64
	Pages        float64
	Poster       string
	RawPosterUrl string
	Tags         AttributeList `json:"content_tags"`
	Translators  AttributeList `json:"content_translators"`
	Series       AttributeList `json:"content_series"`
	Artists      AttributeList `json:"content_artists"`
	Images       struct {
		Cover  string
		Sample string
	}
	rawName string
}
type ContentList []Content

func (this *Content) Url() (*url.URL, error) {
	return url.Parse(https + this.RawUrl)
}
func (this *Content) PosterUrl() (*url.URL, error) {
	return url.Parse(https + this.RawPosterUrl)
}
func (this *Content) CoverUrl() (*url.URL, error) {
	return url.Parse(https + this.Images.Cover)
}
func (this *Content) SampleUrl() (*url.URL, error) {
	return url.Parse(https + this.Images.Sample)
}
func (this *Content) Date() time.Time {
	return time.Unix(int64(this.RawDate), 0)
}
func (c *Content) UnmarshalJSON(b []byte) error {
	var f interface{}
	json.Unmarshal(b, &f)
	switch f.(type) {
	case map[string]interface{}:
		m := f.(map[string]interface{})
		contents := m["content"]
		switch contents.(type) {
		case map[string]interface{}:
			v := contents.(map[string]interface{})
			c.populate(v)
			return nil
		case []interface{}:
			q := contents.([]interface{})
			if len(q) == 0 {
				// doesn't exist
				return fmt.Errorf(ErrorContentDoesntExist)
			} else {
				return fmt.Errorf(ErrorUnknownJsonData)
			}
		default:
			return fmt.Errorf(ErrorUnknownJsonLayout)
		}
	case []interface{}:
		q := f.([]interface{})
		if len(q) == 0 {
			// doesn't exist
			return fmt.Errorf(ErrorContentDoesntExist)
		} else {
			return fmt.Errorf(ErrorUnknownJsonData)
		}
	default:
		return fmt.Errorf(ErrorUnknownJsonLayout)
	}
}

func (c *Content) populate(v map[string]interface{}) {
	c.Name = v["content_name"].(string)
	c.RawUrl = v["content_url"].(string)
	c.Description = v["content_description"].(string)
	c.Language = v["content_language"].(string)
	c.Category = v["content_category"].(string)
	c.RawDate = v["content_date"].(float64)
	c.FileSize = v["content_filesize"].(float64)
	c.Favorites = v["content_favorites"].(float64)
	c.CommentCount = v["content_comments"].(float64)
	c.Pages = v["content_pages"].(float64)
	c.Poster = v["content_poster"].(string)
	c.RawPosterUrl = v["content_poster_url"].(string)
	c.Tags = constructAttributeFields(v, "content_tags")
	c.Translators = constructAttributeFields(v, "content_translators")
	c.Series = constructAttributeFields(v, "content_series")
	c.Artists = constructAttributeFields(v, "content_artists")

	tmp := v["content_images"]
	z := tmp.(map[string]interface{})
	c.Images.Cover = z["cover"].(string)
	c.Images.Sample = z["sample"].(string)
}

func GetContent(category, name string) (*Content, error) {
	var c Content
	url := contentApiFunction{Category: category, Name: name}
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		c.rawName = name
		return &c, nil
	}
}

type contentApiFunction struct {
	Category string
	Name     string
}

func (a contentApiFunction) Construct() string {
	return fmt.Sprintf("%s/%s/%s", apiHeader, a.Category, a.Name)
}

type contentCommentApiFunction struct {
	contentApiFunction
	supportsPagination
	TopComments bool
}

func (a contentCommentApiFunction) Construct() string {
	base := fmt.Sprintf("%s/comments", a.contentApiFunction.Construct())
	log.Printf("url: %s", base)
	if a.TopComments {
		return fmt.Sprintf("%s/top", base)
	} else {
		return paginateString(base, a.Page)
	}
}

func getContentCommentsGeneric(url apiFunction) (*Comments, error) {
	var c Comments
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		return &c, nil
	}
}
func ContentComments(category, name string) (*Comments, error) {
	url := contentCommentApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
	}
	return getContentCommentsGeneric(url)
}

func ContentCommentsPage(category, name string, page uint) (*Comments, error) {
	url := contentCommentApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
		supportsPagination: supportsPagination{Page: page},
	}
	return getContentCommentsGeneric(url)
}

func ContentTopComments(category, name string) (*Comments, error) {
	url := contentCommentApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
		TopComments: true,
	}
	return getContentCommentsGeneric(url)
}

func (this *Content) TopComments() (*Comments, error) {
	return ContentTopComments(this.Category, this.rawName)
}
func (this *Content) CommentsPage(page uint) (*Comments, error) {
	return ContentCommentsPage(this.Category, this.rawName, page)
}
func (this *Content) Comments() (*Comments, error) {
	return ContentComments(this.Category, this.rawName)
}

type Comment struct {
	Id           int64   `json:"comment_id"`
	AttachedId   int64   `json:"comment_attached_id"`
	Poster       string  `json:"comment_poster"`
	RawPosterUrl string  `json:"comment_poster_url"`
	Reputation   float64 `json:"comment_reputation"`
	Text         string  `json:"comment_text"`
	RawDate      float64 `json:"comment_date"`
}

func (this *Comment) PosterUrl() (*url.URL, error) {
	return url.Parse(this.RawPosterUrl)
}
func (this *Comment) Date() time.Time {
	return time.Unix(int64(this.RawDate), 0)
}

/*
func (this *Comment) PosterInformation() (*UserProfile, error) {
	return
}
*/

type CommentList []Comment
type Comments struct {
	Comments   CommentList `json:"comments"`
	PageNumber int         `json:"page"`
	Total      int         `json:"total"`
	Pages      int         `json:"pages"`
}
type PageList []Page
type ReadOnlineContent struct {
	Content Content  `json:"content"`
	Pages   PageList `json:"pages"`
}

func (r *ReadOnlineContent) UnmarshalJSON(b []byte) error {
	var f interface{}
	if err := json.Unmarshal(b, &r.Content); err != nil {
		return err
	}
	json.Unmarshal(b, &f)
	// need to check and make sure that the content exists
	switch f.(type) {
	case map[string]interface{}:
		m := f.(map[string]interface{})
		pages := m["pages"]
		v := pages.(map[string]interface{})
		r.Pages = make([]Page, len(v))
		for i := 0; i < len(v); i++ {
			ind := strconv.Itoa(i + 1)
			r.Pages[i].populate(v[ind].(map[string]interface{}))
		}
		return nil
	case []interface{}:
		q := f.([]interface{})
		if len(q) == 0 {
			// doesn't exist
			return fmt.Errorf(ErrorContentDoesntExist)
		} else {
			return fmt.Errorf(ErrorUnknownJsonData)
		}
	default:
		return fmt.Errorf(ErrorUnknownJsonLayout)
	}
}

type Page struct {
	Thumb string
	Image string
}

func (this *Page) populate(c map[string]interface{}) {
	this.Thumb = c["thumb"].(string)
	this.Image = c["image"].(string)
}

func (this *Page) ThumbUrl() (*url.URL, error) {
	return url.Parse(this.Thumb)
}
func (this *Page) ImageUrl() (*url.URL, error) {
	return url.Parse(this.Image)
}

type contentReadOnlineApiFunction struct {
	contentApiFunction
}

func (a contentReadOnlineApiFunction) Construct() string {
	return fmt.Sprintf("%s/read", a.contentApiFunction.Construct())
}

func ReadOnline(category, name string) (*ReadOnlineContent, error) {
	var c ReadOnlineContent
	url := contentReadOnlineApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
	}
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		return &c, nil
	}
}

func (this *Content) ReadOnline() (PageList, error) {
	element, err := ReadOnline(this.Category, this.rawName)
	if err != nil {
		return nil, err
	} else {
		return element.Pages, nil
	}
}

type contentRelatedApiFunction struct {
	contentApiFunction
	supportsPagination
}

func (a contentRelatedApiFunction) Construct() string {
	base := fmt.Sprintf("%s/related", a.contentApiFunction.Construct())
	return paginateString(base, a.Page)
}

type RelatedContentList struct {
	Related ContentList `json:"related"`
	Total   uint        `json:"total"`
	Pages   uint        `json:"pages"`
}

func RelatedContent(category, name string) (*RelatedContentList, error) {
	return RelatedContentPage(category, name, 0)
}

func RelatedContentPage(category, name string, page uint) (*RelatedContentList, error) {
	var c RelatedContentList
	url := contentRelatedApiFunction{
		contentApiFunction: contentApiFunction{
			Category: category,
			Name:     name,
		},
		supportsPagination: supportsPagination{Page: page},
	}
	if err := apiCall(url, &c); err != nil {
		return nil, err
	} else {
		return &c, nil
	}
}
func (this *Content) RelatedContent() (*RelatedContentList, error) {
	return RelatedContent(this.Category, this.rawName)
}
func (this *Content) RelatedContentPage(page uint) (*RelatedContentList, error) {
	return RelatedContentPage(this.Category, this.rawName, page)
}

func (c *RelatedContentList) UnmarshalJSON(b []byte) error {
	// slightly different
	var f interface{}
	json.Unmarshal(b, &f)
	m := f.(map[string]interface{})
	related := m["related"]
	v := related.([]interface{})
	c.Related = make(ContentList, len(v))
	for i := 0; i < len(v); i++ {
		c.Related[i].populate(v[i].(map[string]interface{}))
	}
	c.Total = uint(m["total"].(float64))
	c.Pages = uint(m["pages"].(float64))
	return nil
}
<|endoftext|>


<commit_before>package air

import (
	"bytes"
	"context"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"sync"
)

// Context represents the context of the current HTTP request. It holds request
// and response writer objects, path, path parameters, data and registered
// handler.
type Context struct {
	context.Context
	http.ResponseWriter

	statusCode int

	Request      *http.Request
	PristinePath string
	ParamNames   []string
	ParamValues  []string
	Params       map[string]string
	Handler      HandlerFunc
	Data         JSONMap
	Size         int
	Written      bool
	Air          *Air
}

var contextPool *sync.Pool

// newContext returns a new instance of `Context`.
func newContext(a *Air) *Context {
	return &Context{
		Context: context.Background(),
		Params:  make(map[string]string),
		Handler: NotFoundHandler,
		Data:    make(JSONMap),
		Air:     a,
	}
}

// SetValue sets request-scoped value into the `Context` of the c.
func (c *Context) SetValue(key interface{}, val interface{}) {
	c.Context = context.WithValue(c.Context, key, val)
}

// Write implements `http.ResponseWriter#Write()`.
func (c *Context) Write(bs []byte) (int, error) {
	if !c.Written {
		c.WriteHeader(http.StatusOK)
	}
	n, err := c.ResponseWriter.Write(bs)
	c.Size += n
	return n, err
}

// WriteHeader implements `http.ResponseWriter#WriteHeader()`.
func (c *Context) WriteHeader(code int) {
	if c.Written {
		c.Air.Logger.Warn("response already committed")
		return
	}
	c.statusCode = code
	c.ResponseWriter.WriteHeader(code)
	c.Written = true
}

// StatusCode returns the HTTP status code.
func (c *Context) StatusCode() int {
	return c.statusCode
}

// SetCookie adds a "Set-Cookie" header in HTTP response. The provided cookie
// must have a valid `Name`. Invalid cookies may be silently dropped.
func (c *Context) SetCookie(cookie *http.Cookie) {
	http.SetCookie(c.ResponseWriter, cookie)
}

// Bind binds the request body into provided type i. The default binder does it
// based on "Content-Type" header.
func (c *Context) Bind(i interface{}) error {
	return c.Air.binder.bind(i, c)
}

// Render renders a template with `Data` and `Data["template"]` of the c and
// sends a "text/html" response with `StatusCode` of the c.
func (c *Context) Render() error {
	t, ok := c.Data["template"]
	if !ok || reflect.ValueOf(t).Kind() != reflect.String {
		return errors.New("c.Data[\"template\"] not setted")
	}
	buf := &bytes.Buffer{}
	if err := c.Air.renderer.render(buf, t.(string), c); err != nil {
		return err
	}
	c.Header().Set(HeaderContentType, MIMETextHTML)
	_, err := c.Write(buf.Bytes())
	return err
}

// HTML sends an HTTP response with `StatusCode` and `Data["html"]` of the c.
func (c *Context) HTML() error {
	h, ok := c.Data["html"]
	if !ok || reflect.ValueOf(h).Kind() != reflect.String {
		return errors.New("c.Data[\"html\"] not setted")
	}
	c.Header().Set(HeaderContentType, MIMETextHTML)
	_, err := c.Write([]byte(h.(string)))
	return err
}

// String sends a string response with `StatusCode` and `Data["string"]` of the
// c.
func (c *Context) String() error {
	s, ok := c.Data["string"]
	if !ok || reflect.ValueOf(s).Kind() != reflect.String {
		return errors.New("c.Data[\"string\"] not setted")
	}
	c.Header().Set(HeaderContentType, MIMETextPlain)
	_, err := c.Write([]byte(s.(string)))
	return err
}

// JSON sends a JSON response with `StatusCode` and `Data["json"]` of the c.
func (c *Context) JSON() error {
	j, ok := c.Data["json"]
	if !ok {
		return errors.New("c.Data[\"json\"] not setted")
	}
	bs, err := json.Marshal(j)
	if c.Air.Config.DebugMode {
		bs, err = json.MarshalIndent(j, "", "\t")
	}
	if err != nil {
		return err
	}
	return c.JSONBlob(bs)
}

// JSONBlob sends a JSON blob response with `StatusCode` of the c.
func (c *Context) JSONBlob(bs []byte) error {
	return c.Blob(MIMEApplicationJSON, bs)
}

// JSONP sends a JSONP response with `StatusCode` and `Data["jsonp"]` of the c.
// It uses `Data["callback"]` of the c to construct the JSONP payload.
func (c *Context) JSONP() error {
	j, jok := c.Data["jsonp"]
	if !jok {
		return errors.New("c.Data[\"jsonp\"] not setted")
	}
	bs, err := json.Marshal(j)
	if err != nil {
		return err
	}
	return c.JSONPBlob(bs)
}

// JSONPBlob sends a JSONP blob response with `StatusCode` of the c. It uses
// `Data["callback"]` of the c to construct the JSONP payload.
func (c *Context) JSONPBlob(bs []byte) error {
	cb, cbok := c.Data["callback"]
	if !cbok || reflect.ValueOf(cb).Kind() != reflect.String {
		return errors.New("c.Data[\"callback\"] not setted")
	}
	c.Header().Set(HeaderContentType, MIMEApplicationJavaScript)
	if _, err := c.Write([]byte(cb.(string) + "(")); err != nil {
		return err
	}
	if _, err := c.Write(bs); err != nil {
		return err
	}
	_, err := c.Write([]byte(");"))
	return err
}

// XML sends an XML response with `StatusCode` and `Data["xml"]` of the c.
func (c *Context) XML() error {
	x, ok := c.Data["xml"]
	if !ok {
		return errors.New("c.Data[\"xml\"] not setted")
	}
	bs, err := xml.Marshal(x)
	if c.Air.Config.DebugMode {
		bs, err = xml.MarshalIndent(x, "", "\t")
	}
	if err != nil {
		return err
	}
	return c.XMLBlob(bs)
}

// XMLBlob sends a XML blob response with `StatusCode` of the c.
func (c *Context) XMLBlob(bs []byte) error {
	if _, err := c.Write([]byte(xml.Header)); err != nil {
		return err
	}
	return c.Blob(MIMEApplicationXML, bs)
}

// Blob sends a blob response with `StatusCode` of the c and contentType.
func (c *Context) Blob(contentType string, bs []byte) error {
	c.Header().Set(HeaderContentType, contentType)
	_, err := c.Write(bs)
	return err
}

// Stream sends a streaming response with `StatusCode` of the c and contentType.
func (c *Context) Stream(contentType string, r io.Reader) error {
	c.Header().Set(HeaderContentType, contentType)
	_, err := io.Copy(c, r)
	return err
}

// File sends a response with the content of the file.
func (c *Context) File(file string) error {
	f, err := os.Open(file)
	if err != nil {
		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = filepath.Join(file, "index.html")
		f, err = os.Open(file)
		if err != nil {
			return ErrNotFound
		}
		defer f.Close()
		if fi, err = f.Stat(); err != nil {
			return err
		}
	}
	http.ServeContent(c, c.Request, fi.Name(), fi.ModTime(), f)
	return nil
}

// Attachment sends a response as attachment, prompting client to save the file.
func (c *Context) Attachment(file, name string) error {
	return c.contentDisposition(file, name, "attachment")
}

// Inline sends a response as inline, opening the file in the browser.
func (c *Context) Inline(file, name string) error {
	return c.contentDisposition(file, name, "inline")
}

// contentDisposition sends a response as the dispositionType.
func (c *Context) contentDisposition(file, name, dispositionType string) error {
	c.Header().Set(HeaderContentDisposition,
		fmt.Sprintf("%s; filename=%s", dispositionType, name))
	return c.File(file)
}

// NoContent sends a response with no body.
func (c *Context) NoContent() error { return nil }

// Redirect redirects the request with status code.
func (c *Context) Redirect(code int, url string) error {
	if code < http.StatusMultipleChoices ||
		code > http.StatusTemporaryRedirect {
		return ErrInvalidRedirectCode
	}
	c.Header().Set(HeaderLocation, url)
	c.WriteHeader(code)
	return nil
}

// reset resets all fields in the c.
func (c *Context) reset() {
	c.Context = context.Background()
	c.ResponseWriter = nil
	c.statusCode = 0
	c.Request = nil
	c.PristinePath = ""
	c.ParamNames = c.ParamNames[:0]
	c.ParamValues = c.ParamValues[:0]
	for k := range c.Params {
		delete(c.Params, k)
	}
	c.Handler = NotFoundHandler
	for k := range c.Data {
		delete(c.Data, k)
	}
	c.Size = 0
	c.Written = false
}
<commit_msg>refactor: make `Context#Size` unexported<commit_after>package air

import (
	"bytes"
	"context"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"sync"
)

// Context represents the context of the current HTTP request. It holds request
// and response writer objects, path, path parameters, data and registered
// handler.
type Context struct {
	context.Context
	http.ResponseWriter

	statusCode int
	size       int

	Request      *http.Request
	PristinePath string
	ParamNames   []string
	ParamValues  []string
	Params       map[string]string
	Handler      HandlerFunc
	Data         JSONMap
	Written      bool
	Air          *Air
}

var contextPool *sync.Pool

// newContext returns a new instance of `Context`.
func newContext(a *Air) *Context {
	return &Context{
		Context: context.Background(),
		Params:  make(map[string]string),
		Handler: NotFoundHandler,
		Data:    make(JSONMap),
		Air:     a,
	}
}

// SetValue sets request-scoped value into the `Context` of the c.
func (c *Context) SetValue(key interface{}, val interface{}) {
	c.Context = context.WithValue(c.Context, key, val)
}

// Write implements `http.ResponseWriter#Write()`.
func (c *Context) Write(bs []byte) (int, error) {
	if !c.Written {
		c.WriteHeader(http.StatusOK)
	}
	n, err := c.ResponseWriter.Write(bs)
	c.size += n
	return n, err
}

// WriteHeader implements `http.ResponseWriter#WriteHeader()`.
func (c *Context) WriteHeader(code int) {
	if c.Written {
		c.Air.Logger.Warn("response already committed")
		return
	}
	c.statusCode = code
	c.ResponseWriter.WriteHeader(code)
	c.Written = true
}

// StatusCode returns the HTTP status code.
func (c *Context) StatusCode() int {
	return c.statusCode
}

// Size returns the number of bytes already written into the response HTTP body.
func (c *Context) Size() int {
	return c.size
}

// SetCookie adds a "Set-Cookie" header in HTTP response. The provided cookie
// must have a valid `Name`. Invalid cookies may be silently dropped.
func (c *Context) SetCookie(cookie *http.Cookie) {
	http.SetCookie(c.ResponseWriter, cookie)
}

// Bind binds the request body into provided type i. The default binder does it
// based on "Content-Type" header.
func (c *Context) Bind(i interface{}) error {
	return c.Air.binder.bind(i, c)
}

// Render renders a template with `Data` and `Data["template"]` of the c and
// sends a "text/html" response with `StatusCode` of the c.
func (c *Context) Render() error {
	t, ok := c.Data["template"]
	if !ok || reflect.ValueOf(t).Kind() != reflect.String {
		return errors.New("c.Data[\"template\"] not setted")
	}
	buf := &bytes.Buffer{}
	if err := c.Air.renderer.render(buf, t.(string), c); err != nil {
		return err
	}
	c.Header().Set(HeaderContentType, MIMETextHTML)
	_, err := c.Write(buf.Bytes())
	return err
}

// HTML sends an HTTP response with `StatusCode` and `Data["html"]` of the c.
func (c *Context) HTML() error {
	h, ok := c.Data["html"]
	if !ok || reflect.ValueOf(h).Kind() != reflect.String {
		return errors.New("c.Data[\"html\"] not setted")
	}
	c.Header().Set(HeaderContentType, MIMETextHTML)
	_, err := c.Write([]byte(h.(string)))
	return err
}

// String sends a string response with `StatusCode` and `Data["string"]` of the
// c.
func (c *Context) String() error {
	s, ok := c.Data["string"]
	if !ok || reflect.ValueOf(s).Kind() != reflect.String {
		return errors.New("c.Data[\"string\"] not setted")
	}
	c.Header().Set(HeaderContentType, MIMETextPlain)
	_, err := c.Write([]byte(s.(string)))
	return err
}

// JSON sends a JSON response with `StatusCode` and `Data["json"]` of the c.
func (c *Context) JSON() error {
	j, ok := c.Data["json"]
	if !ok {
		return errors.New("c.Data[\"json\"] not setted")
	}
	bs, err := json.Marshal(j)
	if c.Air.Config.DebugMode {
		bs, err = json.MarshalIndent(j, "", "\t")
	}
	if err != nil {
		return err
	}
	return c.JSONBlob(bs)
}

// JSONBlob sends a JSON blob response with `StatusCode` of the c.
func (c *Context) JSONBlob(bs []byte) error {
	return c.Blob(MIMEApplicationJSON, bs)
}

// JSONP sends a JSONP response with `StatusCode` and `Data["jsonp"]` of the c.
// It uses `Data["callback"]` of the c to construct the JSONP payload.
func (c *Context) JSONP() error {
	j, jok := c.Data["jsonp"]
	if !jok {
		return errors.New("c.Data[\"jsonp\"] not setted")
	}
	bs, err := json.Marshal(j)
	if err != nil {
		return err
	}
	return c.JSONPBlob(bs)
}

// JSONPBlob sends a JSONP blob response with `StatusCode` of the c. It uses
// `Data["callback"]` of the c to construct the JSONP payload.
func (c *Context) JSONPBlob(bs []byte) error {
	cb, cbok := c.Data["callback"]
	if !cbok || reflect.ValueOf(cb).Kind() != reflect.String {
		return errors.New("c.Data[\"callback\"] not setted")
	}
	c.Header().Set(HeaderContentType, MIMEApplicationJavaScript)
	if _, err := c.Write([]byte(cb.(string) + "(")); err != nil {
		return err
	}
	if _, err := c.Write(bs); err != nil {
		return err
	}
	_, err := c.Write([]byte(");"))
	return err
}

// XML sends an XML response with `StatusCode` and `Data["xml"]` of the c.
func (c *Context) XML() error {
	x, ok := c.Data["xml"]
	if !ok {
		return errors.New("c.Data[\"xml\"] not setted")
	}
	bs, err := xml.Marshal(x)
	if c.Air.Config.DebugMode {
		bs, err = xml.MarshalIndent(x, "", "\t")
	}
	if err != nil {
		return err
	}
	return c.XMLBlob(bs)
}

// XMLBlob sends a XML blob response with `StatusCode` of the c.
func (c *Context) XMLBlob(bs []byte) error {
	if _, err := c.Write([]byte(xml.Header)); err != nil {
		return err
	}
	return c.Blob(MIMEApplicationXML, bs)
}

// Blob sends a blob response with `StatusCode` of the c and contentType.
func (c *Context) Blob(contentType string, bs []byte) error {
	c.Header().Set(HeaderContentType, contentType)
	_, err := c.Write(bs)
	return err
}

// Stream sends a streaming response with `StatusCode` of the c and contentType.
func (c *Context) Stream(contentType string, r io.Reader) error {
	c.Header().Set(HeaderContentType, contentType)
	_, err := io.Copy(c, r)
	return err
}

// File sends a response with the content of the file.
func (c *Context) File(file string) error {
	f, err := os.Open(file)
	if err != nil {
		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = filepath.Join(file, "index.html")
		f, err = os.Open(file)
		if err != nil {
			return ErrNotFound
		}
		defer f.Close()
		if fi, err = f.Stat(); err != nil {
			return err
		}
	}
	http.ServeContent(c, c.Request, fi.Name(), fi.ModTime(), f)
	return nil
}

// Attachment sends a response as attachment, prompting client to save the file.
func (c *Context) Attachment(file, name string) error {
	return c.contentDisposition(file, name, "attachment")
}

// Inline sends a response as inline, opening the file in the browser.
func (c *Context) Inline(file, name string) error {
	return c.contentDisposition(file, name, "inline")
}

// contentDisposition sends a response as the dispositionType.
func (c *Context) contentDisposition(file, name, dispositionType string) error {
	c.Header().Set(HeaderContentDisposition,
		fmt.Sprintf("%s; filename=%s", dispositionType, name))
	return c.File(file)
}

// NoContent sends a response with no body.
func (c *Context) NoContent() error { return nil }

// Redirect redirects the request with status code.
func (c *Context) Redirect(code int, url string) error {
	if code < http.StatusMultipleChoices ||
		code > http.StatusTemporaryRedirect {
		return ErrInvalidRedirectCode
	}
	c.Header().Set(HeaderLocation, url)
	c.WriteHeader(code)
	return nil
}

// reset resets all fields in the c.
func (c *Context) reset() {
	c.Context = context.Background()
	c.ResponseWriter = nil
	c.statusCode = 0
	c.size = 0
	c.Request = nil
	c.PristinePath = ""
	c.ParamNames = c.ParamNames[:0]
	c.ParamValues = c.ParamValues[:0]
	for k := range c.Params {
		delete(c.Params, k)
	}
	c.Handler = NotFoundHandler
	for k := range c.Data {
		delete(c.Data, k)
	}
	c.Written = false
}
<|endoftext|>


<commit_before>package agency

import (
	"context"
)

const (
	Priority_Important Priority = 2 - iota
	Priority_Normal
	Priority_Low
)

type (
	Handler func(context.Context, chan<- interface{}) error
	Dropped func()

	Context struct {
		context.Context
		handler  Handler
		dropped  Dropped
		out      chan interface{}
		priority Priority
		backoff  *Backoff
		err      error
	}

	Priority int
)

func WithContext(ctx context.Context, handler Handler, dropped Dropped, priority Priority) (*Context, chan<- interface{}) {
	c := &Context{
		Context:  ctx,
		handler:  handler,
		dropped:  dropped,
		out:      make(chan interface{}),
		priority: priority,
	}
	return c, c.out
}

func (c *Context) clear() {
	c.handler = nil
}

func (c *Context) WithBackoff(b *Backoff) *Context {
	c.backoff = b
	return c
}

func (c *Context) Dropped() {
	if c.dropped != nil {
		c.dropped()
	}
}

func (c *Context) Do() error {
	c.err = c.handler(c.Context, c.out)
	return c.err
}

func (c *Context) Err() error {
	if c.err != nil {
		return c.err
	}
	return c.Context.Err()
}
<commit_msg>return nil if conext.handler is nil<commit_after>package agency

import (
	"context"
)

const (
	Priority_Important Priority = 2 - iota
	Priority_Normal
	Priority_Low
)

type (
	Handler func(context.Context, chan<- interface{}) error
	Dropped func()

	Context struct {
		context.Context
		handler  Handler
		dropped  Dropped
		out      chan interface{}
		priority Priority
		backoff  *Backoff
		err      error
	}

	Priority int
)

func WithContext(ctx context.Context, handler Handler, dropped Dropped, priority Priority) (*Context, chan<- interface{}) {
	c := &Context{
		Context:  ctx,
		handler:  handler,
		dropped:  dropped,
		out:      make(chan interface{}),
		priority: priority,
	}
	return c, c.out
}

func (c *Context) clear() {
	c.handler = nil
}

func (c *Context) WithBackoff(b *Backoff) *Context {
	c.backoff = b
	return c
}

func (c *Context) Dropped() {
	if c.dropped != nil {
		c.dropped()
	}
}

func (c *Context) Do() error {
	if c.handler != nil {
		c.err = c.handler(c.Context, c.out)
	}
	return c.err
}

func (c *Context) Err() error {
	if c.err != nil {
		return c.err
	}
	return c.Context.Err()
}
<|endoftext|>


<commit_before>package goweb

import (
	"encoding/json"
	"html/template"
	"net/http"

	"github.com/dafengge0913/golog"
)

type Context struct {
	log            *golog.Logger
	Req            *http.Request
	params         map[string]string
	responseWriter http.ResponseWriter
	jsonParams     map[string]string
}

func newContext(log *golog.Logger, req *http.Request, rw http.ResponseWriter) *Context {
	return &Context{
		log:            log,
		Req:            req,
		params:         make(map[string]string),
		responseWriter: rw,
	}
}

func (ctx *Context) Params() map[string]string {
	return ctx.params
}

func (ctx *Context) JSONParams() map[string]string {
	if ctx.jsonParams != nil {
		return ctx.jsonParams
	}
	ctx.jsonParams = make(map[string]string)
	var jsonStr string
	for k := range ctx.Params() {
		jsonStr = k
		break
	}

	if len(jsonStr) == 0 {
		return ctx.jsonParams
	}

	if err := json.Unmarshal([]byte(jsonStr), &ctx.jsonParams); err != nil {
		ctx.log.Error("unmarshal json error: %v", err)
		return ctx.jsonParams
	}
	return ctx.jsonParams
}

func (ctx *Context) ResponseTemplate(tpl *template.Template, data interface{}) {
	if err := tpl.Execute(ctx.responseWriter, data); err != nil {
		ctx.log.Error("template execute error: %v", err)
	}
}

func (ctx *Context) ResponseJSON(data interface{}) {
	if jsonBytes, err := json.Marshal(data); err != nil {
		ctx.log.Error("marshal json error: %v", err)
	} else {
		ctx.responseWriter.Write(jsonBytes)
	}
}

func (ctx *Context) SetCookie(name, value string) {
	c := &http.Cookie{
		Name:  name,
		Value: value,
	}
	http.SetCookie(ctx.responseWriter, c)
}

func (ctx *Context) SetRawCookie(c *http.Cookie) {
	http.SetCookie(ctx.responseWriter, c)
}

func (ctx *Context) Cookie(name string) *http.Cookie {
	c, err := ctx.Req.Cookie(name)
	if err != nil {
		return nil
	}
	return c
}

func (ctx *Context) DelCookie(name string) {
	c := ctx.Cookie(name)
	if c != nil {
		c.MaxAge = -1
		http.SetCookie(ctx.responseWriter, c)
	}
}
<commit_msg>Add Param func<commit_after>package goweb

import (
	"encoding/json"
	"html/template"
	"net/http"

	"github.com/dafengge0913/golog"
)

type Context struct {
	log            *golog.Logger
	Req            *http.Request
	params         map[string]string
	responseWriter http.ResponseWriter
	jsonParams     map[string]string
}

func newContext(log *golog.Logger, req *http.Request, rw http.ResponseWriter) *Context {
	return &Context{
		log:            log,
		Req:            req,
		params:         make(map[string]string),
		responseWriter: rw,
	}
}

func (ctx *Context) Param(key string) string {
	return ctx.params[key]
}

func (ctx *Context) Params() map[string]string {
	return ctx.params
}

func (ctx *Context) JSONParams() map[string]string {
	if ctx.jsonParams != nil {
		return ctx.jsonParams
	}
	ctx.jsonParams = make(map[string]string)
	var jsonStr string
	for k := range ctx.Params() {
		jsonStr = k
		break
	}

	if len(jsonStr) == 0 {
		return ctx.jsonParams
	}

	if err := json.Unmarshal([]byte(jsonStr), &ctx.jsonParams); err != nil {
		ctx.log.Error("unmarshal json error: %v", err)
		return ctx.jsonParams
	}
	return ctx.jsonParams
}

func (ctx *Context) ResponseTemplate(tpl *template.Template, data interface{}) {
	if err := tpl.Execute(ctx.responseWriter, data); err != nil {
		ctx.log.Error("template execute error: %v", err)
	}
}

func (ctx *Context) ResponseJSON(data interface{}) {
	if jsonBytes, err := json.Marshal(data); err != nil {
		ctx.log.Error("marshal json error: %v", err)
	} else {
		ctx.responseWriter.Write(jsonBytes)
	}
}

func (ctx *Context) SetCookie(name, value string) {
	c := &http.Cookie{
		Name:  name,
		Value: value,
	}
	http.SetCookie(ctx.responseWriter, c)
}

func (ctx *Context) SetRawCookie(c *http.Cookie) {
	http.SetCookie(ctx.responseWriter, c)
}

func (ctx *Context) Cookie(name string) *http.Cookie {
	c, err := ctx.Req.Cookie(name)
	if err != nil {
		return nil
	}
	return c
}

func (ctx *Context) DelCookie(name string) {
	c := ctx.Cookie(name)
	if c != nil {
		c.MaxAge = -1
		http.SetCookie(ctx.responseWriter, c)
	}
}
<|endoftext|>


<commit_before>package caddy

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"reflect"

	"github.com/mholt/certmagic"
)

// Context is a type which defines the lifetime of modules that
// are loaded and provides access to the parent configuration
// that spawned the modules which are loaded. It should be used
// with care and only wrapped with derivation functions from
// the standard context package if you don't need the Caddy
// specific features. These contexts are cancelled when the
// lifetime of the modules loaded from it are over.
//
// Use NewContext() to get a valid value (but most modules will
// not actually need to do this).
type Context struct {
	context.Context
	moduleInstances map[string][]interface{}
	cfg             *Config
	cleanupFuncs    []func()
}

// NewContext provides a new context derived from the given
// context ctx. Normally, you will not need to call this
// function unless you are loading modules which have a
// different lifespan than the ones for the context the
// module was provisioned with. Be sure to call the cancel
// func when the context is to be cleaned up so that
// modules which are loaded will be properly unloaded.
// See standard library context package's documentation.
func NewContext(ctx Context) (Context, context.CancelFunc) {
	newCtx := Context{moduleInstances: make(map[string][]interface{}), cfg: ctx.cfg}
	c, cancel := context.WithCancel(ctx.Context)
	wrappedCancel := func() {
		cancel()

		for _, f := range ctx.cleanupFuncs {
			f()
		}

		for modName, modInstances := range newCtx.moduleInstances {
			for _, inst := range modInstances {
				if cu, ok := inst.(CleanerUpper); ok {
					err := cu.Cleanup()
					if err != nil {
						log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err)
					}
				}
			}
		}
	}
	newCtx.Context = c
	return newCtx, wrappedCancel
}

// OnCancel executes f when ctx is cancelled.
func (ctx *Context) OnCancel(f func()) {
	ctx.cleanupFuncs = append(ctx.cleanupFuncs, f)
}

// LoadModule decodes rawMsg into a new instance of mod and
// returns the value. If mod.New() does not return a pointer
// value, it is converted to one so that it is unmarshaled
// into the underlying concrete type. If mod.New is nil, an
// error is returned. If the module implements Validator or
// Provisioner interfaces, those methods are invoked to
// ensure the module is fully configured and valid before
// being used.
func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{}, error) {
	modulesMu.Lock()
	mod, ok := modules[name]
	modulesMu.Unlock()
	if !ok {
		return nil, fmt.Errorf("unknown module: %s", name)
	}

	if mod.New == nil {
		return nil, fmt.Errorf("module '%s' has no constructor", mod.Name)
	}

	val := mod.New()

	// value must be a pointer for unmarshaling into concrete type
	if rv := reflect.ValueOf(val); rv.Kind() != reflect.Ptr {
		val = reflect.New(rv.Type()).Elem().Addr().Interface()
	}

	// fill in its config only if there is a config to fill in
	if len(rawMsg) > 0 {
		err := strictUnmarshalJSON(rawMsg, &val)
		if err != nil {
			return nil, fmt.Errorf("decoding module config: %s: %v", mod.Name, err)
		}
	}

	if prov, ok := val.(Provisioner); ok {
		err := prov.Provision(ctx)
		if err != nil {
			return nil, fmt.Errorf("provision %s: %v", mod.Name, err)
		}
	}

	if validator, ok := val.(Validator); ok {
		err := validator.Validate()
		if err != nil {
			if cleanerUpper, ok := val.(CleanerUpper); ok {
				err2 := cleanerUpper.Cleanup()
				if err2 != nil {
					err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
				}
				return nil, fmt.Errorf("%s: invalid configuration: %v", mod.Name, err)
			}
		}
	}

	ctx.moduleInstances[name] = append(ctx.moduleInstances[name], val)

	return val, nil
}

// LoadModuleInline loads a module from a JSON raw message which decodes
// to a map[string]interface{}, where one of the keys is moduleNameKey
// and the corresponding value is the module name as a string, which
// can be found in the given scope.
//
// This allows modules to be decoded into their concrete types and
// used when their names cannot be the unique key in a map, such as
// when there are multiple instances in the map or it appears in an
// array (where there are no custom keys). In other words, the key
// containing the module name is treated special/separate from all
// the other keys.
func (ctx Context) LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
	moduleName, raw, err := getModuleNameInline(moduleNameKey, raw)
	if err != nil {
		return nil, err
	}

	val, err := ctx.LoadModule(moduleScope+"."+moduleName, raw)
	if err != nil {
		return nil, fmt.Errorf("loading module '%s': %v", moduleName, err)
	}

	return val, nil
}

// App returns the configured app named name. If no app with
// that name is currently configured, a new empty one will be
// instantiated. (The app module must still be registered.)
func (ctx Context) App(name string) (interface{}, error) {
	if app, ok := ctx.cfg.apps[name]; ok {
		return app, nil
	}
	modVal, err := ctx.LoadModule(name, nil)
	if err != nil {
		return nil, fmt.Errorf("instantiating new module %s: %v", name, err)
	}
	ctx.cfg.apps[name] = modVal.(App)
	return modVal, nil
}

// Storage returns the configured Caddy storage implementation.
func (ctx Context) Storage() certmagic.Storage {
	return ctx.cfg.storage
}
<commit_msg>Fix panics by disallowing explicitly-defined null modules in config<commit_after>package caddy

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"reflect"

	"github.com/mholt/certmagic"
)

// Context is a type which defines the lifetime of modules that
// are loaded and provides access to the parent configuration
// that spawned the modules which are loaded. It should be used
// with care and only wrapped with derivation functions from
// the standard context package if you don't need the Caddy
// specific features. These contexts are cancelled when the
// lifetime of the modules loaded from it are over.
//
// Use NewContext() to get a valid value (but most modules will
// not actually need to do this).
type Context struct {
	context.Context
	moduleInstances map[string][]interface{}
	cfg             *Config
	cleanupFuncs    []func()
}

// NewContext provides a new context derived from the given
// context ctx. Normally, you will not need to call this
// function unless you are loading modules which have a
// different lifespan than the ones for the context the
// module was provisioned with. Be sure to call the cancel
// func when the context is to be cleaned up so that
// modules which are loaded will be properly unloaded.
// See standard library context package's documentation.
func NewContext(ctx Context) (Context, context.CancelFunc) {
	newCtx := Context{moduleInstances: make(map[string][]interface{}), cfg: ctx.cfg}
	c, cancel := context.WithCancel(ctx.Context)
	wrappedCancel := func() {
		cancel()

		for _, f := range ctx.cleanupFuncs {
			f()
		}

		for modName, modInstances := range newCtx.moduleInstances {
			for _, inst := range modInstances {
				if cu, ok := inst.(CleanerUpper); ok {
					err := cu.Cleanup()
					if err != nil {
						log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err)
					}
				}
			}
		}
	}
	newCtx.Context = c
	return newCtx, wrappedCancel
}

// OnCancel executes f when ctx is cancelled.
func (ctx *Context) OnCancel(f func()) {
	ctx.cleanupFuncs = append(ctx.cleanupFuncs, f)
}

// LoadModule decodes rawMsg into a new instance of mod and
// returns the value. If mod.New() does not return a pointer
// value, it is converted to one so that it is unmarshaled
// into the underlying concrete type. If mod.New is nil, an
// error is returned. If the module implements Validator or
// Provisioner interfaces, those methods are invoked to
// ensure the module is fully configured and valid before
// being used.
func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{}, error) {
	modulesMu.Lock()
	mod, ok := modules[name]
	modulesMu.Unlock()
	if !ok {
		return nil, fmt.Errorf("unknown module: %s", name)
	}

	if mod.New == nil {
		return nil, fmt.Errorf("module '%s' has no constructor", mod.Name)
	}

	val := mod.New()

	// value must be a pointer for unmarshaling into concrete type
	if rv := reflect.ValueOf(val); rv.Kind() != reflect.Ptr {
		val = reflect.New(rv.Type()).Elem().Addr().Interface()
	}

	// fill in its config only if there is a config to fill in
	if len(rawMsg) > 0 {
		err := strictUnmarshalJSON(rawMsg, &val)
		if err != nil {
			return nil, fmt.Errorf("decoding module config: %s: %v", mod.Name, err)
		}
	}

	if val == nil {
		// returned module values are almost always type-asserted
		// before being used, so a nil value would panic; and there
		// is no good reason to explicitly declare null modules in
		// a config; it might be because the user is trying to
		// achieve a result they aren't expecting, which is a smell
		return nil, fmt.Errorf("module value cannot be null")
	}

	if prov, ok := val.(Provisioner); ok {
		err := prov.Provision(ctx)
		if err != nil {
			return nil, fmt.Errorf("provision %s: %v", mod.Name, err)
		}
	}

	if validator, ok := val.(Validator); ok {
		err := validator.Validate()
		if err != nil {
			if cleanerUpper, ok := val.(CleanerUpper); ok {
				err2 := cleanerUpper.Cleanup()
				if err2 != nil {
					err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
				}
				return nil, fmt.Errorf("%s: invalid configuration: %v", mod.Name, err)
			}
		}
	}

	ctx.moduleInstances[name] = append(ctx.moduleInstances[name], val)

	return val, nil
}

// LoadModuleInline loads a module from a JSON raw message which decodes
// to a map[string]interface{}, where one of the keys is moduleNameKey
// and the corresponding value is the module name as a string, which
// can be found in the given scope.
//
// This allows modules to be decoded into their concrete types and
// used when their names cannot be the unique key in a map, such as
// when there are multiple instances in the map or it appears in an
// array (where there are no custom keys). In other words, the key
// containing the module name is treated special/separate from all
// the other keys.
func (ctx Context) LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
	moduleName, raw, err := getModuleNameInline(moduleNameKey, raw)
	if err != nil {
		return nil, err
	}

	val, err := ctx.LoadModule(moduleScope+"."+moduleName, raw)
	if err != nil {
		return nil, fmt.Errorf("loading module '%s': %v", moduleName, err)
	}

	return val, nil
}

// App returns the configured app named name. If no app with
// that name is currently configured, a new empty one will be
// instantiated. (The app module must still be registered.)
func (ctx Context) App(name string) (interface{}, error) {
	if app, ok := ctx.cfg.apps[name]; ok {
		return app, nil
	}
	modVal, err := ctx.LoadModule(name, nil)
	if err != nil {
		return nil, fmt.Errorf("instantiating new module %s: %v", name, err)
	}
	ctx.cfg.apps[name] = modVal.(App)
	return modVal, nil
}

// Storage returns the configured Caddy storage implementation.
func (ctx Context) Storage() certmagic.Storage {
	return ctx.cfg.storage
}
<|endoftext|>