<commit_before>package xsearch

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func getTestFileContents() (string, error) {
	testFile := expandPath("~/src/git/xsearch/shared/testFiles/testFile2.txt")
	r, err1 := os.Open(testFile)
	if err1 != nil {
		return "", err1
	}
	bytes, err2 := ioutil.ReadAll(r)
	if err2 != nil {
		return "", err2
	}
	return string(bytes), err2
}

func getSettings() *SearchSettings {
	settings := GetDefaultSearchSettings()
	settings.AddSearchPattern("Searcher")
	settings.StartPath = "."
	return settings
}

func getSearcher() *Searcher {
	settings := getSettings()
	return NewSearcher(settings)
}

/*************************************************************
 * isSearchDir tests
 *************************************************************/

func TestIsSearchDir_SingleDot_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := "."
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_DoubleDot_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := ".."
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_IsHidden_False(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := ".git"
	if searcher.isSearchDir(&d) {
		t.Errorf("expected false")
	}
}

func TestIsSearchDir_IsHiddenIncludeHidden_True(t *testing.T) {
	settings := getSettings()
	settings.ExcludeHidden = false
	searcher := NewSearcher(settings)
	d := ".git"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_NoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := "/Users"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_MatchesInPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddInDirPattern("Search")
	searcher := NewSearcher(settings)
	d := "CsSearch"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_MatchesOutPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutDirPattern("Search")
	searcher := NewSearcher(settings)
	d := "CsSearch"
	if searcher.isSearchDir(&d) {
		t.Errorf("expected false")
	}
}

func TestIsSearchDir_DoesNotMatchInPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddInDirPattern("SearchFiles")
	searcher := NewSearcher(settings)
	d := "CsSearch"
	if searcher.isSearchDir(&d) {
		t.Errorf("expected false")
	}
}

func TestIsSearchDir_DoesNotMatchOutPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutDirPattern("SearchFiles")
	searcher := NewSearcher(settings)
	var d = "CsSearch"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

/*************************************************************
 * isSearchFile tests
*************************************************************/

func TestisSearchFile_NoExtensionsNoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_MatchesInExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddInExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_DoesNotMatchInExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddInExtension("java")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_MatchesOutExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_DoesNotMatchOutExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("java")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_MatchesInPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddInFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "Searcher.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_DoesNotMatchInPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddInFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_MatchesOutPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "Searcher.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_DoesNotMatchOutPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

/*************************************************************
 * isArchiveSearchFile tests
*************************************************************/

func TestIsArchiveSearchFile_NoExtensionsNoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_MatchesInExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchInExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveExtension("gz")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_MatchesOutExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchOutExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveExtension("gz")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_MatchesInPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveFilePattern("arch")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchInPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveFilePattern("archives")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_MatchesOutPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveFilePattern("arch")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchOutPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveFilePattern("archives")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

/*************************************************************
 * filterFile tests
*************************************************************/

func TestFilterFile_IsHidden_False(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := ".gitignore"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_IsHiddenIncludeHidden_True(t *testing.T) {
	settings := getSettings()
	settings.ExcludeHidden = false
	searcher := NewSearcher(settings)
	f := ".gitignore"
	fmt.Printf("isHidden(\"%s\"): %v\n", f, isHidden(f))
	fmt.Printf("isSearchFile(\"%s\"): %v\n", f, searcher.isSearchFile(&f))
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_ArchiveNoSearchArchives_False(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_ArchiveSearchArchives_True(t *testing.T) {
	settings := getSettings()
	settings.SearchArchives = true
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_IsArchiveSearchFile_True(t *testing.T) {
	settings := getSettings()
	settings.SearchArchives = true
	settings.AddInArchiveExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_NotIsArchiveSearchFile_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_ArchiveFileArchivesOnly_True(t *testing.T) {
	settings := getSettings()
	settings.ArchivesOnly = true
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_NoExtensionsNoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_isSearchFile_True(t *testing.T) {
	settings := getSettings()
	settings.AddInExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_NotisSearchFile_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_NonArchiveFileArchivesOnly_False(t *testing.T) {
	settings := getSettings()
	settings.ArchivesOnly = true
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

/*************************************************************
 * SearchTextReaderLines test
 *************************************************************/
func TestSearchTextReaderLines(t *testing.T) {
	contents, err := getTestFileContents()
	if err != nil {
		t.Errorf("error from getTestFileContents()")
		panic(err)
	}
	searcher := getSearcher()
	results := searcher.SearchTextReaderLines(strings.NewReader(contents))

	if len(results) != 2 {
		t.Errorf("len(results)=%d, expected=2", len(results))
	}

	firstResult := results[0]
	expectedFirstLineNum := 23
	if firstResult.LineNum != expectedFirstLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *firstResult,
			expectedFirstLineNum)
	}

	secondResult := results[1]
	expectedSecondLineNum := 29
	if secondResult.LineNum != expectedSecondLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *secondResult,
			expectedSecondLineNum)
	}
}

/*************************************************************
 * SearchMultiLineString test
 *************************************************************/
func TestSearchMultiLineString(t *testing.T) {
	contents, err := getTestFileContents()
	if err != nil {
		t.Errorf("error from getTestFileContents()")
	}
	searcher := getSearcher()
	results := searcher.SearchMultiLineString(contents)

	if len(results) != 2 {
		t.Errorf("len(results)=%d, expected=2", len(results))
	}

	firstResult := results[0]
	expectedFirstLineNum := 23
	if firstResult.LineNum != expectedFirstLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *firstResult,
			expectedFirstLineNum)
	}

	secondResult := results[1]
	expectedSecondLineNum := 29
	if secondResult.LineNum != expectedSecondLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *secondResult,
			expectedSecondLineNum)
	}
}
<commit_msg>Added unit testing match start and end index in go<commit_after>package xsearch

import (
	// "fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func getTestFileContents() (string, error) {
	testFile := expandPath("~/src/git/xsearch/shared/testFiles/testFile2.txt")
	r, err1 := os.Open(testFile)
	if err1 != nil {
		return "", err1
	}
	bytes, err2 := ioutil.ReadAll(r)
	if err2 != nil {
		return "", err2
	}
	return string(bytes), err2
}

func getSettings() *SearchSettings {
	settings := GetDefaultSearchSettings()
	settings.AddSearchPattern("Searcher")
	settings.StartPath = "."
	return settings
}

func getSearcher() *Searcher {
	settings := getSettings()
	return NewSearcher(settings)
}

/*************************************************************
 * isSearchDir tests
 *************************************************************/

func TestIsSearchDir_SingleDot_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := "."
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_DoubleDot_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := ".."
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_IsHidden_False(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := ".git"
	if searcher.isSearchDir(&d) {
		t.Errorf("expected false")
	}
}

func TestIsSearchDir_IsHiddenIncludeHidden_True(t *testing.T) {
	settings := getSettings()
	settings.ExcludeHidden = false
	searcher := NewSearcher(settings)
	d := ".git"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_NoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	d := "/Users"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_MatchesInPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddInDirPattern("Search")
	searcher := NewSearcher(settings)
	d := "CsSearch"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

func TestIsSearchDir_MatchesOutPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutDirPattern("Search")
	searcher := NewSearcher(settings)
	d := "CsSearch"
	if searcher.isSearchDir(&d) {
		t.Errorf("expected false")
	}
}

func TestIsSearchDir_DoesNotMatchInPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddInDirPattern("SearchFiles")
	searcher := NewSearcher(settings)
	d := "CsSearch"
	if searcher.isSearchDir(&d) {
		t.Errorf("expected false")
	}
}

func TestIsSearchDir_DoesNotMatchOutPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutDirPattern("SearchFiles")
	searcher := NewSearcher(settings)
	var d = "CsSearch"
	if !searcher.isSearchDir(&d) {
		t.Errorf("expected true")
	}
}

/*************************************************************
 * isSearchFile tests
*************************************************************/

func TestisSearchFile_NoExtensionsNoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_MatchesInExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddInExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_DoesNotMatchInExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddInExtension("java")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_MatchesOutExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_DoesNotMatchOutExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("java")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_MatchesInPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddInFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "Searcher.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestisSearchFile_DoesNotMatchInPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddInFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_MatchesOutPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "Searcher.cs"
	if searcher.isSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestisSearchFile_DoesNotMatchOutPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutFilePattern("Search")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.isSearchFile(&f) {
		t.Errorf("expected true")
	}
}

/*************************************************************
 * isArchiveSearchFile tests
*************************************************************/

func TestIsArchiveSearchFile_NoExtensionsNoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_MatchesInExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchInExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveExtension("gz")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_MatchesOutExtension_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchOutExtension_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveExtension("gz")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_MatchesInPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveFilePattern("arch")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchInPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddInArchiveFilePattern("archives")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_MatchesOutPattern_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveFilePattern("arch")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected false")
	}
}

func TestIsArchiveSearchFile_DoesNotMatchOutPattern_True(t *testing.T) {
	settings := getSettings()
	settings.AddOutArchiveFilePattern("archives")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.isArchiveSearchFile(&f) {
		t.Errorf("expected true")
	}
}

/*************************************************************
 * filterFile tests
*************************************************************/

