<commit_before>package surveys

import (
	"encoding/json"
	"log"
	"net/http"
	"regexp"

	"github.com/AreaHQ/jsonhal"
	"github.com/ONSdigital/go-launch-a-survey/settings"
)

// LauncherSchema is a representation of a schema in the Launcher
type LauncherSchema struct {
	Name     string
	EqID     string
	FormType string
	URL      string
}

// LauncherSchemas is a separation of Test and Live schemas
type LauncherSchemas struct {
	Business []LauncherSchema
	Census   []LauncherSchema
	Social   []LauncherSchema
	Test     []LauncherSchema
	Other    []LauncherSchema
}

// RegisterResponse is the response from the eq-survey-register request
type RegisterResponse struct {
	jsonhal.Hal
}

// Schemas is a list of Schema
type Schemas []Schema

// Schema is an available schema
type Schema struct {
	jsonhal.Hal
	Name string `json:"name"`
}

var eqIDFormTypeRegex = regexp.MustCompile(`^(?P<eq_id>[a-z0-9]+)_(?P<form_type>\w+)`)

func extractEqIDFormType(schema string) (EqID, formType string) {
	match := eqIDFormTypeRegex.FindStringSubmatch(schema)
	if match != nil {
		EqID = match[1]
		formType = match[2]
	}
	return
}

// LauncherSchemaFromFilename creates a LauncherSchema record from a schema filename
func LauncherSchemaFromFilename(filename string) LauncherSchema {
	EqID, formType := extractEqIDFormType(filename)
	return LauncherSchema{
		Name:     filename,
		EqID:     EqID,
		FormType: formType,
	}
}

// GetAvailableSchemas Gets the list of static schemas an joins them with any schemas from the eq-survey-register if defined
func GetAvailableSchemas() LauncherSchemas {
	schemaList := LauncherSchemas{
		Business: []LauncherSchema{
			LauncherSchemaFromFilename("1_0005.json"),
			LauncherSchemaFromFilename("1_0102.json"),
			LauncherSchemaFromFilename("1_0112.json"),
			LauncherSchemaFromFilename("1_0203.json"),
			LauncherSchemaFromFilename("1_0205.json"),
			LauncherSchemaFromFilename("1_0213.json"),
			LauncherSchemaFromFilename("1_0215.json"),
			LauncherSchemaFromFilename("2_0001.json"),
			LauncherSchemaFromFilename("e_commerce.json"),
			LauncherSchemaFromFilename("mbs_0106.json"),
			LauncherSchemaFromFilename("mbs_0111.json"),
			LauncherSchemaFromFilename("mbs_0117.json"),
			LauncherSchemaFromFilename("mbs_0123.json"),
			LauncherSchemaFromFilename("mbs_0158.json"),
			LauncherSchemaFromFilename("mbs_0161.json"),
			LauncherSchemaFromFilename("mbs_0167.json"),
			LauncherSchemaFromFilename("mbs_0173.json"),
			LauncherSchemaFromFilename("mbs_0201.json"),
			LauncherSchemaFromFilename("mbs_0202.json"),
			LauncherSchemaFromFilename("mbs_0203.json"),
			LauncherSchemaFromFilename("mbs_0204.json"),
			LauncherSchemaFromFilename("mbs_0205.json"),
			LauncherSchemaFromFilename("mbs_0216.json"),
			LauncherSchemaFromFilename("mbs_0251.json"),
			LauncherSchemaFromFilename("mbs_0253.json"),
			LauncherSchemaFromFilename("mbs_0255.json"),
			LauncherSchemaFromFilename("mbs_0817.json"),
			LauncherSchemaFromFilename("mbs_0823.json"),
			LauncherSchemaFromFilename("mbs_0867.json"),
			LauncherSchemaFromFilename("mbs_0873.json"),
			LauncherSchemaFromFilename("mci_transformation.json"),
			LauncherSchemaFromFilename("rsi_transformation.json"),
		},
		Census: []LauncherSchema{
			LauncherSchemaFromFilename("census_communal.json"),
			LauncherSchemaFromFilename("census_household.json"),
			LauncherSchemaFromFilename("census_individual.json"),
		},
		Social: []LauncherSchema{
			LauncherSchemaFromFilename("lms_1.json"),
		},
		Test: []LauncherSchema{
			LauncherSchemaFromFilename("0_star_wars.json"),
			LauncherSchemaFromFilename("multiple_answers.json"),
			LauncherSchemaFromFilename("test_big_list_naughty_strings.json"),
			LauncherSchemaFromFilename("test_checkbox.json"),
			LauncherSchemaFromFilename("test_checkbox_mutually_exclusive.json"),
			LauncherSchemaFromFilename("test_conditional_dates.json"),
			LauncherSchemaFromFilename("test_conditional_routing.json"),
			LauncherSchemaFromFilename("test_confirmation_question.json"),
			LauncherSchemaFromFilename("test_currency.json"),
			LauncherSchemaFromFilename("test_date_validation_combined.json"),
			LauncherSchemaFromFilename("test_date_validation_mm_yyyy_combined.json"),
			LauncherSchemaFromFilename("test_date_validation_yyyy_combined.json"),
			LauncherSchemaFromFilename("test_date_validation_range.json"),
			LauncherSchemaFromFilename("test_date_validation_single.json"),
			LauncherSchemaFromFilename("test_dates.json"),
			LauncherSchemaFromFilename("test_default.json"),
			LauncherSchemaFromFilename("test_dependencies_calculation.json"),
			LauncherSchemaFromFilename("test_dependencies_max_value.json"),
			LauncherSchemaFromFilename("test_dependencies_min_value.json"),
			LauncherSchemaFromFilename("test_difference_in_years.json"),
			LauncherSchemaFromFilename("test_difference_in_years_month_year.json"),
			LauncherSchemaFromFilename("test_difference_in_years_month_year_range.json"),
			LauncherSchemaFromFilename("test_difference_in_years_range.json"),
			LauncherSchemaFromFilename("test_dropdown_mandatory.json"),
			LauncherSchemaFromFilename("test_dropdown_mandatory_with_overridden_error.json"),
			LauncherSchemaFromFilename("test_dropdown_optional.json"),
			LauncherSchemaFromFilename("test_durations.json"),
			LauncherSchemaFromFilename("test_error_messages.json"),
			LauncherSchemaFromFilename("test_final_confirmation.json"),
			LauncherSchemaFromFilename("test_household_question.json"),
			LauncherSchemaFromFilename("test_interstitial_page.json"),
			LauncherSchemaFromFilename("test_introduction.json"),
			LauncherSchemaFromFilename("test_language.json"),
			LauncherSchemaFromFilename("test_markup.json"),
			LauncherSchemaFromFilename("test_metadata_routing.json"),
			LauncherSchemaFromFilename("test_multiple_piping.json"),
			LauncherSchemaFromFilename("test_navigation.json"),
			LauncherSchemaFromFilename("test_navigation_completeness.json"),
			LauncherSchemaFromFilename("test_navigation_confirmation.json"),
			LauncherSchemaFromFilename("test_navigation_routing.json"),
			LauncherSchemaFromFilename("test_numbers.json"),
			LauncherSchemaFromFilename("test_percentage.json"),
			LauncherSchemaFromFilename("test_question_definition.json"),
			LauncherSchemaFromFilename("test_question_guidance.json"),
			LauncherSchemaFromFilename("test_radio_checkbox_descriptions.json"),
			LauncherSchemaFromFilename("test_radio_mandatory.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other_overridden_error.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_optional_other.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_overridden_error.json"),
			LauncherSchemaFromFilename("test_radio_optional.json"),
			LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other.json"),
			LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other_overridden_error.json"),
			LauncherSchemaFromFilename("test_radio_optional_with_optional_other.json"),
			LauncherSchemaFromFilename("test_relationship_household.json"),
			LauncherSchemaFromFilename("test_repeating_and_conditional_routing.json"),
			LauncherSchemaFromFilename("test_repeating_household.json"),
			LauncherSchemaFromFilename("test_repeating_household_routing.json"),
			LauncherSchemaFromFilename("test_routing_date_equals.json"),
			LauncherSchemaFromFilename("test_routing_date_greater_than.json"),
			LauncherSchemaFromFilename("test_routing_date_less_than.json"),
			LauncherSchemaFromFilename("test_routing_date_not_equals.json"),
			LauncherSchemaFromFilename("test_routing_group.json"),
			LauncherSchemaFromFilename("test_routing_number_equals.json"),
			LauncherSchemaFromFilename("test_routing_number_greater_than.json"),
			LauncherSchemaFromFilename("test_routing_number_greater_than_or_equal.json"),
			LauncherSchemaFromFilename("test_routing_number_less_than.json"),
			LauncherSchemaFromFilename("test_routing_number_less_than_or_equal.json"),
			LauncherSchemaFromFilename("test_routing_number_not_equals.json"),
			LauncherSchemaFromFilename("test_routing_on_multiple_select.json"),
			LauncherSchemaFromFilename("test_skip_condition.json"),
			LauncherSchemaFromFilename("test_skip_condition_block.json"),
			LauncherSchemaFromFilename("test_skip_condition_group.json"),
			LauncherSchemaFromFilename("test_skip_condition_not_set.json"),
			LauncherSchemaFromFilename("test_skip_condition_set.json"),
			LauncherSchemaFromFilename("test_summary.json"),
			LauncherSchemaFromFilename("test_section_summary.json"),
			LauncherSchemaFromFilename("test_sum_equal_validation_against_total.json"),
			LauncherSchemaFromFilename("test_sum_equal_or_less_validation_against_total.json"),
			LauncherSchemaFromFilename("test_sum_less_validation_against_total.json"),
			LauncherSchemaFromFilename("test_sum_multi_validation_against_total.json"),
			LauncherSchemaFromFilename("test_titles.json"),
			LauncherSchemaFromFilename("test_titles_conditional_within_repeating_block.json"),
			LauncherSchemaFromFilename("test_titles_radio_and_checkbox.json"),
			LauncherSchemaFromFilename("test_titles_within_repeating_blocks.json"),
            LauncherSchemaFromFilename("test_titles_repeating_non_repeating_dependency.json"),
			LauncherSchemaFromFilename("test_view_submitted_response.json"),
			LauncherSchemaFromFilename("test_textarea.json"),
			LauncherSchemaFromFilename("test_textfield.json"),
			LauncherSchemaFromFilename("test_timeout.json"),
			LauncherSchemaFromFilename("test_total_breakdown.json"),
			LauncherSchemaFromFilename("test_unit_patterns.json"),
		},
	}

	schemaList.Other = getAvailableSchemasFromRegister()

	return schemaList
}

func getAvailableSchemasFromRegister() []LauncherSchema {

	schemaList := []LauncherSchema{}

	if settings.Get("SURVEY_REGISTER_URL") != "" {
		req, err := http.NewRequest("GET", settings.Get("SURVEY_REGISTER_URL"), nil)
		if err != nil {
			log.Fatal("NewRequest: ", err)
			return []LauncherSchema{}
		}
		client := &http.Client{}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Do: ", err)
			return []LauncherSchema{}
		}

		defer resp.Body.Close()

		var registerResponse RegisterResponse

		if err := json.NewDecoder(resp.Body).Decode(&registerResponse); err != nil {
			log.Println(err)
		}

		var schemas Schemas

		schemasJSON, _ := json.Marshal(registerResponse.Embedded["schemas"])

		if err := json.Unmarshal(schemasJSON, &schemas); err != nil {
			log.Println(err)
		}

		for _, schema := range schemas {
			url := schema.Links["self"]
			EqID, formType := extractEqIDFormType(schema.Name)
			schemaList = append(schemaList, LauncherSchema{
				Name:     schema.Name,
				URL:      url.Href,
				EqID:     EqID,
				FormType: formType,
			})
		}
	}

	return schemaList
}

