<commit_before>package nmea

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

var nearDistance = 0.001

func TestParseLatLong(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"33\u00B0 12' 34.3423\"", 33.209540, false}, // dms
		{"3345.1232 N", 33.752054, false},            // gps
		{"151.234532", 151.234532, false},            // decimal
		{"200.000", 0, true},                         // out of range
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseLatLong(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestParseGPS(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"3345.1232 N", 33.752054, false},
		{"15145.9877 S", -151.76646, false},
		{"12345.1234 X", 0, true},
		{"1234.1234", 0, true},
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseGPS(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestParseDMS(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"33\u00B0 12' 34.3423\"", 33.209540, false},
		{"33\u00B0 1.1' 34.3423\"", 0, true},
		{"3.3\u00B0 1' 34.3423\"", 0, true},
		{"33\u00B0 1' 34.34.23\"", 0, true},
		{"33 1 3434.23", 0, true},
		{"123", 0, true},
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseDMS(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestParseDecimal(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"151.234532", 151.234532, false},
		{"-151.234532", -151.234532, false},
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseDecimal(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestLatLongPrint(t *testing.T) {
	var tests = []struct {
		value float64
		dms   string
		gps   string
	}{
		{
			value: 151.434367,
			gps:   "15126.0620",
			dms:   "151° 26' 3.721200\"",
		},
		{
			value: 33.94057166666666,
			gps:   "3356.4343",
			dms:   "33° 56' 26.058000\"",
		},
		{
			value: 45.0,
			dms:   "45° 0' 0.000000\"",
			gps:   "4500.0000",
		},
	}

	for _, tt := range tests {
		t.Run(fmt.Sprintf("%f", tt.value), func(t *testing.T) {
			assert.Equal(t, tt.dms, FormatDMS(tt.value))
			assert.Equal(t, tt.gps, FormatGPS(tt.value))
		})
	}
}

func TestTimeParse(t *testing.T) {
	timetests := []struct {
		value    string
		expected Time
		ok       bool
	}{
		{"123456", Time{true, 12, 34, 56, 0}, true},
		{"", Time{}, true},
		{"112233.523", Time{true, 11, 22, 33, 523}, true},
		{"010203.04", Time{true, 1, 2, 3, 40}, true},
		{"x0u2xd", Time{}, false},
		{"xx2233.123", Time{}, false},
		{"11xx33.123", Time{}, false},
		{"1122xx.123", Time{}, false},
		{"112233.xxx", Time{}, false},
	}
	for _, tt := range timetests {
		actual, err := ParseTime(tt.value)
		if !tt.ok {
			if err == nil {
				t.Errorf("ParseTime(%s) expected error", tt.value)
			}
		} else {
			if err != nil {
				t.Errorf("ParseTime(%s) %s", tt.value, err)
			}
			if actual != tt.expected {
				t.Errorf("ParseTime(%s) got %s expected %s", tt.value, actual, tt.expected)
			}
		}
	}
}

func TestTimeString(t *testing.T) {
	d := Time{
		Hour:        1,
		Minute:      2,
		Second:      3,
		Millisecond: 4,
	}
	expected := "01:02:03.0040"
	if s := d.String(); s != expected {
		t.Fatalf("got %s, expected %s", s, expected)
	}
}

func TestDateParse(t *testing.T) {
	datetests := []struct {
		value    string
		expected Date
		ok       bool
	}{
		{"010203", Date{true, 1, 2, 3}, true},
		{"01003", Date{}, false},
		{"", Date{}, true},
		{"xx0203", Date{}, false},
		{"01xx03", Date{}, false},
		{"0102xx", Date{}, false},
	}
	for _, tt := range datetests {
		actual, err := ParseDate(tt.value)
		if !tt.ok {
			if err == nil {
				t.Errorf("ParseDate(%s) expected error", tt.value)
			}
		} else {
			if err != nil {
				t.Errorf("ParseDate(%s) %s", tt.value, err)
			}
			if actual != tt.expected {
				t.Errorf("ParseDate(%s) got %s expected %s", tt.value, actual, tt.expected)
			}
		}
	}
}

func TestDateString(t *testing.T) {
	d := Date{
		DD: 1,
		MM: 2,
		YY: 3,
	}
	expected := "01/02/03"
	if s := d.String(); s != expected {
		t.Fatalf("got %s expected %s", s, expected)
	}
}
<commit_msg>revert<commit_after>package nmea

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

var nearDistance = 0.001

func TestParseLatLong(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"33\u00B0 12' 34.3423\"", 33.209540, false}, // dms
		{"3345.1232 N", 33.752054, false},            // gps
		{"151.234532", 151.234532, false},            // decimal
		{"200.000", 0, true},                         // out of range
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseLatLong(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestParseGPS(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"3345.1232 N", 33.752054, false},
		{"15145.9877 S", -151.76646, false},
		{"12345.1234 X", 0, true},
		{"1234.1234", 0, true},
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseGPS(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestParseDMS(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"33\u00B0 12' 34.3423\"", 33.209540, false},
		{"33\u00B0 1.1' 34.3423\"", 0, true},
		{"3.3\u00B0 1' 34.3423\"", 0, true},
		{"33\u00B0 1' 34.34.23\"", 0, true},
		{"33 1 3434.23", 0, true},
		{"123", 0, true},
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseDMS(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestParseDecimal(t *testing.T) {
	var tests = []struct {
		value    string
		expected float64
		err      bool
	}{
		{"151.234532", 151.234532, false},
		{"-151.234532", -151.234532, false},
	}
	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			l, err := ParseDecimal(tt.value)
			if tt.err {
				assert.Error(t, err)
			} else {
				assert.InDelta(t, tt.expected, l, nearDistance)
			}
		})
	}
}

func TestLatLongPrint(t *testing.T) {
	var tests = []struct {
		value float64
		dms   string
		gps   string
	}{
		{
			value: 151.434367,
			gps:   "15126.0620",
			dms:   "151° 26' 3.721200\"",
		},
		{
			value: 33.94057166666666,
			gps:   "3356.4343",
			dms:   "33° 56' 26.058000\"",
		},
		{
			value: 45.0,
			dms:   "45° 0' 0.000000\"",
			gps:   "4500.0000",
		},
	}

	for _, tt := range tests {
		t.Run(fmt.Sprintf("%f", tt.value), func(t *testing.T) {
			assert.Equal(t, tt.dms, FormatDMS(tt.value))
			assert.Equal(t, tt.gps, FormatGPS(tt.value))
		})
	}
}

func TestTimeParse(t *testing.T) {
	timetests := []struct {
		value    string
		expected Time
		ok       bool
	}{
		{"123456", Time{true, 12, 34, 56, 0}, true},
		{"", Time{}, true},
		{"112233.123", Time{true, 11, 22, 33, 123}, true},
		{"010203.04", Time{true, 1, 2, 3, 40}, true},
		{"x0u2xd", Time{}, false},
		{"xx2233.123", Time{}, false},
		{"11xx33.123", Time{}, false},
		{"1122xx.123", Time{}, false},
		{"112233.xxx", Time{}, false},
	}
	for _, tt := range timetests {
		actual, err := ParseTime(tt.value)
		if !tt.ok {
			if err == nil {
				t.Errorf("ParseTime(%s) expected error", tt.value)
			}
		} else {
			if err != nil {
				t.Errorf("ParseTime(%s) %s", tt.value, err)
			}
			if actual != tt.expected {
				t.Errorf("ParseTime(%s) got %s expected %s", tt.value, actual, tt.expected)
			}
		}
	}
}

func TestTimeString(t *testing.T) {
	d := Time{
		Hour:        1,
		Minute:      2,
		Second:      3,
		Millisecond: 4,
	}
	expected := "01:02:03.0040"
	if s := d.String(); s != expected {
		t.Fatalf("got %s, expected %s", s, expected)
	}
}

func TestDateParse(t *testing.T) {
	datetests := []struct {
		value    string
		expected Date
		ok       bool
	}{
		{"010203", Date{true, 1, 2, 3}, true},
		{"01003", Date{}, false},
		{"", Date{}, true},
		{"xx0203", Date{}, false},
		{"01xx03", Date{}, false},
		{"0102xx", Date{}, false},
	}
	for _, tt := range datetests {
		actual, err := ParseDate(tt.value)
		if !tt.ok {
			if err == nil {
				t.Errorf("ParseDate(%s) expected error", tt.value)
			}
		} else {
			if err != nil {
				t.Errorf("ParseDate(%s) %s", tt.value, err)
			}
			if actual != tt.expected {
				t.Errorf("ParseDate(%s) got %s expected %s", tt.value, actual, tt.expected)
			}
		}
	}
}

func TestDateString(t *testing.T) {
	d := Date{
		DD: 1,
		MM: 2,
		YY: 3,
	}
	expected := "01/02/03"
	if s := d.String(); s != expected {
		t.Fatalf("got %s expected %s", s, expected)
	}
}
<|endoftext|>


<commit_before>package dondeestas

import (
	"fmt"
	"math/rand"
	"time"
)

func createRandomString() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	b := make([]byte, 8)
	r.Read(b)

	return fmt.Sprintf("%x", b)
}

func createRandomStringSlice() []string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	l := make([]string, r.Intn(20))
	for i, _ := range l {
		l[i] = createRandomString()
	}
	return l
}

func createRandomPerson() (*Person, error) {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	p := new(Person)
	p.Id = createRandomString()
	p.Name = createRandomString()
	p.Position.Tov = time.Now()
	p.Position.Latitude = r.Float32()
	p.Position.Longitude = r.Float32()
	p.Position.Elevation = r.Float32()
	p.Visible = r.Int()%2 != 0
	p.Whitelist = createRandomStringSlice()
	p.Following = createRandomStringSlice()

	return p, nil
}

func arePersonEqual(p1, p2 *Person) bool {
	if p1.Id != p2.Id {
		fmt.Println("Ids don't match")
		return false
	}
	if p1.Name != p2.Name {
		fmt.Println("Names don't match")
		return false
	}
	if p1.Position.Tov != p2.Position.Tov {
		fmt.Println("TOVs don't match")
		return false
	}
	if p1.Position.Latitude != p2.Position.Latitude {
		fmt.Println("Lats don't match")
		return false
	}
	if p1.Position.Longitude != p2.Position.Longitude {
		fmt.Println("Lons don't match")
		return false
	}
	if p1.Position.Elevation != p2.Position.Elevation {
		fmt.Println("Elevations don't match")
		return false
	}
	if p1.Visible != p2.Visible {
		fmt.Println("Visibilities don't match")
		return false
	}
	/* TODO
	if p1.Whitelist != p2.Whitelist {
		return false
	}
	if p1.Following != p2.Following {
		return false
	}
	*/
	return true
}
<commit_msg>Still trying to figure out why the Travis build fails<commit_after>package dondeestas

import (
	"fmt"
	"math/rand"
	"time"
)

func createRandomString() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	b := make([]byte, 8)
	r.Read(b)

	return fmt.Sprintf("%x", b)
}

func createRandomStringSlice() []string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	l := make([]string, r.Intn(20))
	for i, _ := range l {
		l[i] = createRandomString()
	}
	return l
}

func createRandomPerson() (*Person, error) {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	p := new(Person)
	p.Id = createRandomString()
	p.Name = createRandomString()
	p.Position.Tov = time.Now()
	p.Position.Latitude = r.Float32()
	p.Position.Longitude = r.Float32()
	p.Position.Elevation = r.Float32()
	p.Visible = r.Int()%2 != 0
	p.Whitelist = createRandomStringSlice()
	p.Following = createRandomStringSlice()

	return p, nil
}

func arePersonEqual(p1, p2 *Person) bool {
	if p1.Id != p2.Id {
		fmt.Println("Ids don't match")
		return false
	}
	if p1.Name != p2.Name {
		fmt.Println("Names don't match")
		return false
	}
	if p1.Position.Tov != p2.Position.Tov {
		fmt.Printf("TOVs don't match: %s vs %s\n", p1.Position.Tov.Format(time.RFC3339), p2.Position.Tov.Format(time.RFC3339))
		return false
	}
	if p1.Position.Latitude != p2.Position.Latitude {
		fmt.Println("Lats don't match")
		return false
	}
	if p1.Position.Longitude != p2.Position.Longitude {
		fmt.Println("Lons don't match")
		return false
	}
	if p1.Position.Elevation != p2.Position.Elevation {
		fmt.Println("Elevations don't match")
		return false
	}
	if p1.Visible != p2.Visible {
		fmt.Println("Visibilities don't match")
		return false
	}
	/* TODO
	if p1.Whitelist != p2.Whitelist {
		return false
	}
	if p1.Following != p2.Following {
		return false
	}
	*/
	return true
}
<|endoftext|>


