func (svc *Service) UpdateRangeStrings(id, a1Range string, values [][]string) (*sheets.UpdateValuesResponse, error) {
	var vals = make([][]interface{}, len(values))
	for i := range vals {
		vals[i] = make([]interface{}, len(values[i]))
	}
	for r, row := range values {
		for c, v := range row {
			vals[r][c] = v
		}
	}
	resp, err := svc.values.BatchUpdate(id, &sheets.BatchUpdateValuesRequest{
		Data: []*sheets.ValueRange{
			&sheets.ValueRange{
				MajorDimension: "ROWS",
				Range:          a1Range,
				Values:         vals,
			},
		},
		ValueInputOption: "USER_ENTERED",
	}).
		Context(svc.ctx).
		Do()
	if err != nil {
		return nil, err
	}
	return resp.Responses[0], nil
}
UpdateRangeStrings update values in 'a1Range' in the spreadsheet doc
identified by 'id' to 'values'.
Values will be parsed by Google Sheets as if they were typed in by the user
(so strings containing numerals may be converted to numbers, etc.)
package gsheets
import (
	"bytes"
	"context"
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"google.golang.org/api/sheets/v4"
)
type ssService interface {
	Create(*sheets.Spreadsheet) *sheets.SpreadsheetsCreateCall
	Get(string) *sheets.SpreadsheetsGetCall
	BatchUpdate(string, *sheets.BatchUpdateSpreadsheetRequest) *sheets.SpreadsheetsBatchUpdateCall
}
type valueService interface {
	BatchGet(string) *sheets.SpreadsheetsValuesBatchGetCall
	BatchUpdate(string, *sheets.BatchUpdateValuesRequest) *sheets.SpreadsheetsValuesBatchUpdateCall
	BatchClear(string, *sheets.BatchClearValuesRequest) *sheets.SpreadsheetsValuesBatchClearCall
}
type Service struct {
	Sep    rune  
	ctx    context.Context
	sheet  ssService
	values valueService
}
func NewServiceWithCtx(ctx context.Context) (*Service, error) {
	ssvc, err := sheets.NewService(ctx)
	if err != nil {
		return nil, err
	}
	return &Service{
		Sep:    ',',
		ctx:    ctx,
		sheet:  ssvc.Spreadsheets,
		values: ssvc.Spreadsheets.Values,
	}, nil
}
func (svc *Service) SpreadsheetsService() *sheets.SpreadsheetsService {
	return svc.sheet.(*sheets.SpreadsheetsService)
}
func (svc *Service) NewSheet(id, title string) error {
	_, err := svc.sheet.BatchUpdate(id, &sheets.BatchUpdateSpreadsheetRequest{
		Requests: []*sheets.Request{
			&sheets.Request{
				AddSheet: &sheets.AddSheetRequest{
					Properties: &sheets.SheetProperties{
						Title: title,
					},
				},
			},
		},
	}).Context(svc.ctx).Do()
	return err
}
func (svc *Service) SheetFromTitle(id, title string) (*int64, error) {
	ss, err := svc.sheet.Get(id).Context(svc.ctx).Do()
	if err != nil {
		return nil, err
	}
	var sheetId *int64
	for _, sheet := range ss.Sheets {
		if sheet.Properties.Title == title {
			sheetId = &sheet.Properties.SheetId
			break
		}
	}
	return sheetId, nil
}
func (svc *Service) DeleteSheet(id, title string) error {
	sheetId, err := svc.SheetFromTitle(id, title)
	if err != nil {
		return err
	}
	if sheetId == nil {
		return errors.New("No sheet found with title: " + title)
	}
	_, err = svc.sheet.BatchUpdate(id, &sheets.BatchUpdateSpreadsheetRequest{
		Requests: []*sheets.Request{
			&sheets.Request{
				DeleteSheet: &sheets.DeleteSheetRequest{
					SheetId: *sheetId,
				},
			},
		},
	}).Context(svc.ctx).Do()
	return err
}
func (svc *Service) GetRangeRaw(id string, a1Range string) ([][]interface{}, error) {
	resp, err := svc.values.BatchGet(id).
		Context(svc.ctx).
		MajorDimension("ROWS").
		Ranges(a1Range).
		ValueRenderOption("UNFORMATTED_VALUE").
		Do()
	if err != nil {
		return nil, err
	}
	return resp.ValueRanges[0].Values, nil
}
func (svc *Service) GetRangeFormatted(id string, a1Range string) ([][]string, error) {
	resp, err := svc.values.BatchGet(id).
		Context(svc.ctx).
		MajorDimension("ROWS").
		Ranges(a1Range).
		ValueRenderOption("FORMATTED_VALUE").
		Do()
	if err != nil {
		return nil, err
	}
	values := resp.ValueRanges[0].Values
	var stringVals = make([][]string, len(values))
	for i := range stringVals {
		stringVals[i] = make([]string, len(values[i]))
	}
	for r, row := range values {
		for c, v := range row {
			stringVals[r][c] = v.(string)
		}
	}
	return stringVals, nil
}
func (svc *Service) GetRangeCSV(id, a1Range string) ([]byte, error) {
	rows, err := svc.GetRangeFormatted(id, a1Range)
	if err != nil {
		return nil, err
	}
	buf := bytes.NewBuffer([]byte{})
	csvW := csv.NewWriter(buf)
	csvW.Comma = svc.Sep
	err = csvW.WriteAll(rows)
	if err != nil {
		return nil, err
	}
	csvW.Flush()
	err = csvW.Error()
	return buf.Bytes(), err
}
func (svc *Service) UpdateRangeRaw(id, a1Range string, values [][]interface{}) (*sheets.UpdateValuesResponse, error) {
	resp, err := svc.values.BatchUpdate(id, &sheets.BatchUpdateValuesRequest{
		Data: []*sheets.ValueRange{
			&sheets.ValueRange{
				MajorDimension: "ROWS",
				Range:          a1Range,
				Values:         values,
			},
		},
		ValueInputOption: "RAW",
	}).
		Context(svc.ctx).
		Do()
	if err != nil {
		return nil, err
	}
	return resp.Responses[0], nil
}