// FindSurveyByName Finds the schema in the list of available schemas
func FindSurveyByName(name string) LauncherSchema {
	for _, survey := range GetAvailableSchemas().Business {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Census {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Social {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Test {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Other {
		if survey.Name == name {
			return survey
		}
	}
	panic("Survey not found")
}
<commit_msg>Added answer_count routing test survey<commit_after>package surveys

import (
	"encoding/json"
	"log"
	"net/http"
	"regexp"

	"github.com/AreaHQ/jsonhal"
	"github.com/ONSdigital/go-launch-a-survey/settings"
)

// LauncherSchema is a representation of a schema in the Launcher
type LauncherSchema struct {
	Name     string
	EqID     string
	FormType string
	URL      string
}

// LauncherSchemas is a separation of Test and Live schemas
type LauncherSchemas struct {
	Business []LauncherSchema
	Census   []LauncherSchema
	Social   []LauncherSchema
	Test     []LauncherSchema
	Other    []LauncherSchema
}

// RegisterResponse is the response from the eq-survey-register request
type RegisterResponse struct {
	jsonhal.Hal
}

// Schemas is a list of Schema
type Schemas []Schema

// Schema is an available schema
type Schema struct {
	jsonhal.Hal
	Name string `json:"name"`
}

var eqIDFormTypeRegex = regexp.MustCompile(`^(?P<eq_id>[a-z0-9]+)_(?P<form_type>\w+)`)

func extractEqIDFormType(schema string) (EqID, formType string) {
	match := eqIDFormTypeRegex.FindStringSubmatch(schema)
	if match != nil {
		EqID = match[1]
		formType = match[2]
	}
	return
}

// LauncherSchemaFromFilename creates a LauncherSchema record from a schema filename
func LauncherSchemaFromFilename(filename string) LauncherSchema {
	EqID, formType := extractEqIDFormType(filename)
	return LauncherSchema{
		Name:     filename,
		EqID:     EqID,
		FormType: formType,
	}
}

// GetAvailableSchemas Gets the list of static schemas an joins them with any schemas from the eq-survey-register if defined
func GetAvailableSchemas() LauncherSchemas {
	schemaList := LauncherSchemas{
		Business: []LauncherSchema{
			LauncherSchemaFromFilename("1_0005.json"),
			LauncherSchemaFromFilename("1_0102.json"),
			LauncherSchemaFromFilename("1_0112.json"),
			LauncherSchemaFromFilename("1_0203.json"),
			LauncherSchemaFromFilename("1_0205.json"),
			LauncherSchemaFromFilename("1_0213.json"),
			LauncherSchemaFromFilename("1_0215.json"),
			LauncherSchemaFromFilename("2_0001.json"),
			LauncherSchemaFromFilename("e_commerce.json"),
			LauncherSchemaFromFilename("mbs_0106.json"),
			LauncherSchemaFromFilename("mbs_0111.json"),
			LauncherSchemaFromFilename("mbs_0117.json"),
			LauncherSchemaFromFilename("mbs_0123.json"),
			LauncherSchemaFromFilename("mbs_0158.json"),
			LauncherSchemaFromFilename("mbs_0161.json"),
			LauncherSchemaFromFilename("mbs_0167.json"),
			LauncherSchemaFromFilename("mbs_0173.json"),
			LauncherSchemaFromFilename("mbs_0201.json"),
			LauncherSchemaFromFilename("mbs_0202.json"),
			LauncherSchemaFromFilename("mbs_0203.json"),
			LauncherSchemaFromFilename("mbs_0204.json"),
			LauncherSchemaFromFilename("mbs_0205.json"),
			LauncherSchemaFromFilename("mbs_0216.json"),
			LauncherSchemaFromFilename("mbs_0251.json"),
			LauncherSchemaFromFilename("mbs_0253.json"),
			LauncherSchemaFromFilename("mbs_0255.json"),
			LauncherSchemaFromFilename("mbs_0817.json"),
			LauncherSchemaFromFilename("mbs_0823.json"),
			LauncherSchemaFromFilename("mbs_0867.json"),
			LauncherSchemaFromFilename("mbs_0873.json"),
			LauncherSchemaFromFilename("mci_transformation.json"),
			LauncherSchemaFromFilename("rsi_transformation.json"),
		},
		Census: []LauncherSchema{
			LauncherSchemaFromFilename("census_communal.json"),
			LauncherSchemaFromFilename("census_household.json"),
			LauncherSchemaFromFilename("census_individual.json"),
		},
		Social: []LauncherSchema{
			LauncherSchemaFromFilename("lms_1.json"),
		},
		Test: []LauncherSchema{
			LauncherSchemaFromFilename("0_star_wars.json"),
			LauncherSchemaFromFilename("multiple_answers.json"),
			LauncherSchemaFromFilename("test_big_list_naughty_strings.json"),
			LauncherSchemaFromFilename("test_checkbox.json"),
			LauncherSchemaFromFilename("test_checkbox_mutually_exclusive.json"),
			LauncherSchemaFromFilename("test_conditional_dates.json"),
			LauncherSchemaFromFilename("test_conditional_routing.json"),
			LauncherSchemaFromFilename("test_confirmation_question.json"),
			LauncherSchemaFromFilename("test_currency.json"),
			LauncherSchemaFromFilename("test_date_validation_combined.json"),
			LauncherSchemaFromFilename("test_date_validation_mm_yyyy_combined.json"),
			LauncherSchemaFromFilename("test_date_validation_yyyy_combined.json"),
			LauncherSchemaFromFilename("test_date_validation_range.json"),
			LauncherSchemaFromFilename("test_date_validation_single.json"),
			LauncherSchemaFromFilename("test_dates.json"),
			LauncherSchemaFromFilename("test_default.json"),
			LauncherSchemaFromFilename("test_dependencies_calculation.json"),
			LauncherSchemaFromFilename("test_dependencies_max_value.json"),
			LauncherSchemaFromFilename("test_dependencies_min_value.json"),
			LauncherSchemaFromFilename("test_difference_in_years.json"),
			LauncherSchemaFromFilename("test_difference_in_years_month_year.json"),
			LauncherSchemaFromFilename("test_difference_in_years_month_year_range.json"),
			LauncherSchemaFromFilename("test_difference_in_years_range.json"),
			LauncherSchemaFromFilename("test_dropdown_mandatory.json"),
			LauncherSchemaFromFilename("test_dropdown_mandatory_with_overridden_error.json"),
			LauncherSchemaFromFilename("test_dropdown_optional.json"),
			LauncherSchemaFromFilename("test_durations.json"),
			LauncherSchemaFromFilename("test_error_messages.json"),
			LauncherSchemaFromFilename("test_final_confirmation.json"),
			LauncherSchemaFromFilename("test_household_question.json"),
			LauncherSchemaFromFilename("test_interstitial_page.json"),
			LauncherSchemaFromFilename("test_introduction.json"),
			LauncherSchemaFromFilename("test_language.json"),
			LauncherSchemaFromFilename("test_markup.json"),
			LauncherSchemaFromFilename("test_metadata_routing.json"),
			LauncherSchemaFromFilename("test_multiple_piping.json"),
			LauncherSchemaFromFilename("test_navigation.json"),
			LauncherSchemaFromFilename("test_navigation_completeness.json"),
			LauncherSchemaFromFilename("test_navigation_confirmation.json"),
			LauncherSchemaFromFilename("test_navigation_routing.json"),
			LauncherSchemaFromFilename("test_numbers.json"),
			LauncherSchemaFromFilename("test_percentage.json"),
			LauncherSchemaFromFilename("test_question_definition.json"),
			LauncherSchemaFromFilename("test_question_guidance.json"),
			LauncherSchemaFromFilename("test_radio_checkbox_descriptions.json"),
			LauncherSchemaFromFilename("test_radio_mandatory.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_mandatory_other_overridden_error.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_optional_other.json"),
			LauncherSchemaFromFilename("test_radio_mandatory_with_overridden_error.json"),
			LauncherSchemaFromFilename("test_radio_optional.json"),
			LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other.json"),
			LauncherSchemaFromFilename("test_radio_optional_with_mandatory_other_overridden_error.json"),
			LauncherSchemaFromFilename("test_radio_optional_with_optional_other.json"),
			LauncherSchemaFromFilename("test_relationship_household.json"),
			LauncherSchemaFromFilename("test_repeating_and_conditional_routing.json"),
			LauncherSchemaFromFilename("test_repeating_household.json"),
			LauncherSchemaFromFilename("test_repeating_household_routing.json"),
			LauncherSchemaFromFilename("test_routing_answer_count.json"),
			LauncherSchemaFromFilename("test_routing_date_equals.json"),
			LauncherSchemaFromFilename("test_routing_date_greater_than.json"),
			LauncherSchemaFromFilename("test_routing_date_less_than.json"),
			LauncherSchemaFromFilename("test_routing_date_not_equals.json"),
			LauncherSchemaFromFilename("test_routing_group.json"),
			LauncherSchemaFromFilename("test_routing_number_equals.json"),
			LauncherSchemaFromFilename("test_routing_number_greater_than.json"),
			LauncherSchemaFromFilename("test_routing_number_greater_than_or_equal.json"),
			LauncherSchemaFromFilename("test_routing_number_less_than.json"),
			LauncherSchemaFromFilename("test_routing_number_less_than_or_equal.json"),
			LauncherSchemaFromFilename("test_routing_number_not_equals.json"),
			LauncherSchemaFromFilename("test_routing_on_multiple_select.json"),
			LauncherSchemaFromFilename("test_skip_condition.json"),
			LauncherSchemaFromFilename("test_skip_condition_block.json"),
			LauncherSchemaFromFilename("test_skip_condition_group.json"),
			LauncherSchemaFromFilename("test_skip_condition_not_set.json"),
			LauncherSchemaFromFilename("test_skip_condition_set.json"),
			LauncherSchemaFromFilename("test_summary.json"),
			LauncherSchemaFromFilename("test_section_summary.json"),
			LauncherSchemaFromFilename("test_sum_equal_validation_against_total.json"),
			LauncherSchemaFromFilename("test_sum_equal_or_less_validation_against_total.json"),
			LauncherSchemaFromFilename("test_sum_less_validation_against_total.json"),
			LauncherSchemaFromFilename("test_sum_multi_validation_against_total.json"),
			LauncherSchemaFromFilename("test_titles.json"),
			LauncherSchemaFromFilename("test_titles_conditional_within_repeating_block.json"),
			LauncherSchemaFromFilename("test_titles_radio_and_checkbox.json"),
			LauncherSchemaFromFilename("test_titles_within_repeating_blocks.json"),
            LauncherSchemaFromFilename("test_titles_repeating_non_repeating_dependency.json"),
			LauncherSchemaFromFilename("test_view_submitted_response.json"),
			LauncherSchemaFromFilename("test_textarea.json"),
			LauncherSchemaFromFilename("test_textfield.json"),
			LauncherSchemaFromFilename("test_timeout.json"),
			LauncherSchemaFromFilename("test_total_breakdown.json"),
			LauncherSchemaFromFilename("test_unit_patterns.json"),
		},
	}

	schemaList.Other = getAvailableSchemasFromRegister()

	return schemaList
}

func getAvailableSchemasFromRegister() []LauncherSchema {

	schemaList := []LauncherSchema{}

	if settings.Get("SURVEY_REGISTER_URL") != "" {
		req, err := http.NewRequest("GET", settings.Get("SURVEY_REGISTER_URL"), nil)
		if err != nil {
			log.Fatal("NewRequest: ", err)
			return []LauncherSchema{}
		}
		client := &http.Client{}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Do: ", err)
			return []LauncherSchema{}
		}

		defer resp.Body.Close()

		var registerResponse RegisterResponse

		if err := json.NewDecoder(resp.Body).Decode(&registerResponse); err != nil {
			log.Println(err)
		}

		var schemas Schemas

		schemasJSON, _ := json.Marshal(registerResponse.Embedded["schemas"])

		if err := json.Unmarshal(schemasJSON, &schemas); err != nil {
			log.Println(err)
		}

		for _, schema := range schemas {
			url := schema.Links["self"]
			EqID, formType := extractEqIDFormType(schema.Name)
			schemaList = append(schemaList, LauncherSchema{
				Name:     schema.Name,
				URL:      url.Href,
				EqID:     EqID,
				FormType: formType,
			})
		}
	}

	return schemaList
}

// FindSurveyByName Finds the schema in the list of available schemas
func FindSurveyByName(name string) LauncherSchema {
	for _, survey := range GetAvailableSchemas().Business {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Census {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Social {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Test {
		if survey.Name == name {
			return survey
		}
	}
	for _, survey := range GetAvailableSchemas().Other {
		if survey.Name == name {
			return survey
		}
	}
	panic("Survey not found")
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"encoding/gob"
	"flag"
	"fmt"
	"os"
)

var Seed int64

func read() *Player {
	f, err := os.Open(*file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	g, err := gzip.NewReader(f)
	if err != nil {
		panic(err)
	}
	defer g.Close()

	var player Player
	err = gob.NewDecoder(g).Decode(&player)
	if err != nil {
		panic(err)
	}

	return &player
}

func write(player *Player) {
	f, err := os.Create(*file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	g, err := gzip.NewWriterLevel(f, gzip.BestCompression)
	if err != nil {
		panic(err)
	}
	defer g.Close()

	err = gob.NewEncoder(g).Encode(&player)
	if err != nil {
		panic(err)
	}
}

func toggleadmin() {
	player := read()
	player.Admin = !player.Admin
	write(player)
}

func print() {
	player := read()

	fmt.Println("Name:", player.Name())
	fmt.Println("Admin:", player.Admin)
}

var (
	file      = flag.String("file", "", "file to edit [required]")
	flagAdmin = flag.Bool("a", false, "toggle player admin status")
	flagPrint = flag.Bool("p", false, "print info")
)

func main() {
	flag.Parse()
	if *file == "" {
		flag.Usage()
		os.Exit(1)
	}
	count := 0
	if *flagAdmin {
		count++
	}
	if *flagPrint {
		count++
	}
	if count != 1 {
		flag.Usage()
		os.Exit(1)
	}
	if *flagAdmin {
		toggleadmin()
	}
	if *flagPrint {
		print()
	}
}
<commit_msg>nickname changer<commit_after>package main

import (
	"compress/gzip"
	"encoding/gob"
	"flag"
	"fmt"
	"os"
)

var Seed int64

func read() *Player {
	f, err := os.Open(*file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	g, err := gzip.NewReader(f)
	if err != nil {
		panic(err)
	}
	defer g.Close()

	var player Player
	err = gob.NewDecoder(g).Decode(&player)
	if err != nil {
		panic(err)
	}

	return &player
}

func write(player *Player) {
	f, err := os.Create(*file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	g, err := gzip.NewWriterLevel(f, gzip.BestCompression)
	if err != nil {
		panic(err)
	}
	defer g.Close()

	err = gob.NewEncoder(g).Encode(&player)
	if err != nil {
		panic(err)
	}
}

func toggleadmin() {
	player := read()
	player.Admin = !player.Admin
	write(player)
}

func print() {
	player := read()

	fmt.Println("Name:", player.Name())
	fmt.Println("Admin:", player.Admin)
}

func changenickname() {
	player := read()
	player.HeroName.Nickname = *data
	write(player)
}

var (
	file         = flag.String("file", "", "file to edit [required]")
	data         = flag.String("data", "", "data [varies by mode]")
	flagAdmin    = flag.Bool("a", false, "toggle player admin status")
	flagPrint    = flag.Bool("p", false, "print info")
	flagNickname = flag.Bool("nn", false, "modify nickname to -data")
)

func main() {
	flag.Parse()
	if *file == "" {
		flag.Usage()
		os.Exit(1)
	}
	count := 0
	if *flagAdmin {
		count++
	}
	if *flagPrint {
		count++
	}
	if *flagNickname {
		count++
	}
	if count != 1 {
		flag.Usage()
		os.Exit(1)
	}
	if *flagAdmin {
		toggleadmin()
	}
	if *flagPrint {
		print()
	}
	if *flagNickname {
		changenickname()
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"log"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

type QuickBg struct {
	Details        *Details
	SelectedAnswer string
	lang           struct {
		Comment     string `json:"comment"`
		Question    string `json:"question"`
		Review      string `json:"review"`
		ReviewYesNo yesNo  `json:"reviewYesNo"`
		Above       option `json:"above"`
		In          option `json:"in"`
		Below       option `json:"below"`
		Thank       string `json:"thank"`
	}
	Parts
}

func (this *QuickBg) loadLanguage() {

	encoded := `{
        "comment": "OK lets save that blood sugar for you.",
        "question": "So your last blood sugar was ... ",
        "review": "Would you like to review your result?",
        "reviewYesNo" : {
        	"yes":"Sure thing!",
        	"no": "No thanks"
        },
        "above": {
            "text": "Above what I would like",
            "feedback":["Remember to keep your fuilds up","Just remember it happens to the best of us"],
            "followUp" :["Has it been high for a while?"]
        },
        "in": {
            "text": "About right",
            "feedback":["Awesome work!!","Its never as easy as its made out aye :)","How does it feel to be perfect :)"],
            "followUp" :["Did you feel you could do this again and again?"]
        },
        "below":{
            "text": "Below what I would like",
            "feedback":["Damn lows, they always happen at the wrost time.","I Hope you keep your low supplies stocked up!!"],
            "followUp" :["Do you have any idea why you went low?"]
        },
        "thank":"You rock! Just had to say that before you go :)"
    }`

	err := json.Unmarshal([]byte(encoded), &this.lang)
	if err != nil {
		log.Panic("could not load BG language ", err.Error())
	}
}
func NewQuickBg(d *Details) *QuickBg {
	bg := &QuickBg{Details: d}
	bg.loadLanguage()
	bg.Parts = append(
		bg.Parts,
		&Part{Func: bg.askBg, ToBeRun: true},
		&Part{Func: bg.askReview, ToBeRun: true},
		&Part{Func: bg.replyReview, ToBeRun: true},
	)
	return bg
}

func (this *QuickBg) Run(m telebot.Message) {
	for i := range this.Parts {
		log.Println("checking ", i, "of", len(this.Parts), "still to run?", this.Parts[i].ToBeRun)
		if this.Parts[i].ToBeRun {
			log.Println("running ", i)
			this.Parts[i].ToBeRun = false
			this.Parts[i].Func(m)
			log.Println("all done ", i)
			return
		}
	}
	return
}

func (this *QuickBg) CanRun() bool {
	return len(this.Parts) > 0
}

func (this *QuickBg) askBg(msg telebot.Message) {
	this.Details.sendWithKeyboard(this.lang.Question, makeKeyBoard(this.lang.Above.Text, this.lang.In.Text, this.lang.Below.Text))
	return
}

func (this *QuickBg) askReview(msg telebot.Message) {
	this.SelectedAnswer = msg.Text
	this.Details.sendWithKeyboard(this.lang.Review, makeKeyBoard(this.lang.ReviewYesNo.Yes, this.lang.ReviewYesNo.No))
	return
}

func (this *QuickBg) replyReview(msg telebot.Message) {
	switch {
	case msg.Text == this.lang.ReviewYesNo.Yes:
		this.Parts = append(
			this.Parts,
			&Part{Func: this.doReview, ToBeRun: true},
			&Part{Func: this.onYa, ToBeRun: true},
		)
	case msg.Text == this.lang.ReviewYesNo.No:
		this.Parts = append(
			this.Parts,
			&Part{Func: this.onYa, ToBeRun: true},
		)
	}
}

func (this *QuickBg) doReview(msg telebot.Message) {
	switch {
	case this.SelectedAnswer == this.lang.Above.Text:
		this.Details.send(getLangText(this.lang.Above.Feedback))
		this.Details.sendWithKeyboard(getLangText(this.lang.Above.FollowUpQuestion), makeKeyBoard("Sure has", "Nope"))
		return
	case this.SelectedAnswer == this.lang.In.Text:
		this.Details.send(getLangText(this.lang.In.Feedback))
		this.Details.sendWithKeyboard(getLangText(this.lang.In.FollowUpQuestion), makeKeyBoard("Totally!", "Not so sure"))
		return
	case this.SelectedAnswer == this.lang.Below.Text:
		this.Details.send(getLangText(this.lang.Below.Feedback))
		this.Details.sendWithKeyboard(getLangText(this.lang.Below.FollowUpQuestion), makeKeyBoard("Yeah I have a hunch", "No, I just don't get it"))
		return
	}
	return
}

func (this *QuickBg) onYa(msg telebot.Message) {
	this.Details.sendWithKeyboard(this.lang.Thank, makeKeyBoard("It does aye. See you!"))
	return
}
<commit_msg>debug<commit_after>package lib

import (
	"encoding/json"
	"log"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

type QuickBg struct {
	Details        *Details
	SelectedAnswer string
	lang           struct {
		Comment     string `json:"comment"`
		Question    string `json:"question"`
		Review      string `json:"review"`
		ReviewYesNo yesNo  `json:"reviewYesNo"`
		Above       option `json:"above"`
		In          option `json:"in"`
		Below       option `json:"below"`
		Thank       string `json:"thank"`
	}
	Parts
}

func (this *QuickBg) loadLanguage() {

	encoded := `{
        "comment": "OK lets save that blood sugar for you.",
        "question": "So your last blood sugar was ... ",
        "review": "Would you like to review your result?",
        "reviewYesNo" : {
        	"yes":"Sure thing!",
        	"no": "No thanks"
        },
        "above": {
            "text": "Above what I would like",
            "feedback":["Remember to keep your fuilds up","Just remember it happens to the best of us"],
            "followUp" :["Has it been high for a while?"]
        },
        "in": {
            "text": "About right",
            "feedback":["Awesome work!!","Its never as easy as its made out aye :)","How does it feel to be perfect :)"],
            "followUp" :["Did you feel you could do this again and again?"]
        },
        "below":{
            "text": "Below what I would like",
            "feedback":["Damn lows, they always happen at the wrost time.","I Hope you keep your low supplies stocked up!!"],
            "followUp" :["Do you have any idea why you went low?"]
        },
        "thank":"You rock! Just had to say that before you go :)"
    }`

	err := json.Unmarshal([]byte(encoded), &this.lang)
	if err != nil {
		log.Panic("could not load BG language ", err.Error())
	}
}
func NewQuickBg(d *Details) *QuickBg {
	bg := &QuickBg{Details: d}
	bg.loadLanguage()
	bg.Parts = append(
		bg.Parts,
		&Part{Func: bg.askBg, ToBeRun: true},
		&Part{Func: bg.askReview, ToBeRun: true},
		&Part{Func: bg.replyReview, ToBeRun: true},
	)
	return bg
}

func (this *QuickBg) Run(m telebot.Message) {
	for i := range this.Parts {
		log.Println("checking ", i, "of", len(this.Parts), "still to run?", this.Parts[i].ToBeRun)
		if this.Parts[i].ToBeRun {
			log.Println("running ", i)
			this.Parts[i].ToBeRun = false
			this.Parts[i].Func(m)
			log.Println("all done ", i)
			return
		}
	}
	return
}

func (this *QuickBg) CanRun() bool {
	return len(this.Parts) > 0
}

func (this *QuickBg) askBg(msg telebot.Message) {
	this.Details.sendWithKeyboard(this.lang.Question, makeKeyBoard(this.lang.Above.Text, this.lang.In.Text, this.lang.Below.Text))
	return
}

func (this *QuickBg) askReview(msg telebot.Message) {
	this.SelectedAnswer = msg.Text
	this.Details.sendWithKeyboard(this.lang.Review, makeKeyBoard(this.lang.ReviewYesNo.Yes, this.lang.ReviewYesNo.No))
	return
}

func (this *QuickBg) replyReview(msg telebot.Message) {
	log.Println("did you want to review?", msg.Text)
	switch {
	case msg.Text == this.lang.ReviewYesNo.Yes:
		this.Parts = append(
			this.Parts,
			&Part{Func: this.doReview, ToBeRun: true},
			&Part{Func: this.onYa, ToBeRun: true},
		)
	case msg.Text == this.lang.ReviewYesNo.No:
		this.Parts = append(
			this.Parts,
			&Part{Func: this.onYa, ToBeRun: true},
		)
	}
	return
}

func (this *QuickBg) doReview(msg telebot.Message) {
	log.Println("BG", this.SelectedAnswer)
	switch {
	case this.SelectedAnswer == this.lang.Above.Text:
		this.Details.send(getLangText(this.lang.Above.Feedback))
		this.Details.sendWithKeyboard(getLangText(this.lang.Above.FollowUpQuestion), makeKeyBoard("Sure has", "Nope"))
		return
	case this.SelectedAnswer == this.lang.In.Text:
		this.Details.send(getLangText(this.lang.In.Feedback))
		this.Details.sendWithKeyboard(getLangText(this.lang.In.FollowUpQuestion), makeKeyBoard("Totally!", "Not so sure"))
		return
	case this.SelectedAnswer == this.lang.Below.Text:
		this.Details.send(getLangText(this.lang.Below.Feedback))
		this.Details.sendWithKeyboard(getLangText(this.lang.Below.FollowUpQuestion), makeKeyBoard("Yeah I have a hunch", "No, I just don't get it"))
		return
	}
	return
}

func (this *QuickBg) onYa(msg telebot.Message) {
	this.Details.sendWithKeyboard(this.lang.Thank, makeKeyBoard("It does aye. See you!"))
	return
}
<|endoftext|>


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

package usb

// #include <libusb-1.0/libusb.h>
import "C"

import (
	"fmt"
	"reflect"
	"sync"
	"time"
	"unsafe"
)

var DefaultReadTimeout = 1 * time.Second
var DefaultWriteTimeout = 1 * time.Second
var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second

type Device struct {
	handle *C.libusb_device_handle

	// Embed the device information for easy access
	*Descriptor

	// Timeouts
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration
	ControlTimeout time.Duration

	// Claimed interfaces
	lock    *sync.Mutex
	claimed map[uint8]int
}

func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device {
	ifaces := 0
	d := &Device{
		handle:         handle,
		Descriptor:     desc,
		ReadTimeout:    DefaultReadTimeout,
		WriteTimeout:   DefaultWriteTimeout,
		ControlTimeout: DefaultControlTimeout,
		lock:           new(sync.Mutex),
		claimed:        make(map[uint8]int, ifaces),
	}

	return d
}

func (d *Device) Reset() error {
	if errno := C.libusb_reset_device(d.handle); errno != 0 {
		return usbError(errno)
	}
	return nil
}

func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
	//log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data))
	dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	n := C.libusb_control_transfer(
		d.handle,
		C.uint8_t(rType),
		C.uint8_t(request),
		C.uint16_t(val),
		C.uint16_t(idx),
		(*C.uchar)(unsafe.Pointer(dataSlice.Data)),
		C.uint16_t(len(data)),
		C.uint(d.ControlTimeout/time.Millisecond))
	if n < 0 {
		return int(n), usbError(n)
	}
	return int(n), nil
}

// ActiveConfig returns the config id (not the index) of the active configuration.
// This corresponds to the ConfigInfo.Config field.
func (d *Device) ActiveConfig() (uint8, error) {
	var cfg C.int
	if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 {
		return 0, usbError(errno)
	}
	return uint8(cfg), nil
}

// SetConfig attempts to change the active configuration.
// The cfg provided is the config id (not the index) of the configuration to set,
// which corresponds to the ConfigInfo.Config field.
func (d *Device) SetConfig(cfg uint8) error {
	if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 {
		return usbError(errno)
	}
	return nil
}

// Close the device.
func (d *Device) Close() error {
	if d.handle == nil {
		return fmt.Errorf("usb: double close on device")
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	for iface := range d.claimed {
		C.libusb_release_interface(d.handle, C.int(iface))
	}
	C.libusb_close(d.handle)
	d.handle = nil
	return nil
}

func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
	end := &endpoint{
		Device: d,
	}

	var setAlternate bool
	for _, c := range d.Configs {
		if c.Config != conf {
			continue
		}
		debug.Printf("found conf: %#v\n", c)
		for _, i := range c.Interfaces {
			if i.Number != iface {
				continue
			}
			debug.Printf("found iface: %#v\n", i)
			for i, s := range i.Setups {
				if s.Alternate != setup {
					continue
				}
				setAlternate = i != 0

				debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate)
				for _, e := range s.Endpoints {
					debug.Printf("ep %02x search: %#v\n", epoint, s)
					if e.Address != epoint {
						continue
					}
					end.InterfaceSetup = s
					end.EndpointInfo = e
					switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt {
					case TRANSFER_TYPE_BULK:
						end.xfer = bulk_xfer
					case TRANSFER_TYPE_INTERRUPT:
						end.xfer = interrupt_xfer
					case TRANSFER_TYPE_ISOCHRONOUS:
						end.xfer = isochronous_xfer
					default:
						return nil, fmt.Errorf("usb: %s transfer is unsupported", tt)
					}
					goto found
				}
				return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
			}
			return nil, fmt.Errorf("usb: unknown setup %02x", setup)
		}
		return nil, fmt.Errorf("usb: unknown interface %02x", iface)
	}
	return nil, fmt.Errorf("usb: unknown configuration %02x", conf)

found:

	// Set the configuration
	var activeConf C.int
	if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 {
		return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno))
	}
	if int(activeConf) != int(conf) {
		if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
			return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
		}
	}

	// Enable auto detachment of kernel driver if platform supported
	if errno := C.libusb_set_auto_detach_kernel_driver(d.handle, C.int(1)); errno < 0 && errno != C.LIBUSB_ERROR_NOT_SUPPORTED {
		return nil, fmt.Errorf("usb: detachkrndrv: %s", usbError(errno))
	}

	// Claim the interface
	if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
		return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
	}

	// Increment the claim count
	d.lock.Lock()
	d.claimed[iface]++
	d.lock.Unlock() // unlock immediately because the next calls may block

	// Choose the alternate
	if setAlternate {
		if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
			debug.Printf("altsetting error: %s", usbError(errno))
			return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
		}
	}

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

package usb

// #include <libusb-1.0/libusb.h>
import "C"

import (
	"fmt"
	"reflect"
	"sync"
	"time"
	"unsafe"
)

var DefaultReadTimeout = 1 * time.Second
var DefaultWriteTimeout = 1 * time.Second
var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second

type Device struct {
	handle *C.libusb_device_handle

	// Embed the device information for easy access
	*Descriptor

	// Timeouts
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration
	ControlTimeout time.Duration

	// Claimed interfaces
	lock    *sync.Mutex
	claimed map[uint8]int
}

func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device {
	ifaces := 0
	d := &Device{
		handle:         handle,
		Descriptor:     desc,
		ReadTimeout:    DefaultReadTimeout,
		WriteTimeout:   DefaultWriteTimeout,
		ControlTimeout: DefaultControlTimeout,
		lock:           new(sync.Mutex),
		claimed:        make(map[uint8]int, ifaces),
	}

	return d
}

func (d *Device) Reset() error {
	if errno := C.libusb_reset_device(d.handle); errno != 0 {
		return usbError(errno)
	}
	return nil
}

func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
	//log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data))
	dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	n := C.libusb_control_transfer(
		d.handle,
		C.uint8_t(rType),
		C.uint8_t(request),
		C.uint16_t(val),
		C.uint16_t(idx),
		(*C.uchar)(unsafe.Pointer(dataSlice.Data)),
		C.uint16_t(len(data)),
		C.uint(d.ControlTimeout/time.Millisecond))
	if n < 0 {
		return int(n), usbError(n)
	}
	return int(n), nil
}

// ActiveConfig returns the config id (not the index) of the active configuration.
// This corresponds to the ConfigInfo.Config field.
func (d *Device) ActiveConfig() (uint8, error) {
	var cfg C.int
	if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 {
		return 0, usbError(errno)
	}
	return uint8(cfg), nil
}

// SetConfig attempts to change the active configuration.
// The cfg provided is the config id (not the index) of the configuration to set,
// which corresponds to the ConfigInfo.Config field.
func (d *Device) SetConfig(cfg uint8) error {
	if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 {
		return usbError(errno)
	}
	return nil
}

// Close the device.
func (d *Device) Close() error {
	fmt.Print("0\n")
	if d.handle == nil {
		return fmt.Errorf("usb: double close on device")
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	// for iface := range d.claimed {
	// 	C.libusb_release_interface(d.handle, C.int(iface))
	// }
	fmt.Print("1\n")
	C.libusb_close(d.handle)
	fmt.Print("2\n")
	d.handle = nil
	return nil
}

func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
	end := &endpoint{
		Device: d,
	}

	var setAlternate bool
	for _, c := range d.Configs {
		if c.Config != conf {
			continue
		}
		debug.Printf("found conf: %#v\n", c)
		for _, i := range c.Interfaces {
			if i.Number != iface {
				continue
			}
			debug.Printf("found iface: %#v\n", i)
			for i, s := range i.Setups {
				if s.Alternate != setup {
					continue
				}
				setAlternate = i != 0

				debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate)
				for _, e := range s.Endpoints {
					debug.Printf("ep %02x search: %#v\n", epoint, s)
					if e.Address != epoint {
						continue
					}
					end.InterfaceSetup = s
					end.EndpointInfo = e
					switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt {
					case TRANSFER_TYPE_BULK:
						end.xfer = bulk_xfer
					case TRANSFER_TYPE_INTERRUPT:
						end.xfer = interrupt_xfer
					case TRANSFER_TYPE_ISOCHRONOUS:
						end.xfer = isochronous_xfer
					default:
						return nil, fmt.Errorf("usb: %s transfer is unsupported", tt)
					}
					goto found
				}
				return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
			}
			return nil, fmt.Errorf("usb: unknown setup %02x", setup)
		}
		return nil, fmt.Errorf("usb: unknown interface %02x", iface)
	}
	return nil, fmt.Errorf("usb: unknown configuration %02x", conf)

found:

	// Set the configuration
	var activeConf C.int
	if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 {
		return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno))
	}
	if int(activeConf) != int(conf) {
		if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
			return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
		}
	}

	// Enable auto detachment of kernel driver if platform supported
	// if errno := C.libusb_kernel_driver_active(d.handle, C.int(iface)); errno == 1 {
	if errno := C.libusb_set_auto_detach_kernel_driver(d.handle, C.int(iface)); errno != 0 {
		return nil, fmt.Errorf("usb: detachkrndrv: %s", usbError(errno))
	}
	// }

	// Claim the interface
	if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
		return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
	}

	// Increment the claim count
	d.lock.Lock()
	d.claimed[iface]++
	d.lock.Unlock() // unlock immediately because the next calls may block

	// Choose the alternate
	if setAlternate {
		if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
			debug.Printf("altsetting error: %s", usbError(errno))
			return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
		}
	}

	return end, nil
}
<|endoftext|>


