<commit_before>package data

import (
	"database/sql"
	"github.com/UHERO/rest-api/models"
	"time"
)

type SeriesRepository struct {
	DB *sql.DB
}

type transformation struct {
	Statement string
	PlaceholderCount int
}

const (
	Levels = "lvl"
	YoyPCh = "pc1"
)

var transformations map[string]transformation = map[string]transformation{
	"lvl": {
		Statement: `SELECT date, value FROM data_points WHERE series_id = ? and current = 1;`,
		PlaceholderCount: 1,
	},
	"pc1": {
		Statement: `SELECT t1.date, (t1.value/t2.last_value - 1)*100 AS yoy
				FROM (SELECT value, date, DATE_SUB(date, INTERVAL 1 YEAR) AS last_year
				FROM data_points WHERE series_id = ? AND current = 1) AS t1
				LEFT JOIN (SELECT value AS last_value, date
				FROM data_points WHERE series_id = ? and current = 1) AS t2
				ON (t1.last_year = t2.date);`,
		PlaceholderCount: 2,
	},
}

func (r *SeriesRepository) GetSeriesByCategoryAndFreq(
	categoryId int64,
	freq string,
) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(
		`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	LIKE CONCAT(left(name, locate("@", name)), '%') AND
	name LIKE CONCAT('%@%.', ?);`,
		categoryId,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesByCategoryGeoAndFreq(
	categoryId int64,
	geoHandle string,
	freq string,
) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(
		`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	LIKE CONCAT(left(name, locate("@", name)), '%') AND name LIKE CONCAT('%@%', ? ,'%.%') AND
	name LIKE CONCAT('%@%.', ?);`,
		categoryId,
		geoHandle,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesByCategoryAndGeo(
	categoryId int64,
	geoHandle string,
) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(
		`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	LIKE CONCAT(left(name, locate("@", name)), '%') AND name LIKE CONCAT('%@%', ? ,'%.%');`,
		categoryId,
		geoHandle,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesByCategory(categoryId int64) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	LIKE CONCAT(left(name, locate("@", name)), '%');`, categoryId)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesSiblingsById(seriesId int64) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT id, series.name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series JOIN (SELECT name FROM series where id = ?) as original_series
	WHERE series.name LIKE CONCAT(left(original_series.name, locate("@", original_series.name)), '%');`, seriesId)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesById(seriesId int64) (dataPortalSeries models.DataPortalSeries, err error) {
	row := r.DB.QueryRow(`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE id = ?`, seriesId)
	series := models.Series{}
	err = row.Scan(
		&series.Id,
		&series.Name,
		&series.Description,
		&series.Frequency,
		&series.SeasonallyAdjusted,
		&series.UnitsLabel,
		&series.UnitsLabelShort,
		&series.DataPortalName,
	)
	if err != nil {
		return
	}
	dataPortalSeries = models.DataPortalSeries{Id: series.Id, Name: series.Name}
	dataPortalSeries.FrequencyShort = series.Name[len(series.Name)-1:]
	if series.DataPortalName.Valid {
		dataPortalSeries.Title = series.DataPortalName.String
	}
	if series.Description.Valid {
		dataPortalSeries.Description = series.Description.String
	}
	if series.Frequency.Valid {
		dataPortalSeries.Frequency = series.Frequency.String
	}
	if series.SeasonallyAdjusted.Valid {
		dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
	}
	if series.UnitsLabel.Valid {
		dataPortalSeries.UnitsLabel = series.UnitsLabel.String
	}
	if series.UnitsLabelShort.Valid {
		dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
	}
	return
}

func (r *SeriesRepository) GetSeriesObservations(seriesId int64) (seriesObservations models.SeriesObservations, err error) {
	lvlTransform, start, end, err := r.GetTransformation(Levels, seriesId)
	if err != nil {
		return
	}
	yoyTransform, yoyStart, yoyEnd, err := r.GetTransformation(YoyPCh, seriesId)
	if err != nil {
		return
	}
	if yoyStart.Before(start) {
		start = yoyStart
	}
	if end.Before(yoyEnd) {
		end = yoyEnd
	}
	seriesObservations.TransformationResults = []models.TransformationResult{lvlTransform, yoyTransform}
	seriesObservations.ObservationStart = start
	seriesObservations.ObservationEnd = end
	return
}

func variadicSeriesId(seriesId int64, count int) []interface{} {
	variadic := make([]interface{}, count, count)
	for i := range variadic {
		variadic[i] = seriesId
	}
	return variadic
}

func (r *SeriesRepository) GetTransformation(transformation string, seriesId int64) (
	transformationResult models.TransformationResult,
	observationStart time.Time,
	observationEnd time.Time,
	err error,
) {
	rows, err := r.DB.Query(
		transformations[transformation].Statement,
		variadicSeriesId(seriesId, transformations[transformation].PlaceholderCount)...,
	)
	if err != nil {
		return
	}
	var (
		observations []models.DataPortalObservation
	)

	for rows.Next() {
		observation := models.Observation{}
		err = rows.Scan(
			&observation.Date,
			&observation.Value,
		)
		if err != nil {
			return
		}
		if !observation.Value.Valid {
			continue
		}
		if observationStart.IsZero() || observation.Date.Before(observationStart) {
			observationStart = observation.Date
		}
		if observationEnd.IsZero() || observationEnd.Before(observation.Date) {
			observationEnd = observation.Date
		}
		observations = append(
			observations,
			models.DataPortalObservation{
				Date:  observation.Date,
				Value: observation.Value.Float64,
			},
		)
	}
	transformationResult.Transformation = transformation
	transformationResult.Observations = observations
	return
}
<commit_msg>using regexp for series query from data_lists join<commit_after>package data

import (
	"database/sql"
	"github.com/UHERO/rest-api/models"
	"time"
)

type SeriesRepository struct {
	DB *sql.DB
}

type transformation struct {
	Statement string
	PlaceholderCount int
}

const (
	Levels = "lvl"
	YoyPCh = "pc1"
)

var transformations map[string]transformation = map[string]transformation{
	"lvl": {
		Statement: `SELECT date, value FROM data_points WHERE series_id = ? and current = 1;`,
		PlaceholderCount: 1,
	},
	"pc1": {
		Statement: `SELECT t1.date, (t1.value/t2.last_value - 1)*100 AS yoy
				FROM (SELECT value, date, DATE_SUB(date, INTERVAL 1 YEAR) AS last_year
				FROM data_points WHERE series_id = ? AND current = 1) AS t1
				LEFT JOIN (SELECT value AS last_value, date
				FROM data_points WHERE series_id = ? and current = 1) AS t2
				ON (t1.last_year = t2.date);`,
		PlaceholderCount: 2,
	},
}

func (r *SeriesRepository) GetSeriesByCategoryAndFreq(
	categoryId int64,
	freq string,
) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(
		`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	REGEXP CONCAT('[[:<:]]', left(name, locate("@", name)), '.*[[:>:]]') AND
	name LIKE CONCAT('%@%.', ?);`,
		categoryId,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesByCategoryGeoAndFreq(
	categoryId int64,
	geoHandle string,
	freq string,
) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(
		`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	REGEXP CONCAT('[[:<:]]', left(name, locate("@", name)), '.*[[:>:]]') AND name LIKE CONCAT('%@%', ? ,'%.%') AND
	name LIKE CONCAT('%@%.', ?);`,
		categoryId,
		geoHandle,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesByCategoryAndGeo(
	categoryId int64,
	geoHandle string,
) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(
		`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	REGEXP CONCAT('[[:<:]]', left(name, locate("@", name)), '.*[[:>:]]') AND name LIKE CONCAT('%@%', ? ,'%.%');`,
		categoryId,
		geoHandle,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesByCategory(categoryId int64) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE
	(SELECT list FROM data_lists JOIN categories WHERE categories.data_list_id = data_lists.id AND categories.id = ?)
	REGEXP CONCAT('[[:<:]]', left(name, locate("@", name)), '.*[[:>:]]');`, categoryId)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesSiblingsById(seriesId int64) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT id, series.name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series JOIN (SELECT name FROM series where id = ?) as original_series
	WHERE series.name LIKE CONCAT(left(original_series.name, locate("@", original_series.name)), '%');`, seriesId)
	if err != nil {
		return
	}
	for rows.Next() {
		series := models.Series{}
		err = rows.Scan(
			&series.Id,
			&series.Name,
			&series.Description,
			&series.Frequency,
			&series.SeasonallyAdjusted,
			&series.UnitsLabel,
			&series.UnitsLabelShort,
			&series.DataPortalName,
		)
		if err != nil {
			return
		}
		dataPortalSeries := models.DataPortalSeries{Id: series.Id, Name: series.Name}
		if series.DataPortalName.Valid {
			dataPortalSeries.Title = series.DataPortalName.String
		}
		if series.Description.Valid {
			dataPortalSeries.Description = series.Description.String
		}
		if series.Frequency.Valid {
			dataPortalSeries.Frequency = series.Frequency.String
		}
		if series.SeasonallyAdjusted.Valid {
			dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
		}
		if series.UnitsLabel.Valid {
			dataPortalSeries.UnitsLabel = series.UnitsLabel.String
		}
		if series.UnitsLabelShort.Valid {
			dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
		}
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSeriesById(seriesId int64) (dataPortalSeries models.DataPortalSeries, err error) {
	row := r.DB.QueryRow(`SELECT id, name, description, frequency,
	seasonally_adjusted, unitsLabel, unitsLabelShort, dataPortalName
	FROM series WHERE id = ?`, seriesId)
	series := models.Series{}
	err = row.Scan(
		&series.Id,
		&series.Name,
		&series.Description,
		&series.Frequency,
		&series.SeasonallyAdjusted,
		&series.UnitsLabel,
		&series.UnitsLabelShort,
		&series.DataPortalName,
	)
	if err != nil {
		return
	}
	dataPortalSeries = models.DataPortalSeries{Id: series.Id, Name: series.Name}
	dataPortalSeries.FrequencyShort = series.Name[len(series.Name)-1:]
	if series.DataPortalName.Valid {
		dataPortalSeries.Title = series.DataPortalName.String
	}
	if series.Description.Valid {
		dataPortalSeries.Description = series.Description.String
	}
	if series.Frequency.Valid {
		dataPortalSeries.Frequency = series.Frequency.String
	}
	if series.SeasonallyAdjusted.Valid {
		dataPortalSeries.SeasonallyAdjusted = series.SeasonallyAdjusted.Bool
	}
	if series.UnitsLabel.Valid {
		dataPortalSeries.UnitsLabel = series.UnitsLabel.String
	}
	if series.UnitsLabelShort.Valid {
		dataPortalSeries.UnitsLabelShort = series.UnitsLabelShort.String
	}
	return
}

func (r *SeriesRepository) GetSeriesObservations(seriesId int64) (seriesObservations models.SeriesObservations, err error) {
	lvlTransform, start, end, err := r.GetTransformation(Levels, seriesId)
	if err != nil {
		return
	}
	yoyTransform, yoyStart, yoyEnd, err := r.GetTransformation(YoyPCh, seriesId)
	if err != nil {
		return
	}
	if yoyStart.Before(start) {
		start = yoyStart
	}
	if end.Before(yoyEnd) {
		end = yoyEnd
	}
	seriesObservations.TransformationResults = []models.TransformationResult{lvlTransform, yoyTransform}
	seriesObservations.ObservationStart = start
	seriesObservations.ObservationEnd = end
	return
}

func variadicSeriesId(seriesId int64, count int) []interface{} {
	variadic := make([]interface{}, count, count)
	for i := range variadic {
		variadic[i] = seriesId
	}
	return variadic
}

func (r *SeriesRepository) GetTransformation(transformation string, seriesId int64) (
	transformationResult models.TransformationResult,
	observationStart time.Time,
	observationEnd time.Time,
	err error,
) {
	rows, err := r.DB.Query(
		transformations[transformation].Statement,
		variadicSeriesId(seriesId, transformations[transformation].PlaceholderCount)...,
	)
	if err != nil {
		return
	}
	var (
		observations []models.DataPortalObservation
	)

	for rows.Next() {
		observation := models.Observation{}
		err = rows.Scan(
			&observation.Date,
			&observation.Value,
		)
		if err != nil {
			return
		}
		if !observation.Value.Valid {
			continue
		}
		if observationStart.IsZero() || observation.Date.Before(observationStart) {
			observationStart = observation.Date
		}
		if observationEnd.IsZero() || observationEnd.Before(observation.Date) {
			observationEnd = observation.Date
		}
		observations = append(
			observations,
			models.DataPortalObservation{
				Date:  observation.Date,
				Value: observation.Value.Float64,
			},
		)
	}
	transformationResult.Transformation = transformation
	transformationResult.Observations = observations
	return
}
<|endoftext|>


<commit_before>package raygun4go

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/pborman/uuid"

	. "github.com/smartystreets/goconvey/convey"
)

func TestClient(t *testing.T) {
	Convey("Client", t, func() {
		c, _ := New("app", "key")
		So(c.appName, ShouldEqual, "app")
		So(c.apiKey, ShouldEqual, "key")
		So(c.context.Request, ShouldBeNil)
		So(c.context.Identifier(), ShouldHaveSameTypeAs, uuid.New())

		Convey("#New", func() {
			c, err := New("", "test")
			So(c, ShouldEqual, nil)
			So(err, ShouldNotEqual, nil)

			c, err = New("test", "")
			So(c, ShouldEqual, nil)
			So(err, ShouldNotEqual, nil)

			c, err = New("test", "test")
			So(c, ShouldHaveSameTypeAs, &Client{})
			So(err, ShouldEqual, nil)
		})

		Convey("#Request", func() {
			r := &http.Request{}
			c.Request(r)
			So(c.context.Request, ShouldResemble, r)
		})

		Convey("#Version", func() {
			v := "version"
			c.Version(v)
			So(c.context.Version, ShouldResemble, v)
		})

		Convey("#Tags", func() {
			t := []string{"foo", "bar"}
			c.Tags(t)
			So(c.context.Tags, ShouldResemble, t)
		})

		Convey("#CustomData", func() {
			cd := "foo"
			c.CustomData(cd)
			So(c.context.CustomData, ShouldResemble, cd)
		})

		Convey("#User", func() {
			u := "user"
			c.User(u)
			So(c.context.User, ShouldResemble, u)
		})

		Convey("#Silent", func() {
			So(c.silent, ShouldBeFalse)
			c.Silent(true)
			So(c.silent, ShouldBeTrue)
		})

		Convey("#LogToStdOut", func() {
			So(c.logToStdOut, ShouldBeFalse)
			c.LogToStdOut(true)
			So(c.logToStdOut, ShouldBeTrue)
		})

		Convey("#Asynchronous", func() {
			So(c.asynchronous, ShouldBeFalse)
			c.Asynchronous(true)
			So(c.asynchronous, ShouldBeTrue)
		})

		Convey("#HandleError", func() {
			u := "http://www.example.com?foo=bar&fizz[]=buzz&fizz[]=buzz2"
			r, _ := http.NewRequest("GET", u, nil)
			r.RemoteAddr = "1.2.3.4"
			r.PostForm = url.Values{
				"foo":  []string{"bar"},
				"fizz": []string{"buzz", "buzz2"},
			}
			r.Header.Add("Cookie", "cookie1=value1; cookie2=value2")
			c.Silent(true)
			c.Request(r)
			c.apiKey = "key"
			c.context.Version = "goconvey"
			c.context.Tags = []string{"golang", "test"}
			c.context.CustomData = map[string]string{"foo": "bar"}
			c.context.User = "Test User"
			defer c.HandleError()
			panic("Test: See if this works with Raygun")
		})

		Convey("#CreateError", func() {
			ts := raygunEndpointStub()
			defer ts.Close()
			raygunEndpoint = ts.URL
			c, _ := New("app", "key")
			c.Silent(false)
			c.apiKey = "key"
			c.CreateError("Test: See if this works with Raygun")
		})
	})
}

func raygunEndpointStub() *httptest.Server {
	return httptest.NewServer(
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Content-Type", "application/json")

			if req.Method != "POST" || req.RequestURI != "/entries" {
				fmt.Println("raygunEndpointStub: URI not implemented")
				http.Error(w, "Not Found", http.StatusNotFound)
				return
			}

			// 403 Invalid API Key
			// The value specified in the header X-ApiKey did not match with a user.
			if req.Header.Get("X-ApiKey") == "" {
				w.WriteHeader(http.StatusForbidden)
				return
			}
			// 202 OK - Message accepted.
			w.WriteHeader(http.StatusAccepted)
		}))
}
<commit_msg>Unit test<commit_after>package raygun4go

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/pborman/uuid"

	. "github.com/smartystreets/goconvey/convey"
)

func TestClient(t *testing.T) {
	Convey("Client", t, func() {
		c, _ := New("app", "key")
		So(c.appName, ShouldEqual, "app")
		So(c.apiKey, ShouldEqual, "key")
		So(c.context.Request, ShouldBeNil)
		So(c.context.Identifier(), ShouldHaveSameTypeAs, uuid.New())

		Convey("#New", func() {
			c, err := New("", "test")
			So(c, ShouldEqual, nil)
			So(err, ShouldNotEqual, nil)

			c, err = New("test", "")
			So(c, ShouldEqual, nil)
			So(err, ShouldNotEqual, nil)

			c, err = New("test", "test")
			So(c, ShouldHaveSameTypeAs, &Client{})
			So(err, ShouldEqual, nil)
		})

		Convey("#Clone", func()) {
			t := []string{"Critical", "Urgent", "Fix it now!"}
			c.Tags(t)

			cd := "foo"
			c.CustomData(cd)

			r := &http.Request{}
			c.Request(r)

			v := "1.2.3"
			c.Version(v)

			u := "user"
			c.User(u)

			clone := c.Clone()

			So(clone.appName, ShouldResemble, c.appName)
			So(clone.apiKey, ShouldResemble, c.apiKey)
			So(clone.silent, ShouldResemble, c.silent)
			So(clone.logToStdOutlogToStdOut, ShouldResemble, c.logToStdOut)
			So(clone.context.Request, ShouldResemble, c.context.Request)
			So(clone.context.Version, ShouldResemble, c.context.Version)
			So(clone.context.Tags, ShouldResemble, c.context.Tags)
			So(clone.context.CustomData, ShouldResemble, c.context.CustomData)
			So(clone.context.User, ShouldResemble, c.context.User)
			So(clone.context.identifier, ShouldResemble, c.context.identifier)
		}

		Convey("#Request", func() {
			r := &http.Request{}
			c.Request(r)
			So(c.context.Request, ShouldResemble, r)
		})

		Convey("#Version", func() {
			v := "version"
			c.Version(v)
			So(c.context.Version, ShouldResemble, v)
		})

		Convey("#Tags", func() {
			t := []string{"foo", "bar"}
			c.Tags(t)
			So(c.context.Tags, ShouldResemble, t)
		})

		Convey("#CustomData", func() {
			cd := "foo"
			c.CustomData(cd)
			So(c.context.CustomData, ShouldResemble, cd)
		})

		Convey("#User", func() {
			u := "user"
			c.User(u)
			So(c.context.User, ShouldResemble, u)
		})

		Convey("#Silent", func() {
			So(c.silent, ShouldBeFalse)
			c.Silent(true)
			So(c.silent, ShouldBeTrue)
		})

		Convey("#LogToStdOut", func() {
			So(c.logToStdOut, ShouldBeFalse)
			c.LogToStdOut(true)
			So(c.logToStdOut, ShouldBeTrue)
		})

		Convey("#Asynchronous", func() {
			So(c.asynchronous, ShouldBeFalse)
			c.Asynchronous(true)
			So(c.asynchronous, ShouldBeTrue)
		})

		Convey("#HandleError", func() {
			u := "http://www.example.com?foo=bar&fizz[]=buzz&fizz[]=buzz2"
			r, _ := http.NewRequest("GET", u, nil)
			r.RemoteAddr = "1.2.3.4"
			r.PostForm = url.Values{
				"foo":  []string{"bar"},
				"fizz": []string{"buzz", "buzz2"},
			}
			r.Header.Add("Cookie", "cookie1=value1; cookie2=value2")
			c.Silent(true)
			c.Request(r)
			c.apiKey = "key"
			c.context.Version = "goconvey"
			c.context.Tags = []string{"golang", "test"}
			c.context.CustomData = map[string]string{"foo": "bar"}
			c.context.User = "Test User"
			defer c.HandleError()
			panic("Test: See if this works with Raygun")
		})

		Convey("#CreateError", func() {
			ts := raygunEndpointStub()
			defer ts.Close()
			raygunEndpoint = ts.URL
			c, _ := New("app", "key")
			c.Silent(false)
			c.apiKey = "key"
			c.CreateError("Test: See if this works with Raygun")
		})
	})
}

func raygunEndpointStub() *httptest.Server {
	return httptest.NewServer(
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Content-Type", "application/json")

			if req.Method != "POST" || req.RequestURI != "/entries" {
				fmt.Println("raygunEndpointStub: URI not implemented")
				http.Error(w, "Not Found", http.StatusNotFound)
				return
			}

			// 403 Invalid API Key
			// The value specified in the header X-ApiKey did not match with a user.
			if req.Header.Get("X-ApiKey") == "" {
				w.WriteHeader(http.StatusForbidden)
				return
			}
			// 202 OK - Message accepted.
			w.WriteHeader(http.StatusAccepted)
		}))
}
<|endoftext|>