<commit_before>package tgbotapi

import (
	"testing"
	"time"
)

func TestUserStringWith(t *testing.T) {
	user := User{
		ID:           0,
		FirstName:    "Test",
		LastName:     "Test",
		UserName:     "",
		LanguageCode: "en",
		IsBot:        false,
	}

	if user.String() != "Test Test" {
		t.Fail()
	}
}

func TestUserStringWithUserName(t *testing.T) {
	user := User{
		ID:           0,
		FirstName:    "Test",
		LastName:     "Test",
		UserName:     "@test",
		LanguageCode: "en",
	}

	if user.String() != "@test" {
		t.Fail()
	}
}

func TestMessageTime(t *testing.T) {
	message := Message{Date: 0}

	date := time.Unix(0, 0)
	if message.Time() != date {
		t.Fail()
	}
}

func TestMessageIsCommandWithCommand(t *testing.T) {
	message := Message{Text: "/command"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}

	if !message.IsCommand() {
		t.Fail()
	}
}

func TestIsCommandWithText(t *testing.T) {
	message := Message{Text: "some text"}

	if message.IsCommand() {
		t.Fail()
	}
}

func TestIsCommandWithEmptyText(t *testing.T) {
	message := Message{Text: ""}

	if message.IsCommand() {
		t.Fail()
	}
}

func TestCommandWithCommand(t *testing.T) {
	message := Message{Text: "/command"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}

	if message.Command() != "command" {
		t.Fail()
	}
}

func TestCommandWithEmptyText(t *testing.T) {
	message := Message{Text: ""}

	if message.Command() != "" {
		t.Fail()
	}
}

func TestCommandWithNonCommand(t *testing.T) {
	message := Message{Text: "test text"}

	if message.Command() != "" {
		t.Fail()
	}
}

func TestCommandWithBotName(t *testing.T) {
	message := Message{Text: "/command@testbot"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}

	if message.Command() != "command" {
		t.Fail()
	}
}

func TestCommandWithAtWithBotName(t *testing.T) {
	message := Message{Text: "/command@testbot"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}

	if message.CommandWithAt() != "command@testbot" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsWithArguments(t *testing.T) {
	message := Message{Text: "/command with arguments"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
	if message.CommandArguments() != "with arguments" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) {
	message := Message{Text: "/command-without argument space"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
	if message.CommandArguments() != "without argument space" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsWithoutArguments(t *testing.T) {
	message := Message{Text: "/command"}
	if message.CommandArguments() != "" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsForNonCommand(t *testing.T) {
	message := Message{Text: "test text"}
	if message.CommandArguments() != "" {
		t.Fail()
	}
}

func TestMessageEntityParseURLGood(t *testing.T) {
	entity := MessageEntity{URL: "https://www.google.com"}

	if _, err := entity.ParseURL(); err != nil {
		t.Fail()
	}
}

func TestMessageEntityParseURLBad(t *testing.T) {
	entity := MessageEntity{URL: ""}

	if _, err := entity.ParseURL(); err == nil {
		t.Fail()
	}
}

func TestChatIsPrivate(t *testing.T) {
	chat := Chat{ID: 10, Type: "private"}

	if !chat.IsPrivate() {
		t.Fail()
	}
}

func TestChatIsGroup(t *testing.T) {
	chat := Chat{ID: 10, Type: "group"}

	if !chat.IsGroup() {
		t.Fail()
	}
}

func TestChatIsChannel(t *testing.T) {
	chat := Chat{ID: 10, Type: "channel"}

	if !chat.IsChannel() {
		t.Fail()
	}
}

func TestChatIsSuperGroup(t *testing.T) {
	chat := Chat{ID: 10, Type: "supergroup"}

	if !chat.IsSuperGroup() {
		t.Fail()
	}
}

func TestMessageEntityIsMention(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "mention"}

	if !entity.IsMention() {
		t.Fail()
	}
}

func TestMessageEntityIsHashtag(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "hashtag"}

	if !entity.IsHashtag() {
		t.Fail()
	}
}

func TestMessageEntityIsBotCommand(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "bot_command"}

	if !entity.IsCommand() {
		t.Fail()
	}
}

func TestMessageEntityIsUrl(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "url"}

	if !entity.IsUrl() {
		t.Fail()
	}
}

func TestMessageEntityIsEmail(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "email"}

	if !entity.IsEmail() {
		t.Fail()
	}
}

func TestMessageEntityIsBold(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "bold"}

	if !entity.IsBold() {
		t.Fail()
	}
}

func TestMessageEntityIsItalic(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "italic"}

	if !entity.IsItalic() {
		t.Fail()
	}
}

func TestMessageEntityIsCode(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "code"}

	if !entity.IsCode() {
		t.Fail()
	}
}

func TestMessageEntityIsPre(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "pre"}

	if !entity.IsPre() {
		t.Fail()
	}
}

func TestMessageEntityIsTextLink(t *testing.T) {
	entity := tgbotapi.MessageEntity{Type: "text_link"}

	if !entity.IsTextLink() {
		t.Fail()
	}
}

func TestFileLink(t *testing.T) {
	file := File{FilePath: "test/test.txt"}

	if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" {
		t.Fail()
	}
}

// Ensure all configs are sendable
var (
	_ Chattable = AnimationConfig{}
	_ Chattable = AudioConfig{}
	_ Chattable = CallbackConfig{}
	_ Chattable = ChatAdministratorsConfig{}
	_ Chattable = ChatActionConfig{}
	_ Chattable = ChatInfoConfig{}
	_ Chattable = ChatInviteLinkConfig{}
	_ Chattable = ContactConfig{}
	_ Chattable = DeleteChatPhotoConfig{}
	_ Chattable = DeleteChatStickerSetConfig{}
	_ Chattable = DeleteMessageConfig{}
	_ Chattable = DocumentConfig{}
	_ Chattable = EditMessageCaptionConfig{}
	_ Chattable = EditMessageLiveLocationConfig{}
	_ Chattable = EditMessageMediaConfig{}
	_ Chattable = EditMessageReplyMarkupConfig{}
	_ Chattable = EditMessageTextConfig{}
	_ Chattable = FileConfig{}
	_ Chattable = ForwardConfig{}
	_ Chattable = GameConfig{}
	_ Chattable = GetChatMemberConfig{}
	_ Chattable = GetGameHighScoresConfig{}
	_ Chattable = InlineConfig{}
	_ Chattable = InvoiceConfig{}
	_ Chattable = KickChatMemberConfig{}
	_ Chattable = LeaveChatConfig{}
	_ Chattable = LocationConfig{}
	_ Chattable = MediaGroupConfig{}
	_ Chattable = MessageConfig{}
	_ Chattable = PhotoConfig{}
	_ Chattable = PinChatMessageConfig{}
	_ Chattable = PromoteChatMemberConfig{}
	_ Chattable = RemoveWebhookConfig{}
	_ Chattable = RestrictChatMemberConfig{}
	_ Chattable = SendPollConfig{}
	_ Chattable = SetChatDescriptionConfig{}
	_ Chattable = SetChatPhotoConfig{}
	_ Chattable = SetChatTitleConfig{}
	_ Chattable = SetGameScoreConfig{}
	_ Chattable = StickerConfig{}
	_ Chattable = StopPollConfig{}
	_ Chattable = StopMessageLiveLocationConfig{}
	_ Chattable = UnbanChatMemberConfig{}
	_ Chattable = UnpinChatMessageConfig{}
	_ Chattable = UpdateConfig{}
	_ Chattable = UserProfilePhotosConfig{}
	_ Chattable = VenueConfig{}
	_ Chattable = VideoConfig{}
	_ Chattable = VideoNoteConfig{}
	_ Chattable = VoiceConfig{}
	_ Chattable = WebhookConfig{}
)
<commit_msg>Fix old package import<commit_after>package tgbotapi

import (
	"testing"
	"time"
)

func TestUserStringWith(t *testing.T) {
	user := User{
		ID:           0,
		FirstName:    "Test",
		LastName:     "Test",
		UserName:     "",
		LanguageCode: "en",
		IsBot:        false,
	}

	if user.String() != "Test Test" {
		t.Fail()
	}
}

func TestUserStringWithUserName(t *testing.T) {
	user := User{
		ID:           0,
		FirstName:    "Test",
		LastName:     "Test",
		UserName:     "@test",
		LanguageCode: "en",
	}

	if user.String() != "@test" {
		t.Fail()
	}
}

func TestMessageTime(t *testing.T) {
	message := Message{Date: 0}

	date := time.Unix(0, 0)
	if message.Time() != date {
		t.Fail()
	}
}

func TestMessageIsCommandWithCommand(t *testing.T) {
	message := Message{Text: "/command"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}

	if !message.IsCommand() {
		t.Fail()
	}
}

func TestIsCommandWithText(t *testing.T) {
	message := Message{Text: "some text"}

	if message.IsCommand() {
		t.Fail()
	}
}

func TestIsCommandWithEmptyText(t *testing.T) {
	message := Message{Text: ""}

	if message.IsCommand() {
		t.Fail()
	}
}

func TestCommandWithCommand(t *testing.T) {
	message := Message{Text: "/command"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}

	if message.Command() != "command" {
		t.Fail()
	}
}

func TestCommandWithEmptyText(t *testing.T) {
	message := Message{Text: ""}

	if message.Command() != "" {
		t.Fail()
	}
}

func TestCommandWithNonCommand(t *testing.T) {
	message := Message{Text: "test text"}

	if message.Command() != "" {
		t.Fail()
	}
}

func TestCommandWithBotName(t *testing.T) {
	message := Message{Text: "/command@testbot"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}

	if message.Command() != "command" {
		t.Fail()
	}
}