<commit_before>package config

import (
	"regexp"

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

// MockClient is the mock struct
type MockClient struct {
	mock.Mock
}

// ConfigInfo is the mock function.
func (m *MockClient) ConfigInfo() ConfigInfo {
	args := m.Called()
	return args.Get(0).(ConfigInfo)
}

// Get is the mock function.
func (m *MockClient) Get(path string) ([]byte, error) {
	args := m.Called(path)
	return args.Get(0).([]byte), args.Error(1)
}

// AddListener is the mock function.
func (m *MockClient) AddListener(pathRegEx *regexp.Regexp) *chan ModifiedFile {
	args := m.Called(pathRegEx)
	return args.Get(0).(*chan ModifiedFile)
}

// RemoveListener is the mock function.
func (m *MockClient) RemoveListener(ch *chan ModifiedFile) {
	m.Called(ch)
}

// Stop is to stop client
func (m *MockClient) Stop() error {
	args := m.Called()
	return args.Error(0)
}

// List is the mock function.
func (m *MockClient) List(path string) (map[string][]byte, error) {
	args := m.Called(path)
	return args.Get(0).(map[string][]byte), args.Error(1)
}
<commit_msg>add missing Mock<commit_after>package config

import (
	"regexp"

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

// MockClient is the mock struct
type MockClient struct {
	mock.Mock
}

// ConfigInfo is the mock function.
func (m *MockClient) ConfigInfo() ConfigInfo {
	args := m.Called()
	return args.Get(0).(ConfigInfo)
}

// Get is the mock function.
func (m *MockClient) Get(path string) ([]byte, error) {
	args := m.Called(path)
	return args.Get(0).([]byte), args.Error(1)
}

// AddListener is the mock function.
func (m *MockClient) AddListener(pathRegEx *regexp.Regexp) *chan ModifiedFile {
	args := m.Called(pathRegEx)
	return args.Get(0).(*chan ModifiedFile)
}

// RemoveListener is the mock function.
func (m *MockClient) RemoveListener(ch *chan ModifiedFile) {
	m.Called(ch)
}

// Stop is to stop client
func (m *MockClient) Stop() error {
	args := m.Called()
	return args.Error(0)
}

// List is the mock function.
func (m *MockClient) List(path string) (map[string][]byte, error) {
	args := m.Called(path)
	return args.Get(0).(map[string][]byte), args.Error(1)
}

// Watch watches change of a file and invokes callback. It also invokes callback for the
func (m *MockClient) Watch(path string, callback func([]byte) error, errChan chan<- error) error {
	args := m.Called(path, callback, errChan)
	return args.Error(0)
}
<|endoftext|>


<commit_before>package api

import (
	"compress/gzip"
	"net/http"
	"strings"

	"github.com/ponzu-cms/ponzu/system/db"
)

// Gzip wraps a HandlerFunc to compress responses when possible
func Gzip(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		if db.ConfigCache("gzip_disabled").(bool) == true {
			next.ServeHTTP(res, req)
			return
		}

		// check if req header content-encoding supports gzip
		if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
			// gzip response data
			res.Header().Set("Content-Encoding", "gzip")
			var gzres gzipResponseWriter
			if pusher, ok := res.(http.Pusher); ok {
				gzres = gzipResponseWriter{res, pusher, gzip.NewWriter(res)}
			} else {
				gzres = gzipResponseWriter{res, nil, gzip.NewWriter(res)}
			}

			next.ServeHTTP(gzres, req)
			return
		}

		next.ServeHTTP(res, req)
	})
}