<commit_before>package connectors

import (
	"html"
	"log"

	"github.com/nlopes/slack"
	"github.com/projectjane/jane/models"
)

type Slack struct {
	Connector models.Connector
}

func (x Slack) Listen(commandMsgs chan<- models.Message, connector models.Connector) {
	defer Recovery(connector)
	api := slack.New(connector.Key)
	api.SetDebug(connector.Debug)
	rtm := api.NewRTM()
	if connector.Debug {
		log.Print("Starting slack websocket api for " + connector.ID)
	}
	go rtm.ManageConnection()
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				if ev.User != "" {

					if connector.Debug {
						log.Print("Evaluating incoming slack message")
					}

					var r []models.Route
					r = append(r, models.Route{Match: "*", Connectors: connector.ID, Target: ev.Channel})
					for _, cr := range connector.Routes {
						r = append(r, cr)
					}

					var m models.Message
					m.Routes = r
					m.In.Source = connector.ID
					m.In.User = ev.User
					m.In.Text = html.UnescapeString(ev.Text)
					m.In.Process = true
					commandMsgs <- m

				}
			}
		}
	}
}

func (x Slack) Command(message models.Message, publishMsgs chan<- models.Message, connector models.Connector) {
	return
}

func (x Slack) Publish(connector models.Connector, message models.Message, target string) {
	api := slack.New(connector.Key)
	msg := ""
	params := slack.NewPostMessageParameters()
	params.Username = "jane"
	params.IconEmoji = connector.Image
	if target == "" {
		target = "#general"
	}
	if message.Out.Detail != "" {
		color := slackColorMe(message.Out.Status)
		attachment := slack.Attachment{
			Title:     message.Out.Text,
			TitleLink: message.Out.Link,
			Text:      message.Out.Detail,
			Color:     color,
		}
		params.Attachments = []slack.Attachment{attachment}
	} else {
		msg = message.Out.Text
	}

	log.Printf("%s: %s - Target: %s", connector.ID, msg, target)

	api.PostMessage(target, msg, params)
}