func TestCommandWithAtWithBotName(t *testing.T) {
	message := Message{Text: "/command@testbot"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}

	if message.CommandWithAt() != "command@testbot" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsWithArguments(t *testing.T) {
	message := Message{Text: "/command with arguments"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
	if message.CommandArguments() != "with arguments" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) {
	message := Message{Text: "/command-without argument space"}
	message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
	if message.CommandArguments() != "without argument space" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsWithoutArguments(t *testing.T) {
	message := Message{Text: "/command"}
	if message.CommandArguments() != "" {
		t.Fail()
	}
}

func TestMessageCommandArgumentsForNonCommand(t *testing.T) {
	message := Message{Text: "test text"}
	if message.CommandArguments() != "" {
		t.Fail()
	}
}

func TestMessageEntityParseURLGood(t *testing.T) {
	entity := MessageEntity{URL: "https://www.google.com"}

	if _, err := entity.ParseURL(); err != nil {
		t.Fail()
	}
}

func TestMessageEntityParseURLBad(t *testing.T) {
	entity := MessageEntity{URL: ""}

	if _, err := entity.ParseURL(); err == nil {
		t.Fail()
	}
}

func TestChatIsPrivate(t *testing.T) {
	chat := Chat{ID: 10, Type: "private"}

	if !chat.IsPrivate() {
		t.Fail()
	}
}

func TestChatIsGroup(t *testing.T) {
	chat := Chat{ID: 10, Type: "group"}

	if !chat.IsGroup() {
		t.Fail()
	}
}

func TestChatIsChannel(t *testing.T) {
	chat := Chat{ID: 10, Type: "channel"}

	if !chat.IsChannel() {
		t.Fail()
	}
}

func TestChatIsSuperGroup(t *testing.T) {
	chat := Chat{ID: 10, Type: "supergroup"}

	if !chat.IsSuperGroup() {
		t.Fail()
	}
}

func TestMessageEntityIsMention(t *testing.T) {
	entity := MessageEntity{Type: "mention"}

	if !entity.IsMention() {
		t.Fail()
	}
}

func TestMessageEntityIsHashtag(t *testing.T) {
	entity := MessageEntity{Type: "hashtag"}

	if !entity.IsHashtag() {
		t.Fail()
	}
}

func TestMessageEntityIsBotCommand(t *testing.T) {
	entity := MessageEntity{Type: "bot_command"}

	if !entity.IsCommand() {
		t.Fail()
	}
}

func TestMessageEntityIsUrl(t *testing.T) {
	entity := MessageEntity{Type: "url"}

	if !entity.IsUrl() {
		t.Fail()
	}
}

func TestMessageEntityIsEmail(t *testing.T) {
	entity := MessageEntity{Type: "email"}

	if !entity.IsEmail() {
		t.Fail()
	}
}

func TestMessageEntityIsBold(t *testing.T) {
	entity := MessageEntity{Type: "bold"}

	if !entity.IsBold() {
		t.Fail()
	}
}

func TestMessageEntityIsItalic(t *testing.T) {
	entity := MessageEntity{Type: "italic"}

	if !entity.IsItalic() {
		t.Fail()
	}
}

func TestMessageEntityIsCode(t *testing.T) {
	entity := MessageEntity{Type: "code"}

	if !entity.IsCode() {
		t.Fail()
	}
}

func TestMessageEntityIsPre(t *testing.T) {
	entity := MessageEntity{Type: "pre"}

	if !entity.IsPre() {
		t.Fail()
	}
}

func TestMessageEntityIsTextLink(t *testing.T) {
	entity := MessageEntity{Type: "text_link"}

	if !entity.IsTextLink() {
		t.Fail()
	}
}

func TestFileLink(t *testing.T) {
	file := File{FilePath: "test/test.txt"}

	if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" {
		t.Fail()
	}
}

// Ensure all configs are sendable
var (
	_ Chattable = AnimationConfig{}
	_ Chattable = AudioConfig{}
	_ Chattable = CallbackConfig{}
	_ Chattable = ChatAdministratorsConfig{}
	_ Chattable = ChatActionConfig{}
	_ Chattable = ChatInfoConfig{}
	_ Chattable = ChatInviteLinkConfig{}
	_ Chattable = ContactConfig{}
	_ Chattable = DeleteChatPhotoConfig{}
	_ Chattable = DeleteChatStickerSetConfig{}
	_ Chattable = DeleteMessageConfig{}
	_ Chattable = DocumentConfig{}
	_ Chattable = EditMessageCaptionConfig{}
	_ Chattable = EditMessageLiveLocationConfig{}
	_ Chattable = EditMessageMediaConfig{}
	_ Chattable = EditMessageReplyMarkupConfig{}
	_ Chattable = EditMessageTextConfig{}
	_ Chattable = FileConfig{}
	_ Chattable = ForwardConfig{}
	_ Chattable = GameConfig{}
	_ Chattable = GetChatMemberConfig{}
	_ Chattable = GetGameHighScoresConfig{}
	_ Chattable = InlineConfig{}
	_ Chattable = InvoiceConfig{}
	_ Chattable = KickChatMemberConfig{}
	_ Chattable = LeaveChatConfig{}
	_ Chattable = LocationConfig{}
	_ Chattable = MediaGroupConfig{}
	_ Chattable = MessageConfig{}
	_ Chattable = PhotoConfig{}
	_ Chattable = PinChatMessageConfig{}
	_ Chattable = PromoteChatMemberConfig{}
	_ Chattable = RemoveWebhookConfig{}
	_ Chattable = RestrictChatMemberConfig{}
	_ Chattable = SendPollConfig{}
	_ Chattable = SetChatDescriptionConfig{}
	_ Chattable = SetChatPhotoConfig{}
	_ Chattable = SetChatTitleConfig{}
	_ Chattable = SetGameScoreConfig{}
	_ Chattable = StickerConfig{}
	_ Chattable = StopPollConfig{}
	_ Chattable = StopMessageLiveLocationConfig{}
	_ Chattable = UnbanChatMemberConfig{}
	_ Chattable = UnpinChatMessageConfig{}
	_ Chattable = UpdateConfig{}
	_ Chattable = UserProfilePhotosConfig{}
	_ Chattable = VenueConfig{}
	_ Chattable = VideoConfig{}
	_ Chattable = VideoNoteConfig{}
	_ Chattable = VoiceConfig{}
	_ Chattable = WebhookConfig{}
)
<|endoftext|>


<commit_before>package comm

import (
	"strings"

	"github.com/Sirupsen/logrus"
)

type State uint8

const (
	Requested State = iota
	Building
	Pushing
	Completed
	Errored
)

type (
	LogChan    chan LogEntry
	StatusChan chan StatusEntry
	ExitChan   chan error
)

type LogEntry interface {
	Entry() *logrus.Entry
}

func NewLogEntry(l *logrus.Entry) LogEntry {
	return (*logEntry)(l)
}

type logEntry logrus.Entry

func (l *logEntry) Entry() *logrus.Entry {
	return (*logrus.Entry)(l)
}

// StatusMessage is the tentative interface for the structs returned on the status channel
type StatusEntry interface {
	BuildID() int
	PreviousState() State
	CurrentState() State
	Note() string
	//Error() error // should be checked for non-nil
}

// NewLogEntryWriter returns a log entry writer initialized with the provided
// channel. The provided writer implements the io.Writer interface
func NewLogEntryWriter(ch LogChan) *logEntryWriter {
	return &logEntryWriter{log: ch}
}

type logEntryWriter struct {
	log LogChan
}

func (writer *logEntryWriter) Write(p []byte) (n int, err error) {
	lines := strings.Split(string(p), "\n")
	for _, line := range lines {
		writer.log <- (*logEntry)(&logrus.Entry{Message: string(line)})
	}
	return len(p), nil
}
<commit_msg>Adding comments, commenting out some things that are not finished<commit_after>package comm

import (
	"strings"

	"github.com/Sirupsen/logrus"
)

//type State uint8

//const (
//Requested State = iota
//Building
//Pushing
//Completed
//Errored
//)

type (
	// LogChan is a channel for log entries
	LogChan chan LogEntry

	// StatusChan is a channel for status updates (somewhat TBD)
	StatusChan chan StatusEntry

	// ExitChan is a channel for receiving the final exit value (error or nil)
	ExitChan chan error
)

// LogEntry is a convenient, extensible way to ship logrus log entries
type LogEntry interface {
	Entry() *logrus.Entry
}

// NewLogEntry produces a log entry that can be sent on a LogChan
func NewLogEntry(l *logrus.Entry) LogEntry {
	return (*logEntry)(l)
}

type logEntry logrus.Entry

func (l *logEntry) Entry() *logrus.Entry {
	return (*logrus.Entry)(l)
}

// StatusEntry is the tentative interface for the structs returned on the status channel
type StatusEntry interface {
	BuildID() int
	//PreviousState() State
	//CurrentState() State
	Note() string
	//Error() error // should be checked for non-nil
}

// NewLogEntryWriter returns a log entry writer initialized with the provided
// channel. The provided writer implements the io.Writer interface
func NewLogEntryWriter(ch LogChan) *LogEntryWriter {
	return &LogEntryWriter{log: ch}
}

// LogEntryWriter is a type for implementing the io.Writer interface
type LogEntryWriter struct {
	log LogChan
}

func (writer *LogEntryWriter) Write(p []byte) (n int, err error) {
	lines := strings.Split(string(p), "\n")
	for _, line := range lines {
		writer.log <- (*logEntry)(&logrus.Entry{Message: string(line)})
	}
	return len(p), nil
}
<|endoftext|>


<commit_before>// Copyright 2009 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 bytes

// Simple byte buffer for marshaling data.

import (
	"io";
	"os";
)

// Copy from string to byte array at offset doff.  Assume there's room.
func copyString(dst []byte, doff int, str string) {
	for soff := 0; soff < len(str); soff++ {
		dst[doff] = str[soff];
		doff++;
	}
}

// Copy from bytes to byte array at offset doff.  Assume there's room.
func copyBytes(dst []byte, doff int, src []byte) {
	if len(src) == 1 {
		dst[doff] = src[0];
		return;
	}
	copy(dst[doff:], src);
}

// A Buffer is a variable-sized buffer of bytes
// with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf		[]byte;		// contents are the bytes buf[off : len(buf)]
	off		int;		// read at &buf[off], write at &buf[len(buf)]
	oneByte		[1]byte;	// avoid allocation of slice on each WriteByte
	bootstrap	[64]byte;	// memory to hold first slice; helps small buffers (Printf) avoid allocation.
}

// Bytes returns the contents of the unread portion of the buffer;
// len(b.Bytes()) == b.Len().
func (b *Buffer) Bytes() []byte	{ return b.buf[b.off:] }

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:]);
}

// Len returns the number of bytes of the unread portion of the buffer;
// b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int	{ return len(b.buf) - b.off }

// Truncate discards all but the first n unread bytes from the buffer.
// It is an error to call b.Truncate(n) with n > b.Len().
func (b *Buffer) Truncate(n int) {
	if n == 0 {
		// Reuse buffer space.
		b.off = 0
	}
	b.buf = b.buf[0 : b.off+n];
}

// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset()	{ b.Truncate(0) }

// Resize buffer to guarantee enough space for n more bytes.
// After this call, the state of b.buf is inconsistent.
// It must be fixed up as is done in Write and WriteString.
func (b *Buffer) resize(n int) {
	var buf []byte;
	if b.buf == nil && n <= len(b.bootstrap) {
		buf = &b.bootstrap
	} else {
		buf = b.buf;
		if len(b.buf)+n > cap(b.buf) {
			// not enough space anywhere
			buf = make([]byte, 2*cap(b.buf)+n)
		}
		copy(buf, b.buf[b.off:]);
	}
	b.buf = buf;
	b.off = 0;
}

// Write appends the contents of p to the buffer.  The return
// value n is the length of p; err is always nil.
func (b *Buffer) Write(p []byte) (n int, err os.Error) {
	m := b.Len();
	n = len(p);
	if len(b.buf)+n > cap(b.buf) {
		b.resize(n)
	}
	b.buf = b.buf[0 : b.off+m+n];
	copyBytes(b.buf, b.off+m, p);
	return n, nil;
}

// WriteString appends the contents of s to the buffer.  The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err os.Error) {
	m := b.Len();
	n = len(s);
	if len(b.buf)+n > cap(b.buf) {
		b.resize(n)
	}
	b.buf = b.buf[0 : b.off+m+n];
	copyString(b.buf, b.off+m, s);
	return n, nil;
}

// MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom.  As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the
// underlying buffer.
const MinRead = 512

// ReadFrom reads data from r until EOF and appends it to the buffer.
// The return value n is the number of bytes read.
// Any error except os.EOF encountered during the read
// is also returned.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) {
	for {
		if cap(b.buf)-len(b.buf) < MinRead {
			var newBuf []byte;
			// can we get space without allocation?
			if b.off+cap(b.buf)-len(b.buf) >= MinRead {
				// reuse beginning of buffer
				newBuf = b.buf[0 : len(b.buf)-b.off]
			} else {
				// not enough space at end; put space on end
				newBuf = make([]byte, len(b.buf)-b.off, 2*(cap(b.buf)-b.off)+MinRead)
			}
			copy(newBuf, b.buf[b.off:]);
			b.buf = newBuf;
			b.off = 0;
		}
		m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]);
		b.buf = b.buf[b.off : len(b.buf)+m];
		n += int64(m);
		if e == os.EOF {
			break
		}
		if e != nil {
			return n, e
		}
	}
	return n, nil;	// err is EOF, so return nil explicitly
}

// WriteTo writes data to w until the buffer is drained or an error
// occurs. The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) {
	for b.off < len(b.buf) {
		m, e := w.Write(b.buf[b.off:]);
		n += int64(m);
		b.off += m;
		if e != nil {
			return n, e
		}
	}
	return;
}

// WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included
// to match bufio.Writer's WriteByte.
func (b *Buffer) WriteByte(c byte) os.Error {
	b.oneByte[0] = c;
	b.Write(&b.oneByte);
	return nil;
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained.  The return value n is the number of bytes read.  If the
// buffer has no data to return, err is os.EOF even if len(p) is zero;
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err os.Error) {
	if b.off >= len(b.buf) {
		return 0, os.EOF
	}
	m := b.Len();
	n = len(p);

	if n > m {
		// more bytes requested than available
		n = m
	}

	copyBytes(p, 0, b.buf[b.off:b.off+n]);
	b.off += n;
	return n, err;
}

// ReadByte reads and returns the next byte from the buffer.
// If no byte is available, it returns error os.EOF.
func (b *Buffer) ReadByte() (c byte, err os.Error) {
	if b.off >= len(b.buf) {
		return 0, os.EOF
	}
	c = b.buf[b.off];
	b.off++;
	return c, nil;
}

// NewBuffer creates and initializes a new Buffer
// using buf as its initial contents.
func NewBuffer(buf []byte) *Buffer	{ return &Buffer{buf: buf} }

// NewBufferString creates and initializes a new Buffer
// using string s as its initial contents.
func NewBufferString(s string) *Buffer {
	buf := make([]byte, len(s));
	copyString(buf, 0, s);
	return &Buffer{buf: buf};
}
<commit_msg>Remove copyBytes completely in favor of copy.<commit_after>// Copyright 2009 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 bytes

// Simple byte buffer for marshaling data.

import (
	"io";
	"os";
)

// Copy from string to byte array at offset doff.  Assume there's room.
func copyString(dst []byte, doff int, str string) {
	for soff := 0; soff < len(str); soff++ {
		dst[doff] = str[soff];
		doff++;
	}
}

// A Buffer is a variable-sized buffer of bytes
// with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf		[]byte;		// contents are the bytes buf[off : len(buf)]
	off		int;		// read at &buf[off], write at &buf[len(buf)]
	oneByte		[1]byte;	// avoid allocation of slice on each WriteByte
	bootstrap	[64]byte;	// memory to hold first slice; helps small buffers (Printf) avoid allocation.
}

// Bytes returns the contents of the unread portion of the buffer;
// len(b.Bytes()) == b.Len().
func (b *Buffer) Bytes() []byte	{ return b.buf[b.off:] }

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:]);
}

// Len returns the number of bytes of the unread portion of the buffer;
// b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int	{ return len(b.buf) - b.off }

// Truncate discards all but the first n unread bytes from the buffer.
// It is an error to call b.Truncate(n) with n > b.Len().
func (b *Buffer) Truncate(n int) {
	if n == 0 {
		// Reuse buffer space.
		b.off = 0
	}
	b.buf = b.buf[0 : b.off+n];
}

// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset()	{ b.Truncate(0) }

// Resize buffer to guarantee enough space for n more bytes.
// After this call, the state of b.buf is inconsistent.
// It must be fixed up as is done in Write and WriteString.
func (b *Buffer) resize(n int) {
	var buf []byte;
	if b.buf == nil && n <= len(b.bootstrap) {
		buf = &b.bootstrap
	} else {
		buf = b.buf;
		if len(b.buf)+n > cap(b.buf) {
			// not enough space anywhere
			buf = make([]byte, 2*cap(b.buf)+n)
		}
		copy(buf, b.buf[b.off:]);
	}
	b.buf = buf;
	b.off = 0;
}

// Write appends the contents of p to the buffer.  The return
// value n is the length of p; err is always nil.
func (b *Buffer) Write(p []byte) (n int, err os.Error) {
	m := b.Len();
	n = len(p);
	if len(b.buf)+n > cap(b.buf) {
		b.resize(n)
	}
	b.buf = b.buf[0 : b.off+m+n];
	copy(b.buf[b.off+m:], p);
	return n, nil;
}

// WriteString appends the contents of s to the buffer.  The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err os.Error) {
	m := b.Len();
	n = len(s);
	if len(b.buf)+n > cap(b.buf) {
		b.resize(n)
	}
	b.buf = b.buf[0 : b.off+m+n];
	copyString(b.buf, b.off+m, s);
	return n, nil;
}

// MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom.  As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the
// underlying buffer.
const MinRead = 512

// ReadFrom reads data from r until EOF and appends it to the buffer.
// The return value n is the number of bytes read.
// Any error except os.EOF encountered during the read
// is also returned.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) {
	for {
		if cap(b.buf)-len(b.buf) < MinRead {
			var newBuf []byte;
			// can we get space without allocation?
			if b.off+cap(b.buf)-len(b.buf) >= MinRead {
				// reuse beginning of buffer
				newBuf = b.buf[0 : len(b.buf)-b.off]
			} else {
				// not enough space at end; put space on end
				newBuf = make([]byte, len(b.buf)-b.off, 2*(cap(b.buf)-b.off)+MinRead)
			}
			copy(newBuf, b.buf[b.off:]);
			b.buf = newBuf;
			b.off = 0;
		}
		m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]);
		b.buf = b.buf[b.off : len(b.buf)+m];
		n += int64(m);
		if e == os.EOF {
			break
		}
		if e != nil {
			return n, e
		}
	}
	return n, nil;	// err is EOF, so return nil explicitly
}

// WriteTo writes data to w until the buffer is drained or an error
// occurs. The return value n is the number of bytes written.
// Any error encountered during the write is also returned.
func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) {
	for b.off < len(b.buf) {
		m, e := w.Write(b.buf[b.off:]);
		n += int64(m);
		b.off += m;
		if e != nil {
			return n, e
		}
	}
	return;
}

// WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included
// to match bufio.Writer's WriteByte.
func (b *Buffer) WriteByte(c byte) os.Error {
	b.oneByte[0] = c;
	b.Write(&b.oneByte);
	return nil;
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained.  The return value n is the number of bytes read.  If the
// buffer has no data to return, err is os.EOF even if len(p) is zero;
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err os.Error) {
	if b.off >= len(b.buf) {
		return 0, os.EOF
	}
	m := b.Len();
	n = len(p);

	if n > m {
		// more bytes requested than available
		n = m
	}

	copy(p, b.buf[b.off:b.off+n]);
	b.off += n;
	return n, err;
}

// ReadByte reads and returns the next byte from the buffer.
// If no byte is available, it returns error os.EOF.
func (b *Buffer) ReadByte() (c byte, err os.Error) {
	if b.off >= len(b.buf) {
		return 0, os.EOF
	}
	c = b.buf[b.off];
	b.off++;
	return c, nil;
}

// NewBuffer creates and initializes a new Buffer
// using buf as its initial contents.
func NewBuffer(buf []byte) *Buffer	{ return &Buffer{buf: buf} }

// NewBufferString creates and initializes a new Buffer
// using string s as its initial contents.
func NewBufferString(s string) *Buffer {
	buf := make([]byte, len(s));
	copyString(buf, 0, s);
	return &Buffer{buf: buf};
}
<|endoftext|>


<commit_before>package concordances

import (
	"fmt"

	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

// Driver interface
type Driver interface {
	ReadByConceptID(ids []string) (concordances Concordances, found bool, err error)
	ReadByAuthority(authority string, ids []string) (concordances Concordances, found bool, err error)
	CheckConnectivity() error
}

// CypherDriver struct
type CypherDriver struct {
	conn neoutils.NeoConnection
	env  string
}

//NewCypherDriver instantiate driver
func NewCypherDriver(conn neoutils.NeoConnection, env string) CypherDriver {
	return CypherDriver{conn, env}
}

// CheckConnectivity tests neo4j by running a simple cypher query
func (pcw CypherDriver) CheckConnectivity() error {
	return neoutils.Check(pcw.conn)
}

type neoReadStruct struct {
	UUID          string        `json:"UUID"`
	Types         []string      `json:"TYPES"`
	NeoIdentifier neoIdentifier `json:"IDENTIFIERS"`
	PrefUUID      string        `json:"prefUUID"`
}

type neoIdentifier struct {
	Labels []string `json:"labels"`
	Value  string   `json:"value"`
}

type neoResultStrunct struct {
	Rs []neoReadStruct
}

func (pcw CypherDriver) ReadByConceptID(identifiers []string) (concordances Concordances, found bool, err error) {
	c, f, err := pcw.readByConceptIDNewModel(identifiers)
	if !f {
		c, f, err = pcw.readByConceptIDOldModel(identifiers)
	}
	return c, f, err
}

func (pcw CypherDriver) readByConceptIDNewModel(identifiers []string) (concordances Concordances, found bool, err error) {
	results := []neoReadStruct{}
	query := &neoism.CypherQuery{
		Statement: `
		MATCH (p:Concept)-[:EQUIVALENT_TO]-(cn:Concept)
		WHERE p.uuid in {identifiers}
		MATCH (cn)-[:EQUIVALENT_TO]-(cnn:Concept)
		RETURN cn.prefUUID as prefUUID, cnn.uuid AS UUID, labels(cnn) AS TYPES, {labels:collect(cnn.authority), value:cnn.authorityValue} as IDENTIFIERS
		`,
		Parameters: neoism.Props{"identifiers": identifiers},
		Result:     &results,
	}

	err = pcw.conn.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up Concordances with query %s from neoism: %+v\n", query.Statement, err)
		return Concordances{}, false, fmt.Errorf("Error accessing Concordance datastore for identifier:")
	}

	log.Info(results)

	if (len(results)) == 0 {
		return Concordances{}, false, nil
	}

	concordances = Concordances{
		Concordance: []Concordance{},
	}

	for _, neoCon := range results {
		log.Info(neoCon)
		// Each record is now two identifiers, one UPP and one other.
		var con = Concordance{}
		var concept = Concept{}
		concept.ID = mapper.IDURL(neoCon.PrefUUID)
		concept.APIURL = mapper.APIURL(neoCon.PrefUUID, neoCon.Types, pcw.env)
		con.Concept = concept
		con.Identifier = Identifier{Authority: mapNeoLabelsToAuthorityValue(neoCon.NeoIdentifier.Labels), IdentifierValue: neoCon.NeoIdentifier.Value}
		concordances.Concordance = append(concordances.Concordance, con)

		var con2 = Concordance{}
		var concept2 = Concept{}
		concept2.ID = mapper.IDURL(neoCon.PrefUUID)
		concept2.APIURL = mapper.APIURL(neoCon.PrefUUID, neoCon.Types, pcw.env)
		con2.Concept = concept
		con2.Identifier = Identifier{Authority: UP_AUTHORITY, IdentifierValue: neoCon.UUID}
		concordances.Concordance = append(concordances.Concordance, con2)
	}

	if (len(concordances.Concordance)) == 0 {
		return Concordances{}, false, nil
	}

	log.Debugf("Returning %v", concordances)
	return concordances, true, nil
}

func (pcw CypherDriver) readByConceptIDOldModel(identifiers []string) (concordances Concordances, found bool, err error) {
	results := []neoReadStruct{}
	query := &neoism.CypherQuery{
		Statement: `
		MATCH (p:Concept)<-[:IDENTIFIES]-(i:UPPIdentifier)
		WHERE i.value in {identifiers}
		MATCH (p:Concept)<-[:IDENTIFIES]-(ids:Identifier)
		RETURN p.uuid as prefUUID, p.uuid AS UUID, labels(p) AS TYPES, {labels:labels(ids), value:ids.value} as IDENTIFIERS
		`,
		Parameters: neoism.Props{"identifiers": identifiers},
		Result:     &results,
	}
	return processCypherQueryToConcordances(pcw, query, &results)
}

func (pcw CypherDriver) ReadByAuthority(authority string, identifierValues []string) (concordances Concordances, found bool, err error) {
	c, f, err := pcw.readByAuthorityNewModel(authority, identifierValues)
	if !f {
		c, f, err = pcw.readByAuthorityOldModel(authority, identifierValues)
	}
	return c, f, err
}