type gzipResponseWriter struct {
	http.ResponseWriter
	pusher http.Pusher

	gw *gzip.Writer
}

func (gzw gzipResponseWriter) Write(p []byte) (int, error) {
	defer gzw.gw.Close()
	return gzw.gw.Write(p)
}

func (gzw gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
	if opts == nil {
		opts = &http.PushOptions{
			Header: make(http.Header),
		}
	}

	opts.Header.Set("Accept-Encoding", "gzip")

	return gzw.pusher.Push(target, opts)
}
<commit_msg>adding check for nil pusher in gzipResponseWriter type<commit_after>package api

import (
	"compress/gzip"
	"net/http"
	"strings"

	"github.com/ponzu-cms/ponzu/system/db"
)

// Gzip wraps a HandlerFunc to compress responses when possible
func Gzip(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		if db.ConfigCache("gzip_disabled").(bool) == true {
			next.ServeHTTP(res, req)
			return
		}

		// check if req header content-encoding supports gzip
		if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
			// gzip response data
			res.Header().Set("Content-Encoding", "gzip")
			var gzres gzipResponseWriter
			if pusher, ok := res.(http.Pusher); ok {
				gzres = gzipResponseWriter{res, pusher, gzip.NewWriter(res)}
			} else {
				gzres = gzipResponseWriter{res, nil, gzip.NewWriter(res)}
			}

			next.ServeHTTP(gzres, req)
			return
		}

		next.ServeHTTP(res, req)
	})
}