func (x Slack) Help(connector models.Connector) (help string) {
	return
}

func slackColorMe(status string) (color string) {
	switch status {
	case "SUCCESS":
		color = "good"
	case "WARN":
		color = "warning"
	case "FAIL":
		color = "danger"
	case "NONE":
		color = "#DDDDDD"
	default:
		color = "#DDDDDD"
	}
	return color
}
<commit_msg>Removing my temporary log<commit_after>package connectors

import (
	"html"
	"log"

	"github.com/nlopes/slack"
	"github.com/projectjane/jane/models"
)

type Slack struct {
	Connector models.Connector
}

func (x Slack) Listen(commandMsgs chan<- models.Message, connector models.Connector) {
	defer Recovery(connector)
	api := slack.New(connector.Key)
	api.SetDebug(connector.Debug)
	rtm := api.NewRTM()
	if connector.Debug {
		log.Print("Starting slack websocket api for " + connector.ID)
	}
	go rtm.ManageConnection()
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				if ev.User != "" {

					if connector.Debug {
						log.Print("Evaluating incoming slack message")
					}

					var r []models.Route
					r = append(r, models.Route{Match: "*", Connectors: connector.ID, Target: ev.Channel})
					for _, cr := range connector.Routes {
						r = append(r, cr)
					}

					var m models.Message
					m.Routes = r
					m.In.Source = connector.ID
					m.In.User = ev.User
					m.In.Text = html.UnescapeString(ev.Text)
					m.In.Process = true
					commandMsgs <- m

				}
			}
		}
	}
}

func (x Slack) Command(message models.Message, publishMsgs chan<- models.Message, connector models.Connector) {
	return
}

func (x Slack) Publish(connector models.Connector, message models.Message, target string) {
	api := slack.New(connector.Key)
	msg := ""
	params := slack.NewPostMessageParameters()
	params.Username = "jane"
	params.IconEmoji = connector.Image
	if target == "" {
		target = "#general"
	}
	if message.Out.Detail != "" {
		color := slackColorMe(message.Out.Status)
		attachment := slack.Attachment{
			Title:     message.Out.Text,
			TitleLink: message.Out.Link,
			Text:      message.Out.Detail,
			Color:     color,
		}
		params.Attachments = []slack.Attachment{attachment}
	} else {
		msg = message.Out.Text
	}
	api.PostMessage(target, msg, params)
}

func (x Slack) Help(connector models.Connector) (help string) {
	return
}

func slackColorMe(status string) (color string) {
	switch status {
	case "SUCCESS":
		color = "good"
	case "WARN":
		color = "warning"
	case "FAIL":
		color = "danger"
	case "NONE":
		color = "#DDDDDD"
	default:
		color = "#DDDDDD"
	}
	return color
}
<|endoftext|>


<commit_before>package checkdisk

import (
	"errors"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
	gpud "github.com/shirou/gopsutil/v3/disk"
)

var opts struct {
	Warning       *string   `short:"w" long:"warning" value-name:"N, N%" description:"Exit with WARNING status if less than N units or N% of disk are free"`
	Critical      *string   `short:"c" long:"critical" value-name:"N, N%" description:"Exit with CRITICAL status if less than N units or N% of disk are free"`
	InodeWarning  *string   `short:"W" long:"iwarning" value-name:"N%" description:"Exit with WARNING status if less than PERCENT of inode space is free"`
	InodeCritical *string   `short:"K" long:"icritical" value-name:"N%" description:"Exit with CRITICAL status if less than PERCENT of inode space is free"`
	Path          *[]string `short:"p" long:"path" value-name:"PATH" description:"Mount point or block device as emitted by the mount(8) command (may be repeated)"`
	Exclude       *[]string `short:"x" long:"exclude-device" value-name:"EXCLUDE PATH" description:"Ignore device (may be repeated; only works if -p unspecified)"`
	All           bool      `short:"A" long:"all" description:"Explicitly select all paths."`
	ExcludeType   *[]string `short:"X" long:"exclude-type" value-name:"TYPE" description:"Ignore all filesystems of indicated type (may be repeated)"`
	IncludeType   *[]string `short:"N" long:"include-type" value-name:"TYPE" description:"Check only filesystems of indicated type (may be repeated)"`
	Units         *string   `short:"u" long:"units" value-name:"STRING" description:"Choose bytes, kB, MB, GB, TB (default: MB)"`
}

const (
	b  = float64(1)
	kb = float64(1024) * b
	mb = float64(1024) * kb
	gb = float64(1024) * mb
	tb = float64(1024) * gb
)

type unit struct {
	Name string
	Size float64
}

func checkDisk(current checkers.Status, threshold string, units float64, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if strings.HasSuffix(threshold, "%") {
		thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		freePct := float64(100) - disk.UsedPercent
		if thresholdPct > freePct {
			current = status
		}
	} else {
		thresholdVal, err := strconv.ParseFloat(threshold, 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		free := float64(disk.Free) / units
		if thresholdVal > free {
			current = status
		}
	}

	return current, nil
}

func checkInodes(current checkers.Status, threshold string, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if !strings.HasSuffix(threshold, "%") {
		return checkers.UNKNOWN, errors.New("-W, -K value should be N%")
	}

	thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
	if err != nil {
		return checkers.UNKNOWN, err
	}

	if disk.InodesTotal == 0 {
		return checkers.UNKNOWN, fmt.Errorf("Disk %s does not have inodes", disk.Path)
	}

	inodesFreePct := float64(100) - disk.InodesUsedPercent
	if thresholdPct > inodesFreePct {
		current = status
	}

	return current, nil
}

func genMessage(disk *gpud.UsageStat, u unit) string {
	all := float64(disk.Total) / u.Size
	used := float64(disk.Used) / u.Size
	free := float64(disk.Free) / u.Size
	freePct := float64(100) - disk.UsedPercent

	inodesFreePctStr := ""
	if disk.InodesTotal != 0 {
		inodesFreePct := float64(100) - disk.InodesUsedPercent
		inodesFreePctStr = fmt.Sprintf(" (inodes %.2f)", inodesFreePct)
	}

	return fmt.Sprintf("Path: %v, All: %.2f %v, Used: %.2f %v, Free: %.2f %v, Free percentage: %.2f%s", disk.Path, all, u.Name, used, u.Name, free, u.Name, freePct, inodesFreePctStr)
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Disk"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	partitions, err := listPartitions()
	if err != nil {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", err))
	}

	if !opts.All {
		// Filtering partitions by Fstype
		if opts.IncludeType != nil {
			partitions = filterPartitionsByInclusion(partitions, *opts.IncludeType, fstypeOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *FsType*")))
			}
		}

		if opts.ExcludeType != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.ExcludeType, fstypeOfPartition)
		}

		// Filtering partions by Mountpoint
		if opts.Path != nil {
			if opts.Exclude != nil {
				return checkers.Unknown(fmt.Sprintf("Invalid arguments: %s", errors.New("-x does not work with -p")))
			}

			partitions = filterPartitionsByInclusion(partitions, *opts.Path, mountpointOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *Mountpoint*")))
			}
		}

		if opts.Path == nil && opts.Exclude != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.Exclude, mountpointOfPartition)
		}
	}

	if len(partitions) == 0 {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found")))
	}

	var disks []*gpud.UsageStat

	for _, partition := range partitions {
		disk, err := gpud.Usage(partition.Mountpoint)
		if err != nil {
			return checkers.Unknown(fmt.Sprintf("Failed to fetch disk usage: %s", err))
		}

		if disk.Total != 0 {
			disks = append(disks, disk)
		}
	}

	u := unit{"MB", mb}
	if opts.Units != nil {
		us := strings.ToLower(*opts.Units)
		if us == "bytes" {
			u = unit{us, b}
		} else if us == "kb" {
			u = unit{us, mb}
		} else if us == "gb" {
			u = unit{us, gb}
		} else if us == "tb" {
			u = unit{us, tb}
		} else {
			return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", errors.New("Invalid argument flag '-u, --units'")))
		}
	}

	checkSt := checkers.OK
	if opts.InodeCritical != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeCritical, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.Critical != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Critical, u.Size, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.InodeWarning != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeWarning, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	if checkSt == checkers.OK && opts.Warning != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Warning, u.Size, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	sortDisks(disks)

	var msgs []string

	for _, disk := range disks {
		msg := genMessage(disk, u)
		msgs = append(msgs, msg)
	}
	msgss := strings.Join(msgs, ";\n")

	return checkers.NewChecker(checkSt, msgss)
}