func (pcw CypherDriver) readByAuthorityNewModel(authority string, identifierValues []string) (concordances Concordances, found bool, err error) {
	log.Info("readByAuthorityNewModel")
	concordances = Concordances{}
	results := []neoReadStruct{}

	authorityProperty := mapAuthorityToAuthorityProperty(authority)
	log.Info(authorityProperty)
	if authorityProperty == "" {
		return Concordances{}, false, nil
	}

	readByAuthorityQueryStatement := `
		MATCH (p:Concept)-[:EQUIVALENT_TO]-(cn:Concept)
		WHERE p.authorityValue in {identifierValues} AND p.authority = {authority}
		RETURN cn.prefUUID as prefUUID, cn.prefUUID AS UUID, labels(cn) AS TYPES, {labels:collect(p.authority), value:p.authorityValue} as IDENTIFIERS
		`
	if authorityProperty == "UPP" {
		readByAuthorityQueryStatement = `
			MATCH (p:Concept)-[:EQUIVALENT_TO]-(cn:Concept)
			WHERE p.uuid in {identifierValues}
			RETURN cn.prefUUID as prefUUID, cn.prefUUID AS UUID, labels(cn) AS TYPES, {labels:collect(p.authority), value:p.authorityValue} as IDENTIFIERS
		`
	}

	query := &neoism.CypherQuery{
		Statement: readByAuthorityQueryStatement,
		Parameters: neoism.Props{
			"identifierValues": identifierValues,
			"authority":        authorityProperty,
		},
		Result: &results,
	}

	err = pcw.conn.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up Concordances with query %s from neoism: %+v\n", query.Statement, err)
		return Concordances{}, false, fmt.Errorf("Error accessing Concordance datastore for identifier:")
	}

	log.Info(results)

	if (len(results)) == 0 {
		return Concordances{}, false, nil
	}

	concordances = Concordances{
		Concordance: []Concordance{},
	}

	for _, neoCon := range results {
		log.Info(neoCon)
		// Each record is now two identifiers, one UPP and one other.
		var con = Concordance{}
		var concept = Concept{}
		concept.ID = mapper.IDURL(neoCon.PrefUUID)
		concept.APIURL = mapper.APIURL(neoCon.PrefUUID, neoCon.Types, pcw.env)
		con.Concept = concept
		con.Identifier = Identifier{Authority: mapNeoLabelsToAuthorityValue(neoCon.NeoIdentifier.Labels), IdentifierValue: neoCon.NeoIdentifier.Value}
		concordances.Concordance = append(concordances.Concordance, con)
	}

	if (len(concordances.Concordance)) == 0 {
		return Concordances{}, false, nil
	}

	log.Debugf("Returning %v", concordances)
	return concordances, true, nil
}

func (pcw CypherDriver) readByAuthorityOldModel(authority string, identifierValues []string) (concordances Concordances, found bool, err error) {
	log.Info("readByAuthorityOldModel")
	concordances = Concordances{}
	results := []neoReadStruct{}

	identifierLabel := mapAuthorityToIdentifierLabel(authority)

	if identifierLabel == "" {
		return Concordances{}, false, nil
	}

	readByAuthorityQueryStatement := fmt.Sprintf(`
		MATCH (p:Concept)<-[:IDENTIFIES]-(i:%s)
		WHERE i.value in {identifierValues}
		RETURN p.uuid as prefUUID, p.uuid AS UUID, labels(p) AS TYPES, {labels:labels(i), value:i.value} as IDENTIFIERS
		`, identifierLabel)

	query := &neoism.CypherQuery{
		Statement: readByAuthorityQueryStatement,
		Parameters: neoism.Props{
			"identifierValues": identifierValues,
			"authority":        authority,
		},
		Result: &results,
	}
	return processCypherQueryToConcordances(pcw, query, &results)
}

func processCypherQueryToConcordances(pcw CypherDriver, q *neoism.CypherQuery, results *[]neoReadStruct) (concordances Concordances, found bool, err error) {
	err = pcw.conn.CypherBatch([]*neoism.CypherQuery{q})
	if err != nil {
		log.Errorf("Error looking up Concordances with query %s from neoism: %+v\n", q.Statement, err)
		return Concordances{}, false, fmt.Errorf("Error accessing Concordance datastore for identifier:")
	}

	if (len(*results)) == 0 {
		return Concordances{}, false, nil
	}

	concordances = neoReadStructToConcordances(results, pcw.env)

	if (len(concordances.Concordance)) == 0 {
		return Concordances{}, false, nil
	}

	log.Debugf("Returning %v", concordances)
	return concordances, true, nil
}

func neoReadStructToConcordances(neo *[]neoReadStruct, env string) (concordances Concordances) {
	log.Debug("Running the old model")
	concordances = Concordances{
		Concordance: []Concordance{},
	}
	for _, neoCon := range *neo {
		var con = Concordance{}
		var concept = Concept{}
		concept.ID = mapper.IDURL(neoCon.UUID)
		concept.APIURL = mapper.APIURL(neoCon.UUID, neoCon.Types, env)
		con.Concept = concept
		con.Identifier = Identifier{Authority: mapNeoLabelsToAuthorityValue(neoCon.NeoIdentifier.Labels), IdentifierValue: neoCon.NeoIdentifier.Value}
		concordances.Concordance = append(concordances.Concordance, con)
	}
	return concordances
}

func mapNeoLabelsToAuthorityValue(labelNames []string) (authority string) {
	for _, label := range labelNames {
		switch label {
		// Old style node label lookup
		case TME_ID_NODE_LABEL:
			return TME_AUTHORITY
		case FS_ID_NODE_LABEL:
			return FS_AUTHORITY
		case UP_ID_NODE_LABEL:
			return UP_AUTHORITY
		case LEI_ID_NODE_LABEL:
			return LEI_AUTHORITY
		case SL_ID_NODE_LABEL:
			return SL_AUTHORITY

		// New style authority properties
		case FS_AUTHORITY_PROPERTY:
			return FS_AUTHORITY
		case UP_AUTHORITY_PROPERTY:
			return UP_AUTHORITY
		case SL_AUTHORITY_PROPERTY:
			return SL_AUTHORITY
		case TME_AUTHORITY_PROPERTY:
			return TME_AUTHORITY
		}
	}
	return ""
}

func mapAuthorityToAuthorityProperty(authority string) string {
	switch authority {
	case TME_AUTHORITY:
		return TME_AUTHORITY_PROPERTY
	case FS_AUTHORITY:
		return FS_AUTHORITY_PROPERTY
	case UP_AUTHORITY:
		return UP_AUTHORITY_PROPERTY
	case SL_AUTHORITY:
		return SL_AUTHORITY_PROPERTY
	default:
		return ""
	}
}

func mapAuthorityToIdentifierLabel(authority string) (label string) {
	switch authority {
	case TME_AUTHORITY:
		return TME_ID_NODE_LABEL
	case FS_AUTHORITY:
		return FS_ID_NODE_LABEL
	case UP_AUTHORITY:
		return UP_ID_NODE_LABEL
	case LEI_AUTHORITY:
		return LEI_ID_NODE_LABEL
	}
	return ""
}

const TME_AUTHORITY = "http://api.ft.com/system/FT-TME"
const FS_AUTHORITY = "http://api.ft.com/system/FACTSET"
const UP_AUTHORITY = "http://api.ft.com/system/UPP"
const LEI_AUTHORITY = "http://api.ft.com/system/LEI"
const SL_AUTHORITY = "http://api.ft.com/system/SMARTLOGIC"

const TME_ID_NODE_LABEL = "TMEIdentifier"
const FS_ID_NODE_LABEL = "FactsetIdentifier"
const UP_ID_NODE_LABEL = "UPPIdentifier"
const LEI_ID_NODE_LABEL = "LegalEntityIdentifier"
const SL_ID_NODE_LABEL = "SmartlogicIdentifier"

const TME_AUTHORITY_PROPERTY = "TME"
const FS_AUTHORITY_PROPERTY = "FACTSET"
const UP_AUTHORITY_PROPERTY = "UPP"
const SL_AUTHORITY_PROPERTY = "Smartlogic"
<commit_msg>Make sure identifiers reports the right information<commit_after>package concordances

import (
	"fmt"

	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

// Driver interface
type Driver interface {
	ReadByConceptID(ids []string) (concordances Concordances, found bool, err error)
	ReadByAuthority(authority string, ids []string) (concordances Concordances, found bool, err error)
	CheckConnectivity() error
}

// CypherDriver struct
type CypherDriver struct {
	conn neoutils.NeoConnection
	env  string
}

//NewCypherDriver instantiate driver
func NewCypherDriver(conn neoutils.NeoConnection, env string) CypherDriver {
	return CypherDriver{conn, env}
}

// CheckConnectivity tests neo4j by running a simple cypher query
func (pcw CypherDriver) CheckConnectivity() error {
	return neoutils.Check(pcw.conn)
}

type neoReadStruct struct {
	UUID          string        `json:"UUID"`
	Types         []string      `json:"TYPES"`
	NeoIdentifier neoIdentifier `json:"IDENTIFIERS"`
	PrefUUID      string        `json:"prefUUID"`
}

type neoIdentifier struct {
	Labels []string `json:"labels"`
	Value  string   `json:"value"`
}

type neoResultStrunct struct {
	Rs []neoReadStruct
}

func (pcw CypherDriver) ReadByConceptID(identifiers []string) (concordances Concordances, found bool, err error) {
	c, f, err := pcw.readByConceptIDNewModel(identifiers)
	if !f {
		c, f, err = pcw.readByConceptIDOldModel(identifiers)
	}
	return c, f, err
}

func (pcw CypherDriver) readByConceptIDNewModel(identifiers []string) (concordances Concordances, found bool, err error) {
	results := []neoReadStruct{}
	query := &neoism.CypherQuery{
		Statement: `
		MATCH (p:Concept)-[:EQUIVALENT_TO]-(cn:Concept)
		WHERE p.uuid in {identifiers}
		MATCH (cn)-[:EQUIVALENT_TO]-(cnn:Concept)
		RETURN cn.prefUUID as prefUUID, cnn.uuid AS UUID, labels(cnn) AS TYPES, {labels:collect(cnn.authority), value:cnn.authorityValue} as IDENTIFIERS
		`,
		Parameters: neoism.Props{"identifiers": identifiers},
		Result:     &results,
	}

	err = pcw.conn.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up Concordances with query %s from neoism: %+v\n", query.Statement, err)
		return Concordances{}, false, fmt.Errorf("Error accessing Concordance datastore for identifier:")
	}

	log.Info(results)

	if (len(results)) == 0 {
		return Concordances{}, false, nil
	}

	concordances = Concordances{
		Concordance: []Concordance{},
	}

	for _, neoCon := range results {
		log.Info(neoCon)
		// Each record is now two identifiers, one UPP and one other.
		var con = Concordance{}
		var concept = Concept{}
		concept.ID = mapper.IDURL(neoCon.PrefUUID)
		concept.APIURL = mapper.APIURL(neoCon.PrefUUID, neoCon.Types, pcw.env)
		con.Concept = concept
		con.Identifier = Identifier{Authority: mapNeoLabelsToAuthorityValue(neoCon.NeoIdentifier.Labels), IdentifierValue: neoCon.NeoIdentifier.Value}
		concordances.Concordance = append(concordances.Concordance, con)

		var con2 = Concordance{}
		var concept2 = Concept{}
		concept2.ID = mapper.IDURL(neoCon.PrefUUID)
		concept2.APIURL = mapper.APIURL(neoCon.PrefUUID, neoCon.Types, pcw.env)
		con2.Concept = concept
		con2.Identifier = Identifier{Authority: UP_AUTHORITY, IdentifierValue: neoCon.UUID}
		concordances.Concordance = append(concordances.Concordance, con2)
	}

	if (len(concordances.Concordance)) == 0 {
		return Concordances{}, false, nil
	}

	log.Debugf("Returning %v", concordances)
	return concordances, true, nil
}

func (pcw CypherDriver) readByConceptIDOldModel(identifiers []string) (concordances Concordances, found bool, err error) {
	results := []neoReadStruct{}
	query := &neoism.CypherQuery{
		Statement: `
		MATCH (p:Concept)<-[:IDENTIFIES]-(i:UPPIdentifier)
		WHERE i.value in {identifiers}
		MATCH (p:Concept)<-[:IDENTIFIES]-(ids:Identifier)
		RETURN p.uuid as prefUUID, p.uuid AS UUID, labels(p) AS TYPES, {labels:labels(ids), value:ids.value} as IDENTIFIERS
		`,
		Parameters: neoism.Props{"identifiers": identifiers},
		Result:     &results,
	}
	return processCypherQueryToConcordances(pcw, query, &results)
}

