<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 fs_test

import (
	"github.com/jacobsa/comeback/fs"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"io"
	"io/ioutil"
	"os"
	"path"
	"testing"
)

func TestCreate(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// CreateFile
////////////////////////////////////////////////////////////////////////

type CreateFileTest struct {
	fileSystemTest

	path  string
	perms os.FileMode

	w   io.WriteCloser
	err error
}

func init() { RegisterTestSuite(&CreateFileTest{}) }

func (t *CreateFileTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0644
}

func (t *CreateFileTest) call() {
	t.w, t.err = t.fileSystem.CreateFile(t.path, t.perms)
}

func (t *CreateFileTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateFileTest) NonExistentParent() {
	t.path = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateFileTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.path = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateFileTest) FileAlreadyExists() {
	// Create
	err := ioutil.WriteFile(t.path, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateFileTest) CreatesCorrectEntry() {
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0674  // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)
	defer t.w.Close()

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeFile, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq(0674, entry.Permissions)
}

func (t *CreateFileTest) SavesDataToCorrectPlace() {
	// Call
	t.call()
	AssertEq(nil, t.err)

	// Write
	expected := []byte("taco")
	_, err := t.w.Write(expected)
	AssertEq(nil, err)

	// Close
	AssertEq(nil, t.w.Close())

	// Read
	data, err := ioutil.ReadFile(t.path)
	AssertEq(nil, err)

	ExpectThat(data, DeepEquals(expected))
}

////////////////////////////////////////////////////////////////////////
// Mkdir
////////////////////////////////////////////////////////////////////////

type MkdirTest struct {
	fileSystemTest

	path  string
	perms os.FileMode

	err error
}

func init() { RegisterTestSuite(&MkdirTest{}) }

func (t *MkdirTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0644
}

func (t *MkdirTest) call() {
	t.err = t.fileSystem.Mkdir(t.path, t.perms)
}

func (t *MkdirTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *MkdirTest) NonExistentParent() {
	t.path = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *MkdirTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.path = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *MkdirTest) FileAlreadyExistsWithSameName() {
	// Create
	err := ioutil.WriteFile(t.path, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *MkdirTest) CreatesCorrectEntry() {
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0674  // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeDirectory, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq(0674, entry.Permissions)
}

////////////////////////////////////////////////////////////////////////
// CreateNamedPipe
////////////////////////////////////////////////////////////////////////

type CreateNamedPipeTest struct {
	fileSystemTest

	path  string
	perms os.FileMode

	err error
}

func init() { RegisterTestSuite(&CreateNamedPipeTest{}) }

func (t *CreateNamedPipeTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0644
}

func (t *CreateNamedPipeTest) call() {
	t.err = t.fileSystem.CreateNamedPipe(t.path, t.perms)
}

func (t *CreateNamedPipeTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateNamedPipeTest) NonExistentParent() {
	t.path = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateNamedPipeTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.path = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateNamedPipeTest) FileAlreadyExistsWithSameName() {
	// Create
	err := ioutil.WriteFile(t.path, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateNamedPipeTest) CreatesCorrectEntry() {
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0674  // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeNamedPipe, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq(0674, entry.Permissions)
}

////////////////////////////////////////////////////////////////////////
// CreateSymlink
////////////////////////////////////////////////////////////////////////

type CreateSymlinkTest struct {
	fileSystemTest

	target  string
	source  string
	perms os.FileMode

	err error
}

func init() { RegisterTestSuite(&CreateSymlinkTest{}) }

func (t *CreateSymlinkTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.source = path.Join(t.baseDir, "taco")
	t.target = "/foo/bar"
	t.perms = 0644
}

func (t *CreateSymlinkTest) call() {
	t.err = t.fileSystem.CreateSymlink(t.target, t.source, t.perms)
}

func (t *CreateSymlinkTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateSymlinkTest) NonExistentParent() {
	t.source = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateSymlinkTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.source = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateSymlinkTest) FileAlreadyExistsWithSameName() {
	// Create
	err := ioutil.WriteFile(t.source, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateSymlinkTest) CreatesCorrectEntry() {
	t.source = path.Join(t.baseDir, "taco")
	t.target = "/burrito"
	t.perms = 0674  // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeSymlink, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq("/burrito", entry.Target)
	ExpectEq(0674, entry.Permissions)
}

////////////////////////////////////////////////////////////////////////
// CreateHardLink
////////////////////////////////////////////////////////////////////////

type CreateHardLinkTest struct {
	fileSystemTest

	target  string
	source  string

	err error
}

func init() { RegisterTestSuite(&CreateHardLinkTest{}) }

func (t *CreateHardLinkTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.source = path.Join(t.baseDir, "taco")
	t.target = path.Join(t.baseDir, "burrito")
}

func (t *CreateHardLinkTest) call() {
	t.err = t.fileSystem.CreateHardLink(t.target, t.source)
}

func (t *CreateHardLinkTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateHardLinkTest) NonExistentParent() {
	t.source = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateHardLinkTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.source = path.Join(dirpath, "taco")

	// Create target
	err := ioutil.WriteFile(t.target, []byte{}, 0644)
	AssertEq(nil, err)

	// Parent
	err = os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateHardLinkTest) TargetDoesntExist() {
	t.source = path.Join(t.baseDir, "taco")
	t.target = "/burrito"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("burrito")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
	ExpectThat(t.err, Error(HasSubstr("file")))
}

func (t *CreateHardLinkTest) FileAlreadyExistsWithSameName() {
	// Create source
	err := ioutil.WriteFile(t.source, []byte{}, 0644)
	AssertEq(nil, err)

	// Create target
	err = ioutil.WriteFile(t.target, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateHardLinkTest) CreatesCorrectEntry() {
	t.source = path.Join(t.baseDir, "taco")
	t.target = path.Join(t.baseDir, "burrito")

	// Create target
	err := ioutil.WriteFile(t.target, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any(), Any()))

	entry0 := entries[0]
	ExpectEq(fs.TypeFile, entry0.Type)
	ExpectEq("burrito", entry0.Name)

	entry1 := entries[1]
	ExpectEq(fs.TypeFile, entry1.Type)
	ExpectEq("taco", entry1.Name)

	AssertNe(0, entry0.ContainingDevice)
	ExpectEq(entry1.ContainingDevice, entry0.ContainingDevice)

	AssertNe(0, entry0.Inode)
	ExpectEq(entry1.Inode, entry0.Inode)
}
<commit_msg>Fixed formatting.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 fs_test

import (
	"github.com/jacobsa/comeback/fs"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"io"
	"io/ioutil"
	"os"
	"path"
	"testing"
)

func TestCreate(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// CreateFile
////////////////////////////////////////////////////////////////////////

type CreateFileTest struct {
	fileSystemTest

	path  string
	perms os.FileMode

	w   io.WriteCloser
	err error
}

func init() { RegisterTestSuite(&CreateFileTest{}) }

func (t *CreateFileTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0644
}

func (t *CreateFileTest) call() {
	t.w, t.err = t.fileSystem.CreateFile(t.path, t.perms)
}

func (t *CreateFileTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateFileTest) NonExistentParent() {
	t.path = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateFileTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.path = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateFileTest) FileAlreadyExists() {
	// Create
	err := ioutil.WriteFile(t.path, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateFileTest) CreatesCorrectEntry() {
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0674 // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)
	defer t.w.Close()

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeFile, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq(0674, entry.Permissions)
}

func (t *CreateFileTest) SavesDataToCorrectPlace() {
	// Call
	t.call()
	AssertEq(nil, t.err)

	// Write
	expected := []byte("taco")
	_, err := t.w.Write(expected)
	AssertEq(nil, err)

	// Close
	AssertEq(nil, t.w.Close())

	// Read
	data, err := ioutil.ReadFile(t.path)
	AssertEq(nil, err)

	ExpectThat(data, DeepEquals(expected))
}

////////////////////////////////////////////////////////////////////////
// Mkdir
////////////////////////////////////////////////////////////////////////

type MkdirTest struct {
	fileSystemTest

	path  string
	perms os.FileMode

	err error
}

func init() { RegisterTestSuite(&MkdirTest{}) }

func (t *MkdirTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0644
}

func (t *MkdirTest) call() {
	t.err = t.fileSystem.Mkdir(t.path, t.perms)
}

func (t *MkdirTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *MkdirTest) NonExistentParent() {
	t.path = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *MkdirTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.path = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *MkdirTest) FileAlreadyExistsWithSameName() {
	// Create
	err := ioutil.WriteFile(t.path, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *MkdirTest) CreatesCorrectEntry() {
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0674 // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeDirectory, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq(0674, entry.Permissions)
}

////////////////////////////////////////////////////////////////////////
// CreateNamedPipe
////////////////////////////////////////////////////////////////////////

type CreateNamedPipeTest struct {
	fileSystemTest

	path  string
	perms os.FileMode

	err error
}

func init() { RegisterTestSuite(&CreateNamedPipeTest{}) }

func (t *CreateNamedPipeTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0644
}

func (t *CreateNamedPipeTest) call() {
	t.err = t.fileSystem.CreateNamedPipe(t.path, t.perms)
}

func (t *CreateNamedPipeTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateNamedPipeTest) NonExistentParent() {
	t.path = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateNamedPipeTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.path = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateNamedPipeTest) FileAlreadyExistsWithSameName() {
	// Create
	err := ioutil.WriteFile(t.path, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateNamedPipeTest) CreatesCorrectEntry() {
	t.path = path.Join(t.baseDir, "taco")
	t.perms = 0674 // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeNamedPipe, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq(0674, entry.Permissions)
}

////////////////////////////////////////////////////////////////////////
// CreateSymlink
////////////////////////////////////////////////////////////////////////

type CreateSymlinkTest struct {
	fileSystemTest

	target string
	source string
	perms  os.FileMode

	err error
}

func init() { RegisterTestSuite(&CreateSymlinkTest{}) }

func (t *CreateSymlinkTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.source = path.Join(t.baseDir, "taco")
	t.target = "/foo/bar"
	t.perms = 0644
}

func (t *CreateSymlinkTest) call() {
	t.err = t.fileSystem.CreateSymlink(t.target, t.source, t.perms)
}

func (t *CreateSymlinkTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateSymlinkTest) NonExistentParent() {
	t.source = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateSymlinkTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.source = path.Join(dirpath, "taco")

	// Parent
	err := os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateSymlinkTest) FileAlreadyExistsWithSameName() {
	// Create
	err := ioutil.WriteFile(t.source, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateSymlinkTest) CreatesCorrectEntry() {
	t.source = path.Join(t.baseDir, "taco")
	t.target = "/burrito"
	t.perms = 0674 // Conflicts with default umask

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any()))
	entry := entries[0]

	ExpectEq(fs.TypeSymlink, entry.Type)
	ExpectEq("taco", entry.Name)
	ExpectEq("/burrito", entry.Target)
	ExpectEq(0674, entry.Permissions)
}

////////////////////////////////////////////////////////////////////////
// CreateHardLink
////////////////////////////////////////////////////////////////////////

type CreateHardLinkTest struct {
	fileSystemTest

	target string
	source string

	err error
}

func init() { RegisterTestSuite(&CreateHardLinkTest{}) }

func (t *CreateHardLinkTest) SetUp(i *TestInfo) {
	// Common
	t.fileSystemTest.SetUp(i)

	// Set up defaults.
	t.source = path.Join(t.baseDir, "taco")
	t.target = path.Join(t.baseDir, "burrito")
}

func (t *CreateHardLinkTest) call() {
	t.err = t.fileSystem.CreateHardLink(t.target, t.source)
}

func (t *CreateHardLinkTest) list() []*fs.DirectoryEntry {
	entries, err := t.fileSystem.ReadDir(t.baseDir)
	AssertEq(nil, err)
	return entries
}

func (t *CreateHardLinkTest) NonExistentParent() {
	t.source = "/foo/bar/baz/qux"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("qux")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
}

func (t *CreateHardLinkTest) NoPermissionsForParent() {
	dirpath := path.Join(t.baseDir, "foo")
	t.source = path.Join(dirpath, "taco")

	// Create target
	err := ioutil.WriteFile(t.target, []byte{}, 0644)
	AssertEq(nil, err)

	// Parent
	err = os.Mkdir(dirpath, 0100)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("foo")))
	ExpectThat(t.err, Error(HasSubstr("permission denied")))
}

func (t *CreateHardLinkTest) TargetDoesntExist() {
	t.source = path.Join(t.baseDir, "taco")
	t.target = "/burrito"

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("burrito")))
	ExpectThat(t.err, Error(HasSubstr("no such")))
	ExpectThat(t.err, Error(HasSubstr("file")))
}

func (t *CreateHardLinkTest) FileAlreadyExistsWithSameName() {
	// Create source
	err := ioutil.WriteFile(t.source, []byte{}, 0644)
	AssertEq(nil, err)

	// Create target
	err = ioutil.WriteFile(t.target, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()

	ExpectThat(t.err, Error(HasSubstr("file exists")))
}

func (t *CreateHardLinkTest) CreatesCorrectEntry() {
	t.source = path.Join(t.baseDir, "taco")
	t.target = path.Join(t.baseDir, "burrito")

	// Create target
	err := ioutil.WriteFile(t.target, []byte{}, 0644)
	AssertEq(nil, err)

	// Call
	t.call()
	AssertEq(nil, t.err)

	// List
	entries := t.list()

	AssertThat(entries, ElementsAre(Any(), Any()))

	entry0 := entries[0]
	ExpectEq(fs.TypeFile, entry0.Type)
	ExpectEq("burrito", entry0.Name)

	entry1 := entries[1]
	ExpectEq(fs.TypeFile, entry1.Type)
	ExpectEq("taco", entry1.Name)

	AssertNe(0, entry0.ContainingDevice)
	ExpectEq(entry1.ContainingDevice, entry0.ContainingDevice)

	AssertNe(0, entry0.Inode)
	ExpectEq(entry1.Inode, entry0.Inode)
}
<|endoftext|>


<commit_before>package core

import (
	"archive/tar"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/fsouza/go-dockerclient"
)

type Docker struct {
	endPoint string
	env      *Environment
	client   *docker.Client
}

func NewDocker(endPoint string, env *Environment) (*Docker, error) {
	Debug("Connected to docker", "end-point", endPoint)

	var c *docker.Client
	var err error
	if env != nil && env.CertPath != "" {
		c, err = docker.NewTLSClient(
			endPoint,
			env.CertPath+"/cert.pem",
			env.CertPath+"/key.pem",
			env.CertPath+"/ca.pem",
		)
	} else {
		c, err = docker.NewClient(endPoint)
	}

	if err != nil {
		return nil, err
	}

	return &Docker{client: c, endPoint: endPoint, env: env}, nil
}

func (d *Docker) Deploy(p *Project, rev Revision, dockerfile *Dockerfile, output io.Writer, force bool) error {
	Debug("Deploying dockerfile", "project", p, "revision", rev, "end-point", d.endPoint)
	if err := d.Clean(p); err != nil {
		return err
	}

	if err := d.BuildImage(p, rev, dockerfile, output); err != nil {
		return err
	}

	return d.Run(p, rev)
}

func (d *Docker) Clean(p *Project) error {
	if err := d.cleanContainers(p); err != nil {
		return err
	}

	if err := d.cleanImages(p); err != nil {
		return err
	}

	return nil
}

func (d *Docker) cleanContainers(p *Project) error {
	l, err := d.ListContainers(p)
	if err != nil {
		return err
	}

	Debug("Cleaning containers", "project", p, "count", len(l), "end-point", d.endPoint)
	for _, c := range l {
		if !c.IsRunning() {
			continue
		}

		Debug("Stoping container and image", "project", p, "container", c.GetShortId(), "end-point", d.endPoint)
		if err := d.killContainer(c); err != nil {
			return err
		}

		Debug("Removing container", "project", p, "container", c.GetShortId(), "end-point", d.endPoint)
		if err := d.removeContainer(c); err != nil {
			return err
		}
	}

	return nil
}

func (d *Docker) killContainer(c *Container) error {
	kopts := docker.KillContainerOptions{ID: c.ID}
	if err := d.client.KillContainer(kopts); err != nil {
		return err
	}

	return nil
}

func (d *Docker) removeContainer(c *Container) error {
	ropts := docker.RemoveContainerOptions{ID: c.ID}
	if err := d.client.RemoveContainer(ropts); err != nil {
		return err
	}

	return nil
}

func (d *Docker) cleanImages(p *Project) error {
	l, err := d.ListImages(p)
	if err != nil {
		return err
	}

	keep := p.History
	if keep < 0 {
		keep = 0
	}

	count := len(l)
	if count < keep {
		return nil
	}

	Debug("Removing old images", "project", p, "count", count-keep, "end-point", d.endPoint)
	for _, i := range l[:count-keep] {
		Debug("Removing image", "project", p, "image", i.ID, "end-point", d.endPoint)
		if err := d.removeImage(i); err != nil {
			return err
		}
	}

	return nil
}

func (d *Docker) removeImage(i *Image) error {
	if err := d.client.RemoveImage(i.ID); err != nil {
		return err
	}

	return nil
}

func (d *Docker) ListContainers(p *Project) ([]*Container, error) {
	Debug("Retrieving current containers", "project", p, "end-point", d.endPoint)

	l, err := d.client.ListContainers(docker.ListContainersOptions{
		All: true,
	})

	if err != nil {
		return nil, err
	}

	r := make([]*Container, 0)
	for _, c := range l {
		container := &Container{
			Image:          ImageId(c.Image),
			APIContainers:  c,
			DockerEndPoint: d.endPoint,
		}

		if container.BelongsTo(p) {
			r = append(r, container)
		}
	}

	sort.Sort(ContainersByCreated(r))

	return r, nil
}

func (d *Docker) ListImages(p *Project) ([]*Image, error) {
	Debug("Retrieving current containers", "project", p, "end-point", d.endPoint)

	l, err := d.client.ListImages(docker.ListImagesOptions{All: true})

	if err != nil {
		return nil, err
	}

	r := make([]*Image, 0)
	for _, i := range l {
		image := &Image{
			APIImages:      i,
			DockerEndPoint: d.endPoint,
		}

		if image.BelongsTo(p) {
			r = append(r, image)
		}
	}

	sort.Sort(ImagesByCreated(r))

	return r, nil
}

func (d *Docker) BuildImage(
	p *Project, rev Revision, dockerfile *Dockerfile, output io.Writer,
) error {
	Debug("Building image", "project", p, "revision", rev, "end-point", d.endPoint)

	input := bytes.NewBuffer(nil)
	if err := d.buildTar(p, dockerfile.Get(), input); err != nil {
		return err
	}

	image := d.getImageName(p, rev)
	opts := docker.BuildImageOptions{
		Name:           string(image),
		NoCache:        p.NoCache,
		RmTmpContainer: p.NoCache,
		InputStream:    input,
		OutputStream:   output,
	}

	return d.client.BuildImage(opts)
}

func (d *Docker) Run(p *Project, rev Revision) error {
	Debug("Creating container from image", "project", p, "revision", rev, "end-point", d.endPoint)
	c, err := d.createContainer(p, d.getImageName(p, rev))
	if err != nil {
		return err
	}

	Info("Running new container",
		"project", p,
		"revision", rev.GetShort(),
		"container", c.GetShortId(),
		"end-point", d.endPoint,
	)

	if err := d.startContainer(p, c); err != nil {
		return err
	}

	return d.restartLinkedContainers(p)
}

func (d *Docker) getImageName(p *Project, rev Revision) ImageId {
	c := rev.String()
	if p.UseShortRevisions {
		c = rev.GetShort()
	}

	return ImageId(fmt.Sprintf("%s:%s", p.Name, c))
}

func (d *Docker) createContainer(p *Project, image ImageId) (*Container, error) {
	c, err := d.client.CreateContainer(docker.CreateContainerOptions{
		Name: p.Name,
		Config: &docker.Config{
			Image: string(image),
		},
	})

	if err != nil {
		return nil, err
	}

	return &Container{Image: image, APIContainers: docker.APIContainers{ID: c.ID}}, nil
}

func (d *Docker) startContainer(p *Project, c *Container) error {
	ports, err := d.formatPorts(p.Ports)
	if err != nil {
		return err
	}

	restartPolicy, err := d.formatRestartPolicy(p.Restart)
	if err != nil {
		return err
	}

	return d.client.StartContainer(c.ID, &docker.HostConfig{
		PortBindings:  ports,
		RestartPolicy: restartPolicy,
		Links:         d.formatLinks(p.Links),
	})
}

func (d *Docker) formatLinks(links map[string]*Link) []string {
	r := make([]string, 0)
	for _, link := range links {
		r = append(r, link.String())
	}

	return r
}

func (d *Docker) formatRestartPolicy(restart string) (policy docker.RestartPolicy, err error) {
	values := strings.SplitN(restart, ":", 2)
	if values[0] == "no" || restart == "" {
		policy = docker.NeverRestart()
		return
	}

	if values[0] == "always" {
		policy = docker.AlwaysRestart()
		return
	}

	if values[0] == "on-failure" {
		var maxRetry int
		maxRetry, err = strconv.Atoi(values[1])
		if err != nil {
			return
		}

		policy = docker.RestartOnFailure(maxRetry)
		return
	}

	err = errors.New(fmt.Sprintf("Malformed restart policy %q", restart))
	return
}

func (d *Docker) formatPorts(ports []string) (map[docker.Port][]docker.PortBinding, error) {
	r := make(map[docker.Port][]docker.PortBinding, 0)
	for _, p := range ports {
		guest, host, err := d.formatPort(p)
		if err != nil {
			return nil, err
		}

		if _, ok := r[guest]; !ok {
			r[guest] = make([]docker.PortBinding, 0)
		}

		r[guest] = append(r[guest], host)
	}

	return r, nil
}

// <host_interface>:<host_port>:<container_port>/<proto>
func (d *Docker) formatPort(port string) (guest docker.Port, host docker.PortBinding, err error) {
	p1 := strings.SplitN(port, "@", 2)
	if len(p1) == 2 && d.env != nil && d.env.Name != p1[1] {
		return
	}

	p2 := strings.SplitN(p1[0], "/", 2)
	p3 := strings.SplitN(p2[0], ":", 3)

	if len(p2) != 2 || len(p3) != 3 {
		err = errors.New(fmt.Sprintf("Malformed port %q", port))
		return
	}

	guest = docker.Port(fmt.Sprintf("%s/%s", p3[2], p2[1]))
	host = docker.PortBinding{
		HostIP:   p3[0],
		HostPort: p3[1],
	}

	return
}

func (d *Docker) buildTar(p *Project, dockerfile []byte, buf *bytes.Buffer) error {
	t := time.Now()

	tr := tar.NewWriter(buf)
	tr.WriteHeader(&tar.Header{
		Name:       "Dockerfile",
		Size:       int64(len(dockerfile)),
		ModTime:    t,
		AccessTime: t,
		ChangeTime: t,
	})

	if _, err := tr.Write(dockerfile); err != nil {
		return err
	}

	for _, file := range p.Files {
		if err := d.addFileToTar(file, tr); err != nil {
			return err
		}
	}

	tr.Close()
	return nil
}

func (d *Docker) addFileToTar(file string, tr *tar.Writer) error {
	content, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	fInfo, err := os.Lstat(file)
	if err != nil {
		return err
	}

	h, err := tar.FileInfoHeader(fInfo, "")
	h.Name = path.Base(file)
	if err != nil {
		return err
	}

	if err := tr.WriteHeader(h); err != nil {
		return err
	}

	if _, err := tr.Write(content); err != nil {
		return err
	}

	return nil
}

func (d *Docker) restartLinkedContainers(p *Project) error {
	failed := false
	for _, linked := range p.LinkedBy {
		list, err := d.ListContainers(linked)
		if err != nil {
			failed = true
			Error(err.Error(), "project", p)
			continue
		}

		for _, lc := range list {
			Info("Restarting linked container", "project", linked, "container", lc.GetShortId())
			if err := d.restartContainer(linked, lc); err != nil {
				failed = true
				Error("Unable to restart container", "project", linked, "container", lc.GetShortId())
			}
		}
	}

	if failed {
		return errors.New("Unable to restart one or more containers")
	}

	return nil
}

func (d *Docker) restartContainer(p *Project, c *Container) error {
	if !c.IsRunning() {
		return nil
	}

	if err := d.killContainer(c); err != nil {
		return err
	}

	if err := d.startContainer(p, c); err != nil {
		return err
	}

	return nil
}
<commit_msg>Deploy: don't clean containers until the new image is built.<commit_after>package core

import (
	"archive/tar"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/fsouza/go-dockerclient"
)

type Docker struct {
	endPoint string
	env      *Environment
	client   *docker.Client
}

func NewDocker(endPoint string, env *Environment) (*Docker, error) {
	Debug("Connected to docker", "end-point", endPoint)

	var c *docker.Client
	var err error
	if env != nil && env.CertPath != "" {
		c, err = docker.NewTLSClient(
			endPoint,
			env.CertPath+"/cert.pem",
			env.CertPath+"/key.pem",
			env.CertPath+"/ca.pem",
		)
	} else {
		c, err = docker.NewClient(endPoint)
	}

	if err != nil {
		return nil, err
	}

	return &Docker{client: c, endPoint: endPoint, env: env}, nil
}

func (d *Docker) Deploy(p *Project, rev Revision, dockerfile *Dockerfile, output io.Writer, force bool) error {
	Debug("Deploying dockerfile", "project", p, "revision", rev, "end-point", d.endPoint)

	if err := d.cleanImages(p); err != nil {
		return err
	}

	if err := d.BuildImage(p, rev, dockerfile, output); err != nil {
		return err
	}

	if err := d.cleanContainers(p); err != nil {
		return err
	}

	return d.Run(p, rev)
}

func (d *Docker) Clean(p *Project) error {
	if err := d.cleanContainers(p); err != nil {
		return err
	}

	if err := d.cleanImages(p); err != nil {
		return err
	}

	return nil
}

func (d *Docker) cleanContainers(p *Project) error {
	l, err := d.ListContainers(p)
	if err != nil {
		return err
	}

	Debug("Cleaning containers", "project", p, "count", len(l), "end-point", d.endPoint)
	for _, c := range l {
		if !c.IsRunning() {
			continue
		}

		Debug("Stoping container and image", "project", p, "container", c.GetShortId(), "end-point", d.endPoint)
		if err := d.killContainer(c); err != nil {
			return err
		}

		Debug("Removing container", "project", p, "container", c.GetShortId(), "end-point", d.endPoint)
		if err := d.removeContainer(c); err != nil {
			return err
		}
	}

	return nil
}

func (d *Docker) killContainer(c *Container) error {
	kopts := docker.KillContainerOptions{ID: c.ID}
	if err := d.client.KillContainer(kopts); err != nil {
		return err
	}

	return nil
}

func (d *Docker) removeContainer(c *Container) error {
	ropts := docker.RemoveContainerOptions{ID: c.ID}
	if err := d.client.RemoveContainer(ropts); err != nil {
		return err
	}

	return nil
}

func (d *Docker) cleanImages(p *Project) error {
	l, err := d.ListImages(p)
	if err != nil {
		return err
	}

	keep := p.History
	if keep < 0 {
		keep = 0
	}

	count := len(l)
	if count < keep {
		return nil
	}

	Debug("Removing old images", "project", p, "count", count-keep, "end-point", d.endPoint)
	for _, i := range l[:count-keep] {
		Debug("Removing image", "project", p, "image", i.ID, "end-point", d.endPoint)
		if err := d.removeImage(i); err != nil {
			return err
		}
	}

	return nil
}

func (d *Docker) removeImage(i *Image) error {
	if err := d.client.RemoveImage(i.ID); err != nil {
		return err
	}

	return nil
}

func (d *Docker) ListContainers(p *Project) ([]*Container, error) {
	Debug("Retrieving current containers", "project", p, "end-point", d.endPoint)

	l, err := d.client.ListContainers(docker.ListContainersOptions{
		All: true,
	})

	if err != nil {
		return nil, err
	}

	r := make([]*Container, 0)
	for _, c := range l {
		container := &Container{
			Image:          ImageId(c.Image),
			APIContainers:  c,
			DockerEndPoint: d.endPoint,
		}

		if container.BelongsTo(p) {
			r = append(r, container)
		}
	}

	sort.Sort(ContainersByCreated(r))

	return r, nil
}

func (d *Docker) ListImages(p *Project) ([]*Image, error) {
	Debug("Retrieving current containers", "project", p, "end-point", d.endPoint)

	l, err := d.client.ListImages(docker.ListImagesOptions{All: true})

	if err != nil {
		return nil, err
	}

	r := make([]*Image, 0)
	for _, i := range l {
		image := &Image{
			APIImages:      i,
			DockerEndPoint: d.endPoint,
		}

		if image.BelongsTo(p) {
			r = append(r, image)
		}
	}

	sort.Sort(ImagesByCreated(r))

	return r, nil
}

func (d *Docker) BuildImage(
	p *Project, rev Revision, dockerfile *Dockerfile, output io.Writer,
) error {
	Debug("Building image", "project", p, "revision", rev, "end-point", d.endPoint)

	input := bytes.NewBuffer(nil)
	if err := d.buildTar(p, dockerfile.Get(), input); err != nil {
		return err
	}

	image := d.getImageName(p, rev)
	opts := docker.BuildImageOptions{
		Name:           string(image),
		NoCache:        p.NoCache,
		RmTmpContainer: p.NoCache,
		InputStream:    input,
		OutputStream:   output,
	}

	return d.client.BuildImage(opts)
}

func (d *Docker) Run(p *Project, rev Revision) error {
	Debug("Creating container from image", "project", p, "revision", rev, "end-point", d.endPoint)
	c, err := d.createContainer(p, d.getImageName(p, rev))
	if err != nil {
		return err
	}

	Info("Running new container",
		"project", p,
		"revision", rev.GetShort(),
		"container", c.GetShortId(),
		"end-point", d.endPoint,
	)

	if err := d.startContainer(p, c); err != nil {
		return err
	}

	return d.restartLinkedContainers(p)
}

func (d *Docker) getImageName(p *Project, rev Revision) ImageId {
	c := rev.String()
	if p.UseShortRevisions {
		c = rev.GetShort()
	}

	return ImageId(fmt.Sprintf("%s:%s", p.Name, c))
}

func (d *Docker) createContainer(p *Project, image ImageId) (*Container, error) {
	c, err := d.client.CreateContainer(docker.CreateContainerOptions{
		Name: p.Name,
		Config: &docker.Config{
			Image: string(image),
		},
	})

	if err != nil {
		return nil, err
	}

	return &Container{Image: image, APIContainers: docker.APIContainers{ID: c.ID}}, nil
}

func (d *Docker) startContainer(p *Project, c *Container) error {
	ports, err := d.formatPorts(p.Ports)
	if err != nil {
		return err
	}

	restartPolicy, err := d.formatRestartPolicy(p.Restart)
	if err != nil {
		return err
	}

	return d.client.StartContainer(c.ID, &docker.HostConfig{
		PortBindings:  ports,
		RestartPolicy: restartPolicy,
		Links:         d.formatLinks(p.Links),
	})
}

func (d *Docker) formatLinks(links map[string]*Link) []string {
	r := make([]string, 0)
	for _, link := range links {
		r = append(r, link.String())
	}

	return r
}

func (d *Docker) formatRestartPolicy(restart string) (policy docker.RestartPolicy, err error) {
	values := strings.SplitN(restart, ":", 2)
	if values[0] == "no" || restart == "" {
		policy = docker.NeverRestart()
		return
	}

	if values[0] == "always" {
		policy = docker.AlwaysRestart()
		return
	}

	if values[0] == "on-failure" {
		var maxRetry int
		maxRetry, err = strconv.Atoi(values[1])
		if err != nil {
			return
		}

		policy = docker.RestartOnFailure(maxRetry)
		return
	}

	err = errors.New(fmt.Sprintf("Malformed restart policy %q", restart))
	return
}

func (d *Docker) formatPorts(ports []string) (map[docker.Port][]docker.PortBinding, error) {
	r := make(map[docker.Port][]docker.PortBinding, 0)
	for _, p := range ports {
		guest, host, err := d.formatPort(p)
		if err != nil {
			return nil, err
		}

		if _, ok := r[guest]; !ok {
			r[guest] = make([]docker.PortBinding, 0)
		}

		r[guest] = append(r[guest], host)
	}

	return r, nil
}

// <host_interface>:<host_port>:<container_port>/<proto>
func (d *Docker) formatPort(port string) (guest docker.Port, host docker.PortBinding, err error) {
	p1 := strings.SplitN(port, "@", 2)
	if len(p1) == 2 && d.env != nil && d.env.Name != p1[1] {
		return
	}

	p2 := strings.SplitN(p1[0], "/", 2)
	p3 := strings.SplitN(p2[0], ":", 3)

	if len(p2) != 2 || len(p3) != 3 {
		err = errors.New(fmt.Sprintf("Malformed port %q", port))
		return
	}

	guest = docker.Port(fmt.Sprintf("%s/%s", p3[2], p2[1]))
	host = docker.PortBinding{
		HostIP:   p3[0],
		HostPort: p3[1],
	}

	return
}

func (d *Docker) buildTar(p *Project, dockerfile []byte, buf *bytes.Buffer) error {
	t := time.Now()

	tr := tar.NewWriter(buf)
	tr.WriteHeader(&tar.Header{
		Name:       "Dockerfile",
		Size:       int64(len(dockerfile)),
		ModTime:    t,
		AccessTime: t,
		ChangeTime: t,
	})

	if _, err := tr.Write(dockerfile); err != nil {
		return err
	}

	for _, file := range p.Files {
		if err := d.addFileToTar(file, tr); err != nil {
			return err
		}
	}

	tr.Close()
	return nil
}

func (d *Docker) addFileToTar(file string, tr *tar.Writer) error {
	content, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	fInfo, err := os.Lstat(file)
	if err != nil {
		return err
	}

	h, err := tar.FileInfoHeader(fInfo, "")
	h.Name = path.Base(file)
	if err != nil {
		return err
	}

	if err := tr.WriteHeader(h); err != nil {
		return err
	}

	if _, err := tr.Write(content); err != nil {
		return err
	}

	return nil
}

func (d *Docker) restartLinkedContainers(p *Project) error {
	failed := false
	for _, linked := range p.LinkedBy {
		list, err := d.ListContainers(linked)
		if err != nil {
			failed = true
			Error(err.Error(), "project", p)
			continue
		}

		for _, lc := range list {
			Info("Restarting linked container", "project", linked, "container", lc.GetShortId())
			if err := d.restartContainer(linked, lc); err != nil {
				failed = true
				Error("Unable to restart container", "project", linked, "container", lc.GetShortId())
			}
		}
	}

	if failed {
		return errors.New("Unable to restart one or more containers")
	}

	return nil
}

func (d *Docker) restartContainer(p *Project, c *Container) error {
	if !c.IsRunning() {
		return nil
	}

	if err := d.killContainer(c); err != nil {
		return err
	}

	if err := d.startContainer(p, c); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package members

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/logger"
	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/models"
	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/retry"
	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/utils"
	"github.com/spf13/cobra"
)

func init() {
	note := "Note: The 'version' value from the command input will be ignored and the latest version value will be retrieved from the API instead"
	addCmd.SetHelpTemplate(fmt.Sprintf("%s\nOperation:\n  %s %s\n\n%s\n", addCmd.UsageTemplate(), addMembersCommand.Method, addMembersCommand.Path, note))
	utils.AddFileFlagIfUpsert(addCmd.Flags(), addMembersCommand.Method)
	membersCmd.AddCommand(addCmd)
}

type groupMembers struct {
	Version int `json:"version"`
}

type addGroupMembersBody struct {
	MemberIds []string `json:"memberIds"`
	Version   int      `json:"version"`
}

var (
	addMembersCommand = models.HandWrittenCommand{
		Path:   "/api/v2/groups/{groupId}/members",
		Method: http.MethodPost,
	}
	getMembersCommand = models.HandWrittenCommand{
		Path:   "/api/v2/groups/{groupId}/members",
		Method: http.MethodGet,
	}
)

var addCmd = &cobra.Command{
	Use:   "add [groupId]",
	Short: "Add members",
	Long:  `Add members`,
	Args:  utils.DetermineArgs([]string{"groupId"}),

	Run: func(cmd *cobra.Command, args []string) {
		groupId, args := args[0], args[1:]
		path := strings.Replace(addMembersCommand.Path, "{groupId}", fmt.Sprintf("%v", groupId), -1)

		currentVersion := getGroupVersion(path)

		inputData := utils.ResolveInputData(cmd)
		body := &addGroupMembersBody{}
		err := json.Unmarshal([]byte(inputData), body)
		if err != nil {
			logger.Fatal(err)
		}

		body.Version = currentVersion
		bodyString, _ := json.Marshal(body)

		retryFunc := retry.RetryWithData(path, string(bodyString), CommandService.Post)
		// TODO read from config file
		retryConfig := &retry.RetryConfiguration{
			RetryWaitMin: 5 * time.Second,
			RetryWaitMax: 60 * time.Second,
			RetryMax:     20,
		}
		results, err := retryFunc(retryConfig)
		if err != nil {
			logger.Fatal(err)
		}

		utils.Render(results)
	},
}

func getGroupVersion(path string) int {
	retryFunc := CommandService.DetermineAction(getMembersCommand.Method, "get", path, getMembersCommand.Path)
	retryConfig := &retry.RetryConfiguration{
		RetryWaitMin: 5 * time.Second,
		RetryWaitMax: 60 * time.Second,
		RetryMax:     20,
	}
	results, err := retryFunc(retryConfig)
	if err != nil {
		logger.Fatal(err)
	}

	groupMember := make([]groupMembers, 0)
	err = json.Unmarshal([]byte(results), &groupMember)
	if err != nil {
		logger.Fatal(err)
	}

	return groupMember[0].Version
}
<commit_msg>Fixing bug in custom add groups members command due to using wrong API<commit_after>package members

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/logger"
	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/models"
	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/retry"
	"github.com/mypurecloud/platform-client-sdk-cli/build/gc/utils"
	"github.com/spf13/cobra"
)

func init() {
	note := "Note: The 'version' value from the command input will be ignored and the latest version value will be retrieved from the API instead"
	addCmd.SetHelpTemplate(fmt.Sprintf("%s\nOperation:\n  %s %s\n\n%s\n", addCmd.UsageTemplate(), addMembersCommand.Method, addMembersCommand.Path, note))
	utils.AddFileFlagIfUpsert(addCmd.Flags(), addMembersCommand.Method)
	membersCmd.AddCommand(addCmd)
}

type group struct {
	Version int `json:"version"`
}

type addGroupMembersBody struct {
	MemberIds []string `json:"memberIds"`
	Version   int      `json:"version"`
}

var (
	addMembersCommand = models.HandWrittenCommand{
		Path:   "/api/v2/groups/{groupId}/members",
		Method: http.MethodPost,
	}
	getMembersCommand = models.HandWrittenCommand{
		Path:   "/api/v2/groups/{groupId}",
		Method: http.MethodGet,
	}
)

var addCmd = &cobra.Command{
	Use:   "add [groupId]",
	Short: "Add members",
	Long:  `Add members`,
	Args:  utils.DetermineArgs([]string{"groupId"}),

	Run: func(cmd *cobra.Command, args []string) {
		groupId, args := args[0], args[1:]
		path := strings.Replace(getMembersCommand.Path, "{groupId}", fmt.Sprintf("%v", groupId), -1)

		currentVersion := getGroupVersion(path)

		inputData := utils.ResolveInputData(cmd)
		body := &addGroupMembersBody{}
		err := json.Unmarshal([]byte(inputData), body)
		if err != nil {
			logger.Fatal(err)
		}

		body.Version = currentVersion
		bodyString, _ := json.Marshal(body)

		path = strings.Replace(addMembersCommand.Path, "{groupId}", fmt.Sprintf("%v", groupId), -1)
		retryFunc := retry.RetryWithData(path, string(bodyString), CommandService.Post)
		// TODO read from config file
		retryConfig := &retry.RetryConfiguration{
			RetryWaitMin: 5 * time.Second,
			RetryWaitMax: 60 * time.Second,
			RetryMax:     20,
		}
		results, err := retryFunc(retryConfig)
		if err != nil {
			logger.Fatal(err)
		}

		utils.Render(results)
	},
}

func getGroupVersion(path string) int {
	retryFunc := CommandService.DetermineAction(getMembersCommand.Method, "get", path, getMembersCommand.Path)
	retryConfig := &retry.RetryConfiguration{
		RetryWaitMin: 5 * time.Second,
		RetryWaitMax: 60 * time.Second,
		RetryMax:     20,
	}
	results, err := retryFunc(retryConfig)
	if err != nil {
		logger.Fatal(err)
	}

	groupResult := group{}
	err = json.Unmarshal([]byte(results), &groupResult)
	if err != nil {
		logger.Fatal(err)
	}

	return groupResult.Version
}
<|endoftext|>


<commit_before>package goCompany

import "testing"

func TestGetCompanyInfo(t *testing.T) {
	ok := 5990130
	result, _ := GetCompanyInfo("7736002426")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetEmployees(t *testing.T) {
	ok := 149735376
	result, _ := GetEmployees("32357")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetIndivEntrep(t *testing.T) {
	ok := 1
	result, _ := GetIndivEntrep("7528374")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetCompFounder(t *testing.T) {
	ok := 5545071
	result, _ := GetCompFounder("2191023")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetDepComp(t *testing.T) {
	ok := 1425227
	result, _ := GetDepComp("7030")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetPerson(t *testing.T) {
	ok := "АЛЕКСЕЙ"
	result, _ := GetPerson("2191023")
	if result.FirstName != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetPositions(t *testing.T) {
	ok := 147863776
	result, _ := GetPositions("2191023")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetIDData(t *testing.T) {
	ok := 7030
	result, _ := GetIDData("7030")
	if result.ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetFounders(t *testing.T) {
	ok := 253175464
	result, _ := GetFounders("7030")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestResponse(t *testing.T) {
	ok := []byte("[ ]")
	result := response("https://ru.rus.company/интеграция/компании/")
	if result[0] != ok[0] {
		t.Fatalf("Want %v, but got %v", result[0], ok[0])
	}
}
<commit_msg>tests err<commit_after>package goCompany

import "testing"

func TestGetCompanyInfo(t *testing.T) {
	ok := 5990130
	result, err := GetCompanyInfo("7736002426")
	if err != nil {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetEmployees(t *testing.T) {
	ok := 149735376
	result, err := GetEmployees("32357")
	if err != nil {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetIndivEntrep(t *testing.T) {
	ok := 1
	result, _ := GetIndivEntrep("7528374")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetCompFounder(t *testing.T) {
	ok := 5545071
	result, _ := GetCompFounder("2191023")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetDepComp(t *testing.T) {
	ok := 1425227
	result, _ := GetDepComp("7030")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetPerson(t *testing.T) {
	ok := "АЛЕКСЕЙ"
	result, _ := GetPerson("2191023")
	if result.FirstName != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetPositions(t *testing.T) {
	ok := 147863776
	result, _ := GetPositions("2191023")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetIDData(t *testing.T) {
	ok := 7030
	result, _ := GetIDData("7030")
	if result.ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestGetFounders(t *testing.T) {
	ok := 253175464
	result, _ := GetFounders("7030")
	if result[0].ID != ok {
		t.Fatalf("Want %v, but got %v", result, ok)
	}
}

func TestResponse(t *testing.T) {
	ok := []byte("[ ]")
	result := response("https://ru.rus.company/интеграция/компании/")
	if result[0] != ok[0] {
		t.Fatalf("Want %v, but got %v", result[0], ok[0])
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Numgrad Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

// Package typecheck is a Numengrad type checker.
package typecheck

import (
	"bytes"
	"fmt"
	"go/constant"
	gotoken "go/token"
	"math/big"

	"numgrad.io/lang/expr"
	"numgrad.io/lang/stmt"
	"numgrad.io/lang/tipe"
	"numgrad.io/lang/token"
)

type Checker struct {
	// TODO: we could put these on our AST. Should we?
	Types  map[expr.Expr]tipe.Type
	Defs   map[*expr.Ident]*Obj
	Values map[expr.Expr]constant.Value

	// TODO NamedInfo map[*tipe.Named]NamedInfo

	cur *Scope
}

// TODO type NamedInfo struct {
//	Obj *Obj
//	Methods []*Obj
//}

func New() *Checker {
	return &Checker{
		Types:  make(map[expr.Expr]tipe.Type),
		Defs:   make(map[*expr.Ident]*Obj),
		Values: make(map[expr.Expr]constant.Value),
		cur:    &Scope{Objs: make(map[string]*Obj)},
	}
}

type partialMode int

const (
	modeInvalid partialMode = iota
	modeVoid
	modeConst
	modeVar
	modeBuiltin
)

type partial struct {
	mode partialMode
	typ  tipe.Type
	val  constant.Value
	expr expr.Expr
}

func (c *Checker) errorf(format string, args ...interface{}) {
	fmt.Printf("typecheck error: %s\n", fmt.Sprintf(format, args...))
}

func defaultType(t tipe.Type) tipe.Type {
	b, ok := t.(tipe.Basic)
	if !ok {
		return t
	}
	switch b {
	case tipe.UntypedBool:
		return tipe.Bool
	case tipe.UntypedInteger:
		return tipe.Integer
	case tipe.UntypedFloat:
		return tipe.Float
	}
	return t
}

func (c *Checker) stmt(s stmt.Stmt) {
	switch s := s.(type) {
	case *stmt.Assign:
		if len(s.Left) != len(s.Right) {
			panic("TODO artity mismatch, i.e. x, y := f()")
		}
		var partials []partial
		for _, rhs := range s.Right {
			partials = append(partials, c.expr(rhs))
		}
		if s.Decl {
			for i, lhs := range s.Left {
				p := partials[i]
				if isUntyped(p.typ) {
					c.constrainUntyped(&p, defaultType(p.typ))
				}
				obj := &Obj{Type: partials[i].typ}
				c.Defs[lhs.(*expr.Ident)] = obj
				c.cur.Objs[lhs.(*expr.Ident).Name] = obj
			}
		} else {
			for i, lhs := range s.Left {
				p := partials[i]
				lhsP := c.expr(lhs)
				if isUntyped(p.typ) {
					c.constrainUntyped(&p, c.Types[lhsP.expr])
				}
			}
		}

	default:
		panic(fmt.Sprintf("typecheck: unknown stmt %T", s))
	}
}

func (c *Checker) expr(e expr.Expr) (p partial) {
	// TODO more mode adjustment
	p = c.exprPartial(e)
	if p.mode == modeConst {
		c.Values[p.expr] = p.val
		c.Types[p.expr] = p.typ
	}
	return p
}

func (c *Checker) exprPartial(e expr.Expr) (p partial) {
	fmt.Printf("exprPartial(%s)\n", e.Sexp())
	p.expr = e
	switch e := e.(type) {
	case *expr.Ident:
		obj := c.cur.LookupRec(e.Name)
		if obj == nil {
			p.mode = modeInvalid
			c.errorf("undeclared identifier: %s", e.Name)
			return p
		}
		c.Defs[e] = obj // TODO Defs is more than definitions? rename?
		p.mode = modeVar
		return p
	case *expr.BasicLiteral:
		p.mode = modeConst
		// TODO: use constant.Value in BasicLiteral directly.
		switch v := e.Value.(type) {
		case *big.Int:
			p.typ = tipe.UntypedInteger
			p.val = constant.MakeFromLiteral(v.String(), gotoken.INT, 0)
		case *big.Float:
			p.typ = tipe.UntypedFloat
			p.val = constant.MakeFromLiteral(v.String(), gotoken.FLOAT, 0)
		}
		return p
	case *expr.Binary:
		left := c.expr(e.Left)
		right := c.expr(e.Right)
		c.constrainUntyped(&left, right.typ)
		c.constrainUntyped(&right, left.typ)
		if left.mode == modeInvalid {
			return left
		}
		if right.mode == modeInvalid {
			return right
		}
		left.expr = e
		// TODO check for division by zero
		// TODO check for comparison
		if left.mode == modeConst && right.mode == modeConst {
			left.val = constant.BinaryOp(left.val, convGoOp(e.Op), right.val)
			// TODO check rounding
		}

		return left
	default:
		panic(fmt.Sprintf("expr TODO: %T", e))
	}
}

func convGoOp(op token.Token) gotoken.Token {
	switch op {
	case token.Add:
		return gotoken.ADD
	case token.Sub:
		return gotoken.SUB
	case token.Mul:
		return gotoken.MUL
	case token.Div:
		return gotoken.QUO // TODO: QUO_ASSIGN for int div
	case token.Rem:
		return gotoken.REM
	case token.Pow:
		panic("TODO token.Pow")
		return gotoken.REM
	default:
		panic(fmt.Sprintf("typecheck: bad op: %s", op))
	}
}

func (c *Checker) constrainUntyped(p *partial, t tipe.Type) {
	if p.mode == modeInvalid || isTyped(p.typ) || t == tipe.Invalid {
		return
	}

	// catch invalid constraints
	if isUntyped(t) {
		switch {
		case t == tipe.UntypedFloat && p.typ == tipe.UntypedInteger:
			// promote untyped int to float
		case t == tipe.UntypedComplex && (p.typ == tipe.UntypedInteger || p.typ == tipe.UntypedFloat):
			// promote untyped int or float to complex
		case t != p.typ:
			panic("cannot convert untyped")
			// TODO c.errorf("cannot convert %s to %s", x, typ)
		}
	} else {
		switch t := Underlying(t).(type) {
		case tipe.Basic:
			switch p.mode {
			case modeConst:
				p.val = round(p.val, t)
				if p.val == nil {
					panic("cannot convert")
					// TODO c.errorf
				}
			case modeVar:
				panic("TODO coerce var to basic")
			}
		}
	}

	p.typ = t
	c.constrainExprType(p.expr, p.typ)
}

// constrainExprType descends an expression constraining the type.
func (c *Checker) constrainExprType(e expr.Expr, t tipe.Type) {
	oldt := c.Types[e]
	if oldt == t {
		return
	}
	c.Types[e] = t

	switch e := e.(type) {
	case *expr.Bad, *expr.FuncLiteral: // TODO etc
		return
	case *expr.Binary:
		if c.Values[e] != nil {
			break
		}
		switch e.Op {
		case token.Equal, token.NotEqual,
			token.Less, token.LessEqual,
			token.Greater, token.GreaterEqual:
			// comparisons generate their own bool type
			return
		}
		c.constrainExprType(e.Left, t)
		c.constrainExprType(e.Right, t)
	}

	c.Types[e] = t
}

func round(v constant.Value, t tipe.Basic) constant.Value {
	switch v.Kind() {
	case constant.Unknown:
		return v
	case constant.Bool:
		if t == tipe.Bool || t == tipe.UntypedBool {
			return v
		} else {
			return nil
		}
	case constant.Int:
		switch t {
		case tipe.Integer, tipe.UntypedInteger:
			return v
		case tipe.Float, tipe.UntypedFloat, tipe.UntypedComplex:
			return v
		case tipe.Int64:
			if _, ok := constant.Int64Val(v); ok {
				return v
			} else {
				return nil
			}
		}
	case constant.Float:
		switch t {
		case tipe.Float, tipe.UntypedFloat, tipe.UntypedComplex:
			return v
		case tipe.Float32:
			r, _ := constant.Float32Val(v)
			return constant.MakeFloat64(float64(r))
		case tipe.Float64:
			r, _ := constant.Float64Val(v)
			return constant.MakeFloat64(float64(r))
		}
	}
	// TODO many more comparisons
	return nil
}

func (c *Checker) Add(s stmt.Stmt) {
	c.stmt(s)
}

func (c *Checker) String() string {
	buf := new(bytes.Buffer)
	buf.WriteString("typecheck.Checker{\n")
	buf.WriteString("\tTypes: map[expr.Expr]tipe.Type{\n")
	for k, v := range c.Types {
		fmt.Fprintf(buf, "\t\t(%p)%s: %s\n", k, k.Sexp(), v.Sexp())
	}
	buf.WriteString("\t},\n")
	buf.WriteString("\tDefs: map[*expr.Ident]*Obj{\n")
	for k, v := range c.Defs {
		t := "niltype"
		if v.Type != nil {
			t = v.Type.Sexp()
		}
		fmt.Fprintf(buf, "\t\t(%p)%s: (%p).Type:%s\n", k, k.Sexp(), v, t)
	}
	buf.WriteString("\t},\n")
	buf.WriteString("\tValues : map[expr.Expr]constant.Value{\n")
	for k, v := range c.Values {
		fmt.Fprintf(buf, "\t\t(%p)%s: %s\n", k, k.Sexp(), v)
	}
	buf.WriteString("\t},\n")
	buf.WriteString("}")
	return buf.String()
}

type Scope struct {
	Parent *Scope
	Objs   map[string]*Obj
}

func (s *Scope) LookupRec(name string) *Obj {
	for s != nil {
		if o := s.Objs[name]; o != nil {
			return o
		}
		s = s.Parent
	}
	return nil
}

// An Obj represents a declared constant, type, variable, or function.
type Obj struct {
	Type tipe.Type
	Used bool
}

func Underlying(t tipe.Type) tipe.Type {
	if n, ok := t.(*tipe.Named); ok {
		return n.Underlying
	}
	return t
}

func isTyped(t tipe.Type) bool {
	return Underlying(t) != tipe.Invalid && !isUntyped(t)
}

func isUntyped(t tipe.Type) bool {
	switch Underlying(t) {
	case tipe.UntypedBool, tipe.UntypedInteger, tipe.UntypedFloat, tipe.UntypedComplex:
		return true
	}
	return false
}
<commit_msg>lang/typecheck: propagate ident types to decls<commit_after>// Copyright 2015 The Numgrad Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

// Package typecheck is a Numengrad type checker.
package typecheck

import (
	"bytes"
	"fmt"
	"go/constant"
	gotoken "go/token"
	"math/big"

	"numgrad.io/lang/expr"
	"numgrad.io/lang/stmt"
	"numgrad.io/lang/tipe"
	"numgrad.io/lang/token"
)

type Checker struct {
	// TODO: we could put these on our AST. Should we?
	Types  map[expr.Expr]tipe.Type
	Defs   map[*expr.Ident]*Obj
	Values map[expr.Expr]constant.Value

	// TODO NamedInfo map[*tipe.Named]NamedInfo

	cur *Scope
}

// TODO type NamedInfo struct {
//	Obj *Obj
//	Methods []*Obj
//}

func New() *Checker {
	return &Checker{
		Types:  make(map[expr.Expr]tipe.Type),
		Defs:   make(map[*expr.Ident]*Obj),
		Values: make(map[expr.Expr]constant.Value),
		cur:    &Scope{Objs: make(map[string]*Obj)},
	}
}

type partialMode int

const (
	modeInvalid partialMode = iota
	modeVoid
	modeConst
	modeVar
	modeBuiltin
)

type partial struct {
	mode partialMode
	typ  tipe.Type
	val  constant.Value
	expr expr.Expr
}

func (c *Checker) errorf(format string, args ...interface{}) {
	fmt.Printf("typecheck error: %s\n", fmt.Sprintf(format, args...))
}

func defaultType(t tipe.Type) tipe.Type {
	b, ok := t.(tipe.Basic)
	if !ok {
		return t
	}
	switch b {
	case tipe.UntypedBool:
		return tipe.Bool
	case tipe.UntypedInteger:
		return tipe.Integer
	case tipe.UntypedFloat:
		return tipe.Float
	}
	return t
}

func (c *Checker) stmt(s stmt.Stmt) {
	switch s := s.(type) {
	case *stmt.Assign:
		if len(s.Left) != len(s.Right) {
			panic("TODO artity mismatch, i.e. x, y := f()")
		}
		var partials []partial
		for _, rhs := range s.Right {
			partials = append(partials, c.expr(rhs))
		}
		if s.Decl {
			for i, lhs := range s.Left {
				p := partials[i]
				if isUntyped(p.typ) {
					c.constrainUntyped(&p, defaultType(p.typ))
				}
				obj := &Obj{Type: p.typ}
				c.Defs[lhs.(*expr.Ident)] = obj
				c.cur.Objs[lhs.(*expr.Ident).Name] = obj
			}
		} else {
			for i, lhs := range s.Left {
				p := partials[i]
				lhsP := c.expr(lhs)
				if isUntyped(p.typ) {
					c.constrainUntyped(&p, lhsP.typ)
				}
			}
		}

	default:
		panic(fmt.Sprintf("typecheck: unknown stmt %T", s))
	}
}

func (c *Checker) expr(e expr.Expr) (p partial) {
	// TODO more mode adjustment
	p = c.exprPartial(e)
	if p.mode == modeConst {
		c.Values[p.expr] = p.val
		c.Types[p.expr] = p.typ
	}
	return p
}

func (c *Checker) exprPartial(e expr.Expr) (p partial) {
	fmt.Printf("exprPartial(%s)\n", e.Sexp())
	p.expr = e
	switch e := e.(type) {
	case *expr.Ident:
		obj := c.cur.LookupRec(e.Name)
		if obj == nil {
			p.mode = modeInvalid
			c.errorf("undeclared identifier: %s", e.Name)
			return p
		}
		c.Defs[e] = obj // TODO Defs is more than definitions? rename?
		p.mode = modeVar
		p.typ = obj.Type
		return p
	case *expr.BasicLiteral:
		p.mode = modeConst
		// TODO: use constant.Value in BasicLiteral directly.
		switch v := e.Value.(type) {
		case *big.Int:
			p.typ = tipe.UntypedInteger
			p.val = constant.MakeFromLiteral(v.String(), gotoken.INT, 0)
		case *big.Float:
			p.typ = tipe.UntypedFloat
			p.val = constant.MakeFromLiteral(v.String(), gotoken.FLOAT, 0)
		}
		return p
	case *expr.Binary:
		left := c.expr(e.Left)
		right := c.expr(e.Right)
		c.constrainUntyped(&left, right.typ)
		c.constrainUntyped(&right, left.typ)
		if left.mode == modeInvalid {
			return left
		}
		if right.mode == modeInvalid {
			return right
		}
		left.expr = e
		// TODO check for division by zero
		// TODO check for comparison
		if left.mode == modeConst && right.mode == modeConst {
			left.val = constant.BinaryOp(left.val, convGoOp(e.Op), right.val)
			// TODO check rounding
		}

		return left
	default:
		panic(fmt.Sprintf("expr TODO: %T", e))
	}
}

func convGoOp(op token.Token) gotoken.Token {
	switch op {
	case token.Add:
		return gotoken.ADD
	case token.Sub:
		return gotoken.SUB
	case token.Mul:
		return gotoken.MUL
	case token.Div:
		return gotoken.QUO // TODO: QUO_ASSIGN for int div
	case token.Rem:
		return gotoken.REM
	case token.Pow:
		panic("TODO token.Pow")
		return gotoken.REM
	default:
		panic(fmt.Sprintf("typecheck: bad op: %s", op))
	}
}

func (c *Checker) constrainUntyped(p *partial, t tipe.Type) {
	if p.mode == modeInvalid || isTyped(p.typ) || t == tipe.Invalid {
		return
	}

	// catch invalid constraints
	if isUntyped(t) {
		switch {
		case t == tipe.UntypedFloat && p.typ == tipe.UntypedInteger:
			// promote untyped int to float
		case t == tipe.UntypedComplex && (p.typ == tipe.UntypedInteger || p.typ == tipe.UntypedFloat):
			// promote untyped int or float to complex
		case t != p.typ:
			panic("cannot convert untyped")
			// TODO c.errorf("cannot convert %s to %s", x, typ)
		}
	} else {
		switch t := Underlying(t).(type) {
		case tipe.Basic:
			switch p.mode {
			case modeConst:
				p.val = round(p.val, t)
				if p.val == nil {
					panic("cannot convert")
					// TODO c.errorf
				}
			case modeVar:
				panic("TODO coerce var to basic")
			}
		}
	}

	p.typ = t
	c.constrainExprType(p.expr, p.typ)
}

// constrainExprType descends an expression constraining the type.
func (c *Checker) constrainExprType(e expr.Expr, t tipe.Type) {
	oldt := c.Types[e]
	if oldt == t {
		return
	}
	c.Types[e] = t

	switch e := e.(type) {
	case *expr.Bad, *expr.FuncLiteral: // TODO etc
		return
	case *expr.Binary:
		if c.Values[e] != nil {
			break
		}
		switch e.Op {
		case token.Equal, token.NotEqual,
			token.Less, token.LessEqual,
			token.Greater, token.GreaterEqual:
			// comparisons generate their own bool type
			return
		}
		c.constrainExprType(e.Left, t)
		c.constrainExprType(e.Right, t)
	}

	c.Types[e] = t
}

func round(v constant.Value, t tipe.Basic) constant.Value {
	switch v.Kind() {
	case constant.Unknown:
		return v
	case constant.Bool:
		if t == tipe.Bool || t == tipe.UntypedBool {
			return v
		} else {
			return nil
		}
	case constant.Int:
		switch t {
		case tipe.Integer, tipe.UntypedInteger:
			return v
		case tipe.Float, tipe.UntypedFloat, tipe.UntypedComplex:
			return v
		case tipe.Int64:
			if _, ok := constant.Int64Val(v); ok {
				return v
			} else {
				return nil
			}
		}
	case constant.Float:
		switch t {
		case tipe.Float, tipe.UntypedFloat, tipe.UntypedComplex:
			return v
		case tipe.Float32:
			r, _ := constant.Float32Val(v)
			return constant.MakeFloat64(float64(r))
		case tipe.Float64:
			r, _ := constant.Float64Val(v)
			return constant.MakeFloat64(float64(r))
		}
	}
	// TODO many more comparisons
	return nil
}

func (c *Checker) Add(s stmt.Stmt) {
	c.stmt(s)
}

func (c *Checker) String() string {
	buf := new(bytes.Buffer)
	buf.WriteString("typecheck.Checker{\n")
	buf.WriteString("\tTypes: map[expr.Expr]tipe.Type{\n")
	for k, v := range c.Types {
		fmt.Fprintf(buf, "\t\t(%p)%s: %s\n", k, k.Sexp(), v.Sexp())
	}
	buf.WriteString("\t},\n")
	buf.WriteString("\tDefs: map[*expr.Ident]*Obj{\n")
	for k, v := range c.Defs {
		t := "niltype"
		if v.Type != nil {
			t = v.Type.Sexp()
		}
		fmt.Fprintf(buf, "\t\t(%p)%s: (%p).Type:%s\n", k, k.Sexp(), v, t)
	}
	buf.WriteString("\t},\n")
	buf.WriteString("\tValues : map[expr.Expr]constant.Value{\n")
	for k, v := range c.Values {
		fmt.Fprintf(buf, "\t\t(%p)%s: %s\n", k, k.Sexp(), v)
	}
	buf.WriteString("\t},\n")
	buf.WriteString("}")
	return buf.String()
}

type Scope struct {
	Parent *Scope
	Objs   map[string]*Obj
}

func (s *Scope) LookupRec(name string) *Obj {
	for s != nil {
		if o := s.Objs[name]; o != nil {
			return o
		}
		s = s.Parent
	}
	return nil
}

// An Obj represents a declared constant, type, variable, or function.
type Obj struct {
	Type tipe.Type
	Used bool
}

func Underlying(t tipe.Type) tipe.Type {
	if n, ok := t.(*tipe.Named); ok {
		return n.Underlying
	}
	return t
}

func isTyped(t tipe.Type) bool {
	return Underlying(t) != tipe.Invalid && !isUntyped(t)
}

func isUntyped(t tipe.Type) bool {
	switch Underlying(t) {
	case tipe.UntypedBool, tipe.UntypedInteger, tipe.UntypedFloat, tipe.UntypedComplex:
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var testData uint32

func checkSymbols(t *testing.T, nmoutput []byte) {
	switch runtime.GOOS {
	case "linux", "darwin", "solaris":
		t.Skip("skipping test; see http://golang.org/issue/7829")
	}
	var checkSymbolsFound, testDataFound bool
	scanner := bufio.NewScanner(bytes.NewBuffer(nmoutput))
	for scanner.Scan() {
		f := strings.Fields(scanner.Text())
		if len(f) < 3 {
			t.Error("nm must have at least 3 columns")
			continue
		}
		switch f[2] {
		case "cmd/nm.checkSymbols":
			checkSymbolsFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", checkSymbols) {
				t.Errorf("nm shows wrong address %v for checkSymbols (%p)", addr, checkSymbols)
			}
		case "cmd/nm.testData":
			testDataFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", &testData) {
				t.Errorf("nm shows wrong address %v for testData (%p)", addr, &testData)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		t.Errorf("error while reading symbols: %v", err)
		return
	}
	if !checkSymbolsFound {
		t.Error("nm shows no checkSymbols symbol")
	}
	if !testDataFound {
		t.Error("nm shows no testData symbol")
	}
}

func TestNM(t *testing.T) {
	out, err := exec.Command("go", "build", "-o", "testnm.exe", "cmd/nm").CombinedOutput()
	if err != nil {
		t.Fatalf("go build -o testnm.exe cmd/nm: %v\n%s", err, string(out))
	}
	defer os.Remove("testnm.exe")

	testfiles := []string{
		"elf/testdata/gcc-386-freebsd-exec",
		"elf/testdata/gcc-amd64-linux-exec",
		"macho/testdata/gcc-386-darwin-exec",
		"macho/testdata/gcc-amd64-darwin-exec",
		"pe/testdata/gcc-amd64-mingw-exec",
		"pe/testdata/gcc-386-mingw-exec",
		"plan9obj/testdata/amd64-plan9-exec",
		"plan9obj/testdata/386-plan9-exec",
	}
	for _, f := range testfiles {
		exepath := filepath.Join(runtime.GOROOT(), "src", "pkg", "debug", f)
		cmd := exec.Command("./testnm.exe", exepath)
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
		}
	}

	cmd := exec.Command("./testnm.exe", os.Args[0])
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
	}
	checkSymbols(t, out)
}
<commit_msg>cmd/nm: do not fail TestNM if symbol has less then 3 columns in nm output<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var testData uint32

func checkSymbols(t *testing.T, nmoutput []byte) {
	var checkSymbolsFound, testDataFound bool
	scanner := bufio.NewScanner(bytes.NewBuffer(nmoutput))
	for scanner.Scan() {
		f := strings.Fields(scanner.Text())
		if len(f) < 3 {
			continue
		}
		switch f[2] {
		case "cmd/nm.checkSymbols":
			checkSymbolsFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", checkSymbols) {
				t.Errorf("nm shows wrong address %v for checkSymbols (%p)", addr, checkSymbols)
			}
		case "cmd/nm.testData":
			testDataFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", &testData) {
				t.Errorf("nm shows wrong address %v for testData (%p)", addr, &testData)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		t.Errorf("error while reading symbols: %v", err)
		return
	}
	if !checkSymbolsFound {
		t.Error("nm shows no checkSymbols symbol")
	}
	if !testDataFound {
		t.Error("nm shows no testData symbol")
	}
}

func TestNM(t *testing.T) {
	out, err := exec.Command("go", "build", "-o", "testnm.exe", "cmd/nm").CombinedOutput()
	if err != nil {
		t.Fatalf("go build -o testnm.exe cmd/nm: %v\n%s", err, string(out))
	}
	defer os.Remove("testnm.exe")

	testfiles := []string{
		"elf/testdata/gcc-386-freebsd-exec",
		"elf/testdata/gcc-amd64-linux-exec",
		"macho/testdata/gcc-386-darwin-exec",
		"macho/testdata/gcc-amd64-darwin-exec",
		"pe/testdata/gcc-amd64-mingw-exec",
		"pe/testdata/gcc-386-mingw-exec",
		"plan9obj/testdata/amd64-plan9-exec",
		"plan9obj/testdata/386-plan9-exec",
	}
	for _, f := range testfiles {
		exepath := filepath.Join(runtime.GOROOT(), "src", "pkg", "debug", f)
		cmd := exec.Command("./testnm.exe", exepath)
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
		}
	}

	cmd := exec.Command("./testnm.exe", os.Args[0])
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
	}
	checkSymbols(t, out)
}
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"errors"
	"fmt"
	"math"
)

type Parser struct {
	end      int
	len      int
	position int
	data     []byte
}

func NewParser(data []byte) *Parser {
	p := &Parser{
		data: data,
		len:  len(data),
		end:  len(data) - 1,
	}
	return p
}

func (p *Parser) ReadLiteral() *Literal {
	start := p.position
	for {
		if p.SkipUntil('%') == false {
			return &Literal{clone(p.data[start:p.len])}
		}
		if p.Prev() == '<' {
			p.position++ //move past the %
			return &Literal{clone(p.data[start : p.position-2])}
		}
	}
}

func (p *Parser) ReadValue() (Value, error) {
	first := p.SkipSpaces()
	negate := false
	if first == '-' {
		negate = true
		p.position++
		first = p.SkipSpaces()
	}
	var value Value
	var err error
	if first == 0 {
		return nil, p.error("Expected value, got nothing")
	}
	if first >= '0' && first <= '9' {
		 value, err = p.ReadNumber(negate)
	} else if first == '\'' {
		value, err = p.ReadChar(negate)
	} else if first == '"' {
		value, err =  p.ReadString(negate)
	} else {
		value, err = p.ReadDynamic(negate)
	}
	if err != nil {
		return nil, err
	}
	c1 := p.SkipSpaces()
	if c1 == '%' && p.data[p.position+1] == '>' {
		return value, nil
	}
	factory, ok := Operations[c1]
	if ok == false {
		return value, nil
	}
	p.position++
	right, err := p.ReadValue()
	if err != nil {
		return nil, err
	}
	return factory(value, right), nil
}

func (p *Parser) ReadNumber(negate bool) (Value, error) {
	integer := 0
	fraction := 0
	target := &integer
	partLength := 0
	isDecimal := false
	for ; p.position < p.end; p.position++ {
		c := p.data[p.position]
		if c == '.' {
			if isDecimal {
				break
			}
			target = &fraction
			partLength = 0
			isDecimal = true
			continue
		}
		if c < '0' || c > '9' {
			break
		}
		partLength++
		*target = *target*10 + int(c-'0')
	}

	if isDecimal {
		value := float64(integer) + float64(fraction)/math.Pow10(partLength)
		if negate {
			value *= -1
		}
		return &StaticValue{value}, nil
	}
	if negate {
		integer *= -1
	}
	return &StaticValue{integer}, nil
}

func (p *Parser) ReadChar(negate bool) (Value, error) {
	if negate {
		return nil, p.error("Don't know what to do with a negative character")
	}
	c := p.Next()
	if c == '\\' {
		c = p.Next()
	}
	if p.Next() != '\'' {
		return nil, p.error("Invalid character")
	}
	p.position++
	return &StaticValue{c}, nil
}

func (p *Parser) ReadString(negate bool) (Value, error) {
	if negate {
		return nil, p.error("Don't know what to do with a negative string")
	}
	p.position++
	start := p.position
	escaped := 0

	for ; p.position < p.end; p.position++ {
		c := p.data[p.position]
		if c == '\\' {
			escaped++
			p.position++
			continue
		}
		if c == '"' {
			break
		}
	}

	var data []byte
	var err error
	if escaped > 0 {
		data, err = p.unescape(p.data[start:p.position], escaped)
		if err != nil {
			return nil, err
		}
	} else {
		data = p.data[start:p.position]
	}
	p.position++ //consume the "
	return &StaticValue{string(data)}, nil
}

func (p *Parser) ReadDynamic(negate bool) (Value, error) {
	start := p.position
	fields := make([]string, 0, 5)
	types := make([]DynamicFieldType, 0, 5)
	args := make([][]Value, 0, 5)
	for ;p.position < p.end; p.position++ {
		c := p.data[p.position]
		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' {
			continue
		}
		field := string(bytes.ToLower(p.data[start:p.position]))
		isEnd := c != '.' && c != '(' && c != '['
		if c == '.' || isEnd {
			if isEnd && p.position - start == 1 {
				break
			}
			fields = append(fields, field)
			types = append(types, FieldType)
			args = append(args, nil)
			start = p.position+1
			if isEnd {
				break
			}
		} else if c == '[' {
			println(field)
			fields = append(fields, field)
			types = append(types, IndexedType)
			p.position++
			arg, err := p.ReadIndexing()
			if err != nil {
				return nil, err
			}
			args = append(args, arg)
			start = p.position
		} else if c == '(' {
			fields = append(fields, field)
			types = append(types, MethodType)
			p.position++
			arg, err := p.ReadArgs()
			if err != nil {
				return nil, err
			}
			args = append(args, arg)
			start = p.position+1
		}
	}
	return &DynamicValue{fields, types, args}, nil
}

func (p *Parser) ReadIndexing() ([]Value, error) {
	first, err := p.ReadValue()
	if err != nil {
		return nil, err
	}

	c := p.SkipSpaces()
	if c == ']' {
		return []Value{first}, nil
	}
	if c != ':' {
		return nil, p.error("Unrecognized array/map index")
	}

	p.position++
	second, err := p.ReadValue()
	if err != nil {
		return nil, err
	}
	if c = p.SkipSpaces(); c != ']' {
		return nil, p.error("Expected closing array/map bracket")
	}
	return []Value{first, second}, nil
}

func (p *Parser) ReadArgs() ([]Value, error) {
	if p.data[p.position] == ')' {
		return nil, nil
	}

	values := make([]Value, 0, 3)
	for {
		value, err := p.ReadValue()
		if err != nil {
			return nil, err
		}
		values = append(values, value)
		c := p.SkipSpaces()
		if c == ')' {
			break
		}
		if c != ',' {
			return nil, p.error("Invalid argument list given to function")
		}
	}
	return values, nil
}

func (p *Parser) ReadTagType() TagType {
	switch p.Consume() {
	case 0:
		return NoTag
	case '=':
		return OutputTag
	case '!':
		return UnsafeTag
	default:
		return NoTag //todo CodeTag
	}
}

func (p *Parser) ReadCloseTag() error {
	if p.SkipSpaces() != '%' || p.Next() != '>' {
		return p.error("Expected closing tag")
	}
	p.position++
	return nil
}

func (p *Parser) SkipUntil(b byte) bool {
	if at := bytes.IndexByte(p.data[p.position:], b); at != -1 {
		p.position = p.position + at
		return true
	}
	p.position = len(p.data)
	return false
}

func (p *Parser) SkipSpaces() byte {
	for ; p.position < p.end; p.position++ {
		c := p.data[p.position]
		if c != ' ' && c != '\t' && c != '\n' && c != '\r' {
			return c
		}
	}
	return 0
}

func (p *Parser) Consume() byte {
	if p.position > p.end {
		return 0
	}
	c := p.data[p.position]
	p.position++
	return c
}

func (p *Parser) Next() byte {
	p.position++
	if p.position > p.end {
		return 0
	}
	return p.data[p.position]
}

func (p *Parser) Prev() byte {
	return p.data[p.position-1]
}

func (p *Parser) Dump() {
	fmt.Println(string(p.data[p.position:]))
}

func (p *Parser) error(s string) error {
	end := p.position
	for ; end < p.end; end++ {
		if p.data[end] == '%' && p.data[end+1] == '>' {
			break
		}
	}
	end += 2 //consume the > + this is exclusive
	if end > p.len {
		end = p.len
	}
	start := p.position
	for ; start > 0; start-- {
		if p.data[start] == '%' && p.data[start-1] == '<' {
			start--
			break
		}
	}
	return errors.New(fmt.Sprintf("%s: %v", s, string(p.data[start:end])))
}

func (p *Parser) unescape(data []byte, escaped int) ([]byte, error) {
	value := make([]byte, len(data)-escaped)
	at := 0
	for {
		index := bytes.IndexByte(data, '\\')
		if index == -1 {
			copy(value[at:], data)
			break
		}
		at += copy(value[at:], data[:index])
		switch data[index+1] {
		case 'n':
			value[at] = '\n'
		case 'r':
			value[at] = '\r'
		case 't':
			value[at] = '\t'
		case '"':
			value[at] = '"'
		case '\\':
			value[at] = '\\'
		default:
			return nil, p.error(fmt.Sprintf("Unknown escape sequence \\%s", string(data[index+1])))
		}
		at++
		data = data[index+2:]
	}
	return value, nil
}

func clone(data []byte) []byte {
	c := make([]byte, len(data))
	copy(c, data)
	return c
}
<commit_msg>support [1:] and [:4] slices<commit_after>package core

import (
	"bytes"
	"errors"
	"fmt"
	"math"
)

type Parser struct {
	end      int
	len      int
	position int
	data     []byte
}

func NewParser(data []byte) *Parser {
	p := &Parser{
		data: data,
		len:  len(data),
		end:  len(data) - 1,
	}
	return p
}

func (p *Parser) ReadLiteral() *Literal {
	start := p.position
	for {
		if p.SkipUntil('%') == false {
			return &Literal{clone(p.data[start:p.len])}
		}
		if p.Prev() == '<' {
			p.position++ //move past the %
			return &Literal{clone(p.data[start : p.position-2])}
		}
	}
}

func (p *Parser) ReadValue() (Value, error) {
	first := p.SkipSpaces()
	negate := false
	if first == '-' {
		negate = true
		p.position++
		first = p.SkipSpaces()
	}
	var value Value
	var err error
	if first == 0 {
		return nil, p.error("Expected value, got nothing")
	}
	if first >= '0' && first <= '9' {
		 value, err = p.ReadNumber(negate)
	} else if first == '\'' {
		value, err = p.ReadChar(negate)
	} else if first == '"' {
		value, err =  p.ReadString(negate)
	} else {
		value, err = p.ReadDynamic(negate)
	}
	if err != nil {
		return nil, err
	}
	c1 := p.SkipSpaces()
	if c1 == '%' && p.data[p.position+1] == '>' {
		return value, nil
	}
	factory, ok := Operations[c1]
	if ok == false {
		return value, nil
	}
	p.position++
	right, err := p.ReadValue()
	if err != nil {
		return nil, err
	}
	return factory(value, right), nil
}

func (p *Parser) ReadNumber(negate bool) (Value, error) {
	integer := 0
	fraction := 0
	target := &integer
	partLength := 0
	isDecimal := false
	for ; p.position < p.end; p.position++ {
		c := p.data[p.position]
		if c == '.' {
			if isDecimal {
				break
			}
			target = &fraction
			partLength = 0
			isDecimal = true
			continue
		}
		if c < '0' || c > '9' {
			break
		}
		partLength++
		*target = *target*10 + int(c-'0')
	}

	if isDecimal {
		value := float64(integer) + float64(fraction)/math.Pow10(partLength)
		if negate {
			value *= -1
		}
		return &StaticValue{value}, nil
	}
	if negate {
		integer *= -1
	}
	return &StaticValue{integer}, nil
}

func (p *Parser) ReadChar(negate bool) (Value, error) {
	if negate {
		return nil, p.error("Don't know what to do with a negative character")
	}
	c := p.Next()
	if c == '\\' {
		c = p.Next()
	}
	if p.Next() != '\'' {
		return nil, p.error("Invalid character")
	}
	p.position++
	return &StaticValue{c}, nil
}

func (p *Parser) ReadString(negate bool) (Value, error) {
	if negate {
		return nil, p.error("Don't know what to do with a negative string")
	}
	p.position++
	start := p.position
	escaped := 0

	for ; p.position < p.end; p.position++ {
		c := p.data[p.position]
		if c == '\\' {
			escaped++
			p.position++
			continue
		}
		if c == '"' {
			break
		}
	}

	var data []byte
	var err error
	if escaped > 0 {
		data, err = p.unescape(p.data[start:p.position], escaped)
		if err != nil {
			return nil, err
		}
	} else {
		data = p.data[start:p.position]
	}
	p.position++ //consume the "
	return &StaticValue{string(data)}, nil
}

func (p *Parser) ReadDynamic(negate bool) (Value, error) {
	start := p.position
	fields := make([]string, 0, 5)
	types := make([]DynamicFieldType, 0, 5)
	args := make([][]Value, 0, 5)
	for ;p.position < p.end; p.position++ {
		c := p.data[p.position]
		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' {
			continue
		}
		field := string(bytes.ToLower(p.data[start:p.position]))
		isEnd := c != '.' && c != '(' && c != '['
		if c == '.' || isEnd {
			if isEnd && p.position - start == 1 {
				break
			}
			fields = append(fields, field)
			types = append(types, FieldType)
			args = append(args, nil)
			start = p.position+1
			if isEnd {
				break
			}
		} else if c == '[' {
			println(field)
			fields = append(fields, field)
			types = append(types, IndexedType)
			p.position++
			arg, err := p.ReadIndexing()
			if err != nil {
				return nil, err
			}
			args = append(args, arg)
			start = p.position
		} else if c == '(' {
			fields = append(fields, field)
			types = append(types, MethodType)
			p.position++
			arg, err := p.ReadArgs()
			if err != nil {
				return nil, err
			}
			args = append(args, arg)
			start = p.position+1
		}
	}
	return &DynamicValue{fields, types, args}, nil
}

func (p *Parser) ReadIndexing() ([]Value, error) {
	implicitStart := false
	if p.SkipSpaces() == ':' {
		implicitStart = true
		p.position++
	}
	first, err := p.ReadValue()
	if err != nil {
		return nil, err
	}
	if implicitStart {
		return []Value{&StaticValue{0}, first}, nil
	}

	c := p.SkipSpaces()
	if c == ']' {
		return []Value{first}, nil
	}
	if c != ':' {
		return nil, p.error("Unrecognized array/map index")
	}

	p.position++
	if p.SkipSpaces() == ']' {
		return []Value{first}, nil
	}
	second, err := p.ReadValue()
	if err != nil {
		return nil, err
	}

	if c = p.SkipSpaces(); c != ']' {
		return nil, p.error("Expected closing array/map bracket")
	}
	return []Value{first, second}, nil
}

func (p *Parser) ReadArgs() ([]Value, error) {
	if p.data[p.position] == ')' {
		return nil, nil
	}

	values := make([]Value, 0, 3)
	for {
		value, err := p.ReadValue()
		if err != nil {
			return nil, err
		}
		values = append(values, value)
		c := p.SkipSpaces()
		if c == ')' {
			break
		}
		if c != ',' {
			return nil, p.error("Invalid argument list given to function")
		}
	}
	return values, nil
}

func (p *Parser) ReadTagType() TagType {
	switch p.Consume() {
	case 0:
		return NoTag
	case '=':
		return OutputTag
	case '!':
		return UnsafeTag
	default:
		return NoTag //todo CodeTag
	}
}

func (p *Parser) ReadCloseTag() error {
	if p.SkipSpaces() != '%' || p.Next() != '>' {
		return p.error("Expected closing tag")
	}
	p.position++
	return nil
}

func (p *Parser) SkipUntil(b byte) bool {
	if at := bytes.IndexByte(p.data[p.position:], b); at != -1 {
		p.position = p.position + at
		return true
	}
	p.position = len(p.data)
	return false
}

func (p *Parser) SkipSpaces() byte {
	for ; p.position < p.end; p.position++ {
		c := p.data[p.position]
		if c != ' ' && c != '\t' && c != '\n' && c != '\r' {
			return c
		}
	}
	return 0
}

func (p *Parser) Consume() byte {
	if p.position > p.end {
		return 0
	}
	c := p.data[p.position]
	p.position++
	return c
}

func (p *Parser) Next() byte {
	p.position++
	if p.position > p.end {
		return 0
	}
	return p.data[p.position]
}

func (p *Parser) Prev() byte {
	return p.data[p.position-1]
}

func (p *Parser) Dump() {
	fmt.Println(string(p.data[p.position:]))
}

func (p *Parser) error(s string) error {
	end := p.position
	for ; end < p.end; end++ {
		if p.data[end] == '%' && p.data[end+1] == '>' {
			break
		}
	}
	end += 2 //consume the > + this is exclusive
	if end > p.len {
		end = p.len
	}
	start := p.position
	for ; start > 0; start-- {
		if p.data[start] == '%' && p.data[start-1] == '<' {
			start--
			break
		}
	}
	return errors.New(fmt.Sprintf("%s: %v", s, string(p.data[start:end])))
}

func (p *Parser) unescape(data []byte, escaped int) ([]byte, error) {
	value := make([]byte, len(data)-escaped)
	at := 0
	for {
		index := bytes.IndexByte(data, '\\')
		if index == -1 {
			copy(value[at:], data)
			break
		}
		at += copy(value[at:], data[:index])
		switch data[index+1] {
		case 'n':
			value[at] = '\n'
		case 'r':
			value[at] = '\r'
		case 't':
			value[at] = '\t'
		case '"':
			value[at] = '"'
		case '\\':
			value[at] = '\\'
		default:
			return nil, p.error(fmt.Sprintf("Unknown escape sequence \\%s", string(data[index+1])))
		}
		at++
		data = data[index+2:]
	}
	return value, nil
}

func clone(data []byte) []byte {
	c := make([]byte, len(data))
	copy(c, data)
	return c
}
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"sync"

	"text/template"

	"github.com/bulletind/khabar/config"
	"github.com/bulletind/khabar/db"
	"github.com/bulletind/khabar/dbapi/gully"
	"github.com/bulletind/khabar/dbapi/pending"
	"github.com/bulletind/khabar/dbapi/topics"
	"github.com/bulletind/khabar/dbapi/user_locale"
	"github.com/nicksnyder/go-i18n/i18n"
	"gopkg.in/simversity/gotracer.v1"
)

const webIdent = "web"
const DEFAULT_LOCALE = "en_US"
const DEFAULT_TIMEZONE = "GMT+0.0"

func sendToChannel(
	pending_item *pending.PendingItem,
	text, channelIdent string,
	context map[string]interface{},
) {
	handlerFunc, ok := ChannelMap[channelIdent]
	if !ok {
		log.Println("No handler for Topic:" + pending_item.Topic + " Channel:" + channelIdent)
		return
	}

	defer gotracer.Tracer{Dummy: true}.Notify()
	handlerFunc(pending_item, text, context)
}

func getText(locale, ident string, pending_item *pending.PendingItem) string {
	T, _ := i18n.Tfunc(
		locale+"_"+pending_item.AppName+"_"+pending_item.Organization+"_"+ident,
		locale+"_"+pending_item.AppName+"_"+ident,
		locale+"_"+ident,
	)

	text := T(pending_item.Topic, pending_item.Context)
	if text == pending_item.Topic {
		text = ""
	}

	return text
}

func send(locale, channelIdent string, pending_item *pending.PendingItem) {

	if !topics.ChannelAllowed(pending_item.User, pending_item.AppName,
		pending_item.Organization, pending_item.Topic, channelIdent) {
		log.Println("Channel :" + channelIdent + " " + "is blocked for topic :" + pending_item.Topic)
		return
	}

	channel, err := gully.FindOne(
		pending_item.User,
		pending_item.AppName, pending_item.Organization,
		channelIdent,
	)

	if err != nil {
		log.Println("Unable to find channel : " + channelIdent + err.Error())
		return
	}

	text := getText(locale, channelIdent, pending_item)
	if text == "" {
		// If Topic == text, do not send the notification. This can happen
		// if the translation fails to find a sensible string in the JSON files
		// OR the translation provided was meaningless. To prevent the users
		// from being annpyed, abort this routine.

		log.Println("No translation for:" + channelIdent + pending_item.Topic)
		return
	}

	if channel.Ident == EMAIL || channel.Ident == PUSH {
		var buffer bytes.Buffer
		buffer.WriteString(channelIdent)
		buffer.WriteString("_subject")
		subjectIdent := buffer.String()

		subject := getText(locale, subjectIdent, pending_item)
		if subject != "" {
			pending_item.Context["subject"] = subject
		}
	}

	if channel.Ident == EMAIL {
		buffer := new(bytes.Buffer)

		transDir := config.Settings.Khabar.TranslationDirectory
		path := transDir + "/" + locale + "_base_email.tmpl"

		if _, err := os.Stat(path); err == nil {
			content, err := ioutil.ReadFile(path)
			if err != nil {
				log.Println("Cannot Load the base email template")
			} else {
				t := template.Must(template.New("email").Parse(string(content)))

				data := struct{ Content string }{text}
				t.Execute(buffer, &data)
				text = buffer.String()
			}
		}
	}

	sendToChannel(pending_item, text, channel.Ident, channel.Data)
}

func SendNotification(pending_item *pending.PendingItem) {
	userLocale, err := user_locale.Get(pending_item.User)
	if err != nil {
		log.Println("Unable to find locale for user :" + err.Error())
		userLocale = new(db.UserLocale)

		//FIXME:: Please do not hardcode this.
		userLocale.Locale = DEFAULT_LOCALE
		userLocale.TimeZone = DEFAULT_TIMEZONE
	}

	childwg := new(sync.WaitGroup)

	for channel, _ := range ChannelMap {
		childwg.Add(1)

		go func(
			locale, channelIdent string,
			pending_item *pending.PendingItem,
		) {
			defer childwg.Done()
			send(locale, channelIdent, pending_item)
		}(userLocale.Locale, channel, pending_item)
	}

	childwg.Wait()
}
<commit_msg>Changes for proper subject to be loaded<commit_after>package core

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"sync"

	"text/template"

	"github.com/bulletind/khabar/config"
	"github.com/bulletind/khabar/db"
	"github.com/bulletind/khabar/dbapi/gully"
	"github.com/bulletind/khabar/dbapi/pending"
	"github.com/bulletind/khabar/dbapi/topics"
	"github.com/bulletind/khabar/dbapi/user_locale"
	"github.com/nicksnyder/go-i18n/i18n"
	"gopkg.in/simversity/gotracer.v1"
)

const webIdent = "web"
const DEFAULT_LOCALE = "en_US"
const DEFAULT_TIMEZONE = "GMT+0.0"

func sendToChannel(
	pending_item *pending.PendingItem,
	text, channelIdent string,
	context map[string]interface{},
) {
	handlerFunc, ok := ChannelMap[channelIdent]
	if !ok {
		log.Println("No handler for Topic:" + pending_item.Topic + " Channel:" + channelIdent)
		return
	}

	defer gotracer.Tracer{Dummy: true}.Notify()
	handlerFunc(pending_item, text, context)
}

func getText(locale, ident, channel string, pending_item *pending.PendingItem) string {
	T, _ := i18n.Tfunc(
		locale+"_"+pending_item.AppName+"_"+pending_item.Organization+"_"+channel,
		locale+"_"+pending_item.AppName+"_"+channel,
		locale+"_"+channel,
	)

	text := T(ident, pending_item.Context)
	if text == ident {
		text = ""
	}

	return text
}

func send(locale, channelIdent string, pending_item *pending.PendingItem) {

	if !topics.ChannelAllowed(pending_item.User, pending_item.AppName,
		pending_item.Organization, pending_item.Topic, channelIdent) {
		log.Println("Channel :" + channelIdent + " " + "is blocked for topic :" + pending_item.Topic)
		return
	}

	channel, err := gully.FindOne(
		pending_item.User,
		pending_item.AppName, pending_item.Organization,
		channelIdent,
	)

	if err != nil {
		log.Println("Unable to find channel : " + channelIdent + err.Error())
		return
	}

	text := getText(locale, pending_item.Topic, channelIdent, pending_item)
	if text == "" {
		// If Topic == text, do not send the notification. This can happen
		// if the translation fails to find a sensible string in the JSON files
		// OR the translation provided was meaningless. To prevent the users
		// from being annpyed, abort this routine.

		log.Println("No translation for:" + channelIdent + pending_item.Topic)
		return
	}

	if channelIdent == EMAIL || channel.Ident == PUSH {
		subject := getText(locale, pending_item.Topic+"_subject", channelIdent, pending_item)

		if subject != "" {
			pending_item.Context["subject"] = subject
		}
	}

	if channelIdent == EMAIL {
		buffer := new(bytes.Buffer)

		transDir := config.Settings.Khabar.TranslationDirectory
		path := transDir + "/" + locale + "_base_email.tmpl"

		if _, err := os.Stat(path); err == nil {
			content, err := ioutil.ReadFile(path)
			if err != nil {
				log.Println("Cannot Load the base email template")
			} else {
				t := template.Must(template.New("email").Parse(string(content)))

				data := struct{ Content string }{text}
				t.Execute(buffer, &data)
				text = buffer.String()
			}
		}
	}

	sendToChannel(pending_item, text, channel.Ident, channel.Data)
}

func SendNotification(pending_item *pending.PendingItem) {
	userLocale, err := user_locale.Get(pending_item.User)
	if err != nil {
		log.Println("Unable to find locale for user :" + err.Error())
		userLocale = new(db.UserLocale)

		//FIXME:: Please do not hardcode this.
		userLocale.Locale = DEFAULT_LOCALE
		userLocale.TimeZone = DEFAULT_TIMEZONE
	}

	childwg := new(sync.WaitGroup)

	for channel, _ := range ChannelMap {
		childwg.Add(1)

		go func(
			locale, channelIdent string,
			pending_item *pending.PendingItem,
		) {
			defer childwg.Done()
			send(locale, channelIdent, pending_item)
		}(userLocale.Locale, channel, pending_item)
	}

	childwg.Wait()
}
<|endoftext|>