// ref: mountlist.c in gnulib
// https://github.com/coreutils/gnulib/blob/a742bdb3/lib/mountlist.c#L168
func listPartitions() ([]gpud.PartitionStat, error) {
	allPartitions, err := gpud.Partitions(true)
	if err != nil {
		return nil, err
	}
	partitions := make([]gpud.PartitionStat, 0, len(allPartitions))
	for _, p := range allPartitions {
		switch p.Fstype {
		case "autofs",
			"proc",
			"subfs",
			"debugfs",
			"devpts",
			"fusectl",
			"mqueue",
			"rpc_pipefs",
			"sysfs",
			"devfs",
			"kernfs",
			"ignore":
			continue
		case "none":
			if !strings.Contains(p.Opts, "bind") {
				partitions = append(partitions, p)
			}
		default:
			partitions = append(partitions, p)
		}
	}

	return partitions, nil
}

func mountpointOfPartition(partition gpud.PartitionStat) string {
	return partition.Mountpoint
}

func fstypeOfPartition(partition gpud.PartitionStat) string {
	return partition.Fstype
}

func filterPartitionsByInclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = false
		for _, l := range list {
			if l == key(partition) {
				ok = true
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

func filterPartitionsByExclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = true
		for _, l := range list {
			if l == key(partition) {
				ok = false
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

// sortDisks sorts by UsedPercent only at the moment. If this is refactored, ideally this sort would match what threshold was tripped.
func sortDisks(disks []*gpud.UsageStat) {
	if len(disks) <= 1 {
		return
	}

	sort.SliceStable(disks, func(i, j int) bool {
		return disks[i].UsedPercent > disks[j].UsedPercent
	})

}
<commit_msg>PartitionStat.Opts is now []string<commit_after>package checkdisk

import (
	"errors"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
	gpud "github.com/shirou/gopsutil/v3/disk"
)

var opts struct {
	Warning       *string   `short:"w" long:"warning" value-name:"N, N%" description:"Exit with WARNING status if less than N units or N% of disk are free"`
	Critical      *string   `short:"c" long:"critical" value-name:"N, N%" description:"Exit with CRITICAL status if less than N units or N% of disk are free"`
	InodeWarning  *string   `short:"W" long:"iwarning" value-name:"N%" description:"Exit with WARNING status if less than PERCENT of inode space is free"`
	InodeCritical *string   `short:"K" long:"icritical" value-name:"N%" description:"Exit with CRITICAL status if less than PERCENT of inode space is free"`
	Path          *[]string `short:"p" long:"path" value-name:"PATH" description:"Mount point or block device as emitted by the mount(8) command (may be repeated)"`
	Exclude       *[]string `short:"x" long:"exclude-device" value-name:"EXCLUDE PATH" description:"Ignore device (may be repeated; only works if -p unspecified)"`
	All           bool      `short:"A" long:"all" description:"Explicitly select all paths."`
	ExcludeType   *[]string `short:"X" long:"exclude-type" value-name:"TYPE" description:"Ignore all filesystems of indicated type (may be repeated)"`
	IncludeType   *[]string `short:"N" long:"include-type" value-name:"TYPE" description:"Check only filesystems of indicated type (may be repeated)"`
	Units         *string   `short:"u" long:"units" value-name:"STRING" description:"Choose bytes, kB, MB, GB, TB (default: MB)"`
}

const (
	b  = float64(1)
	kb = float64(1024) * b
	mb = float64(1024) * kb
	gb = float64(1024) * mb
	tb = float64(1024) * gb
)

type unit struct {
	Name string
	Size float64
}

func checkDisk(current checkers.Status, threshold string, units float64, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if strings.HasSuffix(threshold, "%") {
		thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		freePct := float64(100) - disk.UsedPercent
		if thresholdPct > freePct {
			current = status
		}
	} else {
		thresholdVal, err := strconv.ParseFloat(threshold, 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		free := float64(disk.Free) / units
		if thresholdVal > free {
			current = status
		}
	}

	return current, nil
}

func checkInodes(current checkers.Status, threshold string, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if !strings.HasSuffix(threshold, "%") {
		return checkers.UNKNOWN, errors.New("-W, -K value should be N%")
	}

	thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
	if err != nil {
		return checkers.UNKNOWN, err
	}

	if disk.InodesTotal == 0 {
		return checkers.UNKNOWN, fmt.Errorf("Disk %s does not have inodes", disk.Path)
	}

	inodesFreePct := float64(100) - disk.InodesUsedPercent
	if thresholdPct > inodesFreePct {
		current = status
	}

	return current, nil
}

func genMessage(disk *gpud.UsageStat, u unit) string {
	all := float64(disk.Total) / u.Size
	used := float64(disk.Used) / u.Size
	free := float64(disk.Free) / u.Size
	freePct := float64(100) - disk.UsedPercent

	inodesFreePctStr := ""
	if disk.InodesTotal != 0 {
		inodesFreePct := float64(100) - disk.InodesUsedPercent
		inodesFreePctStr = fmt.Sprintf(" (inodes %.2f)", inodesFreePct)
	}

	return fmt.Sprintf("Path: %v, All: %.2f %v, Used: %.2f %v, Free: %.2f %v, Free percentage: %.2f%s", disk.Path, all, u.Name, used, u.Name, free, u.Name, freePct, inodesFreePctStr)
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Disk"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	partitions, err := listPartitions()
	if err != nil {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", err))
	}

	if !opts.All {
		// Filtering partitions by Fstype
		if opts.IncludeType != nil {
			partitions = filterPartitionsByInclusion(partitions, *opts.IncludeType, fstypeOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *FsType*")))
			}
		}

		if opts.ExcludeType != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.ExcludeType, fstypeOfPartition)
		}

		// Filtering partions by Mountpoint
		if opts.Path != nil {
			if opts.Exclude != nil {
				return checkers.Unknown(fmt.Sprintf("Invalid arguments: %s", errors.New("-x does not work with -p")))
			}

			partitions = filterPartitionsByInclusion(partitions, *opts.Path, mountpointOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *Mountpoint*")))
			}
		}

		if opts.Path == nil && opts.Exclude != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.Exclude, mountpointOfPartition)
		}
	}

	if len(partitions) == 0 {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found")))
	}

	var disks []*gpud.UsageStat

	for _, partition := range partitions {
		disk, err := gpud.Usage(partition.Mountpoint)
		if err != nil {
			return checkers.Unknown(fmt.Sprintf("Failed to fetch disk usage: %s", err))
		}

		if disk.Total != 0 {
			disks = append(disks, disk)
		}
	}

	u := unit{"MB", mb}
	if opts.Units != nil {
		us := strings.ToLower(*opts.Units)
		if us == "bytes" {
			u = unit{us, b}
		} else if us == "kb" {
			u = unit{us, mb}
		} else if us == "gb" {
			u = unit{us, gb}
		} else if us == "tb" {
			u = unit{us, tb}
		} else {
			return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", errors.New("Invalid argument flag '-u, --units'")))
		}
	}

	checkSt := checkers.OK
	if opts.InodeCritical != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeCritical, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.Critical != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Critical, u.Size, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.InodeWarning != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeWarning, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	if checkSt == checkers.OK && opts.Warning != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Warning, u.Size, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	sortDisks(disks)

	var msgs []string

	for _, disk := range disks {
		msg := genMessage(disk, u)
		msgs = append(msgs, msg)
	}
	msgss := strings.Join(msgs, ";\n")

	return checkers.NewChecker(checkSt, msgss)
}

// ref: mountlist.c in gnulib
// https://github.com/coreutils/gnulib/blob/a742bdb3/lib/mountlist.c#L168
func listPartitions() ([]gpud.PartitionStat, error) {
	allPartitions, err := gpud.Partitions(true)
	if err != nil {
		return nil, err
	}
	partitions := make([]gpud.PartitionStat, 0, len(allPartitions))
	for _, p := range allPartitions {
		switch p.Fstype {
		case "autofs",
			"proc",
			"subfs",
			"debugfs",
			"devpts",
			"fusectl",
			"mqueue",
			"rpc_pipefs",
			"sysfs",
			"devfs",
			"kernfs",
			"ignore":
			continue
		case "none":
			if !isBindMount(p.Opts) {
				partitions = append(partitions, p)
			}
		default:
			partitions = append(partitions, p)
		}
	}

	return partitions, nil
}

func isBindMount(mountOpts []string) bool {
	for _, opt := range mountOpts {
		if opt == "bind" {
			return true
		}
	}
	return false
}

func mountpointOfPartition(partition gpud.PartitionStat) string {
	return partition.Mountpoint
}

func fstypeOfPartition(partition gpud.PartitionStat) string {
	return partition.Fstype
}

func filterPartitionsByInclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = false
		for _, l := range list {
			if l == key(partition) {
				ok = true
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

func filterPartitionsByExclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = true
		for _, l := range list {
			if l == key(partition) {
				ok = false
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

// sortDisks sorts by UsedPercent only at the moment. If this is refactored, ideally this sort would match what threshold was tripped.
func sortDisks(disks []*gpud.UsageStat) {
	if len(disks) <= 1 {
		return
	}

	sort.SliceStable(disks, func(i, j int) bool {
		return disks[i].UsedPercent > disks[j].UsedPercent
	})

}
<|endoftext|>


<commit_before>// Copyright 2017 The casbin Authors. 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 xormadapter

import (
	"errors"
	"runtime"

	"github.com/casbin/casbin/model"
	"github.com/casbin/casbin/persist"
	"github.com/go-xorm/xorm"
	"github.com/lib/pq"
)

type CasbinRule struct {
	PType string `xorm:"varchar(100) index"`
	V0    string `xorm:"varchar(100) index"`
	V1    string `xorm:"varchar(100) index"`
	V2    string `xorm:"varchar(100) index"`
	V3    string `xorm:"varchar(100) index"`
	V4    string `xorm:"varchar(100) index"`
	V5    string `xorm:"varchar(100) index"`
}

// Adapter represents the Xorm adapter for policy storage.
type Adapter struct {
	driverName     string
	dataSourceName string
	dbSpecified    bool
	engine         *xorm.Engine
}

// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
	err := a.engine.Close()
	if err != nil {
		panic(err)
	}
}

// NewAdapter is the constructor for Adapter.
// dbSpecified is an optional bool parameter. The default value is false.
// It's up to whether you have specified an existing DB in dataSourceName.
// If dbSpecified == true, you need to make sure the DB in dataSourceName exists.
// If dbSpecified == false, the adapter will automatically create a DB named "casbin".
func NewAdapter(driverName string, dataSourceName string, dbSpecified ...bool) (*Adapter, error) {
	a := &Adapter{}
	a.driverName = driverName
	a.dataSourceName = dataSourceName

	if len(dbSpecified) == 0 {
		a.dbSpecified = false
	} else if len(dbSpecified) == 1 {
		a.dbSpecified = dbSpecified[0]
	} else {
		return nil, errors.New("invalid parameter: dbSpecified")
	}

	// Open the DB, create it if not existed.
	err := a.open()
	if err != nil {
		return nil, err
	}

	// Call the destructor when the object is released.
	runtime.SetFinalizer(a, finalizer)

	return a, nil
}

func NewAdapterByEngine(engine *xorm.Engine) (*Adapter, error) {
	a := &Adapter{
		engine: engine,
	}

	err := a.createTable()
	if err != nil {
		return nil, err
	}

	return a, nil
}

func (a *Adapter) createDatabase() error {
	var err error
	var engine *xorm.Engine
	if a.driverName == "postgres" {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=postgres")
	} else {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
	}
	if err != nil {
		return err
	}

	if a.driverName == "postgres" {
		if _, err = engine.Exec("CREATE DATABASE casbin"); err != nil {
			// 42P04 is	duplicate_database
			if pqerr, ok := err.(*pq.Error); ok && pqerr.Code == "42P04" {
				engine.Close()
				return nil
			}
		}
	} else if a.driverName != "sqlite3" {
		_, err = engine.Exec("CREATE DATABASE IF NOT EXISTS casbin")
	}
	if err != nil {
		engine.Close()
		return err
	}

	return engine.Close()
}

func (a *Adapter) open() error {
	var err error
	var engine *xorm.Engine

	if a.dbSpecified {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
		if err != nil {
			return err
		}
	} else {
		if err = a.createDatabase(); err != nil {
			return err
		}

		if a.driverName == "postgres" {
			engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=casbin")
		} else if a.driverName == "sqlite3" {
			engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
		} else {
			engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+"casbin")
		}
		if err != nil {
			return err
		}
	}

	a.engine = engine

	return a.createTable()
}

func (a *Adapter) close() error {
	err := a.engine.Close()
	if err != nil {
		return err
	}

	a.engine = nil
	return nil
}

func (a *Adapter) createTable() error {
	return a.engine.Sync2(new(CasbinRule))
}

func (a *Adapter) dropTable() error {
	return a.engine.DropTables(new(CasbinRule))
}

func loadPolicyLine(line CasbinRule, model model.Model) {
	lineText := line.PType
	if line.V0 != "" {
		lineText += ", " + line.V0
	}
	if line.V1 != "" {
		lineText += ", " + line.V1
	}
	if line.V2 != "" {
		lineText += ", " + line.V2
	}
	if line.V3 != "" {
		lineText += ", " + line.V3
	}
	if line.V4 != "" {
		lineText += ", " + line.V4
	}
	if line.V5 != "" {
		lineText += ", " + line.V5
	}

	persist.LoadPolicyLine(lineText, model)
}

// LoadPolicy loads policy from database.
func (a *Adapter) LoadPolicy(model model.Model) error {
	var lines []CasbinRule
	err := a.engine.Find(&lines)
	if err != nil {
		return err
	}

	for _, line := range lines {
		loadPolicyLine(line, model)
	}

	return nil
}

func savePolicyLine(ptype string, rule []string) CasbinRule {
	line := CasbinRule{}

	line.PType = ptype
	if len(rule) > 0 {
		line.V0 = rule[0]
	}
	if len(rule) > 1 {
		line.V1 = rule[1]
	}
	if len(rule) > 2 {
		line.V2 = rule[2]
	}
	if len(rule) > 3 {
		line.V3 = rule[3]
	}
	if len(rule) > 4 {
		line.V4 = rule[4]
	}
	if len(rule) > 5 {
		line.V5 = rule[5]
	}

	return line
}

// SavePolicy saves policy to database.
func (a *Adapter) SavePolicy(model model.Model) error {
	err := a.dropTable()
	if err != nil {
		return err
	}
	err = a.createTable()
	if err != nil {
		return err
	}

	var lines []CasbinRule

	for ptype, ast := range model["p"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	for ptype, ast := range model["g"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	_, err = a.engine.Insert(&lines)
	return err
}

// AddPolicy adds a policy rule to the storage.
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Insert(line)
	return err
}

// RemovePolicy removes a policy rule from the storage.
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Delete(line)
	return err
}

// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
	line := CasbinRule{}

	line.PType = ptype
	if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) {
		line.V0 = fieldValues[0-fieldIndex]
	}
	if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) {
		line.V1 = fieldValues[1-fieldIndex]
	}
	if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) {
		line.V2 = fieldValues[2-fieldIndex]
	}
	if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) {
		line.V3 = fieldValues[3-fieldIndex]
	}
	if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) {
		line.V4 = fieldValues[4-fieldIndex]
	}
	if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) {
		line.V5 = fieldValues[5-fieldIndex]
	}

	_, err := a.engine.Delete(line)
	return err
}
<commit_msg>Improve Efficiency<commit_after>// Copyright 2017 The casbin Authors. 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 xormadapter