type gzipResponseWriter struct {
	http.ResponseWriter
	pusher http.Pusher

	gw *gzip.Writer
}

func (gzw gzipResponseWriter) Write(p []byte) (int, error) {
	defer gzw.gw.Close()
	return gzw.gw.Write(p)
}

func (gzw gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
	if gzw.pusher == nil {
		return nil
	}

	if opts == nil {
		opts = &http.PushOptions{
			Header: make(http.Header),
		}
	}

	opts.Header.Set("Accept-Encoding", "gzip")

	return gzw.pusher.Push(target, opts)
}
<|endoftext|>


<commit_before>package opts

// BarChart
type BarChart struct {
	// Name of stack. On the same category axis, the series with the
	// same stack name would be put on top of each other.
	Stack string

	// The gap between bars between different series, is a percent value like '30%',
	// which means 30% of the bar width.
	// Set barGap as '-100%' can overlap bars that belong to different series,
	// which is useful when making a series of bar be background.
	// In a single coordinate system, this attribute is shared by multiple 'bar' series.
	// This attribute should be set on the last 'bar' series in the coordinate system,
	// then it will be adopted by all 'bar' series in the coordinate system.
	BarGap string

	// The bar gap of a single series, defaults to be 20% of the category gap,
	// can be set as a fixed value.
	// In a single coordinate system, this attribute is shared by multiple 'bar' series.
	// This attribute should be set on the last 'bar' series in the coordinate system,
	// then it will be adopted by all 'bar' series in the coordinate system.
	BarCategoryGap string

	// Index of x axis to combine with, which is useful for multiple x axes in one chart.
	XAxisIndex int

	// Index of y axis to combine with, which is useful for multiple y axes in one chart.
	YAxisIndex int
}

// BarData
type BarData struct {
	// The name of data item.
	Name string `json:"name,omitempty"`

	// The value of a single data item.
	Value interface{} `json:"value,omitempty"`

	// The style setting of the text label in a single bar.
	*Label `json:"label,omitempty"`

	// ItemStyle settings in this series data.
	*ItemStyle `json:"itemStyle,omitempty"`

	// Tooltip settings in this series data.
	*Tooltip `json:"tooltip,omitempty"`
}

// BoxPlotData
type BoxPlotData struct {
	// The name of data item.
	Name string `json:"name,omitempty"`

	// The value of a single data item.
	Value interface{} `json:"value,omitempty"`

	// The style setting of the text label in a single bar.
	*Label `json:"label,omitempty"`

	// ItemStyle settings in this series data.
	*ItemStyle `json:"itemStyle,omitempty"`

	// Emphasis settings in this series data.
	*Emphasis `json:"emphasis,omitempty"`

	// Tooltip settings in this series data.
	*Tooltip `json:"tooltip,omitempty"`
}

// EffectScatterData
type EffectScatterData struct {
}