func (pcw CypherDriver) ReadByAuthority(authority string, identifierValues []string) (concordances Concordances, found bool, err error) {
	c, f, err := pcw.readByAuthorityNewModel(authority, identifierValues)
	if !f {
		c, f, err = pcw.readByAuthorityOldModel(authority, identifierValues)
	}
	return c, f, err
}

func (pcw CypherDriver) readByAuthorityNewModel(authority string, identifierValues []string) (concordances Concordances, found bool, err error) {
	log.Info("readByAuthorityNewModel")
	concordances = Concordances{}
	results := []neoReadStruct{}

	authorityProperty := mapAuthorityToAuthorityProperty(authority)
	log.Info(authorityProperty)
	if authorityProperty == "" {
		return Concordances{}, false, nil
	}

	readByAuthorityQueryStatement := `
		MATCH (p:Concept)-[:EQUIVALENT_TO]-(cn:Concept)
		WHERE p.authorityValue in {identifierValues} AND p.authority = {authority}
		RETURN cn.prefUUID as prefUUID, cn.prefUUID AS UUID, labels(cn) AS TYPES, {labels:collect(p.authority), value:p.authorityValue} as IDENTIFIERS
		`

	// Special handling for UPP authority.  In for compatibility with the old model but may be removed once identifiers are removed.
	if authorityProperty == "UPP" {
		readByAuthorityQueryStatement = `
			MATCH (p:Concept)-[:EQUIVALENT_TO]-(cn:Concept)
			WHERE p.uuid in {identifierValues}
			RETURN cn.prefUUID as prefUUID, cn.prefUUID AS UUID, labels(cn) AS TYPES, {labels:["UPP"], value:p.uuid} as IDENTIFIERS
		`
	}

	query := &neoism.CypherQuery{
		Statement: readByAuthorityQueryStatement,
		Parameters: neoism.Props{
			"identifierValues": identifierValues,
			"authority":        authorityProperty,
		},
		Result: &results,
	}

	err = pcw.conn.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up Concordances with query %s from neoism: %+v\n", query.Statement, err)
		return Concordances{}, false, fmt.Errorf("Error accessing Concordance datastore for identifier:")
	}

	log.Info(results)

	if (len(results)) == 0 {
		return Concordances{}, false, nil
	}

	concordances = Concordances{
		Concordance: []Concordance{},
	}

	for _, neoCon := range results {
		log.Info(neoCon)
		// Each record is now two identifiers, one UPP and one other.
		var con = Concordance{}
		var concept = Concept{}
		concept.ID = mapper.IDURL(neoCon.PrefUUID)
		concept.APIURL = mapper.APIURL(neoCon.PrefUUID, neoCon.Types, pcw.env)
		con.Concept = concept
		con.Identifier = Identifier{Authority: mapNeoLabelsToAuthorityValue(neoCon.NeoIdentifier.Labels), IdentifierValue: neoCon.NeoIdentifier.Value}
		concordances.Concordance = append(concordances.Concordance, con)
	}

	if (len(concordances.Concordance)) == 0 {
		return Concordances{}, false, nil
	}

	log.Debugf("Returning %v", concordances)
	return concordances, true, nil
}

func (pcw CypherDriver) readByAuthorityOldModel(authority string, identifierValues []string) (concordances Concordances, found bool, err error) {
	log.Info("readByAuthorityOldModel")
	concordances = Concordances{}
	results := []neoReadStruct{}

	identifierLabel := mapAuthorityToIdentifierLabel(authority)

	if identifierLabel == "" {
		return Concordances{}, false, nil
	}

	readByAuthorityQueryStatement := fmt.Sprintf(`
		MATCH (p:Concept)<-[:IDENTIFIES]-(i:%s)
		WHERE i.value in {identifierValues}
		RETURN p.uuid as prefUUID, p.uuid AS UUID, labels(p) AS TYPES, {labels:labels(i), value:i.value} as IDENTIFIERS
		`, identifierLabel)

	query := &neoism.CypherQuery{
		Statement: readByAuthorityQueryStatement,
		Parameters: neoism.Props{
			"identifierValues": identifierValues,
			"authority":        authority,
		},
		Result: &results,
	}
	return processCypherQueryToConcordances(pcw, query, &results)
}

func processCypherQueryToConcordances(pcw CypherDriver, q *neoism.CypherQuery, results *[]neoReadStruct) (concordances Concordances, found bool, err error) {
	err = pcw.conn.CypherBatch([]*neoism.CypherQuery{q})
	if err != nil {
		log.Errorf("Error looking up Concordances with query %s from neoism: %+v\n", q.Statement, err)
		return Concordances{}, false, fmt.Errorf("Error accessing Concordance datastore for identifier:")
	}

	if (len(*results)) == 0 {
		return Concordances{}, false, nil
	}

	concordances = neoReadStructToConcordances(results, pcw.env)

	if (len(concordances.Concordance)) == 0 {
		return Concordances{}, false, nil
	}

	log.Debugf("Returning %v", concordances)
	return concordances, true, nil
}

func neoReadStructToConcordances(neo *[]neoReadStruct, env string) (concordances Concordances) {
	log.Debug("Running the old model")
	concordances = Concordances{
		Concordance: []Concordance{},
	}
	for _, neoCon := range *neo {
		var con = Concordance{}
		var concept = Concept{}
		concept.ID = mapper.IDURL(neoCon.UUID)
		concept.APIURL = mapper.APIURL(neoCon.UUID, neoCon.Types, env)
		con.Concept = concept
		con.Identifier = Identifier{Authority: mapNeoLabelsToAuthorityValue(neoCon.NeoIdentifier.Labels), IdentifierValue: neoCon.NeoIdentifier.Value}
		concordances.Concordance = append(concordances.Concordance, con)
	}
	return concordances
}

func mapNeoLabelsToAuthorityValue(labelNames []string) (authority string) {
	for _, label := range labelNames {
		switch label {
		// Old style node label lookup
		case TME_ID_NODE_LABEL:
			return TME_AUTHORITY
		case FS_ID_NODE_LABEL:
			return FS_AUTHORITY
		case UP_ID_NODE_LABEL:
			return UP_AUTHORITY
		case LEI_ID_NODE_LABEL:
			return LEI_AUTHORITY
		case SL_ID_NODE_LABEL:
			return SL_AUTHORITY

		// New style authority properties
		case FS_AUTHORITY_PROPERTY:
			return FS_AUTHORITY
		case UP_AUTHORITY_PROPERTY:
			return UP_AUTHORITY
		case SL_AUTHORITY_PROPERTY:
			return SL_AUTHORITY
		case TME_AUTHORITY_PROPERTY:
			return TME_AUTHORITY
		}
	}
	return ""
}

func mapAuthorityToAuthorityProperty(authority string) string {
	switch authority {
	case TME_AUTHORITY:
		return TME_AUTHORITY_PROPERTY
	case FS_AUTHORITY:
		return FS_AUTHORITY_PROPERTY
	case UP_AUTHORITY:
		return UP_AUTHORITY_PROPERTY
	case SL_AUTHORITY:
		return SL_AUTHORITY_PROPERTY
	default:
		return ""
	}
}

func mapAuthorityToIdentifierLabel(authority string) (label string) {
	switch authority {
	case TME_AUTHORITY:
		return TME_ID_NODE_LABEL
	case FS_AUTHORITY:
		return FS_ID_NODE_LABEL
	case UP_AUTHORITY:
		return UP_ID_NODE_LABEL
	case LEI_AUTHORITY:
		return LEI_ID_NODE_LABEL
	}
	return ""
}

const TME_AUTHORITY = "http://api.ft.com/system/FT-TME"
const FS_AUTHORITY = "http://api.ft.com/system/FACTSET"
const UP_AUTHORITY = "http://api.ft.com/system/UPP"
const LEI_AUTHORITY = "http://api.ft.com/system/LEI"
const SL_AUTHORITY = "http://api.ft.com/system/SMARTLOGIC"

const TME_ID_NODE_LABEL = "TMEIdentifier"
const FS_ID_NODE_LABEL = "FactsetIdentifier"
const UP_ID_NODE_LABEL = "UPPIdentifier"
const LEI_ID_NODE_LABEL = "LegalEntityIdentifier"
const SL_ID_NODE_LABEL = "SmartlogicIdentifier"

const TME_AUTHORITY_PROPERTY = "TME"
const FS_AUTHORITY_PROPERTY = "FACTSET"
const UP_AUTHORITY_PROPERTY = "UPP"
const SL_AUTHORITY_PROPERTY = "Smartlogic"
<|endoftext|>


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

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"runtime/pprof"
	"strings"

	flag "github.com/ogier/pflag"

	. "github.com/yzhs/alexandria-go"
	render "github.com/yzhs/alexandria-go/render/xelatex"
)

func printStats() string {
	stats := render.ComputeStatistics()
	n := stats.Num()
	size := float32(stats.Size()) / 1024.0
	return fmt.Sprintf("The library contains %v scrolls with a total size of %.1f kiB.\n", n, size)
}

func statsHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, printStats())
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	headers := w.Header()
	headers["Content-Type"] = []string{"application/x-alexandria-edit"}
	id := r.FormValue("id")
	fmt.Fprintf(w, id)
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
	html, err := loadHtmlTemplate("main")
	if err != nil {
		fmt.Fprintf(w, "%v", err)
		return
	}
	fmt.Fprintln(w, string(html))
}

func loadHtmlTemplate(name string) ([]byte, error) {
	return ioutil.ReadFile(Config.TemplateDirectory + name + ".html")
}

type result struct {
	Query      string
	Matches    []Id
	NumMatches int
}

func renderTemplate(w http.ResponseWriter, templateFile string, resultData result) {
	t, err := template.ParseFiles(Config.TemplateDirectory + templateFile + ".html")
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
		return
	}
	t.Execute(w, resultData)
}

func min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

func queryHandler(w http.ResponseWriter, r *http.Request) {
	query := r.FormValue("q")
	ids, err := render.FindScrolls(strings.Split(query, " "))
	if err != nil {
		panic(err)
	}
	data := result{Query: query, NumMatches: len(ids), Matches: ids[:min(20, len(ids))]}
	renderTemplate(w, "search", data)
}

func serveDirectory(prefix string, directory string) {
	http.Handle(prefix, http.StripPrefix(prefix, http.FileServer(http.Dir(directory))))
}

func main() {
	var profile, version bool
	flag.BoolVarP(&version, "version", "v", false, "\tShow version")
	flag.BoolVar(&profile, "profile", false, "\tEnable profiler")
	flag.Parse()

	InitConfig()
	Config.MaxResults = 20

	if profile {
		f, err := os.Create("alexandria.prof")
		if err != nil {
			panic(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// TODO run render.GenerateIndex() when there is something new

	if version {
		fmt.Println(NAME, VERSION)
		return
	}

	http.HandleFunc("/", mainHandler)
	http.HandleFunc("/stats", statsHandler)
	http.HandleFunc("/search", queryHandler)
	http.HandleFunc("/edit", editHandler)
	serveDirectory("/images/", Config.CacheDirectory)
	serveDirectory("/styles/", Config.TemplateDirectory+"styles")
	http.ListenAndServe("127.0.0.1:8080", nil)
}
<commit_msg>Change the port to 41665<commit_after>// Alexandria
//
// Copyright (C) 2015  Colin Benner
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"runtime/pprof"
	"strings"

	flag "github.com/ogier/pflag"

	. "github.com/yzhs/alexandria-go"
	render "github.com/yzhs/alexandria-go/render/xelatex"
)

func printStats() string {
	stats := render.ComputeStatistics()
	n := stats.Num()
	size := float32(stats.Size()) / 1024.0
	return fmt.Sprintf("The library contains %v scrolls with a total size of %.1f kiB.\n", n, size)
}

func statsHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, printStats())
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	headers := w.Header()
	headers["Content-Type"] = []string{"application/x-alexandria-edit"}
	id := r.FormValue("id")
	fmt.Fprintf(w, id)
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
	html, err := loadHtmlTemplate("main")
	if err != nil {
		fmt.Fprintf(w, "%v", err)
		return
	}
	fmt.Fprintln(w, string(html))
}

func loadHtmlTemplate(name string) ([]byte, error) {
	return ioutil.ReadFile(Config.TemplateDirectory + name + ".html")
}