import (
	"errors"
	"runtime"

	"github.com/casbin/casbin/model"
	"github.com/casbin/casbin/persist"
	"github.com/go-xorm/xorm"
	"github.com/lib/pq"
)

type CasbinRule struct {
	PType string `xorm:"varchar(100) index not null default ''"`
	V0    string `xorm:"varchar(100) index not null default ''"`
	V1    string `xorm:"varchar(100) index not null default ''"`
	V2    string `xorm:"varchar(100) index not null default ''"`
	V3    string `xorm:"varchar(100) index not null default ''"`
	V4    string `xorm:"varchar(100) index not null default ''"`
	V5    string `xorm:"varchar(100) index not null default ''"`
}

// Adapter represents the Xorm adapter for policy storage.
type Adapter struct {
	driverName     string
	dataSourceName string
	dbSpecified    bool
	engine         *xorm.Engine
}

// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
	err := a.engine.Close()
	if err != nil {
		panic(err)
	}
}

// NewAdapter is the constructor for Adapter.
// dbSpecified is an optional bool parameter. The default value is false.
// It's up to whether you have specified an existing DB in dataSourceName.
// If dbSpecified == true, you need to make sure the DB in dataSourceName exists.
// If dbSpecified == false, the adapter will automatically create a DB named "casbin".
func NewAdapter(driverName string, dataSourceName string, dbSpecified ...bool) (*Adapter, error) {
	a := &Adapter{}
	a.driverName = driverName
	a.dataSourceName = dataSourceName

	if len(dbSpecified) == 0 {
		a.dbSpecified = false
	} else if len(dbSpecified) == 1 {
		a.dbSpecified = dbSpecified[0]
	} else {
		return nil, errors.New("invalid parameter: dbSpecified")
	}

	// Open the DB, create it if not existed.
	err := a.open()
	if err != nil {
		return nil, err
	}

	// Call the destructor when the object is released.
	runtime.SetFinalizer(a, finalizer)

	return a, nil
}

func NewAdapterByEngine(engine *xorm.Engine) (*Adapter, error) {
	a := &Adapter{
		engine: engine,
	}

	err := a.createTable()
	if err != nil {
		return nil, err
	}

	return a, nil
}

func (a *Adapter) createDatabase() error {
	var err error
	var engine *xorm.Engine
	if a.driverName == "postgres" {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=postgres")
	} else {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
	}
	if err != nil {
		return err
	}

	if a.driverName == "postgres" {
		if _, err = engine.Exec("CREATE DATABASE casbin"); err != nil {
			// 42P04 is	duplicate_database
			if pqerr, ok := err.(*pq.Error); ok && pqerr.Code == "42P04" {
				engine.Close()
				return nil
			}
		}
	} else if a.driverName != "sqlite3" {
		_, err = engine.Exec("CREATE DATABASE IF NOT EXISTS casbin")
	}
	if err != nil {
		engine.Close()
		return err
	}

	return engine.Close()
}

func (a *Adapter) open() error {
	var err error
	var engine *xorm.Engine

	if a.dbSpecified {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
		if err != nil {
			return err
		}
	} else {
		if err = a.createDatabase(); err != nil {
			return err
		}

		if a.driverName == "postgres" {
			engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=casbin")
		} else if a.driverName == "sqlite3" {
			engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
		} else {
			engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+"casbin")
		}
		if err != nil {
			return err
		}
	}

	a.engine = engine

	return a.createTable()
}