func TestFilterFile_IsHidden_False(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := ".gitignore"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_IsHiddenIncludeHidden_True(t *testing.T) {
	settings := getSettings()
	settings.ExcludeHidden = false
	searcher := NewSearcher(settings)
	f := ".gitignore"
	// fmt.Printf("isHidden(\"%s\"): %v\n", f, isHidden(f))
	// fmt.Printf("isSearchFile(\"%s\"): %v\n", f, searcher.isSearchFile(&f))
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_ArchiveNoSearchArchives_False(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_ArchiveSearchArchives_True(t *testing.T) {
	settings := getSettings()
	settings.SearchArchives = true
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_IsArchiveSearchFile_True(t *testing.T) {
	settings := getSettings()
	settings.SearchArchives = true
	settings.AddInArchiveExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_NotIsArchiveSearchFile_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("zip")
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_ArchiveFileArchivesOnly_True(t *testing.T) {
	settings := getSettings()
	settings.ArchivesOnly = true
	searcher := NewSearcher(settings)
	f := "archive.zip"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_NoExtensionsNoPatterns_True(t *testing.T) {
	settings := getSettings()
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_isSearchFile_True(t *testing.T) {
	settings := getSettings()
	settings.AddInExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if !searcher.filterFile(&f) {
		t.Errorf("expected true")
	}
}

func TestFilterFile_NotisSearchFile_False(t *testing.T) {
	settings := getSettings()
	settings.AddOutExtension("cs")
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

func TestFilterFile_NonArchiveFileArchivesOnly_False(t *testing.T) {
	settings := getSettings()
	settings.ArchivesOnly = true
	searcher := NewSearcher(settings)
	f := "FileUtil.cs"
	if searcher.filterFile(&f) {
		t.Errorf("expected false")
	}
}

/*************************************************************
 * SearchTextReaderLines test
 *************************************************************/
func TestSearchTextReaderLines(t *testing.T) {
	contents, err := getTestFileContents()
	if err != nil {
		t.Errorf("error from getTestFileContents()")
		panic(err)
	}
	searcher := getSearcher()
	results := searcher.SearchTextReaderLines(strings.NewReader(contents))

	if len(results) != 2 {
		t.Errorf("len(results)=%d, expected=2", len(results))
	}

	firstResult := results[0]
	expectedFirstLineNum := 23
	if firstResult.LineNum != expectedFirstLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *firstResult,
			expectedFirstLineNum)
	}

	secondResult := results[1]
	expectedSecondLineNum := 29
	if secondResult.LineNum != expectedSecondLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *secondResult,
			expectedSecondLineNum)
	}
}

/*************************************************************
 * SearchMultiLineString test
 *************************************************************/
func TestSearchMultiLineString(t *testing.T) {
	contents, err := getTestFileContents()
	if err != nil {
		t.Errorf("error from getTestFileContents()")
	}
	searcher := getSearcher()
	results := searcher.SearchMultiLineString(contents)

	if len(results) != 2 {
		t.Errorf("len(results)=%d, expected=2", len(results))
	}

	firstResult := results[0]
	expectedFirstLineNum := 23
	if firstResult.LineNum != expectedFirstLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *firstResult,
			expectedFirstLineNum)
	}
	expectedFirstMatchStartIndex := 3
	if firstResult.MatchStartIndex != expectedFirstMatchStartIndex {
		t.Errorf("firstResult=%v, expected MatchStartIndex=%d", *firstResult,
			expectedFirstMatchStartIndex)
	}
	expectedFirstMatchEndIndex := 11
	if firstResult.MatchEndIndex != expectedFirstMatchEndIndex {
		t.Errorf("firstResult=%v, expected MatchEndIndex=%d", *firstResult,
			expectedFirstMatchEndIndex)
	}

	secondResult := results[1]
	expectedSecondLineNum := 29
	if secondResult.LineNum != expectedSecondLineNum {
		t.Errorf("firstResult=%v, expected LineNum=%d", *secondResult,
			expectedSecondLineNum)
	}
	expectedSecondMatchStartIndex := 24
	if secondResult.MatchStartIndex != expectedSecondMatchStartIndex {
		t.Errorf("firstResult=%v, expected MatchStartIndex=%d", *secondResult,
			expectedSecondMatchStartIndex)
	}
	expectedSecondMatchEndIndex := 32
	if secondResult.MatchEndIndex != expectedSecondMatchEndIndex {
		t.Errorf("secondResult=%v, expected MatchEndIndex=%d", *secondResult,
			expectedSecondMatchEndIndex)
	}
}
<|endoftext|>


<commit_before>package stripe

import (
	"koding/db/mongodb/modelhelper"
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"

	stripe "github.com/stripe/stripe-go"
	stripeCustomer "github.com/stripe/stripe-go/customer"
)

// CreateCustomer creates customer in Stripe and saves customer with
// Stripe's customer_id; token is previously acquired from Stripe,
// represents customer's cc info; accId is the `jAccount` id from mongo.
func CreateCustomer(token, accId, email string) (*paymentmodel.Customer, error) {
	if IsEmpty(token) {
		return nil, paymenterrors.ErrTokenIsEmpty
	}

	params := &stripe.CustomerParams{
		Desc:  accId,
		Email: email,
		Token: token,
	}

	account, err := modelhelper.GetAccountById(accId)
	if err == nil {
		params.Meta = map[string]string{
			"username": account.Profile.Nickname,
		}
	}

	externalCustomer, err := stripeCustomer.New(params)
	if err != nil {
		return nil, handleStripeError(err)
	}

	customerModel := &paymentmodel.Customer{
		OldId:              accId,
		ProviderCustomerId: externalCustomer.Id,
		Provider:           ProviderName,
	}

	err = customerModel.Create()
	if err != nil {
		return nil, err
	}

	return customerModel, nil
}

func FindCustomerByOldId(oldId string) (*paymentmodel.Customer, error) {
	customerModel := &paymentmodel.Customer{
		OldId: oldId,
	}

	exists, err := customerModel.ByOldId()
	if err != nil {
		return nil, err
	}

	if !exists {
		return nil, paymenterrors.ErrCustomerNotFound
	}

	return customerModel, nil
}

func GetCustomer(id string) (*stripe.Customer, error) {
	customer, err := stripeCustomer.Get(id, nil)
	if err != nil {
		return nil, handleStripeError(err)
	}

	return customer, nil
}

func DeleteCustomer(accId string) error {
	customer := paymentmodel.NewCustomer()
	err := customer.FindByOldId(accId)
	if err != nil {
		return err
	}

	currentSubscription, err := customer.FindActiveSubscription()
	if err != nil {
		return err
	}

	err = CancelSubscriptionAndRemoveCC(customer, currentSubscription)
	if err != nil {
		return err
	}

	err = stripeCustomer.Del(customer.ProviderCustomerId)

	return err
}
<commit_msg>payment: send more user data as metadata to stripe<commit_after>package stripe

import (
	"koding/db/mongodb/modelhelper"
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"

	stripe "github.com/stripe/stripe-go"
	stripeCustomer "github.com/stripe/stripe-go/customer"
)

// CreateCustomer creates customer in Stripe and saves customer with
// Stripe's customer_id; token is previously acquired from Stripe,
// represents customer's cc info; accId is the `jAccount` id from mongo.
func CreateCustomer(token, accId, email string) (*paymentmodel.Customer, error) {
	if IsEmpty(token) {
		return nil, paymenterrors.ErrTokenIsEmpty
	}

	params := &stripe.CustomerParams{
		Desc:  accId,
		Email: email,
		Token: token,
	}

	account, err := modelhelper.GetAccountById(accId)
	if err == nil {
		params.Meta = map[string]string{
			"username":  account.Profile.Nickname,
			"createdAt": account.Meta.CreatedAt.String(),
			"status":    account.Status,
			"firstName": account.Profile.FirstName,
			"lastName":  account.Profile.LastName,
		}
	}

	externalCustomer, err := stripeCustomer.New(params)
	if err != nil {
		return nil, handleStripeError(err)
	}

	customerModel := &paymentmodel.Customer{
		OldId:              accId,
		ProviderCustomerId: externalCustomer.Id,
		Provider:           ProviderName,
	}

	err = customerModel.Create()
	if err != nil {
		return nil, err
	}

	return customerModel, nil
}

func FindCustomerByOldId(oldId string) (*paymentmodel.Customer, error) {
	customerModel := &paymentmodel.Customer{
		OldId: oldId,
	}

	exists, err := customerModel.ByOldId()
	if err != nil {
		return nil, err
	}

	if !exists {
		return nil, paymenterrors.ErrCustomerNotFound
	}

	return customerModel, nil
}

func GetCustomer(id string) (*stripe.Customer, error) {
	customer, err := stripeCustomer.Get(id, nil)
	if err != nil {
		return nil, handleStripeError(err)
	}

	return customer, nil
}

func DeleteCustomer(accId string) error {
	customer := paymentmodel.NewCustomer()
	err := customer.FindByOldId(accId)
	if err != nil {
		return err
	}

	currentSubscription, err := customer.FindActiveSubscription()
	if err != nil {
		return err
	}

	err = CancelSubscriptionAndRemoveCC(customer, currentSubscription)
	if err != nil {
		return err
	}

	err = stripeCustomer.Del(customer.ProviderCustomerId)

	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package factory

import (
	"context"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/pkg/transport"
	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
	"google.golang.org/grpc"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/storage"
	"k8s.io/apiserver/pkg/storage/etcd3"
	"k8s.io/apiserver/pkg/storage/storagebackend"
	"k8s.io/apiserver/pkg/storage/value"
)

// The short keepalive timeout and interval have been chosen to aggressively
// detect a failed etcd server without introducing much overhead.
const keepaliveTime = 30 * time.Second
const keepaliveTimeout = 10 * time.Second

// dialTimeout is the timeout for failing to establish a connection.
// It is set to 20 seconds as times shorter than that will cause TLS connections to fail
// on heavily loaded arm64 CPUs (issue #64649)
const dialTimeout = 20 * time.Second

func newETCD3HealthCheck(c storagebackend.Config) (func() error, error) {
	// constructing the etcd v3 client blocks and times out if etcd is not available.
	// retry in a loop in the background until we successfully create the client, storing the client or error encountered

	clientValue := &atomic.Value{}

	clientErrMsg := &atomic.Value{}
	clientErrMsg.Store("etcd client connection not yet established")

	go wait.PollUntil(time.Second, func() (bool, error) {
		client, err := newETCD3Client(c.Transport)
		if err != nil {
			clientErrMsg.Store(err.Error())
			return false, nil
		}
		clientValue.Store(client)
		clientErrMsg.Store("")
		return true, nil
	}, wait.NeverStop)

	return func() error {
		if errMsg := clientErrMsg.Load().(string); len(errMsg) > 0 {
			return fmt.Errorf(errMsg)
		}
		client := clientValue.Load().(*clientv3.Client)
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancel()
		// See https://github.com/etcd-io/etcd/blob/master/etcdctl/ctlv3/command/ep_command.go#L118
		_, err := client.Get(ctx, "health")
		if err == nil {
			return nil
		}
		return fmt.Errorf("error getting data from etcd: %v", err)
	}, nil
}

func newETCD3Client(c storagebackend.TransportConfig) (*clientv3.Client, error) {
	tlsInfo := transport.TLSInfo{
		CertFile: c.CertFile,
		KeyFile:  c.KeyFile,
		CAFile:   c.CAFile,
	}
	tlsConfig, err := tlsInfo.ClientConfig()
	if err != nil {
		return nil, err
	}
	// NOTE: Client relies on nil tlsConfig
	// for non-secure connections, update the implicit variable
	if len(c.CertFile) == 0 && len(c.KeyFile) == 0 && len(c.CAFile) == 0 {
		tlsConfig = nil
	}
	cfg := clientv3.Config{
		DialTimeout:          dialTimeout,
		DialKeepAliveTime:    keepaliveTime,
		DialKeepAliveTimeout: keepaliveTimeout,
		DialOptions: []grpc.DialOption{
			grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
			grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
		},
		Endpoints: c.ServerList,
		TLS:       tlsConfig,
	}

	return clientv3.New(cfg)
}

type runningCompactor struct {
	interval time.Duration
	cancel   context.CancelFunc
	client   *clientv3.Client
	refs     int
}

var (
	lock       sync.Mutex
	compactors = map[string]*runningCompactor{}
)

// startCompactorOnce start one compactor per transport. If the interval get smaller on repeated calls, the
// compactor is replaced. A destroy func is returned. If all destroy funcs with the same transport are called,
// the compactor is stopped.
func startCompactorOnce(c storagebackend.TransportConfig, interval time.Duration) (func(), error) {
	lock.Lock()
	defer lock.Unlock()

	key := fmt.Sprintf("%v", c) // gives: {[server1 server2] keyFile certFile caFile}
	if compactor, foundBefore := compactors[key]; !foundBefore || compactor.interval > interval {
		compactorClient, err := newETCD3Client(c)
		if err != nil {
			return nil, err
		}

		if foundBefore {
			// replace compactor
			compactor.cancel()
			compactor.client.Close()
		} else {
			// start new compactor
			compactor = &runningCompactor{}
			compactors[key] = compactor
		}

		ctx, cancel := context.WithCancel(context.Background())

		compactor.interval = interval
		compactor.cancel = cancel
		compactor.client = compactorClient

		etcd3.StartCompactor(ctx, compactorClient, interval)
	}

	compactors[key].refs++

	return func() {
		lock.Lock()
		defer lock.Unlock()

		compactor := compactors[key]
		compactor.refs--
		if compactor.refs == 0 {
			compactor.cancel()
			compactor.client.Close()
			delete(compactors, key)
		}
	}, nil
}

func newETCD3Storage(c storagebackend.Config) (storage.Interface, DestroyFunc, error) {
	stopCompactor, err := startCompactorOnce(c.Transport, c.CompactionInterval)
	if err != nil {
		return nil, nil, err
	}

	client, err := newETCD3Client(c.Transport)
	if err != nil {
		stopCompactor()
		return nil, nil, err
	}

	var once sync.Once
	destroyFunc := func() {
		// we know that storage destroy funcs are called multiple times (due to reuse in subresources).
		// Hence, we only destroy once.
		// TODO: fix duplicated storage destroy calls higher level
		once.Do(func() {
			stopCompactor()
			client.Close()
		})
	}
	transformer := c.Transformer
	if transformer == nil {
		transformer = value.IdentityTransformer
	}
	return etcd3.New(client, c.Codec, c.Prefix, transformer, c.Paging), destroyFunc, nil
}
<commit_msg>Consider prefix in health check<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package factory

import (
	"context"
	"fmt"
	"path"
	"sync"
	"sync/atomic"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/pkg/transport"
	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
	"google.golang.org/grpc"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/storage"
	"k8s.io/apiserver/pkg/storage/etcd3"
	"k8s.io/apiserver/pkg/storage/storagebackend"
	"k8s.io/apiserver/pkg/storage/value"
)

// The short keepalive timeout and interval have been chosen to aggressively
// detect a failed etcd server without introducing much overhead.
const keepaliveTime = 30 * time.Second
const keepaliveTimeout = 10 * time.Second

// dialTimeout is the timeout for failing to establish a connection.
// It is set to 20 seconds as times shorter than that will cause TLS connections to fail
// on heavily loaded arm64 CPUs (issue #64649)
const dialTimeout = 20 * time.Second

func newETCD3HealthCheck(c storagebackend.Config) (func() error, error) {
	// constructing the etcd v3 client blocks and times out if etcd is not available.
	// retry in a loop in the background until we successfully create the client, storing the client or error encountered

	clientValue := &atomic.Value{}

	clientErrMsg := &atomic.Value{}
	clientErrMsg.Store("etcd client connection not yet established")

	go wait.PollUntil(time.Second, func() (bool, error) {
		client, err := newETCD3Client(c.Transport)
		if err != nil {
			clientErrMsg.Store(err.Error())
			return false, nil
		}
		clientValue.Store(client)
		clientErrMsg.Store("")
		return true, nil
	}, wait.NeverStop)

	return func() error {
		if errMsg := clientErrMsg.Load().(string); len(errMsg) > 0 {
			return fmt.Errorf(errMsg)
		}
		client := clientValue.Load().(*clientv3.Client)
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancel()
		// See https://github.com/etcd-io/etcd/blob/master/etcdctl/ctlv3/command/ep_command.go#L118
		_, err := client.Get(ctx, path.Join(c.Prefix, "health"))
		if err == nil {
			return nil
		}
		return fmt.Errorf("error getting data from etcd: %v", err)
	}, nil
}

func newETCD3Client(c storagebackend.TransportConfig) (*clientv3.Client, error) {
	tlsInfo := transport.TLSInfo{
		CertFile: c.CertFile,
		KeyFile:  c.KeyFile,
		CAFile:   c.CAFile,
	}
	tlsConfig, err := tlsInfo.ClientConfig()
	if err != nil {
		return nil, err
	}
	// NOTE: Client relies on nil tlsConfig
	// for non-secure connections, update the implicit variable
	if len(c.CertFile) == 0 && len(c.KeyFile) == 0 && len(c.CAFile) == 0 {
		tlsConfig = nil
	}
	cfg := clientv3.Config{
		DialTimeout:          dialTimeout,
		DialKeepAliveTime:    keepaliveTime,
		DialKeepAliveTimeout: keepaliveTimeout,
		DialOptions: []grpc.DialOption{
			grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
			grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
		},
		Endpoints: c.ServerList,
		TLS:       tlsConfig,
	}

	return clientv3.New(cfg)
}

type runningCompactor struct {
	interval time.Duration
	cancel   context.CancelFunc
	client   *clientv3.Client
	refs     int
}

var (
	lock       sync.Mutex
	compactors = map[string]*runningCompactor{}
)

// startCompactorOnce start one compactor per transport. If the interval get smaller on repeated calls, the
// compactor is replaced. A destroy func is returned. If all destroy funcs with the same transport are called,
// the compactor is stopped.
func startCompactorOnce(c storagebackend.TransportConfig, interval time.Duration) (func(), error) {
	lock.Lock()
	defer lock.Unlock()

	key := fmt.Sprintf("%v", c) // gives: {[server1 server2] keyFile certFile caFile}
	if compactor, foundBefore := compactors[key]; !foundBefore || compactor.interval > interval {
		compactorClient, err := newETCD3Client(c)
		if err != nil {
			return nil, err
		}

		if foundBefore {
			// replace compactor
			compactor.cancel()
			compactor.client.Close()
		} else {
			// start new compactor
			compactor = &runningCompactor{}
			compactors[key] = compactor
		}

		ctx, cancel := context.WithCancel(context.Background())

		compactor.interval = interval
		compactor.cancel = cancel
		compactor.client = compactorClient

		etcd3.StartCompactor(ctx, compactorClient, interval)
	}

	compactors[key].refs++

	return func() {
		lock.Lock()
		defer lock.Unlock()

		compactor := compactors[key]
		compactor.refs--
		if compactor.refs == 0 {
			compactor.cancel()
			compactor.client.Close()
			delete(compactors, key)
		}
	}, nil
}

func newETCD3Storage(c storagebackend.Config) (storage.Interface, DestroyFunc, error) {
	stopCompactor, err := startCompactorOnce(c.Transport, c.CompactionInterval)
	if err != nil {
		return nil, nil, err
	}

	client, err := newETCD3Client(c.Transport)
	if err != nil {
		stopCompactor()
		return nil, nil, err
	}

	var once sync.Once
	destroyFunc := func() {
		// we know that storage destroy funcs are called multiple times (due to reuse in subresources).
		// Hence, we only destroy once.
		// TODO: fix duplicated storage destroy calls higher level
		once.Do(func() {
			stopCompactor()
			client.Close()
		})
	}
	transformer := c.Transformer
	if transformer == nil {
		transformer = value.IdentityTransformer
	}
	return etcd3.New(client, c.Codec, c.Prefix, transformer, c.Paging), destroyFunc, nil
}
<|endoftext|>


<commit_before>// +build !ignore_autogenerated

/*

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// autogenerated by controller-gen object, do not modify manually

package v1alpha1

import (
	runtime "k8s.io/apimachinery/pkg/runtime"
)

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AffectedObject) DeepCopyInto(out *AffectedObject) {
	*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AffectedObject.
func (in *AffectedObject) DeepCopy() *AffectedObject {
	if in == nil {
		return nil
	}
	out := new(AffectedObject)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
	*out = *in
	if in.Affects != nil {
		in, out := &in.Affects, &out.Affects
		*out = make([]AffectedObject, len(*in))
		copy(*out, *in)
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
	if in == nil {
		return nil
	}
	out := new(Condition)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfiguration) DeepCopyInto(out *HierarchyConfiguration) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
	in.Spec.DeepCopyInto(&out.Spec)
	in.Status.DeepCopyInto(&out.Status)
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfiguration.
func (in *HierarchyConfiguration) DeepCopy() *HierarchyConfiguration {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfiguration)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *HierarchyConfiguration) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfigurationList) DeepCopyInto(out *HierarchyConfigurationList) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	out.ListMeta = in.ListMeta
	if in.Items != nil {
		in, out := &in.Items, &out.Items
		*out = make([]HierarchyConfiguration, len(*in))
		for i := range *in {
			(*in)[i].DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfigurationList.
func (in *HierarchyConfigurationList) DeepCopy() *HierarchyConfigurationList {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfigurationList)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *HierarchyConfigurationList) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfigurationSpec) DeepCopyInto(out *HierarchyConfigurationSpec) {
	*out = *in
	if in.RequiredChildren != nil {
		in, out := &in.RequiredChildren, &out.RequiredChildren
		*out = make([]string, len(*in))
		copy(*out, *in)
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfigurationSpec.
func (in *HierarchyConfigurationSpec) DeepCopy() *HierarchyConfigurationSpec {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfigurationSpec)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfigurationStatus) DeepCopyInto(out *HierarchyConfigurationStatus) {
	*out = *in
	if in.Children != nil {
		in, out := &in.Children, &out.Children
		*out = make([]string, len(*in))
		copy(*out, *in)
	}
	if in.Conditions != nil {
		in, out := &in.Conditions, &out.Conditions
		*out = make([]Condition, len(*in))
		for i := range *in {
			(*in)[i].DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfigurationStatus.
func (in *HierarchyConfigurationStatus) DeepCopy() *HierarchyConfigurationStatus {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfigurationStatus)
	in.DeepCopyInto(out)
	return out
}
<commit_msg>Minor change from controller-gen update that I missed<commit_after>// +build !ignore_autogenerated

/*

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Code generated by controller-gen. DO NOT EDIT.

package v1alpha1

import (
	runtime "k8s.io/apimachinery/pkg/runtime"
)

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AffectedObject) DeepCopyInto(out *AffectedObject) {
	*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AffectedObject.
func (in *AffectedObject) DeepCopy() *AffectedObject {
	if in == nil {
		return nil
	}
	out := new(AffectedObject)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
	*out = *in
	if in.Affects != nil {
		in, out := &in.Affects, &out.Affects
		*out = make([]AffectedObject, len(*in))
		copy(*out, *in)
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
	if in == nil {
		return nil
	}
	out := new(Condition)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfiguration) DeepCopyInto(out *HierarchyConfiguration) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
	in.Spec.DeepCopyInto(&out.Spec)
	in.Status.DeepCopyInto(&out.Status)
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfiguration.
func (in *HierarchyConfiguration) DeepCopy() *HierarchyConfiguration {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfiguration)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *HierarchyConfiguration) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfigurationList) DeepCopyInto(out *HierarchyConfigurationList) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	out.ListMeta = in.ListMeta
	if in.Items != nil {
		in, out := &in.Items, &out.Items
		*out = make([]HierarchyConfiguration, len(*in))
		for i := range *in {
			(*in)[i].DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfigurationList.
func (in *HierarchyConfigurationList) DeepCopy() *HierarchyConfigurationList {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfigurationList)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *HierarchyConfigurationList) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfigurationSpec) DeepCopyInto(out *HierarchyConfigurationSpec) {
	*out = *in
	if in.RequiredChildren != nil {
		in, out := &in.RequiredChildren, &out.RequiredChildren
		*out = make([]string, len(*in))
		copy(*out, *in)
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfigurationSpec.
func (in *HierarchyConfigurationSpec) DeepCopy() *HierarchyConfigurationSpec {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfigurationSpec)
	in.DeepCopyInto(out)
	return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HierarchyConfigurationStatus) DeepCopyInto(out *HierarchyConfigurationStatus) {
	*out = *in
	if in.Children != nil {
		in, out := &in.Children, &out.Children
		*out = make([]string, len(*in))
		copy(*out, *in)
	}
	if in.Conditions != nil {
		in, out := &in.Conditions, &out.Conditions
		*out = make([]Condition, len(*in))
		for i := range *in {
			(*in)[i].DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HierarchyConfigurationStatus.
func (in *HierarchyConfigurationStatus) DeepCopy() *HierarchyConfigurationStatus {
	if in == nil {
		return nil
	}
	out := new(HierarchyConfigurationStatus)
	in.DeepCopyInto(out)
	return out
}
<|endoftext|>


<commit_before>package v1

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"zxq.co/ripple/rippleapi/common"
)

// This basically proxies requests from Medium's API and is used on Ripple's
// home page to display the latest blog posts.

type mediumResp struct {
	Success bool `json:"success"`
	Payload struct {
		Posts []mediumPost `json:"posts"`
	} `json:"payload"`
}

type mediumPost struct {
	ID          string             `json:"id"`
	Creator     mediumUser         `json:"creator"`
	Title       string             `json:"title"`
	CreatedAt   int64              `json:"createdAt"`
	UpdatedAt   int64              `json:"updatedAt"`
	Virtuals    mediumPostVirtuals `json:"virtuals"`
	ImportedURL string             `json:"importedUrl"`
	UniqueSlug  string             `json:"uniqueSlug"`
}

type mediumUser struct {
	UserID   string `json:"userId"`
	Name     string `json:"name"`
	Username string `json:"username"`
}

type mediumPostVirtuals struct {
	CreatedAtRelative string  `json:"createdAtRelative"`
	UpdatedAtRelative string  `json:"updatedAtRelative"`
	Snippet           string  `json:"snippet"`
	WordCount         int     `json:"wordCount"`
	ReadingTime       float64 `json:"readingTime"`
}

// there's gotta be a better way

type blogPost struct {
	ID          string    `json:"id"`
	Creator     blogUser  `json:"creator"`
	Title       string    `json:"title"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	ImportedURL string    `json:"imported_url"`
	UniqueSlug  string    `json:"unique_slug"`

	CreatedAtRelative string  `json:"created_at_relative"`
	UpdatedAtRelative string  `json:"updated_at_relative"`
	Snippet           string  `json:"snippet"`
	WordCount         int     `json:"word_count"`
	ReadingTime       float64 `json:"reading_time"`
}

type blogUser struct {
	UserID   string `json:"user_id"`
	Name     string `json:"name"`
	Username string `json:"username"`
}

type blogPostsResponse struct {
	common.ResponseBase
	Posts []blogPost `json:"posts"`
}

// consts for the medium API
const (
	mediumAPIResponsePrefix = `])}while(1);</x>`
	mediumAPIAllPosts       = `https://blog.ripple.moe/latest?format=json`
)

func init() {
	gob.Register([]blogPost{})
}

// BlogPostsGET retrieves the latest blog posts on the Ripple blog.
func BlogPostsGET(md common.MethodData) common.CodeMessager {
	// check if posts are cached in redis
	res := md.R.Get("api:blog_posts").Val()
	if res != "" {
		// decode values
		posts := make([]blogPost, 0, 20)
		err := gob.NewDecoder(strings.NewReader(res)).Decode(&posts)
		if err != nil {
			md.Err(err)
			return Err500
		}

		// create response and return
		var r blogPostsResponse
		r.Code = 200
		r.Posts = blogLimit(posts, md.Query("l"))
		return r
	}

	// get data from medium api
	resp, err := http.Get(mediumAPIAllPosts)
	if err != nil {
		md.Err(err)
		return Err500
	}

	// read body and trim the prefix
	all, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		md.Err(err)
		return Err500
	}
	all = bytes.TrimPrefix(all, []byte(mediumAPIResponsePrefix))

	// unmarshal into response struct
	var mResp mediumResp
	err = json.Unmarshal(all, &mResp)
	if err != nil {
		md.Err(err)
		return Err500
	}

	if !mResp.Success {
		md.Err(errors.New("medium api call is not successful"))
		return Err500
	}

	// create posts slice and fill it up with converted posts from the medium
	// API
	posts := make([]blogPost, len(mResp.Payload.Posts))
	for idx, mp := range mResp.Payload.Posts {
		var p blogPost

		// convert structs
		p.ID = mp.ID
		p.Title = mp.Title
		p.CreatedAt = time.Unix(0, mp.CreatedAt*1000000)
		p.UpdatedAt = time.Unix(0, mp.UpdatedAt*1000000)
		p.ImportedURL = mp.ImportedURL
		p.UniqueSlug = mp.UniqueSlug

		p.Creator.UserID = mp.Creator.UserID
		p.Creator.Name = mp.Creator.Name
		p.Creator.Username = mp.Creator.Username

		p.CreatedAtRelative = mp.Virtuals.CreatedAtRelative
		p.UpdatedAtRelative = mp.Virtuals.UpdatedAtRelative
		p.Snippet = mp.Virtuals.Snippet
		p.WordCount = mp.Virtuals.WordCount
		p.ReadingTime = mp.Virtuals.ReadingTime

		posts[idx] = p
	}

	// save in redis
	bb := new(bytes.Buffer)
	err = gob.NewEncoder(bb).Encode(posts)
	if err != nil {
		md.Err(err)
		return Err500
	}
	md.R.Set("api:blog_posts", bb.Bytes(), time.Minute*5)

	var r blogPostsResponse
	r.Code = 200
	r.Posts = blogLimit(posts, md.Query("l"))
	return r
}

func blogLimit(posts []blogPost, s string) []blogPost {
	i := common.Int(s)
	if i >= len(posts) || i < 1 {
		return posts
	}
	return posts[:i]
}
<commit_msg>Fix author in posts not working, remove relative times<commit_after>package v1

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"zxq.co/ripple/rippleapi/common"
)

// This basically proxies requests from Medium's API and is used on Ripple's
// home page to display the latest blog posts.

type mediumResp struct {
	Success bool `json:"success"`
	Payload struct {
		Posts      []mediumPost `json:"posts"`
		References struct {
			User map[string]mediumUser
		} `json:"references"`
	} `json:"payload"`
}

type mediumPost struct {
	ID          string             `json:"id"`
	CreatorID   string             `json:"creatorId"`
	Title       string             `json:"title"`
	CreatedAt   int64              `json:"createdAt"`
	UpdatedAt   int64              `json:"updatedAt"`
	Virtuals    mediumPostVirtuals `json:"virtuals"`
	ImportedURL string             `json:"importedUrl"`
	UniqueSlug  string             `json:"uniqueSlug"`
}

type mediumUser struct {
	UserID   string `json:"userId"`
	Name     string `json:"name"`
	Username string `json:"username"`
}

type mediumPostVirtuals struct {
	Snippet     string  `json:"snippet"`
	WordCount   int     `json:"wordCount"`
	ReadingTime float64 `json:"readingTime"`
}

// there's gotta be a better way

type blogPost struct {
	ID          string    `json:"id"`
	Creator     blogUser  `json:"creator"`
	Title       string    `json:"title"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	ImportedURL string    `json:"imported_url"`
	UniqueSlug  string    `json:"unique_slug"`

	Snippet     string  `json:"snippet"`
	WordCount   int     `json:"word_count"`
	ReadingTime float64 `json:"reading_time"`
}

type blogUser struct {
	UserID   string `json:"user_id"`
	Name     string `json:"name"`
	Username string `json:"username"`
}

type blogPostsResponse struct {
	common.ResponseBase
	Posts []blogPost `json:"posts"`
}

// consts for the medium API
const (
	mediumAPIResponsePrefix = `])}while(1);</x>`
	mediumAPIAllPosts       = `https://blog.ripple.moe/latest?format=json`
)

func init() {
	gob.Register([]blogPost{})
}

// BlogPostsGET retrieves the latest blog posts on the Ripple blog.
func BlogPostsGET(md common.MethodData) common.CodeMessager {
	// check if posts are cached in redis
	res := md.R.Get("api:blog_posts").Val()
	if res != "" {
		// decode values
		posts := make([]blogPost, 0, 20)
		err := gob.NewDecoder(strings.NewReader(res)).Decode(&posts)
		if err != nil {
			md.Err(err)
			return Err500
		}

		// create response and return
		var r blogPostsResponse
		r.Code = 200
		r.Posts = blogLimit(posts, md.Query("l"))
		return r
	}

	// get data from medium api
	resp, err := http.Get(mediumAPIAllPosts)
	if err != nil {
		md.Err(err)
		return Err500
	}

	// read body and trim the prefix
	all, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		md.Err(err)
		return Err500
	}
	all = bytes.TrimPrefix(all, []byte(mediumAPIResponsePrefix))

	// unmarshal into response struct
	var mResp mediumResp
	err = json.Unmarshal(all, &mResp)
	if err != nil {
		md.Err(err)
		return Err500
	}

	if !mResp.Success {
		md.Err(errors.New("medium api call is not successful"))
		return Err500
	}

	// create posts slice and fill it up with converted posts from the medium
	// API
	posts := make([]blogPost, len(mResp.Payload.Posts))
	for idx, mp := range mResp.Payload.Posts {
		var p blogPost

		// convert structs
		p.ID = mp.ID
		p.Title = mp.Title
		p.CreatedAt = time.Unix(0, mp.CreatedAt*1000000)
		p.UpdatedAt = time.Unix(0, mp.UpdatedAt*1000000)
		p.ImportedURL = mp.ImportedURL
		p.UniqueSlug = mp.UniqueSlug

		cr := mResp.Payload.References.User[mp.CreatorID]
		p.Creator.UserID = cr.UserID
		p.Creator.Name = cr.Name
		p.Creator.Username = cr.Username

		p.Snippet = mp.Virtuals.Snippet
		p.WordCount = mp.Virtuals.WordCount
		p.ReadingTime = mp.Virtuals.ReadingTime

		posts[idx] = p
	}

	// save in redis
	bb := new(bytes.Buffer)
	err = gob.NewEncoder(bb).Encode(posts)
	if err != nil {
		md.Err(err)
		return Err500
	}
	md.R.Set("api:blog_posts", bb.Bytes(), time.Minute*5)

	var r blogPostsResponse
	r.Code = 200
	r.Posts = blogLimit(posts, md.Query("l"))
	return r
}

func blogLimit(posts []blogPost, s string) []blogPost {
	i := common.Int(s)
	if i >= len(posts) || i < 1 {
		return posts
	}
	return posts[:i]
}
<|endoftext|>


<commit_before>package tview

import (
	"sync"

	"github.com/gdamore/tcell"
)

// The size of the event/update/redraw channels.
const queueSize = 100

// Application represents the top node of an application.
//
// It is not strictly required to use this class as none of the other classes
// depend on it. However, it provides useful tools to set up an application and
// plays nicely with all widgets.
//
// The following command displays a primitive p on the screen until Ctrl-C is
// pressed:
//
//   if err := tview.NewApplication().SetRoot(p, true).Run(); err != nil {
//       panic(err)
//   }
type Application struct {
	sync.RWMutex

	// The application's screen.
	screen tcell.Screen

	// Indicates whether the application's screen is currently active. This is
	// false during suspended mode.
	running bool

	// The primitive which currently has the keyboard focus.
	focus Primitive

	// The root primitive to be seen on the screen.
	root Primitive

	// Whether or not the application resizes the root primitive.
	rootFullscreen bool

	// An optional capture function which receives a key event and returns the
	// event to be forwarded to the default input handler (nil if nothing should
	// be forwarded).
	inputCapture func(event *tcell.EventKey) *tcell.EventKey

	// An optional callback function which is invoked just before the root
	// primitive is drawn.
	beforeDraw func(screen tcell.Screen) bool

	// An optional callback function which is invoked after the root primitive
	// was drawn.
	afterDraw func(screen tcell.Screen)

	// Used to send screen events from separate goroutine to main event loop
	events chan tcell.Event

	// Functions queued from goroutines, used to serialize updates to primitives.
	updates chan func()

	// A channel which signals the end of the suspended mode.
	suspendToken chan struct{}
}

// NewApplication creates and returns a new application.
func NewApplication() *Application {
	return &Application{
		events:       make(chan tcell.Event, queueSize),
		updates:      make(chan func(), queueSize),
		suspendToken: make(chan struct{}, 1),
	}
}

// SetInputCapture sets a function which captures all key events before they are
// forwarded to the key event handler of the primitive which currently has
// focus. This function can then choose to forward that key event (or a
// different one) by returning it or stop the key event processing by returning
// nil.
//
// Note that this also affects the default event handling of the application
// itself: Such a handler can intercept the Ctrl-C event which closes the
// applicatoon.
func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
	a.inputCapture = capture
	return a
}

// GetInputCapture returns the function installed with SetInputCapture() or nil
// if no such function has been installed.
func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
	return a.inputCapture
}

// SetScreen allows you to provide your own tcell.Screen object. For most
// applications, this is not needed and you should be familiar with
// tcell.Screen when using this function. Run() will call Init() and Fini() on
// the provided screen object.
//
// This function is typically called before calling Run(). Calling it while an
// application is running will switch the application to the new screen. Fini()
// will be called on the old screen and Init() on the new screen (errors
// returned by Init() will lead to a panic).
//
// Note that calling Suspend() will invoke Fini() on your screen object and it
// will not be restored when suspended mode ends. Instead, a new default screen
// object will be created.
func (a *Application) SetScreen(screen tcell.Screen) *Application {
	a.Lock()
	defer a.Unlock()
	if a.running {
		a.screen.Fini()
	}
	a.screen = screen
	if a.running {
		if err := a.screen.Init(); err != nil {
			panic(err)
		}
	}
	return a
}

// Run starts the application and thus the event loop. This function returns
// when Stop() was called.
func (a *Application) Run() error {
	var err error
	a.Lock()

	// Make a screen if there is none yet.
	if a.screen == nil {
		a.screen, err = tcell.NewScreen()
		if err != nil {
			a.Unlock()
			return err
		}
	}
	if err = a.screen.Init(); err != nil {
		a.Unlock()
		return err
	}
	a.running = true

	// We catch panics to clean up because they mess up the terminal.
	defer func() {
		if p := recover(); p != nil {
			if a.screen != nil {
				a.screen.Fini()
			}
			a.running = false
			panic(p)
		}
	}()

	// Draw the screen for the first time.
	a.Unlock()
	a.draw()

	// Separate loop to wait for screen events.
	var wg sync.WaitGroup
	wg.Add(1)
	a.suspendToken <- struct{}{} // We need this to get started.
	go func() {
		defer wg.Done()
		for range a.suspendToken {
			for {
				a.RLock()
				screen := a.screen
				a.RUnlock()
				if screen == nil {
					// We have no screen. Let's stop.
					a.QueueEvent(nil)
					break
				}

				// Wait for next event and queue it.
				event := screen.PollEvent()
				if event != nil {
					// Regular event. Queue.
					a.QueueEvent(event)
					continue
				}

				// A screen was finalized (event is nil).
				a.RLock()
				running := a.running
				a.RUnlock()
				if running {
					// The application was stopped. End the event loop.
					a.QueueEvent(nil)
					return
				}

				// We're in suspended mode (running is false). Pause and wait for new
				// token.
				break
			}
		}
	}()

	// Start event loop.
EventLoop:
	for {
		select {
		case event := <-a.events:
			if event == nil {
				break EventLoop
			}

			switch event := event.(type) {
			case *tcell.EventKey:
				a.RLock()
				p := a.focus
				inputCapture := a.inputCapture
				a.RUnlock()

				// Intercept keys.
				if inputCapture != nil {
					event = inputCapture(event)
					if event == nil {
						continue // Don't forward event.
					}
				}

				// Ctrl-C closes the application.
				if event.Key() == tcell.KeyCtrlC {
					a.Stop()
				}

				// Pass other key events to the currently focused primitive.
				if p != nil {
					if handler := p.InputHandler(); handler != nil {
						handler(event, func(p Primitive) {
							a.SetFocus(p)
						})
						a.draw()
					}
				}
			case *tcell.EventResize:
				a.RLock()
				screen := a.screen
				a.RUnlock()
				if screen == nil {
					continue
				}
				screen.Clear()
				a.draw()
			}

		// If we have updates, now is the time to execute them.
		case updater := <-a.updates:
			updater()
		}
	}

	a.running = false
	close(a.suspendToken)
	wg.Wait()

	return nil
}

// Stop stops the application, causing Run() to return.
func (a *Application) Stop() {
	a.Lock()
	defer a.Unlock()
	screen := a.screen
	if screen == nil {
		return
	}
	a.screen = nil
	screen.Fini()
	// a.running is still true, the main loop will clean up.
}

// Suspend temporarily suspends the application by exiting terminal UI mode and
// invoking the provided function "f". When "f" returns, terminal UI mode is
// entered again and the application resumes.
//
// A return value of true indicates that the application was suspended and "f"
// was called. If false is returned, the application was already suspended,
// terminal UI mode was not exited, and "f" was not called.
func (a *Application) Suspend(f func()) bool {
	a.Lock()

	screen := a.screen
	if screen == nil {
		// Screen has not yet been initialized.
		a.Unlock()
		return false
	}

	// Enter suspended mode. Make a new screen here already so our event loop can
	// continue.
	a.screen = nil
	a.running = false
	screen.Fini()
	a.Unlock()

	// Wait for "f" to return.
	f()

	// Initialize our new screen and draw the contents.
	a.Lock()
	var err error
	a.screen, err = tcell.NewScreen()
	if err != nil {
		a.Unlock()
		panic(err)
	}
	if err = a.screen.Init(); err != nil {
		a.Unlock()
		panic(err)
	}
	a.running = true
	a.Unlock()
	a.draw()
	a.suspendToken <- struct{}{}
	// One key event will get lost, see https://github.com/gdamore/tcell/issues/194

	// Continue application loop.
	return true
}

// Draw refreshes the screen (during the next update cycle). It calls the Draw()
// function of the application's root primitive and then syncs the screen
// buffer.
func (a *Application) Draw() *Application {
	a.QueueUpdate(func() {
		a.draw()
	})
	return a
}

// draw actually does what Draw() promises to do.
func (a *Application) draw() *Application {
	a.Lock()
	defer a.Unlock()

	screen := a.screen
	root := a.root
	fullscreen := a.rootFullscreen
	before := a.beforeDraw
	after := a.afterDraw

	// Maybe we're not ready yet or not anymore.
	if screen == nil || root == nil {
		return a
	}

	// Resize if requested.
	if fullscreen && root != nil {
		width, height := screen.Size()
		root.SetRect(0, 0, width, height)
	}

	// Call before handler if there is one.
	if before != nil {
		if before(screen) {
			screen.Show()
			return a
		}
	}

	// Draw all primitives.
	root.Draw(screen)

	// Call after handler if there is one.
	if after != nil {
		after(screen)
	}

	// Sync screen.
	screen.Show()

	return a
}

// SetBeforeDrawFunc installs a callback function which is invoked just before
// the root primitive is drawn during screen updates. If the function returns
// true, drawing will not continue, i.e. the root primitive will not be drawn
// (and an after-draw-handler will not be called).
//
// Note that the screen is not cleared by the application. To clear the screen,
// you may call screen.Clear().
//
// Provide nil to uninstall the callback function.
func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application {
	a.beforeDraw = handler
	return a
}

// GetBeforeDrawFunc returns the callback function installed with
// SetBeforeDrawFunc() or nil if none has been installed.
func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
	return a.beforeDraw
}

// SetAfterDrawFunc installs a callback function which is invoked after the root
// primitive was drawn during screen updates.
//
// Provide nil to uninstall the callback function.
func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application {
	a.afterDraw = handler
	return a
}

// GetAfterDrawFunc returns the callback function installed with
// SetAfterDrawFunc() or nil if none has been installed.
func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
	return a.afterDraw
}

// SetRoot sets the root primitive for this application. If "fullscreen" is set
// to true, the root primitive's position will be changed to fill the screen.
//
// This function must be called at least once or nothing will be displayed when
// the application starts.
//
// It also calls SetFocus() on the primitive.
func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
	a.Lock()
	a.root = root
	a.rootFullscreen = fullscreen
	if a.screen != nil {
		a.screen.Clear()
	}
	a.Unlock()

	a.SetFocus(root)

	return a
}

// ResizeToFullScreen resizes the given primitive such that it fills the entire
// screen.
func (a *Application) ResizeToFullScreen(p Primitive) *Application {
	a.RLock()
	width, height := a.screen.Size()
	a.RUnlock()
	p.SetRect(0, 0, width, height)
	return a
}

// SetFocus sets the focus on a new primitive. All key events will be redirected
// to that primitive. Callers must ensure that the primitive will handle key
// events.
//
// Blur() will be called on the previously focused primitive. Focus() will be
// called on the new primitive.
func (a *Application) SetFocus(p Primitive) *Application {
	a.Lock()
	if a.focus != nil {
		a.focus.Blur()
	}
	a.focus = p
	if a.screen != nil {
		a.screen.HideCursor()
	}
	a.Unlock()
	if p != nil {
		p.Focus(func(p Primitive) {
			a.SetFocus(p)
		})
	}

	return a
}

// GetFocus returns the primitive which has the current focus. If none has it,
// nil is returned.
func (a *Application) GetFocus() Primitive {
	a.RLock()
	defer a.RUnlock()
	return a.focus
}

// QueueUpdate is used to synchronize access to primitives from non-main
// goroutines. The provided function will be executed as part of the event loop
// and thus will not cause race conditions with other such update functions or
// the Draw() function.
//
// Note that Draw() is not implicitly called after the execution of f as that
// may not be desirable. You can call Draw() from f if the screen should be
// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
// up with an immediate refresh of the screen.
func (a *Application) QueueUpdate(f func()) *Application {
	a.updates <- f
	return a
}

// QueueUpdateDraw works like QueueUpdate() except it refreshes the screen
// immediately after executing f.
func (a *Application) QueueUpdateDraw(f func()) *Application {
	a.QueueUpdate(func() {
		f()
		a.draw()
	})
	return a
}

// QueueEvent sends an event to the Application event loop.
//
// It is not recommended for event to be nil.
func (a *Application) QueueEvent(event tcell.Event) *Application {
	a.events <- event
	return a
}
<commit_msg>Making suspended mode work again on Windows. Fixes #179<commit_after>package tview

import (
	"sync"

	"github.com/gdamore/tcell"
)

// The size of the event/update/redraw channels.
const queueSize = 100

// Application represents the top node of an application.
//
// It is not strictly required to use this class as none of the other classes
// depend on it. However, it provides useful tools to set up an application and
// plays nicely with all widgets.
//
// The following command displays a primitive p on the screen until Ctrl-C is
// pressed:
//
//   if err := tview.NewApplication().SetRoot(p, true).Run(); err != nil {
//       panic(err)
//   }
type Application struct {
	sync.RWMutex

	// The application's screen. Apart from Run(), this variable should never be
	// set directly. Always use the screenReplacement channel after calling
	// Fini(), to set a new screen (or nil to stop the application).
	screen tcell.Screen

	// The primitive which currently has the keyboard focus.
	focus Primitive

	// The root primitive to be seen on the screen.
	root Primitive

	// Whether or not the application resizes the root primitive.
	rootFullscreen bool

	// An optional capture function which receives a key event and returns the
	// event to be forwarded to the default input handler (nil if nothing should
	// be forwarded).
	inputCapture func(event *tcell.EventKey) *tcell.EventKey

	// An optional callback function which is invoked just before the root
	// primitive is drawn.
	beforeDraw func(screen tcell.Screen) bool

	// An optional callback function which is invoked after the root primitive
	// was drawn.
	afterDraw func(screen tcell.Screen)

	// Used to send screen events from separate goroutine to main event loop
	events chan tcell.Event

	// Functions queued from goroutines, used to serialize updates to primitives.
	updates chan func()

	// An object that the screen variable will be set to after Fini() was called.
	// Use this channel to set a new screen object for the application
	// (screen.Init() and draw() will be called implicitly). A value of nil will
	// stop the application.
	screenReplacement chan tcell.Screen
}

// NewApplication creates and returns a new application.
func NewApplication() *Application {
	return &Application{
		events:            make(chan tcell.Event, queueSize),
		updates:           make(chan func(), queueSize),
		screenReplacement: make(chan tcell.Screen, 1),
	}
}

// SetInputCapture sets a function which captures all key events before they are
// forwarded to the key event handler of the primitive which currently has
// focus. This function can then choose to forward that key event (or a
// different one) by returning it or stop the key event processing by returning
// nil.
//
// Note that this also affects the default event handling of the application
// itself: Such a handler can intercept the Ctrl-C event which closes the
// applicatoon.
func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
	a.inputCapture = capture
	return a
}

// GetInputCapture returns the function installed with SetInputCapture() or nil
// if no such function has been installed.
func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
	return a.inputCapture
}

// SetScreen allows you to provide your own tcell.Screen object. For most
// applications, this is not needed and you should be familiar with
// tcell.Screen when using this function.
//
// This function is typically called before the first call to Run(). Init() need
// not be called on the screen.
func (a *Application) SetScreen(screen tcell.Screen) *Application {
	if screen == nil {
		return a // Invalid input. Do nothing.
	}

	a.Lock()
	if a.screen == nil {
		// Run() has not been called yet.
		a.screen = screen
		a.Unlock()
		return a
	}

	// Run() is already in progress. Exchange screen.
	oldScreen := a.screen
	a.Unlock()
	oldScreen.Fini()
	a.screenReplacement <- screen

	return a
}

// Run starts the application and thus the event loop. This function returns
// when Stop() was called.
func (a *Application) Run() error {
	var err error
	a.Lock()

	// Make a screen if there is none yet.
	if a.screen == nil {
		a.screen, err = tcell.NewScreen()
		if err != nil {
			a.Unlock()
			return err
		}
		if err = a.screen.Init(); err != nil {
			a.Unlock()
			return err
		}
	}

	// We catch panics to clean up because they mess up the terminal.
	defer func() {
		if p := recover(); p != nil {
			if a.screen != nil {
				a.screen.Fini()
			}
			panic(p)
		}
	}()

	// Draw the screen for the first time.
	a.Unlock()
	a.draw()

	// Separate loop to wait for screen events.
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			a.RLock()
			screen := a.screen
			a.RUnlock()
			if screen == nil {
				// We have no screen. Let's stop.
				a.QueueEvent(nil)
				break
			}

			// Wait for next event and queue it.
			event := screen.PollEvent()
			if event != nil {
				// Regular event. Queue.
				a.QueueEvent(event)
				continue
			}

			// A screen was finalized (event is nil). Wait for a new scren.
			screen = <-a.screenReplacement
			if screen == nil {
				// No new screen. We're done.
				a.QueueEvent(nil)
				return
			}

			// We have a new screen. Keep going.
			a.Lock()
			a.screen = screen
			a.Unlock()

			// Initialize and draw this screen.
			if err := screen.Init(); err != nil {
				panic(err)
			}
			a.draw()
		}
	}()

	// Start event loop.
EventLoop:
	for {
		select {
		case event := <-a.events:
			if event == nil {
				break EventLoop
			}

			switch event := event.(type) {
			case *tcell.EventKey:
				a.RLock()
				p := a.focus
				inputCapture := a.inputCapture
				a.RUnlock()

				// Intercept keys.
				if inputCapture != nil {
					event = inputCapture(event)
					if event == nil {
						continue // Don't forward event.
					}
				}

				// Ctrl-C closes the application.
				if event.Key() == tcell.KeyCtrlC {
					a.Stop()
				}

				// Pass other key events to the currently focused primitive.
				if p != nil {
					if handler := p.InputHandler(); handler != nil {
						handler(event, func(p Primitive) {
							a.SetFocus(p)
						})
						a.draw()
					}
				}
			case *tcell.EventResize:
				a.RLock()
				screen := a.screen
				a.RUnlock()
				if screen == nil {
					continue
				}
				screen.Clear()
				a.draw()
			}

		// If we have updates, now is the time to execute them.
		case updater := <-a.updates:
			updater()
		}
	}

	// Wait for the event loop to finish.
	wg.Wait()
	a.screen = nil

	return nil
}

// Stop stops the application, causing Run() to return.
func (a *Application) Stop() {
	a.Lock()
	defer a.Unlock()
	screen := a.screen
	if screen == nil {
		return
	}
	a.screen = nil
	screen.Fini()
	a.screenReplacement <- nil
}

// Suspend temporarily suspends the application by exiting terminal UI mode and
// invoking the provided function "f". When "f" returns, terminal UI mode is
// entered again and the application resumes.
//
// A return value of true indicates that the application was suspended and "f"
// was called. If false is returned, the application was already suspended,
// terminal UI mode was not exited, and "f" was not called.
func (a *Application) Suspend(f func()) bool {
	a.RLock()
	screen := a.screen
	a.RUnlock()
	if screen == nil {
		return false // Screen has not yet been initialized.
	}

	// Enter suspended mode.
	screen.Fini()

	// Wait for "f" to return.
	f()

	// Make a new screen.
	var err error
	screen, err = tcell.NewScreen()
	if err != nil {
		panic(err)
	}
	a.screenReplacement <- screen
	// One key event will get lost, see https://github.com/gdamore/tcell/issues/194

	// Continue application loop.
	return true
}

// Draw refreshes the screen (during the next update cycle). It calls the Draw()
// function of the application's root primitive and then syncs the screen
// buffer.
func (a *Application) Draw() *Application {
	a.QueueUpdate(func() {
		a.draw()
	})
	return a
}

// draw actually does what Draw() promises to do.
func (a *Application) draw() *Application {
	a.Lock()
	defer a.Unlock()

	screen := a.screen
	root := a.root
	fullscreen := a.rootFullscreen
	before := a.beforeDraw
	after := a.afterDraw

	// Maybe we're not ready yet or not anymore.
	if screen == nil || root == nil {
		return a
	}

	// Resize if requested.
	if fullscreen && root != nil {
		width, height := screen.Size()
		root.SetRect(0, 0, width, height)
	}

	// Call before handler if there is one.
	if before != nil {
		if before(screen) {
			screen.Show()
			return a
		}
	}

	// Draw all primitives.
	root.Draw(screen)

	// Call after handler if there is one.
	if after != nil {
		after(screen)
	}

	// Sync screen.
	screen.Show()

	return a
}

// SetBeforeDrawFunc installs a callback function which is invoked just before
// the root primitive is drawn during screen updates. If the function returns
// true, drawing will not continue, i.e. the root primitive will not be drawn
// (and an after-draw-handler will not be called).
//
// Note that the screen is not cleared by the application. To clear the screen,
// you may call screen.Clear().
//
// Provide nil to uninstall the callback function.
func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application {
	a.beforeDraw = handler
	return a
}

// GetBeforeDrawFunc returns the callback function installed with
// SetBeforeDrawFunc() or nil if none has been installed.
func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
	return a.beforeDraw
}

// SetAfterDrawFunc installs a callback function which is invoked after the root
// primitive was drawn during screen updates.
//
// Provide nil to uninstall the callback function.
func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application {
	a.afterDraw = handler
	return a
}

// GetAfterDrawFunc returns the callback function installed with
// SetAfterDrawFunc() or nil if none has been installed.
func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
	return a.afterDraw
}

// SetRoot sets the root primitive for this application. If "fullscreen" is set
// to true, the root primitive's position will be changed to fill the screen.
//
// This function must be called at least once or nothing will be displayed when
// the application starts.
//
// It also calls SetFocus() on the primitive.
func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
	a.Lock()
	a.root = root
	a.rootFullscreen = fullscreen
	if a.screen != nil {
		a.screen.Clear()
	}
	a.Unlock()

	a.SetFocus(root)

	return a
}

// ResizeToFullScreen resizes the given primitive such that it fills the entire
// screen.
func (a *Application) ResizeToFullScreen(p Primitive) *Application {
	a.RLock()
	width, height := a.screen.Size()
	a.RUnlock()
	p.SetRect(0, 0, width, height)
	return a
}

// SetFocus sets the focus on a new primitive. All key events will be redirected
// to that primitive. Callers must ensure that the primitive will handle key
// events.
//
// Blur() will be called on the previously focused primitive. Focus() will be
// called on the new primitive.
func (a *Application) SetFocus(p Primitive) *Application {
	a.Lock()
	if a.focus != nil {
		a.focus.Blur()
	}
	a.focus = p
	if a.screen != nil {
		a.screen.HideCursor()
	}
	a.Unlock()
	if p != nil {
		p.Focus(func(p Primitive) {
			a.SetFocus(p)
		})
	}

	return a
}

// GetFocus returns the primitive which has the current focus. If none has it,
// nil is returned.
func (a *Application) GetFocus() Primitive {
	a.RLock()
	defer a.RUnlock()
	return a.focus
}

// QueueUpdate is used to synchronize access to primitives from non-main
// goroutines. The provided function will be executed as part of the event loop
// and thus will not cause race conditions with other such update functions or
// the Draw() function.
//
// Note that Draw() is not implicitly called after the execution of f as that
// may not be desirable. You can call Draw() from f if the screen should be
// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
// up with an immediate refresh of the screen.
func (a *Application) QueueUpdate(f func()) *Application {
	a.updates <- f
	return a
}

// QueueUpdateDraw works like QueueUpdate() except it refreshes the screen
// immediately after executing f.
func (a *Application) QueueUpdateDraw(f func()) *Application {
	a.QueueUpdate(func() {
		f()
		a.draw()
	})
	return a
}

// QueueEvent sends an event to the Application event loop.
//
// It is not recommended for event to be nil.
func (a *Application) QueueEvent(event tcell.Event) *Application {
	a.events <- event
	return a
}
<|endoftext|>


<commit_before>package web

import (
	"context"
	"crypto/tls"
	"errors"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var (
	_app  *Application
	_once sync.Once
)

// Callback function
type Callback func(ctx *Context)

// PanicCallback function
type PanicCallback func(http.ResponseWriter, *http.Request, interface{})

// Application is type of a web.Application
type Application struct {
	trees      map[string]*node
	logger     *log.Logger
	panic      PanicCallback
	paramsPool sync.Pool
	maxParams  uint16
	NotFound   http.Handler
}

// Create return a singleton web.Application
func Create() *Application {
	_once.Do(func() {
		_app = newApplication()
	})
	return _app
}

// newApplication return a web.Application
func newApplication() *Application {
	app := &Application{
		logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
	}
	return app
}

// SetLogger set Logger
func (app *Application) SetLogger(logger *log.Logger) {
	app.logger = logger
}

// SetPanic set Logger
func (app *Application) SetPanic(panic PanicCallback) {
	app.panic = panic
}

// Use Add the given callback function to this application.middlewares.
func (app *Application) Use(path string, callback Callback) {

}

// On add event
func (app *Application) On(name string, cb Callback) {

}

// Get method
func (app *Application) Get(path string, cb Callback) {
	app.addRoute(http.MethodGet, path, cb)
}

// Head method
func (app *Application) Head(path string, cb Callback) {
	app.addRoute(http.MethodHead, path, cb)
}

// Post method
func (app *Application) Post(path string, cb Callback) {
	app.addRoute(http.MethodPost, path, cb)
}

// Put method
func (app *Application) Put(path string, cb Callback) {
	app.addRoute(http.MethodPut, path, cb)
}

// Patch method
func (app *Application) Patch(path string, cb Callback) {
	app.addRoute(http.MethodPatch, path, cb)
}

// Delete method
func (app *Application) Delete(path string, cb Callback) {
	app.addRoute(http.MethodDelete, path, cb)
}

// Options method
func (app *Application) Options(path string, cb Callback) {
	app.addRoute(http.MethodOptions, path, cb)
}

func (app *Application) addRoute(method, path string, cb Callback) {

	if method == "" {
		panic("method must not be empty")
	}

	if len(path) < 1 || path[0] != '/' {
		panic("path must begin with '/' in path '" + path + "'")
	}

	if cb == nil {
		panic("callback must not be nil")
	}

	if app.trees == nil {
		app.trees = make(map[string]*node)
	}

	root := app.trees[method]

	if root == nil {
		root = new(node)
		app.trees[method] = root
	}

	root.addRoute(path, cb)

	if pc := countParams(path); pc > app.maxParams {
		app.maxParams = pc
	}

	if app.paramsPool.New == nil && app.maxParams > 0 {
		app.paramsPool.New = func() interface{} {
			ps := make(Params, 0, app.maxParams)
			return &ps
		}
	}
}

// ServeFiles ("/src/*filepath", http.Dir("/var/www"))
func (app *Application) ServeFiles(path string, root http.FileSystem) {
	if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
		panic("path must end with /*filepath in path '" + path + "'")
	}

	fileServer := http.FileServer(root)

	app.Get(path, func(ctx *Context) {
		ctx.Request.URL.Path = ctx.Param("filepath")
		fileServer.ServeHTTP(ctx.ResponseWriter, ctx.Request)
	})
}

// ServeHTTP w, r
func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	defer app.recv(w, r)
	path := r.URL.Path

	if root := app.trees[r.Method]; root != nil {

		if callback, params, _ := root.getValue(path, app.getParams); callback != nil {

			ctx := createContext(w, r, params)
			app.putParams(params)

			runTime := time.Now()

			callback(ctx)

			endTime := time.Now()

			app.logf("%s %s %s %s %s %s", r.Host, r.Method, path, endTime.Sub(startTime), runTime.Sub(startTime), endTime.Sub(runTime))

			return

		}
	}

	if app.NotFound != nil {
		app.NotFound.ServeHTTP(w, r)
	} else {
		http.NotFound(w, r)
	}
}

// ListenAndServe Serve with options on addr
func (app *Application) ListenAndServe(addr string, fns ...func(*http.Server)) error {

	l, err := net.Listen("tcp", addr)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(addr, l, fns...)
}

// ListenAndServeTLS Serve with tls and options on addr
func (app *Application) ListenAndServeTLS(addr string, tlsConfig *tls.Config, fns ...func(*http.Server)) error {

	l, err := tls.Listen("tcp", addr, tlsConfig)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(addr, l, fns...)
}

func (app *Application) serve(addr string, listener net.Listener, fns ...func(*http.Server)) error {

	mux := http.NewServeMux()

	mux.Handle("/", app)

	srv := &http.Server{
		Handler: mux,
	}

	for _, fn := range fns {
		fn(srv)
	}

	defer func() {
		err := srv.Close()
		if err != nil {
			app.logf("srv: %v", err)
		}
	}()

	idleConnsClosed := make(chan struct{})

	go func() {
		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
		<-sigint

		if err := srv.Shutdown(context.Background()); err != nil {
			app.logf("web.go: %v", err)
		}

		close(idleConnsClosed)
	}()

	app.logf("web.go(%d) %s", os.Getpid(), listener.Addr())

	if err := srv.Serve(listener); err != nil {
		app.logf("web.go: %v", err)
	}

	<-idleConnsClosed

	return errors.New("web.go: exit")
}

// Inspect method
func (app *Application) Inspect() string {
	return ""
}

func (app *Application) logf(format string, v ...interface{}) {

	if app.logger != nil {
		app.logger.Printf(format, v...)
	} else {
		log.Printf(format, v...)
	}
}

func (app *Application) getParams() *Params {
	ps := app.paramsPool.Get().(*Params)
	*ps = (*ps)[0:0] // reset slice
	return ps
}

func (app *Application) putParams(ps *Params) {
	if ps != nil {
		app.paramsPool.Put(ps)
	}
}

func (app *Application) recv(w http.ResponseWriter, r *http.Request) {
	if rcv := recover(); rcv != nil {
		if app.panic != nil {
			app.panic(w, r, rcv)
		} else {
			app.logf("%s %s %s: %v", r.Host, r.Method, r.URL.Path, rcv)
		}
	}
}
<commit_msg>remove time<commit_after>package web

import (
	"context"
	"crypto/tls"
	"errors"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

var (
	_app  *Application
	_once sync.Once
)

// Callback function
type Callback func(ctx *Context)

// PanicCallback function
type PanicCallback func(http.ResponseWriter, *http.Request, interface{})

// Application is type of a web.Application
type Application struct {
	trees      map[string]*node
	logger     *log.Logger
	panic      PanicCallback
	paramsPool sync.Pool
	maxParams  uint16
	NotFound   http.Handler
}

// Create return a singleton web.Application
func Create() *Application {
	_once.Do(func() {
		_app = newApplication()
	})
	return _app
}

// newApplication return a web.Application
func newApplication() *Application {
	app := &Application{
		logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
	}
	return app
}

// SetLogger set Logger
func (app *Application) SetLogger(logger *log.Logger) {
	app.logger = logger
}

// SetPanic set Logger
func (app *Application) SetPanic(panic PanicCallback) {
	app.panic = panic
}

// Use Add the given callback function to this application.middlewares.
func (app *Application) Use(path string, callback Callback) {

}

// On add event
func (app *Application) On(name string, cb Callback) {

}

// Get method
func (app *Application) Get(path string, cb Callback) {
	app.addRoute(http.MethodGet, path, cb)
}

// Head method
func (app *Application) Head(path string, cb Callback) {
	app.addRoute(http.MethodHead, path, cb)
}

// Post method
func (app *Application) Post(path string, cb Callback) {
	app.addRoute(http.MethodPost, path, cb)
}

// Put method
func (app *Application) Put(path string, cb Callback) {
	app.addRoute(http.MethodPut, path, cb)
}

// Patch method
func (app *Application) Patch(path string, cb Callback) {
	app.addRoute(http.MethodPatch, path, cb)
}

// Delete method
func (app *Application) Delete(path string, cb Callback) {
	app.addRoute(http.MethodDelete, path, cb)
}

// Options method
func (app *Application) Options(path string, cb Callback) {
	app.addRoute(http.MethodOptions, path, cb)
}

func (app *Application) addRoute(method, path string, cb Callback) {

	if method == "" {
		panic("method must not be empty")
	}

	if len(path) < 1 || path[0] != '/' {
		panic("path must begin with '/' in path '" + path + "'")
	}

	if cb == nil {
		panic("callback must not be nil")
	}

	if app.trees == nil {
		app.trees = make(map[string]*node)
	}

	root := app.trees[method]

	if root == nil {
		root = new(node)
		app.trees[method] = root
	}

	root.addRoute(path, cb)

	if pc := countParams(path); pc > app.maxParams {
		app.maxParams = pc
	}

	if app.paramsPool.New == nil && app.maxParams > 0 {
		app.paramsPool.New = func() interface{} {
			ps := make(Params, 0, app.maxParams)
			return &ps
		}
	}
}

// ServeFiles ("/src/*filepath", http.Dir("/var/www"))
func (app *Application) ServeFiles(path string, root http.FileSystem) {
	if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
		panic("path must end with /*filepath in path '" + path + "'")
	}

	fileServer := http.FileServer(root)

	app.Get(path, func(ctx *Context) {
		ctx.Request.URL.Path = ctx.Param("filepath")
		fileServer.ServeHTTP(ctx.ResponseWriter, ctx.Request)
	})
}

// ServeHTTP w, r
func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer app.recv(w, r)
	path := r.URL.Path

	if root := app.trees[r.Method]; root != nil {

		if callback, params, _ := root.getValue(path, app.getParams); callback != nil {

			ctx := createContext(w, r, params)
			app.putParams(params)

			callback(ctx)

			app.logf("%s %s %s %s", r.RemoteAddr, r.Host, r.Method, path)

			return

		}
	}

	if app.NotFound != nil {
		app.NotFound.ServeHTTP(w, r)
	} else {
		http.NotFound(w, r)
	}
}

// ListenAndServe Serve with options on addr
func (app *Application) ListenAndServe(addr string, fns ...func(*http.Server)) error {

	l, err := net.Listen("tcp", addr)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(addr, l, fns...)
}

// ListenAndServeTLS Serve with tls and options on addr
func (app *Application) ListenAndServeTLS(addr string, tlsConfig *tls.Config, fns ...func(*http.Server)) error {

	l, err := tls.Listen("tcp", addr, tlsConfig)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(addr, l, fns...)
}

func (app *Application) serve(addr string, listener net.Listener, fns ...func(*http.Server)) error {

	mux := http.NewServeMux()

	mux.Handle("/", app)

	srv := &http.Server{
		Handler: mux,
	}

	for _, fn := range fns {
		fn(srv)
	}

	defer func() {
		err := srv.Close()
		if err != nil {
			app.logf("srv: %v", err)
		}
	}()

	idleConnsClosed := make(chan struct{})

	go func() {
		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
		<-sigint

		if err := srv.Shutdown(context.Background()); err != nil {
			app.logf("web.go: %v", err)
		}

		close(idleConnsClosed)
	}()

	app.logf("web.go(%d) %s", os.Getpid(), listener.Addr())

	if err := srv.Serve(listener); err != nil {
		app.logf("web.go: %v", err)
	}

	<-idleConnsClosed

	return errors.New("web.go: exit")
}

// Inspect method
func (app *Application) Inspect() string {
	return ""
}

func (app *Application) logf(format string, v ...interface{}) {

	if app.logger != nil {
		app.logger.Printf(format, v...)
	} else {
		log.Printf(format, v...)
	}
}

func (app *Application) getParams() *Params {
	ps := app.paramsPool.Get().(*Params)
	*ps = (*ps)[0:0] // reset slice
	return ps
}

func (app *Application) putParams(ps *Params) {
	if ps != nil {
		app.paramsPool.Put(ps)
	}
}

func (app *Application) recv(w http.ResponseWriter, r *http.Request) {
	if rcv := recover(); rcv != nil {
		if app.panic != nil {
			app.panic(w, r, rcv)
		} else {
			app.logf("%s %s %s: %v", r.Host, r.Method, r.URL.Path, rcv)
		}
	}
}
<|endoftext|>