type result struct {
	Query      string
	Matches    []Id
	NumMatches int
}

func renderTemplate(w http.ResponseWriter, templateFile string, resultData result) {
	t, err := template.ParseFiles(Config.TemplateDirectory + templateFile + ".html")
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
		return
	}
	t.Execute(w, resultData)
}

func min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

func queryHandler(w http.ResponseWriter, r *http.Request) {
	query := r.FormValue("q")
	ids, err := render.FindScrolls(strings.Split(query, " "))
	if err != nil {
		panic(err)
	}
	data := result{Query: query, NumMatches: len(ids), Matches: ids[:min(20, len(ids))]}
	renderTemplate(w, "search", data)
}

func serveDirectory(prefix string, directory string) {
	http.Handle(prefix, http.StripPrefix(prefix, http.FileServer(http.Dir(directory))))
}

func main() {
	var profile, version bool
	flag.BoolVarP(&version, "version", "v", false, "\tShow version")
	flag.BoolVar(&profile, "profile", false, "\tEnable profiler")
	flag.Parse()

	InitConfig()
	Config.MaxResults = 20

	if profile {
		f, err := os.Create("alexandria.prof")
		if err != nil {
			panic(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// TODO run render.GenerateIndex() when there is something new

	if version {
		fmt.Println(NAME, VERSION)
		return
	}

	http.HandleFunc("/", mainHandler)
	http.HandleFunc("/stats", statsHandler)
	http.HandleFunc("/search", queryHandler)
	http.HandleFunc("/edit", editHandler)
	serveDirectory("/images/", Config.CacheDirectory)
	serveDirectory("/styles/", Config.TemplateDirectory+"styles")
	http.ListenAndServe("127.0.0.1:41665", nil)
}
<|endoftext|>


<commit_before>package TeleGogo

import (
	"bytes"
	"encoding/json"
	"io"
	"mime/multipart"
	"os"
)

// createInputFileBody reads a file and creates a multipart writer for POSTing with the file and
// the form name
func createInputFileBody(path string, formName string) (*multipart.Writer, *bytes.Buffer, *os.File, error) {
	var buffer bytes.Buffer
	writer := multipart.NewWriter(&buffer)

	file, err := os.Open(path)
	if err != nil {
		return nil, nil, nil, err
	}

	fileUpload, err := writer.CreateFormFile(formName, path)
	if err != nil {
		return nil, nil, nil, err
	}
	if _, err := io.Copy(fileUpload, file); err != nil {
		return nil, nil, nil, err
	}
	return writer, &buffer, file, nil
}

// ResendPhotoArgs represents the optional and required arguments used when resending an already
// uploaded photo.
type ResendPhotoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// PhotoID Required. Photo to send. Pass a file ID to resend a photo that is already on the Telegram servers
	PhotoID string `json:"photo"`
	// Caption Optional. Photo caption. 0-200 chars.
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a *ResendPhotoArgs) toJSON() ([]byte, error) {
	return json.Marshal(*a)
}

// SendPhotoArgs represents the optional and required arguments for the SendPhoto method
type SendPhotoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// PhotoID Required. Path to photo file on device.
	PhotoPath string
	// Caption Optional. Photo caption. 0-200 chars.
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a *SendPhotoArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, *os.File, error) {
	writer, buffer, file, err := createInputFileBody(a.PhotoPath, "photo")
	if err != nil {
		return nil, nil, nil, err
	}
	return writer, buffer, file, nil
}

// SendAudioArgs represents the optional and required arguments for the SendAudio method
type SendAudioArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string
	// AudioID Required. Path to audio file on device.
	AudioPath string
	// Duration Optional. Duration of the audio in seconds
	Duration int
	// Performer Optional.
	Performer string
	// Title Optional. Track name
	Title string
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string
}

func (a *SendAudioArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, *os.File, error) {
	writer, buffer, file, err := createInputFileBody(a.AudioPath, "audio")
	if err != nil {
		return nil, nil, nil, err
	}
	return writer, buffer, file, nil
}
<commit_msg>Changed SendAudioArgs to be able to be used for resending as well<commit_after>package TeleGogo

import (
	"bytes"
	"encoding/json"
	"io"
	"mime/multipart"
	"os"
)

// createInputFileBody reads a file and creates a multipart writer for POSTing with the file and
// the form name
func createInputFileBody(path string, formName string) (*multipart.Writer, *bytes.Buffer, *os.File, error) {
	var buffer bytes.Buffer
	writer := multipart.NewWriter(&buffer)

	file, err := os.Open(path)
	if err != nil {
		return nil, nil, nil, err
	}

	fileUpload, err := writer.CreateFormFile(formName, path)
	if err != nil {
		return nil, nil, nil, err
	}
	if _, err := io.Copy(fileUpload, file); err != nil {
		return nil, nil, nil, err
	}
	return writer, &buffer, file, nil
}

// ResendPhotoArgs represents the optional and required arguments used when resending an already
// uploaded photo.
type ResendPhotoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// PhotoID Required. Photo to send. Pass a file ID to resend a photo that is already on the Telegram servers
	PhotoID string `json:"photo"`
	// Caption Optional. Photo caption. 0-200 chars.
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a *ResendPhotoArgs) toJSON() ([]byte, error) {
	return json.Marshal(*a)
}

// SendPhotoArgs represents the optional and required arguments for the SendPhoto method
type SendPhotoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// PhotoID Required. Path to photo file on device.
	PhotoPath string
	// Caption Optional. Photo caption. 0-200 chars.
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a *SendPhotoArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, *os.File, error) {
	writer, buffer, file, err := createInputFileBody(a.PhotoPath, "photo")
	if err != nil {
		return nil, nil, nil, err
	}
	return writer, buffer, file, nil
}