func (a *Adapter) close() error {
	err := a.engine.Close()
	if err != nil {
		return err
	}

	a.engine = nil
	return nil
}

func (a *Adapter) createTable() error {
	return a.engine.Sync2(new(CasbinRule))
}

func (a *Adapter) dropTable() error {
	return a.engine.DropTables(new(CasbinRule))
}

func loadPolicyLine(line *CasbinRule, model model.Model) {
	const prefixLine = ", "

	lineText := line.PType
	if len(line.V0) > 0 {
		lineText += prefixLine + line.V0
	}
	if len(line.V1) > 0 {
		lineText += prefixLine + line.V1
	}
	if len(line.V2) > 0 {
		lineText += prefixLine + line.V2
	}
	if len(line.V3) > 0 {
		lineText += prefixLine + line.V3
	}
	if len(line.V4) > 0 {
		lineText += prefixLine + line.V4
	}
	if len(line.V5) > 0 {
		lineText += prefixLine + line.V5
	}

	persist.LoadPolicyLine(lineText, model)
}

// LoadPolicy loads policy from database.
func (a *Adapter) LoadPolicy(model model.Model) error {
	var lines []*CasbinRule
	if err := a.engine.Find(&lines); err != nil {
		return err
	}

	for _, line := range lines {
		loadPolicyLine(line, model)
	}

	return nil
}

func savePolicyLine(ptype string, rule []string) *CasbinRule {
	line := &CasbinRule{PType: ptype}

	l := len(rule)
	if l > 0 {
		line.V0 = rule[0]
	}
	if l > 1 {
		line.V1 = rule[1]
	}
	if l > 2 {
		line.V2 = rule[2]
	}
	if l > 3 {
		line.V3 = rule[3]
	}
	if l > 4 {
		line.V4 = rule[4]
	}
	if l > 5 {
		line.V5 = rule[5]
	}

	return line
}

// SavePolicy saves policy to database.
func (a *Adapter) SavePolicy(model model.Model) error {
	err := a.dropTable()
	if err != nil {
		return err
	}
	err = a.createTable()
	if err != nil {
		return err
	}

	var lines []*CasbinRule

	for ptype, ast := range model["p"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	for ptype, ast := range model["g"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	_, err = a.engine.Insert(&lines)
	return err
}

// AddPolicy adds a policy rule to the storage.
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Insert(line)
	return err
}

// RemovePolicy removes a policy rule from the storage.
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Delete(line)
	return err
}

// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
	line := &CasbinRule{PType: ptype}

	idx := fieldIndex + len(fieldValues)
	if fieldIndex <= 0 && idx > 0 {
		line.V0 = fieldValues[0-fieldIndex]
	}
	if fieldIndex <= 1 && idx > 1 {
		line.V1 = fieldValues[1-fieldIndex]
	}
	if fieldIndex <= 2 && idx > 2 {
		line.V2 = fieldValues[2-fieldIndex]
	}
	if fieldIndex <= 3 && idx > 3 {
		line.V3 = fieldValues[3-fieldIndex]
	}
	if fieldIndex <= 4 && idx > 4 {
		line.V4 = fieldValues[4-fieldIndex]
	}
	if fieldIndex <= 5 && idx > 5 {
		line.V5 = fieldValues[5-fieldIndex]
	}

	_, err := a.engine.Delete(line)
	return err
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 krepa098 (krepa098 at gmail dot com)
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications,
and to alter it and redistribute it freely, subject to the following restrictions:
	1.	The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
		If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
	2. 	Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
	3. 	This notice may not be removed or altered from any source distribution.
*/

package gosfml2

// #include <SFML/Graphics/RectangleShape.h>
// #include <SFML/Graphics/RenderWindow.h>
// #include <SFML/Graphics/RenderTexture.h>
import "C"
import "runtime"

/////////////////////////////////////
///		STRUCTS
/////////////////////////////////////

type RectangleShape struct {
	cptr    *C.sfRectangleShape
	texture *Texture //to prevent the GC from deleting the texture
}

/////////////////////////////////////
///		FUNCS
/////////////////////////////////////

func NewRectangleShape() *RectangleShape {
	shape := &RectangleShape{C.sfRectangleShape_create(), nil}
	runtime.SetFinalizer(shape, (*RectangleShape).Destroy)
	return shape
}

func (this *RectangleShape) Copy() *RectangleShape {
	shape := &RectangleShape{C.sfRectangleShape_copy(this.cptr), this.texture}
	runtime.SetFinalizer(shape, (*RectangleShape).Destroy)
	return shape
}

func (this *RectangleShape) Destroy() {
	C.sfRectangleShape_destroy(this.cptr)
	this.cptr = nil
}

func (this *RectangleShape) SetPosition(pos Vector2f) {
	C.sfRectangleShape_setPosition(this.cptr, pos.toC())
}

func (this *RectangleShape) SetScale(scale Vector2f) {
	C.sfRectangleShape_setScale(this.cptr, scale.toC())
}

func (this *RectangleShape) SetOrigin(orig Vector2f) {
	C.sfRectangleShape_setOrigin(this.cptr, orig.toC())
}

func (this *RectangleShape) SetRotation(rot float32) {
	C.sfRectangleShape_setRotation(this.cptr, C.float(rot))
}

func (this *RectangleShape) GetRotation() float32 {
	return float32(C.sfRectangleShape_getRotation(this.cptr))
}

func (this *RectangleShape) GetPosition() (position Vector2f) {
	position.fromC(C.sfRectangleShape_getPosition(this.cptr))
	return
}

func (this *RectangleShape) GetScale() (scale Vector2f) {
	scale.fromC(C.sfRectangleShape_getScale(this.cptr))
	return
}

func (this *RectangleShape) GetOrigin() (origin Vector2f) {
	origin.fromC(C.sfRectangleShape_getOrigin(this.cptr))
	return
}

func (this *RectangleShape) Move(offset Vector2f) {
	C.sfRectangleShape_move(this.cptr, offset.toC())
}

func (this *RectangleShape) Scale(factor Vector2f) {
	C.sfRectangleShape_scale(this.cptr, factor.toC())
}

func (this *RectangleShape) Rotate(angle float32) {
	C.sfRectangleShape_rotate(this.cptr, C.float(angle))
}

func (this *RectangleShape) SetTexture(texture *Texture, resetRect bool) {
	C.sfRectangleShape_setTexture(this.cptr, texture.cptr, goBool2C(resetRect))
	this.texture = texture
}

func (this *RectangleShape) SetTextureRect(rect IntRect) {
	C.sfRectangleShape_setTextureRect(this.cptr, rect.toC())
}

func (this *RectangleShape) GetTexture() *Texture {
	return this.texture
}

func (this *RectangleShape) GetTextureRect() (rect IntRect) {
	rect.fromC(C.sfRectangleShape_getTextureRect(this.cptr))
	return
}

func (this *RectangleShape) SetFillColor(color Color) {
	C.sfRectangleShape_setFillColor(this.cptr, color.toC())
}

func (this *RectangleShape) SetOutlineColor(color Color) {
	C.sfRectangleShape_setOutlineColor(this.cptr, color.toC())
}

func (this *RectangleShape) SetOutlineThickness(thickness float32) {
	C.sfRectangleShape_setOutlineThickness(this.cptr, C.float(thickness))
}

func (this *RectangleShape) SetSize(size Vector2f) {
	C.sfRectangleShape_setSize(this.cptr, size.toC())
}

func (this *RectangleShape) GetSize() (size Vector2f) {
	size.fromC(C.sfRectangleShape_getSize(this.cptr))
	return
}

func (this *RectangleShape) GetTransform() (transform Transform) {
	transform.fromC(C.sfRectangleShape_getTransform(this.cptr))
	return
}

func (this *RectangleShape) GetInverseTransform() (transform Transform) {
	transform.fromC(C.sfRectangleShape_getInverseTransform(this.cptr))
	return
}

func (this *RectangleShape) GetFillColor() (color Color) {
	color.fromC(C.sfRectangleShape_getFillColor(this.cptr))
	return
}

func (this *RectangleShape) GetOutlineColor() (color Color) {
	color.fromC(C.sfRectangleShape_getOutlineColor(this.cptr))
	return
}

func (this *RectangleShape) GetOutlineThickness() float32 {
	return float32(C.sfRectangleShape_getOutlineThickness(this.cptr))
}

func (this *RectangleShape) GetPointCount() uint {
	return uint(C.sfRectangleShape_getPointCount(this.cptr))
}

func (this *RectangleShape) GetPoint(index uint) (point Vector2f) {
	point.fromC(C.sfRectangleShape_getPoint(this.cptr, C.uint(index)))
	return
}

func (this *RectangleShape) GetLocalBounds() (rect FloatRect) {
	rect.fromC(C.sfRectangleShape_getLocalBounds(this.cptr))
	return
}

func (this *RectangleShape) GetGlobalBounds() (rect FloatRect) {
	rect.fromC(C.sfRectangleShape_getGlobalBounds(this.cptr))
	return
}

func (this *RectangleShape) Draw(target RenderTarget, renderStates *RenderStates) {
	switch target.(type) {
	case *RenderWindow:
		C.sfRenderWindow_drawRectangleShape(target.(*RenderWindow).cptr, this.cptr, renderStates.toCPtr())
	case *RenderTexture:
		C.sfRenderTexture_drawRectangleShape(target.(*RenderTexture).cptr, this.cptr, renderStates.toCPtr())
	}
}
<commit_msg>documented rectangleShape.go<commit_after>/*
Copyright (c) 2012 krepa098 (krepa098 at gmail dot com)
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications,
and to alter it and redistribute it freely, subject to the following restrictions:
	1.	The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
		If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
	2. 	Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
	3. 	This notice may not be removed or altered from any source distribution.
*/

package gosfml2

// #include <SFML/Graphics/RectangleShape.h>
// #include <SFML/Graphics/RenderWindow.h>
// #include <SFML/Graphics/RenderTexture.h>
import "C"
import "runtime"

/////////////////////////////////////
///		STRUCTS
/////////////////////////////////////

type RectangleShape struct {
	cptr    *C.sfRectangleShape
	texture *Texture //to prevent the GC from deleting the texture
}

/////////////////////////////////////
///		FUNCS
/////////////////////////////////////