// FunnelData
type FunnelData struct {
	Name  string      `json:"name,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// GaugeData
type GaugeData struct {
	Name  string      `json:"name,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// GraphOpts is the option set for graph chart.
//type GraphChart struct {
//	//图的布局。可选：
//	// "none" 不采用任何布局，使用节点中提供的 x， y 作为节点的位置。
//	// "circular" 采用环形布局
//	// "force" 采用力引导布局
//	Layout string
//	// "force", "circular" 布局详细配置项
//	Force GraphForce
//	// 是否开启鼠标缩放和平移漫游。默认不开启。
//	Roam bool
//	// 是否在鼠标移到节点上的时候突出显示节点以及节点的边和邻接节点
//	FocusNodeAdjacency bool
//	//
//	Categories []GraphCategory
//}

// HeatMapChart is the option set for a heatmap chart.
type HeatMapChart struct {
	//使用的 x 轴的 index，在单个图表实例中存在多个 x 轴的时候有用
	XAxisIndex int
	//使用的 y 轴的 index，在单个图表实例中存在多个 y 轴的时候有用
	YAxisIndex int
}

// LineChart is the options set for a line chart.
type LineChart struct {
	// 数据堆叠，同个类目轴上系列配置相同的 stack 值可以堆叠放置
	Stack string
	// 曲线是否平滑
	Smooth bool
	// 是否使用阶梯图
	Step bool
	// 使用的 x 轴的 index，在单个图表实例中存在多个 x 轴的时候有用
	XAxisIndex int
	// 使用的 y 轴的 index，在单个图表实例中存在多个 y 轴的时候有用
	YAxisIndex int
	// 是否连接空数据。
	ConnectNulls bool
}

// LineData
type LineData struct {
	// Name
	Name string `json:"name,omitempty"`
	// Value
	Value interface{} `json:"value,omitempty"`
	// Symbol
	Symbol string `json:"symbol,omitempty"`
	// SymbolSize
	SymbolSize int `json:"symbolSize,omitempty"`

	// Index of x axis to combine with, which is useful for multiple x axes in one chart.
	XAxisIndex int

	// Index of y axis to combine with, which is useful for multiple y axes in one chart.
	YAxisIndex int
}

// LiquidChart
type LiquidChart struct {
	// 水球图形状，可选
	// "circle", "rect", "roundRect", "triangle", "diamond", "pin", "arrow", "none"
	Shape string
	// 是否显示水球轮廓
	IsShowOutline bool
	// 是否停止动画
	IsWaveAnimation bool
}

// LiquidData
type LiquidData struct {
}

// PieChart is the option set for a pie chart.
type PieChart struct {
	// 是否展示成南丁格尔图，通过半径区分数据大小。可选择两种模式：
	// 1."radius": 扇区圆心角展现数据的百分比，半径展现数据的大小。
	// 2."area": 所有扇区圆心角相同，仅通过半径展现数据大小。
	RoseType string
	// 饼图的中心（圆心）坐标，数组的第一项是横坐标，第二项是纵坐标。
	// 支持设置成百分比，设置成百分比时第一项是相对于容器宽度，第二项是相对于容器高度
	// 使用示例
	// 设置成绝对的像素值: center: [400, 300]
	// 设置成相对的百分比: center: ['50%', '50%']
	// 默认 ["50%", "50%"]
	Center interface{}
	// 饼图的半径。可以为如下类型：
	// 1.number：直接指定外半径值。
	// 2.string：例如，'20%'，表示外半径为可视区尺寸（容器高宽中较小一项）的 20% 长度。
	// 3.Array.<number|string>：数组的第一项是内半径，第二项是外半径。
	// 每一项遵从上述 number string 的描述。
	// 默认 [0, "75%"]
	Radius interface{}
}

// PieData
type PieData struct {
	Name       string      `json:"name,omitempty"`
	Value      interface{} `json:"value,omitempty"`
	Selected   bool        `json:"selected,omitempty"`
	*Label     `json:"label,omitempty"`
	*ItemStyle `json:"itemStyle,omitempty"`
	*Tooltip   `json:"tooltip,omitempty"`
}

// ScatterChart is the option set for a scatter chart.
type ScatterChart struct {
	// 使用的 x 轴的 index，在单个图表实例中存在多个 x 轴的时候有用
	XAxisIndex int
	// 使用的 y 轴的 index，在单个图表实例中存在多个 y 轴的时候有用
	YAxisIndex int
}

// Bar3DChart is the option set for a 3D bar chart.
type Bar3DChart struct {
	Shading string
}

// WordCloudChart is the option set for a word cloud chart.
type WordCloudChart struct {
	// shape of WordCloud
	// Optional: "circle", "rect", "roundRect", "triangle", "diamond", "pin", "arrow"
	Shape string
	// range of font size
	SizeRange []float32
	// range of font rotation angle
	RotationRange []float32
}

// SankeyLink represents relationship between two data nodes.
type SankeyLink struct {
	// 边的源节点名称的字符串，也支持使用数字表示源节点的索引
	Source interface{} `json:"source,omitempty"`
	// 边的目标节点名称的字符串，也支持使用数字表示源节点的索引
	Target interface{} `json:"target,omitempty"`
	// 边的数值，可以在力引导布局中用于映射到边的长度
	Value float32 `json:"value,omitempty"`
}

// SankeyNode represents a data node.
type SankeyNode struct {
	// 数据项名称
	Name string `json:"name,omitempty"`
	// 数据项值
	Value string `json:"value,omitempty"`
}

// ThemeRiverChartItem
type ThemeRiverData struct {
}

// RadarChartItem
type RadarData struct {
	// Name
	Name string `json:"name,omitempty"`
	// Value
	Value interface{} `json:"value,omitempty"`
}

// KlineData
type KlineData struct {
}

// ScatterData
type ScatterData struct {
	// Name
	Name string `json:"name,omitempty"`

	// Value
	Value interface{} `json:"value,omitempty"`

	// Symbol
	Symbol string `json:"symbol,omitempty"`

	// SymbolSize
	SymbolSize int `json:"symbolSize,omitempty"`

	// SymbolRotate
	SymbolRotate int `json:"symbolRotate,omitempty"`

	// Index of x axis to combine with, which is useful for multiple x axes in one chart.
	XAxisIndex int

	// Index of y axis to combine with, which is useful for multiple y axes in one chart.
	YAxisIndex int
}

// MapData
type MapData struct {
}

// HeatMapData
type HeatMapData struct {
}

// WordCloudData
type WordCloudData struct {
	Name  string      `json:"name,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// GeoData
type GeoData struct {
}

// ParallelData
type ParallelData struct {
}
<commit_msg>[refactor] liquidData. (#90)<commit_after>package opts

// BarChart
type BarChart struct {
	// Name of stack. On the same category axis, the series with the
	// same stack name would be put on top of each other.
	Stack string

	// The gap between bars between different series, is a percent value like '30%',
	// which means 30% of the bar width.
	// Set barGap as '-100%' can overlap bars that belong to different series,
	// which is useful when making a series of bar be background.
	// In a single coordinate system, this attribute is shared by multiple 'bar' series.
	// This attribute should be set on the last 'bar' series in the coordinate system,
	// then it will be adopted by all 'bar' series in the coordinate system.
	BarGap string

	// The bar gap of a single series, defaults to be 20% of the category gap,
	// can be set as a fixed value.
	// In a single coordinate system, this attribute is shared by multiple 'bar' series.
	// This attribute should be set on the last 'bar' series in the coordinate system,
	// then it will be adopted by all 'bar' series in the coordinate system.
	BarCategoryGap string

	// Index of x axis to combine with, which is useful for multiple x axes in one chart.
	XAxisIndex int

	// Index of y axis to combine with, which is useful for multiple y axes in one chart.
	YAxisIndex int
}

// BarData
type BarData struct {
	// The name of data item.
	Name string `json:"name,omitempty"`

	// The value of a single data item.
	Value interface{} `json:"value,omitempty"`

	// The style setting of the text label in a single bar.
	*Label `json:"label,omitempty"`

	// ItemStyle settings in this series data.
	*ItemStyle `json:"itemStyle,omitempty"`

	// Tooltip settings in this series data.
	*Tooltip `json:"tooltip,omitempty"`
}

// BoxPlotData
type BoxPlotData struct {
	// The name of data item.
	Name string `json:"name,omitempty"`

	// The value of a single data item.
	Value interface{} `json:"value,omitempty"`

	// The style setting of the text label in a single bar.
	*Label `json:"label,omitempty"`

	// ItemStyle settings in this series data.
	*ItemStyle `json:"itemStyle,omitempty"`

	// Emphasis settings in this series data.
	*Emphasis `json:"emphasis,omitempty"`

	// Tooltip settings in this series data.
	*Tooltip `json:"tooltip,omitempty"`
}

// EffectScatterData
type EffectScatterData struct {
}

// FunnelData
type FunnelData struct {
	Name  string      `json:"name,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// GaugeData
type GaugeData struct {
	Name  string      `json:"name,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// GraphOpts is the option set for graph chart.
//type GraphChart struct {
//	//图的布局。可选：
//	// "none" 不采用任何布局，使用节点中提供的 x， y 作为节点的位置。
//	// "circular" 采用环形布局
//	// "force" 采用力引导布局
//	Layout string
//	// "force", "circular" 布局详细配置项
//	Force GraphForce
//	// 是否开启鼠标缩放和平移漫游。默认不开启。
//	Roam bool
//	// 是否在鼠标移到节点上的时候突出显示节点以及节点的边和邻接节点
//	FocusNodeAdjacency bool
//	//
//	Categories []GraphCategory
//}

// HeatMapChart is the option set for a heatmap chart.
type HeatMapChart struct {
	//使用的 x 轴的 index，在单个图表实例中存在多个 x 轴的时候有用
	XAxisIndex int
	//使用的 y 轴的 index，在单个图表实例中存在多个 y 轴的时候有用
	YAxisIndex int
}

// LineChart is the options set for a line chart.
type LineChart struct {
	// 数据堆叠，同个类目轴上系列配置相同的 stack 值可以堆叠放置
	Stack string
	// 曲线是否平滑
	Smooth bool
	// 是否使用阶梯图
	Step bool
	// 使用的 x 轴的 index，在单个图表实例中存在多个 x 轴的时候有用
	XAxisIndex int
	// 使用的 y 轴的 index，在单个图表实例中存在多个 y 轴的时候有用
	YAxisIndex int
	// 是否连接空数据。
	ConnectNulls bool
}

// LineData
type LineData struct {
	// Name
	Name string `json:"name,omitempty"`
	// Value
	Value interface{} `json:"value,omitempty"`
	// Symbol
	Symbol string `json:"symbol,omitempty"`
	// SymbolSize
	SymbolSize int `json:"symbolSize,omitempty"`

	// Index of x axis to combine with, which is useful for multiple x axes in one chart.
	XAxisIndex int

	// Index of y axis to combine with, which is useful for multiple y axes in one chart.
	YAxisIndex int
}

// LiquidChart
type LiquidChart struct {
	// 水球图形状，可选
	// "circle", "rect", "roundRect", "triangle", "diamond", "pin", "arrow", "none"
	Shape string
	// 是否显示水球轮廓
	IsShowOutline bool
	// 是否停止动画
	IsWaveAnimation bool
}

// LiquidData
// reference https://github.com/ecomfe/echarts-liquidfill
type LiquidData struct {
	// Name
	Name string `json:"name,omitempty"`
	// Value
	Value interface{} `json:"value,omitempty"`
}

// PieChart is the option set for a pie chart.
type PieChart struct {
	// 是否展示成南丁格尔图，通过半径区分数据大小。可选择两种模式：
	// 1."radius": 扇区圆心角展现数据的百分比，半径展现数据的大小。
	// 2."area": 所有扇区圆心角相同，仅通过半径展现数据大小。
	RoseType string
	// 饼图的中心（圆心）坐标，数组的第一项是横坐标，第二项是纵坐标。
	// 支持设置成百分比，设置成百分比时第一项是相对于容器宽度，第二项是相对于容器高度
	// 使用示例
	// 设置成绝对的像素值: center: [400, 300]
	// 设置成相对的百分比: center: ['50%', '50%']
	// 默认 ["50%", "50%"]
	Center interface{}
	// 饼图的半径。可以为如下类型：
	// 1.number：直接指定外半径值。
	// 2.string：例如，'20%'，表示外半径为可视区尺寸（容器高宽中较小一项）的 20% 长度。
	// 3.Array.<number|string>：数组的第一项是内半径，第二项是外半径。
	// 每一项遵从上述 number string 的描述。
	// 默认 [0, "75%"]
	Radius interface{}
}

// PieData
type PieData struct {
	Name       string      `json:"name,omitempty"`
	Value      interface{} `json:"value,omitempty"`
	Selected   bool        `json:"selected,omitempty"`
	*Label     `json:"label,omitempty"`
	*ItemStyle `json:"itemStyle,omitempty"`
	*Tooltip   `json:"tooltip,omitempty"`
}

// ScatterChart is the option set for a scatter chart.
type ScatterChart struct {
	// 使用的 x 轴的 index，在单个图表实例中存在多个 x 轴的时候有用
	XAxisIndex int
	// 使用的 y 轴的 index，在单个图表实例中存在多个 y 轴的时候有用
	YAxisIndex int
}

// Bar3DChart is the option set for a 3D bar chart.
type Bar3DChart struct {
	Shading string
}

// WordCloudChart is the option set for a word cloud chart.
type WordCloudChart struct {
	// shape of WordCloud
	// Optional: "circle", "rect", "roundRect", "triangle", "diamond", "pin", "arrow"
	Shape string
	// range of font size
	SizeRange []float32
	// range of font rotation angle
	RotationRange []float32
}

// SankeyLink represents relationship between two data nodes.
type SankeyLink struct {
	// 边的源节点名称的字符串，也支持使用数字表示源节点的索引
	Source interface{} `json:"source,omitempty"`
	// 边的目标节点名称的字符串，也支持使用数字表示源节点的索引
	Target interface{} `json:"target,omitempty"`
	// 边的数值，可以在力引导布局中用于映射到边的长度
	Value float32 `json:"value,omitempty"`
}

// SankeyNode represents a data node.
type SankeyNode struct {
	// 数据项名称
	Name string `json:"name,omitempty"`
	// 数据项值
	Value string `json:"value,omitempty"`
}

// ThemeRiverChartItem
type ThemeRiverData struct {
}

// RadarChartItem
type RadarData struct {
	// Name
	Name string `json:"name,omitempty"`
	// Value
	Value interface{} `json:"value,omitempty"`
}

// KlineData
type KlineData struct {
}

// ScatterData
type ScatterData struct {
	// Name
	Name string `json:"name,omitempty"`

	// Value
	Value interface{} `json:"value,omitempty"`

	// Symbol
	Symbol string `json:"symbol,omitempty"`

	// SymbolSize
	SymbolSize int `json:"symbolSize,omitempty"`

	// SymbolRotate
	SymbolRotate int `json:"symbolRotate,omitempty"`

	// Index of x axis to combine with, which is useful for multiple x axes in one chart.
	XAxisIndex int

	// Index of y axis to combine with, which is useful for multiple y axes in one chart.
	YAxisIndex int
}

// MapData
type MapData struct {
}

// HeatMapData
type HeatMapData struct {
}

// WordCloudData
type WordCloudData struct {
	Name  string      `json:"name,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// GeoData
type GeoData struct {
}

// ParallelData
type ParallelData struct {
}
<|endoftext|>


<commit_before>package utils

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"
	"time"
)

const (
	TimeStampLayout = "2006-01-02T15:04:05.000000-07:00"
)

func extractExitCode(err error) int {
	if err != nil {
		switch err.(type) {
		case *exec.ExitError:
			return err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitStatus()
		case *os.PathError:
			return 127
		}
	}
	return 0
}

func ExecCode(code string, path string, filename string) (output string, exitCode int, startedAt time.Time, finishedAt time.Time) {
	var err error
	var tmp *os.File

	if runtime.GOOS == "windows" {
		tmp, err = os.Create(fmt.Sprintf("%s/%s.bat", path, filename))
	} else {
		tmp, err = os.Create(fmt.Sprintf("%s/%s", path, filename))
	}

	if err != nil {
		log.Fatalf("Error creating temp file : ", err)
	}

	defer tmp.Close()

	_, err = tmp.WriteString(code)
	if err != nil {
		log.Fatalf("Error writing to file : ", err)
	}

	os.Chmod(tmp.Name(), 0777)
	if err != nil {
		log.Fatalf("Error changing permision to file : ", err)
	}

	return RunCmd(tmp.Name())
}

func RunCmd(command string) (output string, exitCode int, startedAt time.Time, finishedAt time.Time) {

	var cmd *exec.Cmd

	if runtime.GOOS == "windows" {
		log.Infof("Command: %s", command)
		cmd = exec.Command("cmd", "/C", command)
	} else {
		log.Infof("Command: %s %s", "/bin/sh", command)
		cmd = exec.Command("/bin/sh", command)
	}

	startedAt = time.Now()
	bytes, err := cmd.CombinedOutput()
	finishedAt = time.Now()
	output = strings.TrimSpace(string(bytes))
	exitCode = extractExitCode(err)

	log.Debugf("Starting Time: %s", startedAt.Format(TimeStampLayout))
	log.Debugf("End Time: %s", finishedAt.Format(TimeStampLayout))
	log.Debugf("Output")
	log.Debugf("")
	log.Debugf("%s", output)
	log.Debugf("")
	log.Infof("Exit Code: %d", exitCode)
	return
}
<commit_msg>Inclusion of Iptables Path<commit_after>package utils

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"
	"time"
)

const (
	TimeStampLayout = "2006-01-02T15:04:05.000000-07:00"
)

func extractExitCode(err error) int {
	if err != nil {
		switch err.(type) {
		case *exec.ExitError:
			return err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitStatus()
		case *os.PathError:
			return 127
		}
	}
	return 0
}

func ExecCode(code string, path string, filename string) (output string, exitCode int, startedAt time.Time, finishedAt time.Time) {
	var err error
	var tmp *os.File

	if runtime.GOOS == "windows" {
		tmp, err = os.Create(fmt.Sprintf("%s/%s.bat", path, filename))
	} else {
		tmp, err = os.Create(fmt.Sprintf("%s/%s", path, filename))
	}

	if err != nil {
		log.Fatalf("Error creating temp file : ", err)
	}

	defer tmp.Close()

	_, err = tmp.WriteString(code)
	if err != nil {
		log.Fatalf("Error writing to file : ", err)
	}

	os.Chmod(tmp.Name(), 0777)
	if err != nil {
		log.Fatalf("Error changing permision to file : ", err)
	}

	return RunCmd(tmp.Name())
}

func RunCmd(command string) (output string, exitCode int, startedAt time.Time, finishedAt time.Time) {

	var cmd *exec.Cmd

	if runtime.GOOS == "windows" {
		log.Infof("Command: %s", command)
		cmd = exec.Command("cmd", "/C", command)
	} else {
		log.Infof("Command: %s %s", "/bin/sh -c", command)
		cmd = exec.Command("/bin/sh", "-c", command)
	}

	startedAt = time.Now()
	bytes, err := cmd.CombinedOutput()
	finishedAt = time.Now()
	output = strings.TrimSpace(string(bytes))
	exitCode = extractExitCode(err)

	log.Debugf("Starting Time: %s", startedAt.Format(TimeStampLayout))
	log.Debugf("End Time: %s", finishedAt.Format(TimeStampLayout))
	log.Debugf("Output")
	log.Debugf("")
	log.Debugf("%s", output)
	log.Debugf("")
	log.Infof("Exit Code: %d", exitCode)
	return
}
<|endoftext|>


<commit_before>// An ad-hoc parser for Wikipedia's 45GB (and growing) XML database.
package parse

import (
	"bufio"
	"encoding/xml"
	"io"
	"log"
	"regexp"
	"strings"
)

var pageStartRegex = regexp.MustCompile(".*<page>.*")
var pageEndRegex = regexp.MustCompile(".*</page>.*")
var redirectRegex = regexp.MustCompile("#REDIRECT[ \t].*?\\[\\[.*?\\]\\]")
var linkRegex = regexp.MustCompile("\\[\\[([^|]+?)\\]\\]")
var categoryRegex = regexp.MustCompile("\\[\\[Category:(.+?)\\]\\]")

// Parse parses given reader as XML and dumps Page objects with links
// into its output channel.
func Parse(reader io.Reader, pages chan<- *Page) {
	chunks := make(chan []byte)
	rawPages := make(chan []byte)
	nonRedirectPages := make(chan []byte)
	somePages := make(chan *Page)

	go GetChunks(reader, chunks)
	go GetRawPages(chunks, rawPages)
	go FilterRedirects(rawPages, nonRedirectPages)
	go GetPages(nonRedirectPages, somePages)
	go GetLinks(somePages, pages)
}

// CategorizedParse is just like Parse, except that it also categorizes pages.
func CategorizedParse(reader io.Reader, out chan<- *Page) {
	pages := make(chan *Page)

	go GetCategories(pages, out)

	Parse(reader, pages)
}

// GetChunks reads an XML file line by line and dumps each line to its output channel.
func GetChunks(reader io.Reader, chunks chan<- []byte) {
	scanner := bufio.NewScanner(reader)

	eof := false

	for !eof {
		if scanner.Scan() {
			chunks <- scanner.Bytes()
		} else {
			if err := scanner.Err(); err != nil {
				log.Println(err.Error() + " skipping line")
			} else {
				eof = true
			}
		}
	}

	close(chunks)
}

// GetRawPages combines individual line elements into complete XML pages
// so that they can be processed by a standard in-memory XML parser.
func GetRawPages(chunks <-chan []byte, pages chan<- []byte) {
	page := []byte("<page>")
	inPage := false

	for {
		select {
		case chunk, ok := <-chunks:
			if !ok {
				close(pages)
				return
			}

			if pageStartRegex.Match(chunk) {
				inPage = true
			} else if pageEndRegex.Match(chunk) {
				if inPage {
					page = append(page, []byte("</page>")...)
					pages <- page
					page = []byte("<page>")
				}

				inPage = false
			} else {
				if inPage {
					page = append(page, chunk...)
				}
			}
		}
	}
}

// FilterRedirects discards all pages that redirect to another page.
func FilterRedirects(rawPages <-chan []byte, nonRedirectPages chan<- []byte) {
	for {
		select {
		case rawPage, ok := <-rawPages:
			if !ok {
				close(nonRedirectPages)
				return
			}

			if redirectRegex.Find(rawPage) == nil {
				nonRedirectPages <- rawPage
			}
		}
	}
}

// GetPages parses a complete XML page into a page object.
func GetPages(rawPages <-chan []byte, pages chan<- *Page) {
	for {
		select {
		case rawPage, ok := <-rawPages:
			if !ok {
				close(pages)
				return
			}

			pageStruct := &Page{}

			err := xml.Unmarshal(rawPage, pageStruct)

			if err != nil {

			} else {
				pages <- pageStruct
			}
		}
	}
}

// GetLinks extracts all Wikipedia links found in pages.
// Only links in the form [[target]] are extracted.
func GetLinks(pages <-chan *Page, linkedPages chan<- *Page) {
	for {
		select {
		case page, ok := <-pages:
			if !ok {
				close(linkedPages)
				return
			}

			links := linkRegex.FindAllStringSubmatch(page.Revision.Text, -1)

			for _, link := range links {
				page.Links = append(page.Links, link[1])
			}

			linkedPages <- page
		}
	}
}

// GetCategories extracts categories out of each Wikipedia page
// and adds them to the given categories object.
// Only links in the form [[Category:target]] are extracted.
func GetCategories(pages <-chan *Page, categorizedPages chan<- *Page) {
	for {
		select {
		case page, ok := <-pages:
			if !ok {
				close(categorizedPages)
				return
			}

			rawCats := categoryRegex.FindAllStringSubmatch(page.Revision.Text, -1)
			cats := make([]string, len(rawCats))

			for i, rawCat := range rawCats {
				cats[i] = strings.Trim(rawCat[1], " \t|")
			}

			page.Categories = cats

			categorizedPages <- page
		}
	}
}
<commit_msg>changes the way files are chunked from a line based system to a token based one. this results in a speedup of almost 4x, so now I'm getting 270 pages/sec on my laptop<commit_after>// An ad-hoc parser for Wikipedia's 45GB (and growing) XML database.
package parse

import (
	"bufio"
	"bytes"
	"encoding/xml"
	"io"
	"log"
	"regexp"
	"strings"
)

var pageStartRegex = regexp.MustCompile(".*<page>.*")
var pageEndRegex = regexp.MustCompile(".*</page>.*")
var redirectRegex = regexp.MustCompile("#REDIRECT[ \t].*?\\[\\[.*?\\]\\]")
var linkRegex = regexp.MustCompile("\\[\\[([^|]+?)\\]\\]")
var categoryRegex = regexp.MustCompile("\\[\\[Category:(.+?)\\]\\]")

// Parse parses given reader as XML and dumps Page objects with links
// into its output channel.
func Parse(reader io.Reader, pages chan<- *Page) {
	rawPages := make(chan []byte)
	nonRedirectPages := make(chan []byte)
	somePages := make(chan *Page)

	go GetRawPages(reader, rawPages)
	go FilterRedirects(rawPages, nonRedirectPages)
	go GetPages(nonRedirectPages, somePages)
	go GetLinks(somePages, pages)
}

// CategorizedParse is just like Parse, except that it also categorizes pages.
func CategorizedParse(reader io.Reader, out chan<- *Page) {
	pages := make(chan *Page)

	go GetCategories(pages, out)

	Parse(reader, pages)
}

// GetChunks reads an XML file line by line and dumps each line to its output channel.
func GetRawPages(rawReader io.Reader, pages chan<- []byte) {
	reader := bufio.NewReader(rawReader)

	buffer := make([]byte, 0)
	inPage := false

	eof := false

	for !eof {
		text, err := reader.ReadBytes('>')

		if err != nil {
			if err == io.EOF {
				eof = true
			} else {
				log.Println(err.Error() + " skipping line")
			}
		}

		startTag := []byte("<page>")
		startIndex := bytes.Index(text, startTag)

		endTag := []byte("</page>")
		endIndex := bytes.Index(text, endTag)

		if startIndex != -1 {
			inPage = true
			buffer = text[startIndex:]
		} else if endIndex != -1 {
			inPage = false
			buffer = append(buffer, text[:endIndex+len(endTag)]...)

			pages <- buffer

			buffer = make([]byte, 0)
		} else if inPage {
			buffer = append(buffer, text...)
		}
	}

	close(pages)
}

// FilterRedirects discards all pages that redirect to another page.
func FilterRedirects(rawPages <-chan []byte, nonRedirectPages chan<- []byte) {
	for {
		select {
		case rawPage, ok := <-rawPages:
			if !ok {
				close(nonRedirectPages)
				return
			}

			if redirectRegex.Find(rawPage) == nil {
				nonRedirectPages <- rawPage
			}
		}
	}
}

// GetPages parses a complete XML page into a page object.
func GetPages(rawPages <-chan []byte, pages chan<- *Page) {
	for {
		select {
		case rawPage, ok := <-rawPages:
			if !ok {
				close(pages)
				return
			}

			pageStruct := &Page{}

			err := xml.Unmarshal(rawPage, pageStruct)

			if err != nil {

			} else {
				pages <- pageStruct
			}
		}
	}
}

// GetLinks extracts all Wikipedia links found in pages.
// Only links in the form [[target]] are extracted.
func GetLinks(pages <-chan *Page, linkedPages chan<- *Page) {
	for {
		select {
		case page, ok := <-pages:
			if !ok {
				close(linkedPages)
				return
			}

			links := linkRegex.FindAllStringSubmatch(page.Revision.Text, -1)

			for _, link := range links {
				page.Links = append(page.Links, link[1])
			}

			linkedPages <- page
		}
	}
}

// GetCategories extracts categories out of each Wikipedia page
// and adds them to the given categories object.
// Only links in the form [[Category:target]] are extracted.
func GetCategories(pages <-chan *Page, categorizedPages chan<- *Page) {
	for {
		select {
		case page, ok := <-pages:
			if !ok {
				close(categorizedPages)
				return
			}

			rawCats := categoryRegex.FindAllStringSubmatch(page.Revision.Text, -1)
			cats := make([]string, len(rawCats))

			for i, rawCat := range rawCats {
				cats[i] = strings.Trim(rawCat[1], " \t|")
			}

			page.Categories = cats

			categorizedPages <- page
		}
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"errors"
	"net/http"
)

func RetryRequest(tries int, client *http.Client, request *http.Request) (*http.Response, error) {
	var resp *http.Response
	var err error
	for try := 0; try < tries; try++ {
		resp, err = client.Do(request)
		if resp.StatusCode != http.StatusOK {
			continue
		}
		return resp, err
	}
	return resp, errors.New("Service unavailable")
}
<commit_msg>Sleep between request retries<commit_after>package utils

import (
	"errors"
	"net/http"
	"time"
)

func RetryRequest(tries int, client *http.Client, request *http.Request) (*http.Response, error) {
	var resp *http.Response
	var err error
	for try := 0; try < tries; try++ {
		resp, err = client.Do(request)
		if err != nil {
			time.Sleep(1 * time.Second)
			continue
		}
		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()
			time.Sleep(2 * time.Second)
			continue
		}
		return resp, err
	}
	return resp, errors.New("Service unavailable")
}
<|endoftext|>


<commit_before>package utils

import (
	"errors"
	"os"
	"os/user"
	"path/filepath"
)

// NormalizeCommand has very similar semantics to `NormalizeFilePath`, except
// we don't "absolute" paths before returning them.
func NormalizeCommand(path string) (string, error) {
	// don't normalize empty strings
	if path == "" {
		return "", nil
	}

	// expand env and home directory
	var err error
	path, err = ExpandHome(os.ExpandEnv(path))
	if err != nil {
		return "", err
	}

	return path, nil
}

// Normalizes a path and returns an clean absolute version. It correctly
// expands environment variables inside paths, converts "~/" into the users
// home directory, and replaces "./" with the current working directory.
func NormalizeFilePath(path string) (string, error) {
	// don't normalize empty strings
	if path == "" {
		return "", nil
	}

	// expand env and home directory
	var err error
	path, err = ExpandHome(os.ExpandEnv(path))
	if err != nil {
		return "", err
	}

	// make sure its absolute
	absolutePath, err := filepath.Abs(path)
	if err != nil {
		return "", err
	}

	return absolutePath, nil
}

// ExpandHome expands the path to include the home directory if the path
// is prefixed with `~`. If it isn't prefixed with `~`, the path is
// returned as-is.
// Via https://github.com/mitchellh/go-homedir/blob/master/homedir.go
func ExpandHome(path string) (string, error) {
	if len(path) == 0 {
		return path, nil
	}

	if path[0] != '~' {
		return path, nil
	}

	if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
		return "", errors.New("cannot expand user-specific home dir")
	}

	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return filepath.Join(usr.HomeDir, path[1:]), nil
}
<commit_msg>Only absolute the command path if it exists when normalizing<commit_after>package utils