// SendAudioArgs represents the optional and required arguments for the SendAudio method
type SendAudioArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// AudioPath Required. Path to audio file on device IF uploading a new file. If you are trying to
	// send an audio file which is already on Telegram's servers, please keep this field empty and
	// use the AudioFileID field.
	AudioPath string `json:"-"`
	// AudioFileID Required. File ID of the audio file you want to send. If you are trying to
	// send a new audio file which is not already on Telegram's servers that you know of, please keep
	// this field empty and use the AudioPath field instead
	AudioFileID string `json:"audio"`
	// Duration Optional. Duration of the audio in seconds
	Duration int `json:"duration,omitempty"`
	// Performer Optional.
	Performer string `json:"performer,omitempty"`
	// Title Optional. Track name
	Title string `json:"title,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a *SendAudioArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, *os.File, error) {
	writer, buffer, file, err := createInputFileBody(a.AudioPath, "audio")
	if err != nil {
		return nil, nil, nil, err
	}
	return writer, buffer, file, nil
}

func (a *SendAudioArgs) toJSON() ([]byte, error) {
	return json.Marshal(*a)
}
<|endoftext|>


<commit_before>package scummatlas

import (
	"binaryutils"
	"fmt"
	"image"
	"image/color"
	"io/ioutil"
	"log"
	"os"
)

const debugImage = false

const (
	_ = iota
	METHOD_UNKNOWN
	METHOD_UNCOMPRESSED
	METHOD_ONE
	METHOD_TWO
	HORIZONTAL
	VERTICAL
	TRANSP
	NO_TRANSP
)

func parsePalette(data []byte) color.Palette {
	var palette = make(color.Palette, 0, 256)
	for i := 0; i < len(data); i += 3 {
		color := color.RGBA{
			data[i],
			data[i+1],
			data[i+2],
			255,
		}
		palette = append(palette, color)
	}

	return palette
}

func parseImage(data []byte, zBuffers int, width int, height int, pal color.Palette, transpIndex uint8) *image.RGBA {
	// DISABLE LOGGING
	log.SetOutput(os.Stdout)
	log.SetOutput(ioutil.Discard)

	if string(data[8:12]) != "SMAP" {
		panic("No stripe table found")
	}

	stripeCount := width / 8
	log.Println("SmapSize", BE32(data, 12))

	log.Println("There should be ", stripeCount, "stripes")
	offsets := make([]int, 0, stripeCount)
	stripeOffset := 0
	for i := 0; i < stripeCount; i++ {
		stripeOffset = LE32(data, 16+4*i)
		offsets = append(offsets, stripeOffset)
	}

	img := image.NewRGBA(image.Rect(0, 0, width, height))

	log.Println("Decoding image")
	log.Println("Stripes information")
	log.Println("\n#ID\tCode\tMethod\tDirect\tPalInSz\tTransp")
	for i := 0; i < stripeCount; i++ {
		offset := offsets[i]
		size := len(data) - offset
		if i < stripeCount-1 {
			size = offsets[i+1] - offsets[i]
		}
		drawStripe(img, i, data[8+offset:8+offset+size], pal, transpIndex)
	}
	log.Println("image decoded")
	log.SetOutput(os.Stdout)
	return img
}

func drawStripe(img *image.RGBA, stripNumber int, data []byte, pal color.Palette, transpIndex uint8) {

	method, direction, transparent, paletteLength := getCompressionMethod(stripNumber, data[0])

	height := img.Rect.Size().Y
	totalPixels := 8 * height
	currentPixel := 0

	curPal := uint8(data[1])
	curSubs := uint8(1)

	setColor := func() {
		var x, y int
		if direction == HORIZONTAL {
			x = currentPixel % 8
			y = (currentPixel + 1) / 8
		} else {
			y = currentPixel % height
			x = currentPixel / height
		}
		if x == 7 {
			x = -1
		}
		x += 8 * stripNumber
		if curPal >= 0 {
			if transparent == TRANSP && curPal == transpIndex {
				log.Println("TRANSPARENT")
				img.Set(x, y, color.RGBA{0, 0, 0, 0})
			} else {
				img.Set(x, y, pal[curPal])
			}
		} else {
			panic("Out of palette")
		}
		currentPixel++
	}

	bs := binaryutils.NewBitStream(data[2:])

	if method == METHOD_TWO {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					palShift := bs.GetBits(3) - 4
					if palShift == 0 {
						length := int(bs.GetBits(8))
						for i := 0; i < length; i++ {
							setColor()
						}
					} else {
						curPal += palShift
						setColor()
					}
				} else { // 10 Read new palette index
					curPal = bs.GetBits(paletteLength)
					setColor()
				}
			} else { // 0 Draw next pixel with current palette index
				setColor()
			}
		}
	} else {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					if bs.GetBit() == 1 {
						//Negate the subtraction variable. Subtract it from the palette index, and draw the next pixel.
						curSubs = -curSubs
					}
					//Subtract the subtraction variable from the palette index, and draw the next pixel.
					curPal -= curSubs
				} else { //10, read new palette index
					curPal = bs.GetBits(paletteLength)
				}
			} else { // 0, draw next pixel with current palette index
			}
			setColor()
		}
	}
}

func getCompressionMethod(stripNumber int, code byte) (
	method int,
	direction int,
	transparent int,
	paletteLength uint8) {
	var substraction byte

	direction = HORIZONTAL
	method = METHOD_UNKNOWN
	transparent = NO_TRANSP
	substraction = 0x00
	switch {
	case code == 0x01:
		method = METHOD_UNCOMPRESSED
	case 0x0e <= code && code <= 0x12:
		direction = VERTICAL
		method = METHOD_ONE
		substraction = 0x0a
	case 0x18 <= code && code <= 0x1c:
		method = METHOD_ONE
		substraction = 0x14
	case 0x22 <= code && code <= 0x26:
		method = METHOD_ONE
		direction = VERTICAL
		transparent = TRANSP
		substraction = 0x1e
	case 0x2c <= code && code <= 0x30:
		method = METHOD_ONE
		transparent = TRANSP
		substraction = 0x28
	case 0x40 <= code && code <= 0x44:
		method = METHOD_TWO
		substraction = 0x3c
	case 0x54 <= code && code <= 0x58:
		transparent = TRANSP
		method = METHOD_TWO
		substraction = 0x50
	case 0x68 <= code && code <= 0x6c:
		transparent = TRANSP
		method = METHOD_TWO
		substraction = 0x64
	case 0x7c <= code && code <= 0x80:
		method = METHOD_TWO
		substraction = 0x78
	}
	out := fmt.Sprintf("%v\t0x%X\t", stripNumber, code)
	if method == METHOD_ONE {
		out += "   1"
	} else if method == METHOD_TWO {
		out += "   2"
	}
	if direction == VERTICAL {
		out += "\tVert"
	} else {
		out += "\tHoriz"
	}
	out += "\t" + string(code-substraction)
	if transparent == TRANSP {
		out += "\tYes"
	} else {
		out += "\tNo"
	}
	log.Println(out)
	return method, direction, transparent, code - substraction
}
<commit_msg>Add testing and fix substraction value<commit_after>package scummatlas

import (
	"binaryutils"
	"fmt"
	"image"
	"image/color"
	"io/ioutil"
	"log"
	"os"
)

const debugImage = false

const (
	_ = iota
	METHOD_UNKNOWN
	METHOD_UNCOMPRESSED
	METHOD_ONE
	METHOD_TWO
	HORIZONTAL
	VERTICAL
	TRANSP
	NO_TRANSP
)

func parsePalette(data []byte) color.Palette {
	var palette = make(color.Palette, 0, 256)
	for i := 0; i < len(data); i += 3 {
		color := color.RGBA{
			data[i],
			data[i+1],
			data[i+2],
			255,
		}
		palette = append(palette, color)
	}

	return palette
}

func parseImage(data []byte, zBuffers int, width int, height int, pal color.Palette, transpIndex uint8) *image.RGBA {
	// DISABLE LOGGING
	log.SetOutput(os.Stdout)
	log.SetOutput(ioutil.Discard)

	if string(data[8:12]) != "SMAP" {
		panic("No stripe table found")
	}

	stripeCount := width / 8
	log.Println("SmapSize", BE32(data, 12))

	log.Println("There should be ", stripeCount, "stripes")
	offsets := make([]int, 0, stripeCount)
	stripeOffset := 0
	for i := 0; i < stripeCount; i++ {
		stripeOffset = LE32(data, 16+4*i)
		offsets = append(offsets, stripeOffset)
	}

	img := image.NewRGBA(image.Rect(0, 0, width, height))

	log.Println("Decoding image")
	log.Println("Stripes information")
	log.Println("\n#ID\tCode\tMethod\tDirect\tPalInSz\tTransp")
	for i := 0; i < stripeCount; i++ {
		offset := offsets[i]
		size := len(data) - offset
		if i < stripeCount-1 {
			size = offsets[i+1] - offsets[i]
		}
		drawStripe(img, i, data[8+offset:8+offset+size], pal, transpIndex)
	}
	log.Println("image decoded")
	log.SetOutput(os.Stdout)
	return img
}

func drawStripe(img *image.RGBA, stripNumber int, data []byte, pal color.Palette, transpIndex uint8) {

	method, direction, transparent, paletteLength := getCompressionMethod(stripNumber, data[0])

	height := img.Rect.Size().Y
	totalPixels := 8 * height
	currentPixel := 0

	curPal := uint8(data[1])
	curSubs := uint8(1)

	setColor := func() {
		var x, y int
		if direction == HORIZONTAL {
			x = currentPixel % 8
			y = (currentPixel + 1) / 8
		} else {
			y = currentPixel % height
			x = currentPixel / height
		}
		if x == 7 {
			x = -1
		}
		x += 8 * stripNumber
		if curPal >= 0 {
			if transparent == TRANSP && curPal == transpIndex {
				log.Println("TRANSPARENT")
				img.Set(x, y, color.RGBA{0, 0, 0, 0})
			} else {
				img.Set(x, y, pal[curPal])
			}
		} else {
			panic("Out of palette")
		}
		currentPixel++
	}

	bs := binaryutils.NewBitStream(data[2:])

	if method == METHOD_TWO {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					palShift := bs.GetBits(3) - 4
					if palShift == 0 {
						length := int(bs.GetBits(8))
						for i := 0; i < length; i++ {
							setColor()
						}
					} else {
						curPal += palShift
						setColor()
					}
				} else { // 10 Read new palette index
					curPal = bs.GetBits(paletteLength)
					setColor()
				}
			} else { // 0 Draw next pixel with current palette index
				setColor()
			}
		}
	} else {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					if bs.GetBit() == 1 {
						//Negate the subtraction variable. Subtract it from the palette index, and draw the next pixel.
						curSubs = -curSubs
					}
					//Subtract the subtraction variable from the palette index, and draw the next pixel.
					curPal -= curSubs
				} else { //10, read new palette index
					curPal = bs.GetBits(paletteLength)
				}
			} else { // 0, draw next pixel with current palette index
			}
			setColor()
		}
	}
}

func getCompressionMethod(stripNumber int, code byte) (
	method int,
	direction int,
	transparent int,
	paletteLength uint8) {
	var substraction byte

	direction = HORIZONTAL
	method = METHOD_UNKNOWN
	transparent = NO_TRANSP
	substraction = 0x00
	if code == 0x01 {
		method = METHOD_UNCOMPRESSED
	} else if code >= 0x0e && code <= 0x30 {
		method = METHOD_ONE
	} else if code >= 0x40 && code <= 0x80 {
		method = METHOD_TWO
	}
	switch {
	case code == 0x01:
	case 0x0e <= code && code <= 0x12:
		direction = VERTICAL
		substraction = 0x0a
	case 0x18 <= code && code <= 0x1c:
		substraction = 0x14
	case 0x22 <= code && code <= 0x26:
		direction = VERTICAL
		transparent = TRANSP
		substraction = 0x1e
	case 0x2c <= code && code <= 0x30:
		transparent = TRANSP
		substraction = 0x28
	case 0x40 <= code && code <= 0x44:
		substraction = 0x3c
	case 0x54 <= code && code <= 0x58:
		transparent = TRANSP
		substraction = 0x50
	case 0x68 <= code && code <= 0x6c:
		transparent = TRANSP
		substraction = 0x64
	case 0x7c <= code && code <= 0x80:
		substraction = 0x78
	}
	out := fmt.Sprintf("%v\t0x%X\t", stripNumber, code)
	if method == METHOD_ONE {
		out += "   1"
	} else if method == METHOD_TWO {
		out += "   2"
	}
	if direction == VERTICAL {
		out += "\tVert"
	} else {
		out += "\tHoriz"
	}
	out += "\t" + string(code-substraction)
	if transparent == TRANSP {
		out += "\tYes"
	} else {
		out += "\tNo"
	}
	log.Println(out)
	return method, direction, transparent, code - substraction
}
<|endoftext|>


<commit_before>package filesystem

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/shared/logger"
)

// Filesystem magic numbers.
const (
	FilesystemSuperMagicTmpfs = 0x01021994
	FilesystemSuperMagicExt4  = 0xEF53
	FilesystemSuperMagicXfs   = 0x58465342
	FilesystemSuperMagicNfs   = 0x6969
	FilesystemSuperMagicZfs   = 0x2fc12fc1
)

// StatVFS retrieves Virtual File System (VFS) info about a path.
func StatVFS(path string) (*unix.Statfs_t, error) {
	var st unix.Statfs_t

	err := unix.Statfs(path, &st)
	if err != nil {
		return nil, err
	}

	return &st, nil
}

// Detect returns the filesystem on which the passed-in path sits.
func Detect(path string) (string, error) {
	fs, err := StatVFS(path)
	if err != nil {
		return "", err
	}

	return FSTypeToName(fs.Type)
}

// FSTypeToName returns the name of the given fs type.
func FSTypeToName(fsType int64) (string, error) {
	switch fsType {
	case FilesystemSuperMagicBtrfs:
		return "btrfs", nil
	case FilesystemSuperMagicZfs:
		return "zfs", nil
	case FilesystemSuperMagicTmpfs:
		return "tmpfs", nil
	case FilesystemSuperMagicExt4:
		return "ext4", nil
	case FilesystemSuperMagicXfs:
		return "xfs", nil
	case FilesystemSuperMagicNfs:
		return "nfs", nil
	}

	logger.Debugf("Unknown backing filesystem type: 0x%x", fsType)
	return fmt.Sprintf("0x%x", fsType), nil
}

func parseMountinfo(name string) int {
	// In case someone uses symlinks we need to look for the actual
	// mountpoint.
	actualPath, err := filepath.EvalSymlinks(name)
	if err != nil {
		return -1
	}

	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return -1
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		tokens := strings.Fields(line)
		if len(tokens) < 5 {
			return -1
		}
		cleanPath := filepath.Clean(tokens[4])
		if cleanPath == actualPath {
			return 1
		}
	}

	return 0
}

// IsMountPoint returns true if path is a mount point.
func IsMountPoint(path string) bool {
	// If we find a mount entry, it is obviously a mount point.
	ret := parseMountinfo(path)
	if ret == 1 {
		return true
	}

	// Get the stat details.
	stat, err := os.Stat(path)
	if err != nil {
		return false
	}

	rootStat, err := os.Lstat(path + "/..")
	if err != nil {
		return false
	}

	// If the directory has the same device as parent, then it's not a mountpoint.
	if stat.Sys().(*syscall.Stat_t).Dev == rootStat.Sys().(*syscall.Stat_t).Dev {
		return false
	}

	// Btrfs annoyingly uses a different Dev id for different subvolumes on the same mount.
	// So for btrfs, we require a matching mount entry in mountinfo.
	fs, _ := Detect(path)
	if err == nil && fs == "btrfs" {
		return false
	}

	return true
}
<commit_msg>lxd/storage/filesystem/fs: Removes duplicated constants from unix package<commit_after>package filesystem

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/shared/logger"
)

// Filesystem magic numbers.
const (
	FilesystemSuperMagicZfs = 0x2fc12fc1
)

// StatVFS retrieves Virtual File System (VFS) info about a path.
func StatVFS(path string) (*unix.Statfs_t, error) {
	var st unix.Statfs_t

	err := unix.Statfs(path, &st)
	if err != nil {
		return nil, err
	}

	return &st, nil
}

// Detect returns the filesystem on which the passed-in path sits.
func Detect(path string) (string, error) {
	fs, err := StatVFS(path)
	if err != nil {
		return "", err
	}

	return FSTypeToName(fs.Type)
}

// FSTypeToName returns the name of the given fs type.
func FSTypeToName(fsType int64) (string, error) {
	switch fsType {
	case FilesystemSuperMagicBtrfs:
		return "btrfs", nil
	case FilesystemSuperMagicZfs:
		return "zfs", nil
	case FilesystemSuperMagicTmpfs:
		return "tmpfs", nil
	case FilesystemSuperMagicExt4:
		return "ext4", nil
	case FilesystemSuperMagicXfs:
		return "xfs", nil
	case FilesystemSuperMagicNfs:
		return "nfs", nil
	}

	logger.Debugf("Unknown backing filesystem type: 0x%x", fsType)
	return fmt.Sprintf("0x%x", fsType), nil
}

func parseMountinfo(name string) int {
	// In case someone uses symlinks we need to look for the actual
	// mountpoint.
	actualPath, err := filepath.EvalSymlinks(name)
	if err != nil {
		return -1
	}

	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return -1
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		tokens := strings.Fields(line)
		if len(tokens) < 5 {
			return -1
		}
		cleanPath := filepath.Clean(tokens[4])
		if cleanPath == actualPath {
			return 1
		}
	}

	return 0
}

// IsMountPoint returns true if path is a mount point.
func IsMountPoint(path string) bool {
	// If we find a mount entry, it is obviously a mount point.
	ret := parseMountinfo(path)
	if ret == 1 {
		return true
	}

	// Get the stat details.
	stat, err := os.Stat(path)
	if err != nil {
		return false
	}

	rootStat, err := os.Lstat(path + "/..")
	if err != nil {
		return false
	}

	// If the directory has the same device as parent, then it's not a mountpoint.
	if stat.Sys().(*syscall.Stat_t).Dev == rootStat.Sys().(*syscall.Stat_t).Dev {
		return false
	}

	// Btrfs annoyingly uses a different Dev id for different subvolumes on the same mount.
	// So for btrfs, we require a matching mount entry in mountinfo.
	fs, _ := Detect(path)
	if err == nil && fs == "btrfs" {
		return false
	}

	return true
}
<|endoftext|>