// Create a new rectangle shape
func NewRectangleShape() *RectangleShape {
	shape := &RectangleShape{C.sfRectangleShape_create(), nil}
	runtime.SetFinalizer(shape, (*RectangleShape).Destroy)
	return shape
}

// Copy an existing rectangle shape
func (this *RectangleShape) Copy() *RectangleShape {
	shape := &RectangleShape{C.sfRectangleShape_copy(this.cptr), this.texture}
	runtime.SetFinalizer(shape, (*RectangleShape).Destroy)
	return shape
}

// Destroy an existing rectangle shape
func (this *RectangleShape) Destroy() {
	C.sfRectangleShape_destroy(this.cptr)
	this.cptr = nil
}

// Set the position of a rectangle shape
//
// This function completely overwrites the previous position.
// See sfRectangleShape_move to apply an offset based on the previous position instead.
// The default position of a circle Shape object is (0, 0).
//
// position: New position
func (this *RectangleShape) SetPosition(pos Vector2f) {
	C.sfRectangleShape_setPosition(this.cptr, pos.toC())
}

// Set the scale factors of a rectangle shape
//
// This function completely overwrites the previous scale.
// See sfRectangleShape_scale to add a factor based on the previous scale instead.
// The default scale of a circle Shape object is (1, 1).
//
// scale: New scale factors
func (this *RectangleShape) SetScale(scale Vector2f) {
	C.sfRectangleShape_setScale(this.cptr, scale.toC())
}

// Set the local origin of a rectangle shape
//
// The origin of an object defines the center point for
// all transformations (position, scale, rotation).
// The coordinates of this point must be relative to the
// top-left corner of the object, and ignore all
// transformations (position, scale, rotation).
// The default origin of a circle Shape object is (0, 0).
//
// origin: New origin
func (this *RectangleShape) SetOrigin(orig Vector2f) {
	C.sfRectangleShape_setOrigin(this.cptr, orig.toC())
}

// Set the orientation of a rectangle shape
//
// This function completely overwrites the previous rotation.
// See sfRectangleShape_rotate to add an angle based on the previous rotation instead.
// The default rotation of a circle Shape object is 0.
//
// angle: New rotation, in degrees
func (this *RectangleShape) SetRotation(rot float32) {
	C.sfRectangleShape_setRotation(this.cptr, C.float(rot))
}

// Get the orientation of a rectangle shape
//
// The rotation is always in the range [0, 360].
func (this *RectangleShape) GetRotation() float32 {
	return float32(C.sfRectangleShape_getRotation(this.cptr))
}

// Get the position of a rectangle shape
func (this *RectangleShape) GetPosition() (position Vector2f) {
	position.fromC(C.sfRectangleShape_getPosition(this.cptr))
	return
}

// Get the current scale of a rectangle shap
func (this *RectangleShape) GetScale() (scale Vector2f) {
	scale.fromC(C.sfRectangleShape_getScale(this.cptr))
	return
}

// Get the local origin of a rectangle shape
func (this *RectangleShape) GetOrigin() (origin Vector2f) {
	origin.fromC(C.sfRectangleShape_getOrigin(this.cptr))
	return
}

// Move a rectangle shape by a given offset
//
// This function adds to the current position of the object,
// unlike RectangleShape.SetPosition which overwrites it.
func (this *RectangleShape) Move(offset Vector2f) {
	C.sfRectangleShape_move(this.cptr, offset.toC())
}

// Scale a rectangle shape
//
// This function multiplies the current scale of the object,
// unlike RectangleShape.SetScale which overwrites it.
func (this *RectangleShape) Scale(factor Vector2f) {
	C.sfRectangleShape_scale(this.cptr, factor.toC())
}

// Rotate a rectangle shape
//
// This function adds to the current rotation of the object,
// unlike RectangleShape.SetRotation which overwrites it.
func (this *RectangleShape) Rotate(angle float32) {
	C.sfRectangleShape_rotate(this.cptr, C.float(angle))
}

// Change the source texture of a rectangle shape
//
// texture can be nil to disable texturing.
// If resetRect is true, the TextureRect property of
// the shape is automatically adjusted to the size of the new
// texture. If it is false, the texture rect is left unchanged.
//
// texture:   New texture
// resetRect: Should the texture rect be reset to the size of the new texture?
func (this *RectangleShape) SetTexture(texture *Texture, resetRect bool) {
	C.sfRectangleShape_setTexture(this.cptr, texture.cptr, goBool2C(resetRect))
	this.texture = texture
}

// Set the sub-rectangle of the texture that a rectangle shape will display
//
// The texture rect is useful when you don't want to display
// the whole texture, but rather a part of it.
// By default, the texture rect covers the entire texture.
//
// rect:  Rectangle defining the region of the texture to display
func (this *RectangleShape) SetTextureRect(rect IntRect) {
	C.sfRectangleShape_setTextureRect(this.cptr, rect.toC())
}

// Get the source texture of a rectangle shape
//
// If the shape has no source texture, a nil pointer is returned.
func (this *RectangleShape) GetTexture() *Texture {
	return this.texture
}

// Get the sub-rectangle of the texture displayed by a rectangle shape
func (this *RectangleShape) GetTextureRect() (rect IntRect) {
	rect.fromC(C.sfRectangleShape_getTextureRect(this.cptr))
	return
}

// Set the fill color of a rectangle shape
//
// This color is modulated (multiplied) with the shape's
// texture if any. It can be used to colorize the shape,
// or change its global opacity.
// You can use sfTransparent to make the inside of
// the shape transparent, and have the outline alone.
// By default, the shape's fill color is opaque white.
//
// color: New color of the shape
func (this *RectangleShape) SetFillColor(color Color) {
	C.sfRectangleShape_setFillColor(this.cptr, color.toC())
}

// Set the outline color of a rectangle shape
//
// You can use sfTransparent to disable the outline.
// By default, the shape's outline color is opaque white.
//
// color: New outline color of the shape
func (this *RectangleShape) SetOutlineColor(color Color) {
	C.sfRectangleShape_setOutlineColor(this.cptr, color.toC())
}

// Set the thickness of a rectangle shape's outline
//
// This number cannot be negative. Using zero disables
// the outline.
// By default, the outline thickness is 0.
//
// thickness: New outline thickness
func (this *RectangleShape) SetOutlineThickness(thickness float32) {
	C.sfRectangleShape_setOutlineThickness(this.cptr, C.float(thickness))
}

// Set the size of a rectangle shape
func (this *RectangleShape) SetSize(size Vector2f) {
	C.sfRectangleShape_setSize(this.cptr, size.toC())
}

// Get the size of a rectangle shape
func (this *RectangleShape) GetSize() (size Vector2f) {
	size.fromC(C.sfRectangleShape_getSize(this.cptr))
	return
}

// Get the combined transform of a rectangle shape
func (this *RectangleShape) GetTransform() (transform Transform) {
	transform.fromC(C.sfRectangleShape_getTransform(this.cptr))
	return
}

// Get the inverse of the combined transform of a rectangle shape
func (this *RectangleShape) GetInverseTransform() (transform Transform) {
	transform.fromC(C.sfRectangleShape_getInverseTransform(this.cptr))
	return
}

// Set the fill color of a rectangle shape
//
// This color is modulated (multiplied) with the shape's
// texture if any. It can be used to colorize the shape,
// or change its global opacity.
// You can use sfTransparent to make the inside of
// the shape transparent, and have the outline alone.
// By default, the shape's fill color is opaque white.
//
// color: New color of the shape
func (this *RectangleShape) GetFillColor() (color Color) {
	color.fromC(C.sfRectangleShape_getFillColor(this.cptr))
	return
}

// Get the outline color of a rectangle shape
func (this *RectangleShape) GetOutlineColor() (color Color) {
	color.fromC(C.sfRectangleShape_getOutlineColor(this.cptr))
	return
}

// Get the outline thickness of a rectangle shape
func (this *RectangleShape) GetOutlineThickness() float32 {
	return float32(C.sfRectangleShape_getOutlineThickness(this.cptr))
}

// Get the total number of points of a rectangle shape
func (this *RectangleShape) GetPointCount() uint {
	return uint(C.sfRectangleShape_getPointCount(this.cptr))
}

// Get a point of a rectangle shape
//
// The result is undefined if index is out of the valid range.
//
// index: Index of the point to get, in range [0 .. GetPointCount() - 1]
func (this *RectangleShape) GetPoint(index uint) (point Vector2f) {
	point.fromC(C.sfRectangleShape_getPoint(this.cptr, C.uint(index)))
	return
}

// Get the local bounding rectangle of a rectangle shape
//
// The returned rectangle is in local coordinates, which means
// that it ignores the transformations (translation, rotation,
// scale, ...) that are applied to the entity.
// In other words, this function returns the bounds of the
// entity in the entity's coordinate system.
func (this *RectangleShape) GetLocalBounds() (rect FloatRect) {
	rect.fromC(C.sfRectangleShape_getLocalBounds(this.cptr))
	return
}

// Get the global bounding rectangle of a rectangle shape
//
// The returned rectangle is in global coordinates, which means
// that it takes in account the transformations (translation,
// rotation, scale, ...) that are applied to the entity.
// In other words, this function returns the bounds of the
// sprite in the global 2D world's coordinate system.
func (this *RectangleShape) GetGlobalBounds() (rect FloatRect) {
	rect.fromC(C.sfRectangleShape_getGlobalBounds(this.cptr))
	return
}