import (
	"errors"
	"os"
	"os/user"
	"path/filepath"
)

// NormalizeCommand has very similar semantics to `NormalizeFilePath`, except
// we only "absolute" the path if it exists on the filesystem.
func NormalizeCommand(path string) (string, error) {
	// don't normalize empty strings
	if path == "" {
		return "", nil
	}

	// expand env and home directory
	var err error
	path, err = ExpandHome(os.ExpandEnv(path))
	if err != nil {
		return "", err
	}

	// if the file exists, absolute it
	if _, err := os.Stat(path); err == nil {
		// make sure its absolute
		absolutePath, err := filepath.Abs(path)
		if err != nil {
			return "", err
		}
		path = absolutePath
	}

	return path, nil
}

// Normalizes a path and returns an clean absolute version. It correctly
// expands environment variables inside paths, converts "~/" into the users
// home directory, and replaces "./" with the current working directory.
func NormalizeFilePath(path string) (string, error) {
	// don't normalize empty strings
	if path == "" {
		return "", nil
	}

	// expand env and home directory
	var err error
	path, err = ExpandHome(os.ExpandEnv(path))
	if err != nil {
		return "", err
	}

	// make sure its absolute
	absolutePath, err := filepath.Abs(path)
	if err != nil {
		return "", err
	}

	return absolutePath, nil
}