//Draws a RectangleShape on a render target
//
//renderStates: can be nil
func (this *RectangleShape) Draw(target RenderTarget, renderStates *RenderStates) {
	switch target.(type) {
	case *RenderWindow:
		C.sfRenderWindow_drawRectangleShape(target.(*RenderWindow).cptr, this.cptr, renderStates.toCPtr())
	case *RenderTexture:
		C.sfRenderTexture_drawRectangleShape(target.(*RenderTexture).cptr, this.cptr, renderStates.toCPtr())
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
)

// run is a simple wrapper for exec.Run/Close
func run(envv []string, dir string, argv ...string) error {
	if *verbose {
		log.Println("run", argv)
	}
	cmd := exec.Command(argv[0], argv[1:]...)
	cmd.Dir = dir
	cmd.Env = envv
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// runLog runs a process and returns the combined stdout/stderr,
// as well as writing it to logfile (if specified). It returns
// process combined stdout and stderr output, exit status and error.
// The error returned is nil, if process is started successfully,
// even if exit status is not 0.
func runLog(envv []string, logfile, dir string, argv ...string) (string, int, error) {
	if *verbose {
		log.Println("runLog", argv)
	}

	b := new(bytes.Buffer)
	var w io.Writer = b
	if logfile != "" {
		f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			return "", 0, err
		}
		defer f.Close()
		w = io.MultiWriter(f, b)
	}

	cmd := exec.Command(argv[0], argv[1:]...)
	cmd.Dir = dir
	cmd.Env = envv
	cmd.Stdout = w
	cmd.Stderr = w

	err := cmd.Run()
	if err != nil {
		if ws, ok := err.(*exec.ExitError); ok {
			return b.String(), ws.ExitStatus(), nil
		}
	}
	return b.String(), 0, err
}
<commit_msg>builder: update for os.Wait changes.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
)

// run is a simple wrapper for exec.Run/Close
func run(envv []string, dir string, argv ...string) error {
	if *verbose {
		log.Println("run", argv)
	}
	cmd := exec.Command(argv[0], argv[1:]...)
	cmd.Dir = dir
	cmd.Env = envv
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// runLog runs a process and returns the combined stdout/stderr,
// as well as writing it to logfile (if specified). It returns
// process combined stdout and stderr output, exit status and error.
// The error returned is nil, if process is started successfully,
// even if exit status is not successful.
func runLog(envv []string, logfile, dir string, argv ...string) (string, int, error) {
	if *verbose {
		log.Println("runLog", argv)
	}

	b := new(bytes.Buffer)
	var w io.Writer = b
	if logfile != "" {
		f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			return "", 0, err
		}
		defer f.Close()
		w = io.MultiWriter(f, b)
	}

	cmd := exec.Command(argv[0], argv[1:]...)
	cmd.Dir = dir
	cmd.Env = envv
	cmd.Stdout = w
	cmd.Stderr = w

	startErr := cmd.Start()
	if startErr != nil {
		return "", 1, startErr
	}
	exitStatus := 0
	if err := cmd.Wait(); err != nil {
		exitStatus = 1 // TODO(bradfitz): this is fake. no callers care, so just return a bool instead.
	}
	return b.String(), exitStatus, nil
}
<|endoftext|>


<commit_before>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

// The code below was generated by lxd-generate - DO NOT EDIT!

import (
	"fmt"

	"github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/db/query"
	"github.com/lxc/lxd/shared/api"
)

var _ = api.ServerEnvironment{}

var instanceProfileObjects = cluster.RegisterStmt(`
SELECT instances_profiles.instance_id, instances_profiles.profile_id, instances_profiles.apply_order
  FROM instances_profiles
  ORDER BY instances_profiles.instance_id
`)

var instanceProfileCreate = cluster.RegisterStmt(`
INSERT INTO instances_profiles (instance_id, profile_id, apply_order)
  VALUES (?, ?, ?)
`)

var instanceProfileDeleteByInstanceID = cluster.RegisterStmt(`
DELETE FROM instances_profiles WHERE instance_id = ?
`)

// GetProfileInstances returns all available instance_profiles.
// generator: instance_profile GetMany
func (c *ClusterTx) GetProfileInstances() (map[int][]int, error) {
	var err error

	// Result slice.
	objects := make([]InstanceProfile, 0)

	stmt := c.stmt(instanceProfileObjects)
	args := []interface{}{}

	// Dest function for scanning a row.
	dest := func(i int) []interface{} {
		objects = append(objects, InstanceProfile{})
		return []interface{}{
			&objects[i].InstanceID,
			&objects[i].ProfileID,
			&objects[i].ApplyOrder,
		}
	}

	// Select.
	err = query.SelectObjects(stmt, dest, args...)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"instances_profiles\" table: %w", err)
	}

	resultMap := map[int][]int{}
	for _, object := range objects {
		resultMap[object.ProfileID] = append(resultMap[object.ProfileID], object.InstanceID)
	}

	return resultMap, nil
}

// GetInstanceProfiles returns all available instance_profiles.
// generator: instance_profile GetMany
func (c *ClusterTx) GetInstanceProfiles() (map[int][]int, error) {
	var err error

	// Result slice.
	objects := make([]InstanceProfile, 0)

	stmt := c.stmt(instanceProfileObjects)
	args := []interface{}{}

	// Dest function for scanning a row.
	dest := func(i int) []interface{} {
		objects = append(objects, InstanceProfile{})
		return []interface{}{
			&objects[i].InstanceID,
			&objects[i].ProfileID,
			&objects[i].ApplyOrder,
		}
	}

	// Select.
	err = query.SelectObjects(stmt, dest, args...)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"instances_profiles\" table: %w", err)
	}

	resultMap := map[int][]int{}
	for _, object := range objects {
		resultMap[object.InstanceID] = append(resultMap[object.InstanceID], object.ProfileID)
	}

	return resultMap, nil
}

// CreateInstanceProfile adds a new instance_profile to the database.
// generator: instance_profile Create
func (c *ClusterTx) CreateInstanceProfile(object InstanceProfile) (int64, error) {
	args := make([]interface{}, 3)

	// Populate the statement arguments.
	args[0] = object.InstanceID
	args[1] = object.ProfileID
	args[2] = object.ApplyOrder

	// Prepared statement to use.
	stmt := c.stmt(instanceProfileCreate)

	// Execute the statement.
	result, err := stmt.Exec(args...)
	if err != nil {
		return -1, fmt.Errorf("Failed to create \"instances_profiles\" entry: %w", err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		return -1, fmt.Errorf("Failed to fetch \"instances_profiles\" entry ID: %w", err)
	}

	return id, nil
}

// DeleteInstanceProfiles deletes the instance_profile matching the given key parameters.
// generator: instance_profile DeleteMany
func (c *ClusterTx) DeleteInstanceProfiles(object Instance) error {
	stmt := c.stmt(instanceProfileDeleteByInstanceID)
	result, err := stmt.Exec(int(object.ID))
	if err != nil {
		return fmt.Errorf("Delete \"instances_profiles\" entry failed: %w", err)
	}

	_, err = result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}

	return nil
}
<commit_msg>lxd/db/instance/profiles.mapper: Update generated code<commit_after>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

// The code below was generated by lxd-generate - DO NOT EDIT!

import (
	"fmt"

	"github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/db/query"
	"github.com/lxc/lxd/shared/api"
)

var _ = api.ServerEnvironment{}

var instanceProfileObjects = cluster.RegisterStmt(`
SELECT instances_profiles.instance_id, instances_profiles.profile_id, instances_profiles.apply_order
  FROM instances_profiles
  ORDER BY instances_profiles.instance_id, instances_profiles.apply_order
`)

var instanceProfileCreate = cluster.RegisterStmt(`
INSERT INTO instances_profiles (instance_id, profile_id, apply_order)
  VALUES (?, ?, ?)
`)

var instanceProfileDeleteByInstanceID = cluster.RegisterStmt(`
DELETE FROM instances_profiles WHERE instance_id = ?
`)

// GetProfileInstances returns all available instance_profiles.
// generator: instance_profile GetMany
func (c *ClusterTx) GetProfileInstances() (map[int][]int, error) {
	var err error

	// Result slice.
	objects := make([]InstanceProfile, 0)

	stmt := c.stmt(instanceProfileObjects)
	args := []interface{}{}

	// Dest function for scanning a row.
	dest := func(i int) []interface{} {
		objects = append(objects, InstanceProfile{})
		return []interface{}{
			&objects[i].InstanceID,
			&objects[i].ProfileID,
			&objects[i].ApplyOrder,
		}
	}

	// Select.
	err = query.SelectObjects(stmt, dest, args...)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"instances_profiles\" table: %w", err)
	}

	resultMap := map[int][]int{}
	for _, object := range objects {
		resultMap[object.ProfileID] = append(resultMap[object.ProfileID], object.InstanceID)
	}

	return resultMap, nil
}

// GetInstanceProfiles returns all available instance_profiles.
// generator: instance_profile GetMany
func (c *ClusterTx) GetInstanceProfiles() (map[int][]int, error) {
	var err error

	// Result slice.
	objects := make([]InstanceProfile, 0)

	stmt := c.stmt(instanceProfileObjects)
	args := []interface{}{}

	// Dest function for scanning a row.
	dest := func(i int) []interface{} {
		objects = append(objects, InstanceProfile{})
		return []interface{}{
			&objects[i].InstanceID,
			&objects[i].ProfileID,
			&objects[i].ApplyOrder,
		}
	}

	// Select.
	err = query.SelectObjects(stmt, dest, args...)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"instances_profiles\" table: %w", err)
	}

	resultMap := map[int][]int{}
	for _, object := range objects {
		resultMap[object.InstanceID] = append(resultMap[object.InstanceID], object.ProfileID)
	}

	return resultMap, nil
}

// CreateInstanceProfile adds a new instance_profile to the database.
// generator: instance_profile Create
func (c *ClusterTx) CreateInstanceProfile(object InstanceProfile) (int64, error) {
	args := make([]interface{}, 3)

	// Populate the statement arguments.
	args[0] = object.InstanceID
	args[1] = object.ProfileID
	args[2] = object.ApplyOrder

	// Prepared statement to use.
	stmt := c.stmt(instanceProfileCreate)

	// Execute the statement.
	result, err := stmt.Exec(args...)
	if err != nil {
		return -1, fmt.Errorf("Failed to create \"instances_profiles\" entry: %w", err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		return -1, fmt.Errorf("Failed to fetch \"instances_profiles\" entry ID: %w", err)
	}

	return id, nil
}

// DeleteInstanceProfiles deletes the instance_profile matching the given key parameters.
// generator: instance_profile DeleteMany
func (c *ClusterTx) DeleteInstanceProfiles(object Instance) error {
	stmt := c.stmt(instanceProfileDeleteByInstanceID)
	result, err := stmt.Exec(int(object.ID))
	if err != nil {
		return fmt.Errorf("Delete \"instances_profiles\" entry failed: %w", err)
	}

	_, err = result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}

	return nil
}
<|endoftext|>