// ExpandHome expands the path to include the home directory if the path
// is prefixed with `~`. If it isn't prefixed with `~`, the path is
// returned as-is.
// Via https://github.com/mitchellh/go-homedir/blob/master/homedir.go
func ExpandHome(path string) (string, error) {
	if len(path) == 0 {
		return path, nil
	}

	if path[0] != '~' {
		return path, nil
	}

	if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
		return "", errors.New("cannot expand user-specific home dir")
	}

	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return filepath.Join(usr.HomeDir, path[1:]), nil
}
<|endoftext|>


<commit_before>// Package utils provides zmq connection pool
// reference github.com/garyburd/redigo/redis
package utils

import (
	"container/list"
	"fmt"
	"sync"
	"time"

	zmq "github.com/pebbe/zmq4"
)

type Pool struct {
	// Stack of idleConn with most recently used at the front.
	pool list.List

	// mu protects fields defined below.
	mu     sync.Mutex
	cond   *sync.Cond
	closed bool

	// 获取连接的方法
	New func() (*zmq.Socket, error)

	Max int

	Wait bool //当连接池满的时候是否等待

	Life time.Duration
}

type PooledSocket struct {
	Soc    *zmq.Socket
	expire time.Time
	inUse  bool
	pool   *Pool
	ele    *list.Element //在list中位置
}

/* {{{ func NewPool(newFn func() (Conn, error), maxIdle int) *Pool
 * NewPool creates a new pool. This function is deprecated. Applications should
 * initialize the Pool fields directly as shown in example.
 */
func NewPool(newFn func() (*zmq.Socket, error), max int, life time.Duration) *Pool {
	return &Pool{New: newFn, Max: max, Life: life}
}

/* }}} */

/* {{{ func (p *Pool) Close()
 *
 */
func (p *Pool) Close() {
	p.mu.Lock()
	defer p.mu.Unlock()
	for e := p.pool.Front(); e != nil; e = e.Next() {
		e.Value.(*PooledSocket).Soc.Close()
	}
	p.pool.Init() // clear
	p.closed = true
	if p.cond != nil { //唤醒所有等待者
		p.cond.Broadcast()
	}
}

/* }}} */

/* {{{ func (p *Pool) Get() *Socket
 *
 */
func (p *Pool) Get() (*PooledSocket, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.closed {
		return nil, fmt.Errorf("pool closed")
	}

	for { //不get到誓不罢休
		// Get pooled item.
		for i, n := 0, p.pool.Len(); i < n; i++ { //最差情况是遍历全部还没找到
			if e := p.pool.Front(); e != nil { //LRU,队列最前面的是最少使用的
				ps := e.Value.(*PooledSocket)
				if !ps.inUse {
					ps.inUse = true
					//ps.expire = time.Now().Add(p.Life) //取的时候不延长, 归还的时候延长
					p.pool.MoveToBack(e) //移到最后
					return ps, nil
				}
			}
		}

		if p.Max <= 0 || p.pool.Len() < p.Max { //池子无限制或者还没满
			// create new.
			if soc, err := p.New(); err != nil { //无法新建
				return nil, err
			} else {
				ps := &PooledSocket{
					Soc:    soc,
					expire: time.Now().Add(p.Life),
					inUse:  true,
					pool:   p,
				}
				ps.ele = p.pool.PushBack(ps)
				return ps, nil
			}
		}

		if !p.Wait { //不等就结束
			p.mu.Unlock()
			return nil, fmt.Errorf("Pool full at %d", p.Max)
		}

		//等待被唤醒
		if p.cond == nil {
			p.cond = sync.NewCond(&p.mu)
		}
		p.cond.Wait()
	}
}

/* }}} */

/* {{{ func (ps *PooledSocket) Close() {
 *
 */
func (ps *PooledSocket) Close(destory ...bool) {
	//归还, 保留销毁的选项,由客户端决定
	p := ps.pool
	p.mu.Lock()
	defer p.mu.Unlock()
	if len(destory) > 0 && destory[0] == true {
		//指示需要销毁
		ps.Soc.Close()
		p.pool.Remove(ps.ele)
	} else {
		ps.inUse = false
		ps.expire = time.Now().Add(p.Life) //过期时间延长
		p.pool.MoveToFront(ps.ele)         //放到最前
	}
	if p.cond != nil { //唤醒一个, 如果有的话
		p.cond.Signal()
	}
}

/* }}} */
<commit_msg>update<commit_after>// Package utils provides zmq connection pool
package utils

import (
	"container/list"
	"fmt"
	"sync"
	"time"

	zmq "github.com/pebbe/zmq4"
)

type Pool struct {
	// Stack of idleConn with most recently used at the front.
	pool list.List

	// mu protects fields defined below.
	mu     sync.Mutex
	cond   *sync.Cond
	closed bool

	// 获取新连接的方法
	New func() (*zmq.Socket, error)

	Max int

	Wait bool //当连接池满的时候是否等待

	Life time.Duration
}

type PooledSocket struct {
	Soc    *zmq.Socket
	expire time.Time
	inUse  bool
	pool   *Pool
	ele    *list.Element //在list中位置
}

/* {{{ func NewPool(newFn func() (Conn, error), maxIdle int) *Pool
 * NewPool creates a new pool. This function is deprecated. Applications should
 * initialize the Pool fields directly as shown in example.
 */
func NewPool(newFn func() (*zmq.Socket, error), max int, life time.Duration) *Pool {
	return &Pool{New: newFn, Max: max, Life: life}
}

/* }}} */

/* {{{ func (p *Pool) Close()
 *
 */
func (p *Pool) Close() {
	p.mu.Lock()
	defer p.mu.Unlock()
	for e := p.pool.Front(); e != nil; e = e.Next() {
		e.Value.(*PooledSocket).Soc.Close()
	}
	p.pool.Init() // clear
	p.closed = true
	if p.cond != nil { //唤醒所有等待者
		p.cond.Broadcast()
	}
}

/* }}} */

/* {{{ func (p *Pool) Get() *Socket
 *
 */
func (p *Pool) Get() (*PooledSocket, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.closed {
		return nil, fmt.Errorf("pool closed")
	}

	for { //不get到誓不罢休
		// Get pooled item.
		if e := p.pool.Front(); e != nil { //如果存在未使用的item,第一个肯定是
			ps := e.Value.(*PooledSocket)
			if !ps.inUse {
				ps.inUse = true
				p.pool.MoveToBack(e) //移到最后
				return ps, nil
			}
		}

		if p.Max <= 0 || p.pool.Len() < p.Max { //池子无限制或者还没满
			// create new.
			if soc, err := p.New(); err != nil { //无法新建
				return nil, err
			} else {
				ps := &PooledSocket{
					Soc:    soc,
					expire: time.Now().Add(p.Life),
					inUse:  true,
					pool:   p,
				}
				ps.ele = p.pool.PushBack(ps)
				return ps, nil
			}
		}

		if !p.Wait { //不等就结束
			p.mu.Unlock()
			return nil, fmt.Errorf("Pool full at %d", p.Max)
		}

		//等待被唤醒
		if p.cond == nil {
			p.cond = sync.NewCond(&p.mu)
		}
		p.cond.Wait()
	}
}

/* }}} */

/* {{{ func (ps *PooledSocket) Close(destory ...bool)
 *
 */
func (ps *PooledSocket) Close(destory ...bool) {
	//归还, 保留销毁的选项,由客户端决定
	p := ps.pool
	p.mu.Lock()
	defer p.mu.Unlock()
	if len(destory) > 0 && destory[0] == true {
		//指示需要销毁
		ps.Soc.Close()
		p.pool.Remove(ps.ele)
	} else {
		ps.inUse = false
		ps.expire = time.Now().Add(p.Life) //过期时间延长
		p.pool.MoveToFront(ps.ele)         //放到最前
	}
	if p.cond != nil { //唤醒一个, 如果有的话
		p.cond.Signal()
	}
}

/* }}} */
<|endoftext|>