<commit_before>package http

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/spf13/afero"

	"github.com/filebrowser/filebrowser/v2/errors"
	"github.com/filebrowser/filebrowser/v2/files"
	"github.com/filebrowser/filebrowser/v2/fileutils"
)

var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
	file, err := files.NewFileInfo(files.FileOptions{
		Fs:         d.user.Fs,
		Path:       r.URL.Path,
		Modify:     d.user.Perm.Modify,
		Expand:     true,
		ReadHeader: d.server.TypeDetectionByHeader,
		Checker:    d,
	})
	if err != nil {
		return errToStatus(err), err
	}

	if file.IsDir {
		file.Listing.Sorting = d.user.Sorting
		file.Listing.ApplySort()
		return renderJSON(w, r, file)
	}

	if checksum := r.URL.Query().Get("checksum"); checksum != "" {
		err := file.Checksum(checksum)
		if err == errors.ErrInvalidOption {
			return http.StatusBadRequest, nil
		} else if err != nil {
			return http.StatusInternalServerError, err
		}

		// do not waste bandwidth if we just want the checksum
		file.Content = ""
	}

	return renderJSON(w, r, file)
})

func resourceDeleteHandler(fileCache FileCache) handleFunc {
	return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if r.URL.Path == "/" || !d.user.Perm.Delete {
			return http.StatusForbidden, nil
		}

		file, err := files.NewFileInfo(files.FileOptions{
			Fs:         d.user.Fs,
			Path:       r.URL.Path,
			Modify:     d.user.Perm.Modify,
			Expand:     true,
			ReadHeader: d.server.TypeDetectionByHeader,
			Checker:    d,
		})
		if err != nil {
			return errToStatus(err), err
		}

		// delete thumbnails
		err = delThumbs(r.Context(), fileCache, file)
		if err != nil {
			return errToStatus(err), err
		}

		err = d.RunHook(func() error {
			return d.user.Fs.RemoveAll(r.URL.Path)
		}, "delete", r.URL.Path, "", d.user)

		if err != nil {
			return errToStatus(err), err
		}

		return http.StatusOK, nil
	})
}

func resourcePostHandler(fileCache FileCache) handleFunc {
	return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if !d.user.Perm.Create || !d.Check(r.URL.Path) {
			return http.StatusForbidden, nil
		}

		defer func() {
			_, _ = io.Copy(ioutil.Discard, r.Body)
		}()

		// Directories creation on POST.
		if strings.HasSuffix(r.URL.Path, "/") {
			err := d.user.Fs.MkdirAll(r.URL.Path, 0775)
			return errToStatus(err), err
		}

		file, err := files.NewFileInfo(files.FileOptions{
			Fs:         d.user.Fs,
			Path:       r.URL.Path,
			Modify:     d.user.Perm.Modify,
			Expand:     true,
			ReadHeader: d.server.TypeDetectionByHeader,
			Checker:    d,
		})
		if err == nil {
			if r.URL.Query().Get("override") != "true" {
				return http.StatusConflict, nil
			}

			err = delThumbs(r.Context(), fileCache, file)
			if err != nil {
				return errToStatus(err), err
			}
		}

		err = d.RunHook(func() error {
			info, writeErr := writeFile(d.user.Fs, r.URL.Path, r.Body)
			if writeErr != nil {
				return writeErr
			}

			etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
			w.Header().Set("ETag", etag)
			return nil
		}, "upload", r.URL.Path, "", d.user)

		if err != nil {
			_ = d.user.Fs.RemoveAll(r.URL.Path)
		}

		return errToStatus(err), err
	})
}

var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
	if !d.user.Perm.Modify || !d.Check(r.URL.Path) {
		return http.StatusForbidden, nil
	}

	defer func() {
		_, _ = io.Copy(ioutil.Discard, r.Body)
	}()

	// Only allow PUT for files.
	if strings.HasSuffix(r.URL.Path, "/") {
		return http.StatusMethodNotAllowed, nil
	}

	err := d.RunHook(func() error {
		info, writeErr := writeFile(d.user.Fs, r.URL.Path, r.Body)
		if writeErr != nil {
			return writeErr
		}

		etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
		w.Header().Set("ETag", etag)
		return nil
	}, "save", r.URL.Path, "", d.user)

	if err != nil {
		_ = d.user.Fs.RemoveAll(r.URL.Path)
	}

	return errToStatus(err), err
})

var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
	src := r.URL.Path
	dst := r.URL.Query().Get("destination")
	action := r.URL.Query().Get("action")
	dst, err := url.QueryUnescape(dst)
	if !d.Check(src) || !d.Check(dst) {
		return http.StatusForbidden, nil
	}
	if err != nil {
		return errToStatus(err), err
	}
	if dst == "/" || src == "/" {
		return http.StatusForbidden, nil
	}
	if err = checkParent(src, dst); err != nil {
		return http.StatusBadRequest, err
	}

	override := r.URL.Query().Get("override") == "true"
	rename := r.URL.Query().Get("rename") == "true"
	if !override && !rename {
		if _, err = d.user.Fs.Stat(dst); err == nil {
			return http.StatusConflict, nil
		}
	}
	if rename {
		dst = addVersionSuffix(dst, d.user.Fs)
	}

	err = d.RunHook(func() error {
		switch action {
		// TODO: use enum
		case "copy":
			if !d.user.Perm.Create {
				return errors.ErrPermissionDenied
			}

			return fileutils.Copy(d.user.Fs, src, dst)
		case "rename":
			if !d.user.Perm.Rename {
				return errors.ErrPermissionDenied
			}
			src = path.Clean("/" + src)
			dst = path.Clean("/" + dst)

			return fileutils.MoveFile(d.user.Fs, src, dst)
		default:
			return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
		}
	}, action, src, dst, d.user)

	return errToStatus(err), err
})

func checkParent(src, dst string) error {
	rel, err := filepath.Rel(src, dst)
	if err != nil {
		return err
	}

	rel = filepath.ToSlash(rel)
	if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." {
		return errors.ErrSourceIsParent
	}

	return nil
}

func addVersionSuffix(source string, fs afero.Fs) string {
	counter := 1
	dir, name := path.Split(source)
	ext := filepath.Ext(name)
	base := strings.TrimSuffix(name, ext)

	for {
		if _, err := fs.Stat(source); err != nil {
			break
		}
		renamed := fmt.Sprintf("%s(%d)%s", base, counter, ext)
		source = path.Join(dir, renamed)
		counter++
	}

	return source
}

func writeFile(fs afero.Fs, dst string, in io.Reader) (os.FileInfo, error) {
	dir, _ := path.Split(dst)
	err := fs.MkdirAll(dir, 0775)
	if err != nil {
		return nil, err
	}

	file, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	_, err = io.Copy(file, in)
	if err != nil {
		return nil, err
	}

	// Gets the info about the file.
	info, err := file.Stat()
	if err != nil {
		return nil, err
	}

	return info, nil
}

func delThumbs(ctx context.Context, fileCache FileCache, file *files.FileInfo) error {
	for _, previewSizeName := range PreviewSizeNames() {
		size, _ := ParsePreviewSize(previewSizeName)
		if err := fileCache.Delete(ctx, previewCacheKey(file.Path, file.ModTime.Unix(), size)); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>fix: check modify permission on file overwrite<commit_after>package http

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/spf13/afero"

	"github.com/filebrowser/filebrowser/v2/errors"
	"github.com/filebrowser/filebrowser/v2/files"
	"github.com/filebrowser/filebrowser/v2/fileutils"
)

var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
	file, err := files.NewFileInfo(files.FileOptions{
		Fs:         d.user.Fs,
		Path:       r.URL.Path,
		Modify:     d.user.Perm.Modify,
		Expand:     true,
		ReadHeader: d.server.TypeDetectionByHeader,
		Checker:    d,
	})
	if err != nil {
		return errToStatus(err), err
	}

	if file.IsDir {
		file.Listing.Sorting = d.user.Sorting
		file.Listing.ApplySort()
		return renderJSON(w, r, file)
	}

	if checksum := r.URL.Query().Get("checksum"); checksum != "" {
		err := file.Checksum(checksum)
		if err == errors.ErrInvalidOption {
			return http.StatusBadRequest, nil
		} else if err != nil {
			return http.StatusInternalServerError, err
		}

		// do not waste bandwidth if we just want the checksum
		file.Content = ""
	}

	return renderJSON(w, r, file)
})

func resourceDeleteHandler(fileCache FileCache) handleFunc {
	return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if r.URL.Path == "/" || !d.user.Perm.Delete {
			return http.StatusForbidden, nil
		}

		file, err := files.NewFileInfo(files.FileOptions{
			Fs:         d.user.Fs,
			Path:       r.URL.Path,
			Modify:     d.user.Perm.Modify,
			Expand:     true,
			ReadHeader: d.server.TypeDetectionByHeader,
			Checker:    d,
		})
		if err != nil {
			return errToStatus(err), err
		}

		// delete thumbnails
		err = delThumbs(r.Context(), fileCache, file)
		if err != nil {
			return errToStatus(err), err
		}

		err = d.RunHook(func() error {
			return d.user.Fs.RemoveAll(r.URL.Path)
		}, "delete", r.URL.Path, "", d.user)

		if err != nil {
			return errToStatus(err), err
		}

		return http.StatusOK, nil
	})
}

func resourcePostHandler(fileCache FileCache) handleFunc {
	return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if !d.user.Perm.Create || !d.Check(r.URL.Path) {
			return http.StatusForbidden, nil
		}

		defer func() {
			_, _ = io.Copy(ioutil.Discard, r.Body)
		}()

		// Directories creation on POST.
		if strings.HasSuffix(r.URL.Path, "/") {
			err := d.user.Fs.MkdirAll(r.URL.Path, 0775)
			return errToStatus(err), err
		}

		file, err := files.NewFileInfo(files.FileOptions{
			Fs:         d.user.Fs,
			Path:       r.URL.Path,
			Modify:     d.user.Perm.Modify,
			Expand:     true,
			ReadHeader: d.server.TypeDetectionByHeader,
			Checker:    d,
		})
		if err == nil {
			if r.URL.Query().Get("override") != "true" {
				return http.StatusConflict, nil
			}

			// Permission for overwriting the file
			if !d.user.Perm.Modify {
				return http.StatusForbidden, nil
			}

			err = delThumbs(r.Context(), fileCache, file)
			if err != nil {
				return errToStatus(err), err
			}
		}

		err = d.RunHook(func() error {
			info, writeErr := writeFile(d.user.Fs, r.URL.Path, r.Body)
			if writeErr != nil {
				return writeErr
			}

			etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
			w.Header().Set("ETag", etag)
			return nil
		}, "upload", r.URL.Path, "", d.user)

		if err != nil {
			_ = d.user.Fs.RemoveAll(r.URL.Path)
		}

		return errToStatus(err), err
	})
}

var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
	if !d.user.Perm.Modify || !d.Check(r.URL.Path) {
		return http.StatusForbidden, nil
	}

	defer func() {
		_, _ = io.Copy(ioutil.Discard, r.Body)
	}()

	// Only allow PUT for files.
	if strings.HasSuffix(r.URL.Path, "/") {
		return http.StatusMethodNotAllowed, nil
	}

	err := d.RunHook(func() error {
		info, writeErr := writeFile(d.user.Fs, r.URL.Path, r.Body)
		if writeErr != nil {
			return writeErr
		}

		etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
		w.Header().Set("ETag", etag)
		return nil
	}, "save", r.URL.Path, "", d.user)

	if err != nil {
		_ = d.user.Fs.RemoveAll(r.URL.Path)
	}

	return errToStatus(err), err
})

var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
	src := r.URL.Path
	dst := r.URL.Query().Get("destination")
	action := r.URL.Query().Get("action")
	dst, err := url.QueryUnescape(dst)
	if !d.Check(src) || !d.Check(dst) {
		return http.StatusForbidden, nil
	}
	if err != nil {
		return errToStatus(err), err
	}
	if dst == "/" || src == "/" {
		return http.StatusForbidden, nil
	}
	if err = checkParent(src, dst); err != nil {
		return http.StatusBadRequest, err
	}

	override := r.URL.Query().Get("override") == "true"
	rename := r.URL.Query().Get("rename") == "true"
	if !override && !rename {
		if _, err = d.user.Fs.Stat(dst); err == nil {
			return http.StatusConflict, nil
		}
	}
	if rename {
		dst = addVersionSuffix(dst, d.user.Fs)
	}

	// Permission for overwriting the file
	if override && !d.user.Perm.Modify {
		return http.StatusForbidden, nil
	}

	err = d.RunHook(func() error {
		switch action {
		// TODO: use enum
		case "copy":
			if !d.user.Perm.Create {
				return errors.ErrPermissionDenied
			}

			return fileutils.Copy(d.user.Fs, src, dst)
		case "rename":
			if !d.user.Perm.Rename {
				return errors.ErrPermissionDenied
			}
			src = path.Clean("/" + src)
			dst = path.Clean("/" + dst)

			return fileutils.MoveFile(d.user.Fs, src, dst)
		default:
			return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
		}
	}, action, src, dst, d.user)

	return errToStatus(err), err
})

func checkParent(src, dst string) error {
	rel, err := filepath.Rel(src, dst)
	if err != nil {
		return err
	}

	rel = filepath.ToSlash(rel)
	if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." {
		return errors.ErrSourceIsParent
	}

	return nil
}

func addVersionSuffix(source string, fs afero.Fs) string {
	counter := 1
	dir, name := path.Split(source)
	ext := filepath.Ext(name)
	base := strings.TrimSuffix(name, ext)

	for {
		if _, err := fs.Stat(source); err != nil {
			break
		}
		renamed := fmt.Sprintf("%s(%d)%s", base, counter, ext)
		source = path.Join(dir, renamed)
		counter++
	}

	return source
}

func writeFile(fs afero.Fs, dst string, in io.Reader) (os.FileInfo, error) {
	dir, _ := path.Split(dst)
	err := fs.MkdirAll(dir, 0775)
	if err != nil {
		return nil, err
	}

	file, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	_, err = io.Copy(file, in)
	if err != nil {
		return nil, err
	}

	// Gets the info about the file.
	info, err := file.Stat()
	if err != nil {
		return nil, err
	}

	return info, nil
}

func delThumbs(ctx context.Context, fileCache FileCache, file *files.FileInfo) error {
	for _, previewSizeName := range PreviewSizeNames() {
		size, _ := ParsePreviewSize(previewSizeName)
		if err := fileCache.Delete(ctx, previewCacheKey(file.Path, file.ModTime.Unix(), size)); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The clang-server 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 symbol

import (
	"path/filepath"
	"reflect"
	"time"

	"github.com/go-clang/v3.9/clang"
	flatbuffers "github.com/google/flatbuffers/go"
	"github.com/zchee/clang-server/symbol/internal/symbol"
)

// ----------------------------------------------------------------------------

// File represents a C/C++ source file.
//
// table File {
//   Name: string;
//   TranslationUnit: string;
//   Symbols: [Info];
//   Headers: [Header];
//   Includes: [string];
// }
type File struct {
	name            string
	translationUnit []byte
	locations       map[Location]ID
	symbols         map[ID]*Info
	headers         []*Header

	builder *flatbuffers.Builder

	file *symbol.File
}

func NewFile(name string) *File {
	return &File{
		name:      name,
		locations: make(map[Location]ID),
		symbols:   make(map[ID]*Info),
		builder:   flatbuffers.NewBuilder(0),
	}
}

func GetRootAsFile(buf []byte, offset flatbuffers.UOffsetT) *File {
	return &File{
		file: symbol.GetRootAsFile(buf, offset),
	}
}

func (f *File) Name() string {
	return string(f.file.Name())
}

func (f *File) TranslationUnit() []byte {
	return f.file.TranslationUnit()
}

func (f *File) Symbols() []*Info {
	n := f.file.SymbolsLength()
	symbols := make([]*Info, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Info)
		if f.file.Symbols(obj, i) {
			symbols[i] = &Info{info: obj}
		}
	}

	return symbols
}

func (f *File) Header() []*Header {
	n := f.file.HeadersLength()
	hedears := make([]*Header, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Header)
		if f.file.Headers(obj, i) {
			hedears[i] = &Header{header: obj}
		}
	}

	return hedears
}

func (f *File) AddTranslationUnit(buf []byte) {
	f.translationUnit = buf
}

// AddSymbol adds the symbol data into File.
func (f *File) addSymbol(loc, def Location) {
	id := ToID(loc.usr)

	sym, ok := f.symbols[id]
	if !ok {
		sym = &Info{id: id}
	}
	sym.decls = append(sym.decls, loc)

	if def.isExist() {
		sym.def = def
	}

	f.locations[loc] = id
	f.symbols[id] = sym
}

// AddDecl add decl data into File.
func (f *File) AddDecl(loc Location) {
	f.addSymbol(loc, Location{})
}

// AddDefinition add definition data into File.
func (f *File) AddDefinition(loc, def Location) {
	f.addSymbol(loc, def)
}

// notExistHeaderName return the not exist header name magic words.
func notExistHeaderName(headPath string) string {
	// adding magic to filename to not confuse it with real files
	return "IDoNotReallyExist-" + filepath.Base(headPath)
}

// AddHeader add header data into File.
func (f *File) AddHeader(includePath string, headerFile clang.File) {
	hdr := new(Header)
	if headerFile.Name() == "" {
		hdr.fileid = ToFileID(notExistHeaderName(filepath.Clean(headerFile.Name())))
		hdr.mtime = time.Now()
	} else {
		hdr.fileid = ToFileID(filepath.Clean(headerFile.Name()))
		hdr.mtime = headerFile.Time()
	}

	f.headers = append(f.headers, hdr)
}

// AddCaller add caller data into File.
func (f *File) AddCaller(sym, def Location, funcCall bool) {
	id := ToID(sym.usr)

	syms, ok := f.symbols[id]
	if !ok {
		syms = &Info{id: id}
	}

	syms.callers = append(syms.callers, &Caller{
		location: sym,
		funcCall: funcCall,
	})

	f.symbols[id] = syms
}

// Serialize serializes the File.
func (f *File) Serialize() *flatbuffers.Builder {
	fname := f.builder.CreateString(f.name)
	tu := f.builder.CreateByteString(f.translationUnit)

	hdrNum := len(f.headers)
	hdrOffsets := make([]flatbuffers.UOffsetT, 0, hdrNum)
	for _, hdr := range f.headers {
		hdrOffsets = append(hdrOffsets, hdr.serialize(f.builder))
	}
	symbol.FileStartHeadersVector(f.builder, hdrNum)
	for i := hdrNum - 1; i >= 0; i-- {
		f.builder.PrependUOffsetT(hdrOffsets[i])
	}
	headerVecOffset := f.builder.EndVector(hdrNum)

	symbolNum := len(f.symbols)
	symbolOffsets := make([]flatbuffers.UOffsetT, 0, symbolNum)
	for _, info := range f.symbols {
		symbolOffsets = append(symbolOffsets, info.serialize(f.builder))
	}
	symbol.FileStartSymbolsVector(f.builder, symbolNum)
	for i := symbolNum - 1; i >= 0; i-- {
		f.builder.PrependUOffsetT(symbolOffsets[i])
	}
	symbolVecOffset := f.builder.EndVector(symbolNum)

	symbol.FileStart(f.builder)
	symbol.FileAddName(f.builder, fname)
	symbol.FileAddTranslationUnit(f.builder, tu)
	symbol.FileAddSymbols(f.builder, symbolVecOffset)
	symbol.FileAddHeaders(f.builder, headerVecOffset)

	f.builder.Finish(symbol.FileEnd(f.builder))

	return f.builder
}

// ----------------------------------------------------------------------------

// Info represents a location of C/C++ cursor symbol information.
//
// table Info {
//   ID: string;
//   Decls: [Location];
//   Def: Location;
// }
type Info struct {
	id      ID
	decls   []Location
	def     Location
	callers []*Caller

	info *symbol.Info
}

// serialize serializes the Info.
func (info *Info) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	id := builder.CreateString(info.id.String())

	declsNum := len(info.decls)
	var declVecOffset flatbuffers.UOffsetT
	if declsNum > 0 {
		declsOffsets := make([]flatbuffers.UOffsetT, 0, declsNum)
		for _, decl := range info.decls {
			declsOffsets = append(declsOffsets, decl.serialize(builder))
		}
		symbol.InfoStartDeclsVector(builder, declsNum)
		for i := declsNum - 1; i >= 0; i-- {
			builder.PrependUOffsetT(declsOffsets[i])
		}
		declVecOffset = builder.EndVector(declsNum)
	}

	defOffset := info.def.serialize(builder)

	callersNum := len(info.callers)
	var callerVecOffset flatbuffers.UOffsetT
	if callersNum > 0 {
		callersOffsets := make([]flatbuffers.UOffsetT, 0, callersNum)
		for _, caller := range info.callers {
			callersOffsets = append(callersOffsets, caller.serialize(builder))
		}
		symbol.InfoStartCallersVector(builder, callersNum)
		for i := callersNum - 1; i >= 0; i-- {
			builder.PrependUOffsetT(callersOffsets[i])
		}
		callerVecOffset = builder.EndVector(callersNum)
	}

	symbol.InfoStart(builder)
	symbol.InfoAddID(builder, id)
	symbol.InfoAddDecls(builder, declVecOffset)
	symbol.InfoAddDef(builder, defOffset)
	symbol.InfoAddCallers(builder, callerVecOffset)

	return symbol.InfoEnd(builder)
}

func (info *Info) ID() ID {
	return ToID(string(info.info.ID()))
}

func (info *Info) Decls() []Location {
	n := info.info.DeclsLength()
	decls := make([]Location, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Location)
		if info.info.Decls(obj, i) {
			decls[i] = Location{location: obj}
		}
	}

	return decls
}

func (info *Info) Def() Location {
	obj := new(symbol.Location)
	info.info.Def(obj)

	return Location{location: obj}
}

func (info *Info) Callers() []*Caller {
	n := info.info.CallersLength()
	callers := make([]*Caller, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Caller)
		if info.info.Callers(obj, i) {
			callers[i] = &Caller{caller: obj}
		}
	}

	return callers
}

// ----------------------------------------------------------------------------

// Header represents a location of include header file.
//
// table Header {
//   FileID: string (id: 0, required, key); // -> []byte
//   Mtime: long (id: 1); // time.Time.Unix(): int64
// }
type Header struct {
	fileid FileID
	mtime  time.Time

	header *symbol.Header
}

func (h *Header) FileID() FileID {
	return ToFileID(string(h.header.FileID()))
}

func (h *Header) Mtime() int64 {
	return h.header.Mtime()
}

func (h *Header) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	fid := builder.CreateString(h.fileid.String())

	symbol.HeaderStart(builder)

	symbol.HeaderAddFileID(builder, fid)
	symbol.HeaderAddMtime(builder, h.mtime.Unix())

	return symbol.HeaderEnd(builder)
}

// ----------------------------------------------------------------------------

// Caller represents a location of caller function.
//
// table Caller {
//   Location: Location (required);
//   FuncCall: bool = false; // -> byte
// }
type Caller struct {
	location Location
	funcCall bool

	caller *symbol.Caller
}

func (c *Caller) Location() Location {
	obj := new(symbol.Location)
	c.caller.Location(obj)

	return Location{location: obj}
}

func (c *Caller) FuncCall() bool {
	return c.caller.FuncCall() != 0
}

func (c *Caller) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	locOffset := c.location.serialize(builder)

	symbol.CallerStart(builder)

	symbol.CallerAddLocation(builder, locOffset)
	funcCall := byte(0)
	if c.funcCall {
		funcCall = byte(1)
	}
	symbol.CallerAddFuncCall(builder, funcCall)

	return symbol.CallerEnd(builder)
}

// ----------------------------------------------------------------------------

// Location location of symbol.
// TODO(zchee): method receiver is pointer for location?
//
// table Location {
//   FileName: string;
//   Line: uint;
//   Col: uint = 0;
//   Offset: uint;
//   USR: string;
// }
type Location struct {
	fileName string
	line     uint32
	col      uint32
	offset   uint32
	usr      string

	location *symbol.Location
}

func (l *Location) FileName() string {
	if l.location == nil {
		return l.fileName
	}
	return string(l.location.FileName())
}

func (l *Location) Line() uint32 {
	return l.location.Line()
}

func (l *Location) Col() uint32 {
	return l.location.Col()
}

func (l *Location) Offset() uint32 {
	return l.location.Offset()
}

func (l *Location) USR() string {
	if l.location == nil {
		return l.usr
	}
	return string(l.location.USR())
}

func (l *Location) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	fname := builder.CreateString(l.fileName)
	usr := builder.CreateString(l.usr)

	symbol.LocationStart(builder)

	symbol.LocationAddFileName(builder, fname)
	symbol.LocationAddLine(builder, l.line)
	symbol.LocationAddCol(builder, l.col)
	symbol.LocationAddOffset(builder, l.offset)
	symbol.LocationAddUSR(builder, usr)

	return symbol.LocationEnd(builder)
}

// TODO(zchee): avoid reflection
func (l Location) isExist() bool {
	return !reflect.DeepEqual(l, Location{})
}
<commit_msg>symbol: change isExist reciever to pointer<commit_after>// Copyright 2016 The clang-server 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 symbol

import (
	"path/filepath"
	"reflect"
	"time"

	"github.com/go-clang/v3.9/clang"
	flatbuffers "github.com/google/flatbuffers/go"
	"github.com/zchee/clang-server/symbol/internal/symbol"
)

// ----------------------------------------------------------------------------

// File represents a C/C++ source file.
//
// table File {
//   Name: string;
//   TranslationUnit: string;
//   Symbols: [Info];
//   Headers: [Header];
//   Includes: [string];
// }
type File struct {
	name            string
	translationUnit []byte
	locations       map[Location]ID
	symbols         map[ID]*Info
	headers         []*Header

	builder *flatbuffers.Builder

	file *symbol.File
}

func NewFile(name string) *File {
	return &File{
		name:      name,
		locations: make(map[Location]ID),
		symbols:   make(map[ID]*Info),
		builder:   flatbuffers.NewBuilder(0),
	}
}

func GetRootAsFile(buf []byte, offset flatbuffers.UOffsetT) *File {
	return &File{
		file: symbol.GetRootAsFile(buf, offset),
	}
}

func (f *File) Name() string {
	return string(f.file.Name())
}

func (f *File) TranslationUnit() []byte {
	return f.file.TranslationUnit()
}

func (f *File) Symbols() []*Info {
	n := f.file.SymbolsLength()
	symbols := make([]*Info, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Info)
		if f.file.Symbols(obj, i) {
			symbols[i] = &Info{info: obj}
		}
	}

	return symbols
}

func (f *File) Header() []*Header {
	n := f.file.HeadersLength()
	hedears := make([]*Header, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Header)
		if f.file.Headers(obj, i) {
			hedears[i] = &Header{header: obj}
		}
	}

	return hedears
}

func (f *File) AddTranslationUnit(buf []byte) {
	f.translationUnit = buf
}

// AddSymbol adds the symbol data into File.
func (f *File) addSymbol(loc, def Location) {
	id := ToID(loc.usr)

	sym, ok := f.symbols[id]
	if !ok {
		sym = &Info{id: id}
	}
	sym.decls = append(sym.decls, loc)

	if def.isExist() {
		sym.def = def
	}

	f.locations[loc] = id
	f.symbols[id] = sym
}

// AddDecl add decl data into File.
func (f *File) AddDecl(loc Location) {
	f.addSymbol(loc, Location{})
}

// AddDefinition add definition data into File.
func (f *File) AddDefinition(loc, def Location) {
	f.addSymbol(loc, def)
}

// notExistHeaderName return the not exist header name magic words.
func notExistHeaderName(headPath string) string {
	// adding magic to filename to not confuse it with real files
	return "IDoNotReallyExist-" + filepath.Base(headPath)
}

// AddHeader add header data into File.
func (f *File) AddHeader(includePath string, headerFile clang.File) {
	hdr := new(Header)
	if headerFile.Name() == "" {
		hdr.fileid = ToFileID(notExistHeaderName(filepath.Clean(headerFile.Name())))
		hdr.mtime = time.Now()
	} else {
		hdr.fileid = ToFileID(filepath.Clean(headerFile.Name()))
		hdr.mtime = headerFile.Time()
	}

	f.headers = append(f.headers, hdr)
}

// AddCaller add caller data into File.
func (f *File) AddCaller(sym, def Location, funcCall bool) {
	id := ToID(sym.usr)

	syms, ok := f.symbols[id]
	if !ok {
		syms = &Info{id: id}
	}

	syms.callers = append(syms.callers, &Caller{
		location: sym,
		funcCall: funcCall,
	})

	f.symbols[id] = syms
}

// Serialize serializes the File.
func (f *File) Serialize() *flatbuffers.Builder {
	fname := f.builder.CreateString(f.name)
	tu := f.builder.CreateByteString(f.translationUnit)

	hdrNum := len(f.headers)
	hdrOffsets := make([]flatbuffers.UOffsetT, 0, hdrNum)
	for _, hdr := range f.headers {
		hdrOffsets = append(hdrOffsets, hdr.serialize(f.builder))
	}
	symbol.FileStartHeadersVector(f.builder, hdrNum)
	for i := hdrNum - 1; i >= 0; i-- {
		f.builder.PrependUOffsetT(hdrOffsets[i])
	}
	headerVecOffset := f.builder.EndVector(hdrNum)

	symbolNum := len(f.symbols)
	symbolOffsets := make([]flatbuffers.UOffsetT, 0, symbolNum)
	for _, info := range f.symbols {
		symbolOffsets = append(symbolOffsets, info.serialize(f.builder))
	}
	symbol.FileStartSymbolsVector(f.builder, symbolNum)
	for i := symbolNum - 1; i >= 0; i-- {
		f.builder.PrependUOffsetT(symbolOffsets[i])
	}
	symbolVecOffset := f.builder.EndVector(symbolNum)

	symbol.FileStart(f.builder)
	symbol.FileAddName(f.builder, fname)
	symbol.FileAddTranslationUnit(f.builder, tu)
	symbol.FileAddSymbols(f.builder, symbolVecOffset)
	symbol.FileAddHeaders(f.builder, headerVecOffset)

	f.builder.Finish(symbol.FileEnd(f.builder))

	return f.builder
}

// ----------------------------------------------------------------------------

// Info represents a location of C/C++ cursor symbol information.
//
// table Info {
//   ID: string;
//   Decls: [Location];
//   Def: Location;
// }
type Info struct {
	id      ID
	decls   []Location
	def     Location
	callers []*Caller

	info *symbol.Info
}

// serialize serializes the Info.
func (info *Info) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	id := builder.CreateString(info.id.String())

	declsNum := len(info.decls)
	var declVecOffset flatbuffers.UOffsetT
	if declsNum > 0 {
		declsOffsets := make([]flatbuffers.UOffsetT, 0, declsNum)
		for _, decl := range info.decls {
			declsOffsets = append(declsOffsets, decl.serialize(builder))
		}
		symbol.InfoStartDeclsVector(builder, declsNum)
		for i := declsNum - 1; i >= 0; i-- {
			builder.PrependUOffsetT(declsOffsets[i])
		}
		declVecOffset = builder.EndVector(declsNum)
	}

	defOffset := info.def.serialize(builder)

	callersNum := len(info.callers)
	var callerVecOffset flatbuffers.UOffsetT
	if callersNum > 0 {
		callersOffsets := make([]flatbuffers.UOffsetT, 0, callersNum)
		for _, caller := range info.callers {
			callersOffsets = append(callersOffsets, caller.serialize(builder))
		}
		symbol.InfoStartCallersVector(builder, callersNum)
		for i := callersNum - 1; i >= 0; i-- {
			builder.PrependUOffsetT(callersOffsets[i])
		}
		callerVecOffset = builder.EndVector(callersNum)
	}

	symbol.InfoStart(builder)
	symbol.InfoAddID(builder, id)
	symbol.InfoAddDecls(builder, declVecOffset)
	symbol.InfoAddDef(builder, defOffset)
	symbol.InfoAddCallers(builder, callerVecOffset)

	return symbol.InfoEnd(builder)
}

func (info *Info) ID() ID {
	return ToID(string(info.info.ID()))
}

func (info *Info) Decls() []Location {
	n := info.info.DeclsLength()
	decls := make([]Location, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Location)
		if info.info.Decls(obj, i) {
			decls[i] = Location{location: obj}
		}
	}

	return decls
}

func (info *Info) Def() Location {
	obj := new(symbol.Location)
	info.info.Def(obj)

	return Location{location: obj}
}

func (info *Info) Callers() []*Caller {
	n := info.info.CallersLength()
	callers := make([]*Caller, n)

	for i := 0; i < n; i++ {
		obj := new(symbol.Caller)
		if info.info.Callers(obj, i) {
			callers[i] = &Caller{caller: obj}
		}
	}

	return callers
}

// ----------------------------------------------------------------------------

// Header represents a location of include header file.
//
// table Header {
//   FileID: string (id: 0, required, key); // -> []byte
//   Mtime: long (id: 1); // time.Time.Unix(): int64
// }
type Header struct {
	fileid FileID
	mtime  time.Time

	header *symbol.Header
}

func (h *Header) FileID() FileID {
	return ToFileID(string(h.header.FileID()))
}

func (h *Header) Mtime() int64 {
	return h.header.Mtime()
}

func (h *Header) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	fid := builder.CreateString(h.fileid.String())

	symbol.HeaderStart(builder)

	symbol.HeaderAddFileID(builder, fid)
	symbol.HeaderAddMtime(builder, h.mtime.Unix())

	return symbol.HeaderEnd(builder)
}

// ----------------------------------------------------------------------------

// Caller represents a location of caller function.
//
// table Caller {
//   Location: Location (required);
//   FuncCall: bool = false; // -> byte
// }
type Caller struct {
	location Location
	funcCall bool

	caller *symbol.Caller
}

func (c *Caller) Location() Location {
	obj := new(symbol.Location)
	c.caller.Location(obj)

	return Location{location: obj}
}

func (c *Caller) FuncCall() bool {
	return c.caller.FuncCall() != 0
}

func (c *Caller) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	locOffset := c.location.serialize(builder)

	symbol.CallerStart(builder)

	symbol.CallerAddLocation(builder, locOffset)
	funcCall := byte(0)
	if c.funcCall {
		funcCall = byte(1)
	}
	symbol.CallerAddFuncCall(builder, funcCall)

	return symbol.CallerEnd(builder)
}

// ----------------------------------------------------------------------------

// Location location of symbol.
// TODO(zchee): method receiver is pointer for location?
//
// table Location {
//   FileName: string;
//   Line: uint;
//   Col: uint = 0;
//   Offset: uint;
//   USR: string;
// }
type Location struct {
	fileName string
	line     uint32
	col      uint32
	offset   uint32
	usr      string

	location *symbol.Location
}

func (l *Location) FileName() string {
	if l.location == nil {
		return l.fileName
	}
	return string(l.location.FileName())
}

func (l *Location) Line() uint32 {
	return l.location.Line()
}

func (l *Location) Col() uint32 {
	return l.location.Col()
}

func (l *Location) Offset() uint32 {
	return l.location.Offset()
}

func (l *Location) USR() string {
	if l.location == nil {
		return l.usr
	}
	return string(l.location.USR())
}

func (l *Location) serialize(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
	fname := builder.CreateString(l.fileName)
	usr := builder.CreateString(l.usr)

	symbol.LocationStart(builder)

	symbol.LocationAddFileName(builder, fname)
	symbol.LocationAddLine(builder, l.line)
	symbol.LocationAddCol(builder, l.col)
	symbol.LocationAddOffset(builder, l.offset)
	symbol.LocationAddUSR(builder, usr)

	return symbol.LocationEnd(builder)
}

// TODO(zchee): avoid reflection
func (l *Location) isExist() bool {
	return !reflect.DeepEqual(l, Location{})
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/vmware/photon-controller-cli/Godeps/_workspace/src/github.com/vmware/photon-controller-go-sdk/photon"
)

// Get limitsList from -limits/-l string flag
func parseLimitsListFromFlag(limits string) ([]photon.QuotaLineItem, error) {
	var limitsList []photon.QuotaLineItem
	if len(limits) != 0 {
		limitsListOri := regexp.MustCompile(`\s*,\s*`).Split(limits, -1)
		for i := 0; i < len(limitsListOri); i++ {
			limit := strings.Fields(limitsListOri[i])
			if len(limit) != 3 {
				return limitsList, fmt.Errorf("Error parsing limits, should be: <key> <value> <unit>, <key> <value> <unit>...")
			}

			key := limit[0]
			value, err := strconv.ParseFloat(limit[1], 64)
			if err != nil {
				return limitsList, fmt.Errorf("Error: %s. Please provide float as value", err.Error())
			}
			unit := limit[2]

			limitsList = append(limitsList, photon.QuotaLineItem{Key: key, Value: value, Unit: unit})
		}
	}
	return limitsList, nil
}

// Get affinitiesList from -affinities/-a string flag
func parseAffinitiesListFromFlag(affinities string) ([]photon.LocalitySpec, error) {
	var affinitiesList []photon.LocalitySpec
	if len(affinities) != 0 {
		affinitiesListOri := regexp.MustCompile(`\s*,\s*`).Split(affinities, -1)
		for i := 0; i < len(affinitiesListOri); i++ {
			affinity := regexp.MustCompile(`\s*:\s*`).Split(affinitiesListOri[i], -1)
			if len(affinity) != 2 {
				return affinitiesList, fmt.Errorf("Error parsing affinities, should be: <kind>:<id>, <kind>:<id>...")
			}

			kind := affinity[0]
			id := affinity[1]

			affinitiesList = append(affinitiesList, photon.LocalitySpec{Kind: kind, ID: id})
		}
	}
	return affinitiesList, nil
}

// Get disksList from -disks/-d string flag
func parseDisksListFromFlag(disks string) ([]photon.AttachedDisk, error) {
	var disksList []photon.AttachedDisk
	if len(disks) != 0 {
		disksListOri := regexp.MustCompile(`\s*,\s*`).Split(disks, -1)
		for i := 0; i < len(disksListOri); i++ {
			disk := strings.Fields(disksListOri[i])
			if len(disk) != 3 {
				return disksList, fmt.Errorf("Error parsing disks, should be: <name> <flavor> <boot=true/capacity>...")
			}

			name := disk[0]
			flavor := disk[1]
			if disk[2] == "boot=true" {
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: true})
			} else {
				capacity, err := strconv.Atoi(disk[2])
				if err != nil {
					return disksList, err
				}
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: false, CapacityGB: capacity})
			}
		}
	}
	return disksList, nil
}

// Get environment Map from -environment/-e string flag
func parseMapFromFlag(cmdFlag string) (map[string]string, error) {
	newMap := make(map[string]string)
	if len(cmdFlag) != 0 {
		entries := regexp.MustCompile(`\s*,\s*`).Split(cmdFlag, -1)
		for i := 0; i < len(entries); i++ {
			entry := regexp.MustCompile(`\s*:\s*`).Split(entries[i], -1)
			if len(entry) != 2 {
				return newMap, fmt.Errorf("Error parsing the command flag, should be: <key>:<value>, <key>:<value>...")
			}

			key := entry[0]
			value := entry[1]

			newMap[key] = value
		}
	}
	return newMap, nil
}
<commit_msg>Fix vsan datastore affinity parse error<commit_after>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/vmware/photon-controller-cli/Godeps/_workspace/src/github.com/vmware/photon-controller-go-sdk/photon"
)

// Get limitsList from -limits/-l string flag
func parseLimitsListFromFlag(limits string) ([]photon.QuotaLineItem, error) {
	var limitsList []photon.QuotaLineItem
	if len(limits) != 0 {
		limitsListOri := regexp.MustCompile(`\s*,\s*`).Split(limits, -1)
		for i := 0; i < len(limitsListOri); i++ {
			limit := strings.Fields(limitsListOri[i])
			if len(limit) != 3 {
				return limitsList, fmt.Errorf("Error parsing limits, should be: <key> <value> <unit>, <key> <value> <unit>...")
			}

			key := limit[0]
			value, err := strconv.ParseFloat(limit[1], 64)
			if err != nil {
				return limitsList, fmt.Errorf("Error: %s. Please provide float as value", err.Error())
			}
			unit := limit[2]

			limitsList = append(limitsList, photon.QuotaLineItem{Key: key, Value: value, Unit: unit})
		}
	}
	return limitsList, nil
}

// Get affinitiesList from -affinities/-a string flag
func parseAffinitiesListFromFlag(affinities string) ([]photon.LocalitySpec, error) {
	var affinitiesList []photon.LocalitySpec
	if len(affinities) != 0 {
		affinitiesListOri := regexp.MustCompile(`\s*,\s*`).Split(affinities, -1)
		for i := 0; i < len(affinitiesListOri); i++ {
			affinity := regexp.MustCompile(`\s*:\s*`).Split(affinitiesListOri[i], 2)
			if len(affinity) != 2 {
				return affinitiesList, fmt.Errorf("Error parsing affinities, should be: <kind>:<id>, <kind>:<id>...")
			}

			kind := affinity[0]
			id := affinity[1]

			affinitiesList = append(affinitiesList, photon.LocalitySpec{Kind: kind, ID: id})
		}
	}
	return affinitiesList, nil
}

// Get disksList from -disks/-d string flag
func parseDisksListFromFlag(disks string) ([]photon.AttachedDisk, error) {
	var disksList []photon.AttachedDisk
	if len(disks) != 0 {
		disksListOri := regexp.MustCompile(`\s*,\s*`).Split(disks, -1)
		for i := 0; i < len(disksListOri); i++ {
			disk := strings.Fields(disksListOri[i])
			if len(disk) != 3 {
				return disksList, fmt.Errorf("Error parsing disks, should be: <name> <flavor> <boot=true/capacity>...")
			}

			name := disk[0]
			flavor := disk[1]
			if disk[2] == "boot=true" {
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: true})
			} else {
				capacity, err := strconv.Atoi(disk[2])
				if err != nil {
					return disksList, err
				}
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: false, CapacityGB: capacity})
			}
		}
	}
	return disksList, nil
}

// Get environment Map from -environment/-e string flag
func parseMapFromFlag(cmdFlag string) (map[string]string, error) {
	newMap := make(map[string]string)
	if len(cmdFlag) != 0 {
		entries := regexp.MustCompile(`\s*,\s*`).Split(cmdFlag, -1)
		for i := 0; i < len(entries); i++ {
			entry := regexp.MustCompile(`\s*:\s*`).Split(entries[i], -1)
			if len(entry) != 2 {
				return newMap, fmt.Errorf("Error parsing the command flag, should be: <key>:<value>, <key>:<value>...")
			}

			key := entry[0]
			value := entry[1]

			newMap[key] = value
		}
	}
	return newMap, nil
}
<|endoftext|>


<commit_before>package catTracks

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/asim/quadtree"
	"github.com/boltdb/bolt"
	"github.com/deet/simpleline"
	"github.com/rotblauer/trackpoints/trackPoint"
	"sort"
	"strings"
	"time"
)

const (
	testesPrefix = "testes-------"
)

var testes = false

// SetTestes run
func SetTestes(flagger bool) {
	testes = flagger
}
func getTestesPrefix() string {
	if testes {
		return testesPrefix
	}
	return ""
}

//Store a snippit of life

// itob returns an 8-byte big endian representation of v.
func itob(v int64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(v))
	return b
}

func storePoints(trackPoints trackPoint.TrackPoints) error {
	var err error
	for _, point := range trackPoints {
		err = storePoint(point)
		if err != nil {
			return err
		}
	}
	return err
}

func storePoint(tp trackPoint.TrackPoint) error {

	var err error
	if tp.Time.IsZero() {
		tp.Time = time.Now()
	}

	go func() {
		GetDB().Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(trackKey))

			// id, _ := b.NextSequence()
			// trackPoint.ID = int(id)
			tp.ID = tp.Time.UnixNano() //dunno if can really get nanoy, or if will just *1000.
			if exists := b.Get(itob(tp.ID)); exists != nil {
				// make sure it's ours
				var existingTrackpoint trackPoint.TrackPoint
				e := json.Unmarshal(exists, &existingTrackpoint)
				if e != nil {
					fmt.Println("Checking on an existing trackpoint and got an error with one of the existing trackpoints unmarshaling.")
				}
				if existingTrackpoint.Name == tp.Name {
					fmt.Println("Got that trackpoint already. Breaking.")
					return nil
				}
			}
			// gets "" case nontestesing
			tp.Name = getTestesPrefix() + tp.Name

			trackPointJSON, err := json.Marshal(tp)
			if err != nil {
				return err
			}
			err = b.Put(itob(tp.ID), trackPointJSON)
			if err != nil {
				fmt.Println("Didn't save post trackPoint in bolt.", err)
				return err
			}
			fmt.Println("Saved trackpoint: ", tp)
			return nil
		})
	}()

	if err != nil {
		fmt.Println(err)
	}
	return err
}

// DeleteTestes wipes the entire database of all points with names prefixed with testes prefix. Saves an rm keystorke
func DeleteTestes() error {
	e := GetDB().Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(trackKey))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var tp trackPoint.TrackPoint
			e := json.Unmarshal(v, &tp)
			if e != nil {
				fmt.Println("Error deleting testes.")
				return e
			}
			if strings.HasPrefix(tp.Name, testesPrefix) {
				b.Delete(k)
			}
		}
		return nil
	})
	return e
}

// DeleteSpain deletes spain
func DeleteSpain() error {
	e := GetDB().Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("tracks"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var tp trackPoint.TrackPoint
			e := json.Unmarshal(v, &tp)
			if e != nil {
				fmt.Println("Error deleting testes.")
				return e
			}
			if tp.Lng < 12.0 && tp.Lng > -10.0 {
				b.Delete(k)
			}
		}
		return nil
	})
	return e
}

//TODO make queryable ala which cat when
func getAllPoints(query *query) ([]*trackPoint.TrackPoint, error) {

	var err error
	var coords []simpleline.Point

	if query != nil && query.IsBounded() {
		//build aabb rect
		var center = make(map[string]float64)
		//not totally sure what halfpoint means but best guess
		center["lat"] = (query.Bounds.NorthEastLat + query.Bounds.SouthWestLat) / 2.0
		center["lng"] = (query.Bounds.NorthEastLng + query.Bounds.SouthWestLng) / 2.0
		cp := quadtree.NewPoint(center["lat"], center["lng"], nil)
		half := trackPoint.Distance(center["lat"], center["lng"], center["lat"], query.Bounds.NorthEastLng)
		hp := cp.HalfPoint(half)
		ab := quadtree.NewAABB(cp, hp)
		//res = GetQT.Search(aabb)
		qres := GetQT().Search(ab)
		//for range res = coords append res[i].data
		//TODO check gainst other query params
		for _, val := range qres {
			coords = append(coords, val.Data().(*trackPoint.TrackPoint))
		}
	} else {

		//TODO make this not what it was

		err = GetDB().View(func(tx *bolt.Tx) error {
			var err error
			b := tx.Bucket([]byte(trackKey))

			// can swap out for- eacher if we figure indexing, or even want it
			b.ForEach(func(trackPointKey, trackPointVal []byte) error {

				var trackPointCurrent trackPoint.TrackPoint
				json.Unmarshal(trackPointVal, &trackPointCurrent)

				coords = append(coords, &trackPointCurrent)
				return nil

			})

			return err
		})

	}

	//? but why is there a null pointer error? how is the func being passed a nil query?
	var epsilon float64
	if query != nil {
		epsilon = query.Epsilon // just so we can separate incoming queryEps and wiggled-to Eps
	} else {
		epsilon = DefaultEpsilon // to default const
	}
	//simpleify line
	// results, sErr := simpleline.RDP(coords, 5, simpleline.Euclidean, true)
	originalCount := len(coords)
	results, err := simpleline.RDP(coords, epsilon, simpleline.Euclidean, true) //0.001 bring a 5700pt run to prox 300 (.001 scale is lat and lng)
	if err != nil {
		fmt.Println("Errrrrrr", err)
		results = coords // return coords, err //better dan nuttin //but not sure want to return the err...
	}
	fmt.Println("eps: ", epsilon)
	fmt.Println("  results: ", len(results))

	var l int
	if query != nil {
		l = query.Limit
	} else {
		l = DefaultLimit
	}
	//just a hacky shot at wiggler. pointslimits -> query eventually?
	for len(results) > l {
		epsilon = epsilon + epsilon/(1-epsilon)
		fmt.Println("wiggling eps: ", epsilon)
		//or could do with results stead of coords?
		results, err = simpleline.RDP(coords, epsilon, simpleline.Euclidean, true)
		if err != nil {
			fmt.Println("Errrrrrr", err)
			results = coords
			continue
		}
		fmt.Println("  results: ", len(results))
	}

	var tps trackPoint.TPs
	for _, insult := range results {
		o, ok := insult.(*trackPoint.TrackPoint)
		if !ok {
			fmt.Println("shittt notok")
		}
		// could send channeler??
		tps = append(tps, o)
	}

	fmt.Println("Serving points.")
	fmt.Println("Original total points: ", originalCount)
	fmt.Println("post-RDP-wiggling point: ", len(results))

	sort.Sort(tps)

	return tps, err
}

//TODO make queryable ala which cat when
// , channel chan *trackPoint.TrackPoint
func socketPointsByQuery(query *query) (trackPoint.TPs, error) {

	var err error
	var coords []simpleline.Point

	if query != nil && query.IsBounded() {
		//build aabb rect
		var center = make(map[string]float64)
		//not totally sure what halfpoint means but best guess
		center["lat"] = (query.Bounds.NorthEastLat + query.Bounds.SouthWestLat) / 2.0
		center["lng"] = (query.Bounds.NorthEastLng + query.Bounds.SouthWestLng) / 2.0
		cp := quadtree.NewPoint(center["lat"], center["lng"], nil)
		half := trackPoint.Distance(center["lat"], center["lng"], center["lat"], query.Bounds.NorthEastLng)
		hp := cp.HalfPoint(half)
		ab := quadtree.NewAABB(cp, hp)
		//res = GetQT.Search(aabb)
		qres := GetQT().Search(ab)
		//for range res = coords append res[i].data
		//TODO check gainst other query params
		fmt.Println("server quad res length: ", len(qres))
		for _, val := range qres {
			coords = append(coords, val.Data().(*trackPoint.TrackPoint))
		}
	} else {

		//TODO make this not what it was

		err = GetDB().View(func(tx *bolt.Tx) error {
			var err error
			b := tx.Bucket([]byte(trackKey))

			// can swap out for- eacher if we figure indexing, or even want it
			b.ForEach(func(trackPointKey, trackPointVal []byte) error {

				var trackPointCurrent trackPoint.TrackPoint
				json.Unmarshal(trackPointVal, &trackPointCurrent)

				coords = append(coords, &trackPointCurrent)
				return nil

			})

			return err
		})

	}

	//? but why is there a null pointer error? how is the func being passed a nil query?
	var epsilon float64
	if query != nil {
		epsilon = query.Epsilon // just so we can separate incoming queryEps and wiggled-to Eps
	} else {
		epsilon = DefaultEpsilon // to default const
	}
	//simpleify line
	// results, sErr := simpleline.RDP(coords, 5, simpleline.Euclidean, true)
	originalCount := len(coords)
	results, err := simpleline.RDP(coords, epsilon, simpleline.Euclidean, true) //0.001 bring a 5700pt run to prox 300 (.001 scale is lat and lng)
	if err != nil {
		fmt.Println("Errrrrrr", err)
		results = coords // return coords, err //better dan nuttin //but not sure want to return the err...
	}
	fmt.Println("eps: ", epsilon)
	fmt.Println("  results: ", len(results))

	var l int
	if query != nil {
		l = query.Limit
	} else {
		l = DefaultLimit
	}
	//just a hacky shot at wiggler. pointslimits -> query eventually?
	for len(results) > l {
		epsilon = epsilon + epsilon/(1-epsilon)
		fmt.Println("wiggling eps: ", epsilon)
		//or could do with results stead of coords?
		results, err = simpleline.RDP(coords, epsilon, simpleline.Euclidean, true)
		if err != nil {
			fmt.Println("Errrrrrr", err)
			results = coords
			continue
		}
		fmt.Println("  results: ", len(results))
	}

	var tps trackPoint.TPs
	for _, insult := range results {
		o, ok := insult.(*trackPoint.TrackPoint)
		if !ok {
			fmt.Println("shittt notok")
		}
		// could send channeler??
		tps = append(tps, o)

	}

	fmt.Println("Serving points.")
	fmt.Println("Original total points: ", originalCount)
	fmt.Println("post-RDP-wiggling point: ", len(results))

	sort.Sort(tps)

	return tps, err
}
<commit_msg>insert points to qt on store points. fixes #7<commit_after>package catTracks

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/asim/quadtree"
	"github.com/boltdb/bolt"
	"github.com/deet/simpleline"
	"github.com/rotblauer/trackpoints/trackPoint"
	"sort"
	"strings"
	"time"
)

const (
	testesPrefix = "testes-------"
)

var testes = false

// SetTestes run
func SetTestes(flagger bool) {
	testes = flagger
}
func getTestesPrefix() string {
	if testes {
		return testesPrefix
	}
	return ""
}

//Store a snippit of life

// itob returns an 8-byte big endian representation of v.
func itob(v int64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(v))
	return b
}

func storePoints(trackPoints trackPoint.TrackPoints) error {
	var err error
	for _, point := range trackPoints {
		err = storePoint(point)
		if err != nil {
			return err
		}
	}
	return err
}

func storePoint(tp trackPoint.TrackPoint) error {

	var err error
	if tp.Time.IsZero() {
		tp.Time = time.Now()
	}

	go func() {
		GetDB().Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(trackKey))

			// id, _ := b.NextSequence()
			// trackPoint.ID = int(id)
			tp.ID = tp.Time.UnixNano() //dunno if can really get nanoy, or if will just *1000.
			if exists := b.Get(itob(tp.ID)); exists != nil {
				// make sure it's ours
				var existingTrackpoint trackPoint.TrackPoint
				e := json.Unmarshal(exists, &existingTrackpoint)
				if e != nil {
					fmt.Println("Checking on an existing trackpoint and got an error with one of the existing trackpoints unmarshaling.")
				}
				if existingTrackpoint.Name == tp.Name {
					fmt.Println("Got that trackpoint already. Breaking.")
					return nil
				}
			}
			// gets "" case nontestesing
			tp.Name = getTestesPrefix() + tp.Name

			trackPointJSON, err := json.Marshal(tp)
			if err != nil {
				return err
			}
			err = b.Put(itob(tp.ID), trackPointJSON)
			if err != nil {
				fmt.Println("Didn't save post trackPoint in bolt.", err)
				return err
			}
			p := quadtree.NewPoint(tp.Lat, tp.Lng, &tp)
			if !GetQT().Insert(p) {
				fmt.Println("Couldn't add to quadtree: ", p)
			}
			fmt.Println("Saved trackpoint: ", tp)
			return nil
		})
	}()

	if err != nil {
		fmt.Println(err)
	}
	return err
}

// DeleteTestes wipes the entire database of all points with names prefixed with testes prefix. Saves an rm keystorke
func DeleteTestes() error {
	e := GetDB().Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(trackKey))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var tp trackPoint.TrackPoint
			e := json.Unmarshal(v, &tp)
			if e != nil {
				fmt.Println("Error deleting testes.")
				return e
			}
			if strings.HasPrefix(tp.Name, testesPrefix) {
				b.Delete(k)
			}
		}
		return nil
	})
	return e
}

// DeleteSpain deletes spain
func DeleteSpain() error {
	e := GetDB().Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("tracks"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var tp trackPoint.TrackPoint
			e := json.Unmarshal(v, &tp)
			if e != nil {
				fmt.Println("Error deleting testes.")
				return e
			}
			if tp.Lng < 12.0 && tp.Lng > -10.0 {
				b.Delete(k)
			}
		}
		return nil
	})
	return e
}

//TODO make queryable ala which cat when
func getAllPoints(query *query) ([]*trackPoint.TrackPoint, error) {

	var err error
	var coords []simpleline.Point

	if query != nil && query.IsBounded() {
		//build aabb rect
		var center = make(map[string]float64)
		//not totally sure what halfpoint means but best guess
		center["lat"] = (query.Bounds.NorthEastLat + query.Bounds.SouthWestLat) / 2.0
		center["lng"] = (query.Bounds.NorthEastLng + query.Bounds.SouthWestLng) / 2.0
		cp := quadtree.NewPoint(center["lat"], center["lng"], nil)
		half := trackPoint.Distance(center["lat"], center["lng"], center["lat"], query.Bounds.NorthEastLng)
		hp := cp.HalfPoint(half)
		ab := quadtree.NewAABB(cp, hp)
		//res = GetQT.Search(aabb)
		qres := GetQT().Search(ab)
		//for range res = coords append res[i].data
		//TODO check gainst other query params
		for _, val := range qres {
			coords = append(coords, val.Data().(*trackPoint.TrackPoint))
		}
	} else {

		//TODO make this not what it was

		err = GetDB().View(func(tx *bolt.Tx) error {
			var err error
			b := tx.Bucket([]byte(trackKey))

			// can swap out for- eacher if we figure indexing, or even want it
			b.ForEach(func(trackPointKey, trackPointVal []byte) error {

				var trackPointCurrent trackPoint.TrackPoint
				json.Unmarshal(trackPointVal, &trackPointCurrent)

				coords = append(coords, &trackPointCurrent)
				return nil

			})

			return err
		})

	}

	//? but why is there a null pointer error? how is the func being passed a nil query?
	var epsilon float64
	if query != nil {
		epsilon = query.Epsilon // just so we can separate incoming queryEps and wiggled-to Eps
	} else {
		epsilon = DefaultEpsilon // to default const
	}
	//simpleify line
	// results, sErr := simpleline.RDP(coords, 5, simpleline.Euclidean, true)
	originalCount := len(coords)
	results, err := simpleline.RDP(coords, epsilon, simpleline.Euclidean, true) //0.001 bring a 5700pt run to prox 300 (.001 scale is lat and lng)
	if err != nil {
		fmt.Println("Errrrrrr", err)
		results = coords // return coords, err //better dan nuttin //but not sure want to return the err...
	}
	fmt.Println("eps: ", epsilon)
	fmt.Println("  results: ", len(results))

	var l int
	if query != nil {
		l = query.Limit
	} else {
		l = DefaultLimit
	}
	//just a hacky shot at wiggler. pointslimits -> query eventually?
	for len(results) > l {
		epsilon = epsilon + epsilon/(1-epsilon)
		fmt.Println("wiggling eps: ", epsilon)
		//or could do with results stead of coords?
		results, err = simpleline.RDP(coords, epsilon, simpleline.Euclidean, true)
		if err != nil {
			fmt.Println("Errrrrrr", err)
			results = coords
			continue
		}
		fmt.Println("  results: ", len(results))
	}

	var tps trackPoint.TPs
	for _, insult := range results {
		o, ok := insult.(*trackPoint.TrackPoint)
		if !ok {
			fmt.Println("shittt notok")
		}
		// could send channeler??
		tps = append(tps, o)
	}

	fmt.Println("Serving points.")
	fmt.Println("Original total points: ", originalCount)
	fmt.Println("post-RDP-wiggling point: ", len(results))

	sort.Sort(tps)

	return tps, err
}

//TODO make queryable ala which cat when
// , channel chan *trackPoint.TrackPoint
func socketPointsByQuery(query *query) (trackPoint.TPs, error) {

	var err error
	var coords []simpleline.Point

	if query != nil && query.IsBounded() {
		//build aabb rect
		var center = make(map[string]float64)
		//not totally sure what halfpoint means but best guess
		center["lat"] = (query.Bounds.NorthEastLat + query.Bounds.SouthWestLat) / 2.0
		center["lng"] = (query.Bounds.NorthEastLng + query.Bounds.SouthWestLng) / 2.0
		cp := quadtree.NewPoint(center["lat"], center["lng"], nil)
		half := trackPoint.Distance(center["lat"], center["lng"], center["lat"], query.Bounds.NorthEastLng)
		hp := cp.HalfPoint(half)
		ab := quadtree.NewAABB(cp, hp)
		//res = GetQT.Search(aabb)
		qres := GetQT().Search(ab)
		//for range res = coords append res[i].data
		//TODO check gainst other query params
		fmt.Println("server quad res length: ", len(qres))
		for _, val := range qres {
			coords = append(coords, val.Data().(*trackPoint.TrackPoint))
		}
	} else {

		//TODO make this not what it was

		err = GetDB().View(func(tx *bolt.Tx) error {
			var err error
			b := tx.Bucket([]byte(trackKey))

			// can swap out for- eacher if we figure indexing, or even want it
			b.ForEach(func(trackPointKey, trackPointVal []byte) error {

				var trackPointCurrent trackPoint.TrackPoint
				json.Unmarshal(trackPointVal, &trackPointCurrent)

				coords = append(coords, &trackPointCurrent)
				return nil

			})

			return err
		})

	}

	//? but why is there a null pointer error? how is the func being passed a nil query?
	var epsilon float64
	if query != nil {
		epsilon = query.Epsilon // just so we can separate incoming queryEps and wiggled-to Eps
	} else {
		epsilon = DefaultEpsilon // to default const
	}
	//simpleify line
	// results, sErr := simpleline.RDP(coords, 5, simpleline.Euclidean, true)
	originalCount := len(coords)
	results, err := simpleline.RDP(coords, epsilon, simpleline.Euclidean, true) //0.001 bring a 5700pt run to prox 300 (.001 scale is lat and lng)
	if err != nil {
		fmt.Println("Errrrrrr", err)
		results = coords // return coords, err //better dan nuttin //but not sure want to return the err...
	}
	fmt.Println("eps: ", epsilon)
	fmt.Println("  results: ", len(results))

	var l int
	if query != nil {
		l = query.Limit
	} else {
		l = DefaultLimit
	}
	//just a hacky shot at wiggler. pointslimits -> query eventually?
	for len(results) > l {
		epsilon = epsilon + epsilon/(1-epsilon)
		fmt.Println("wiggling eps: ", epsilon)
		//or could do with results stead of coords?
		results, err = simpleline.RDP(coords, epsilon, simpleline.Euclidean, true)
		if err != nil {
			fmt.Println("Errrrrrr", err)
			results = coords
			continue
		}
		fmt.Println("  results: ", len(results))
	}

	var tps trackPoint.TPs
	for _, insult := range results {
		o, ok := insult.(*trackPoint.TrackPoint)
		if !ok {
			fmt.Println("shittt notok")
		}
		// could send channeler??
		tps = append(tps, o)

	}

	fmt.Println("Serving points.")
	fmt.Println("Original total points: ", originalCount)
	fmt.Println("post-RDP-wiggling point: ", len(results))

	sort.Sort(tps)

	return tps, err
}
<|endoftext|>


<commit_before>package http

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/influxdata/platform"
	pctx "github.com/influxdata/platform/context"
	"github.com/influxdata/platform/kit/errors"
	"github.com/julienschmidt/httprouter"
)

// TelegrafHandler is the handler for the telegraf service
type TelegrafHandler struct {
	*httprouter.Router

	TelegrafService            platform.TelegrafConfigStore
	UserResourceMappingService platform.UserResourceMappingService
}

const (
	telegrafsPath            = "/api/v2/telegrafs"
	telegrafsIDPath          = "/api/v2/telegrafs/:id"
	telegrafsIDMembersPath   = "/api/v2/telegrafs/:id/members"
	telegrafsIDMembersIDPath = "/api/v2/telegrafs/:id/members/:userID"
	telegrafsIDOwnersPath    = "/api/v2/telegrafs/:id/owners"
	telegrafsIDOwnersIDPath  = "/api/v2/telegrafs/:id/owners/:userID"
)

// NewTelegrafHandler returns a new instance of TelegrafHandler.
func NewTelegrafHandler(mappingService platform.UserResourceMappingService) *TelegrafHandler {
	h := &TelegrafHandler{
		Router:                     httprouter.New(),
		UserResourceMappingService: mappingService,
	}
	h.HandlerFunc("POST", telegrafsPath, h.handlePostTelegraf)
	h.HandlerFunc("GET", telegrafsPath, h.handleGetTelegrafs)
	h.HandlerFunc("GET", telegrafsIDPath, h.handleGetTelegraf)
	h.HandlerFunc("DELETE", telegrafsIDPath, h.handleDeleteTelegraf)
	h.HandlerFunc("PUT", telegrafsIDPath, h.handlePutTelegraf)

	h.HandlerFunc("POST", telegrafsIDMembersIDPath, newPostMemberHandler(h.UserResourceMappingService, platform.TelegrafResourceType, platform.Member))
	h.HandlerFunc("GET", telegrafsIDMembersIDPath, newGetMembersHandler(h.UserResourceMappingService, platform.Member))
	h.HandlerFunc("DELETE", telegrafsIDMembersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Member))

	h.HandlerFunc("POST", telegrafsIDOwnersPath, newPostMemberHandler(h.UserResourceMappingService, platform.TelegrafResourceType, platform.Owner))
	h.HandlerFunc("GET", telegrafsIDOwnersPath, newGetMembersHandler(h.UserResourceMappingService, platform.Owner))
	h.HandlerFunc("DELETE", telegrafsIDOwnersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Owner))

	return h
}

type telegrafLinks struct {
	Self  string `json:"self"`
	Cells string `json:"cells"`
}

type link struct {
	Self string `json:"self"`
}

type telegrafResponse struct {
	*platform.TelegrafConfig
	Links link `json:"links"`
}

type telegrafResponses struct {
	TelegrafConfigs []telegrafResponse `json:"configurations"`
}

func newTelegrafResponse(tc *platform.TelegrafConfig) telegrafResponse {
	return telegrafResponse{
		TelegrafConfig: tc,
		Links: link{
			Self: fmt.Sprintf("/api/v2/telegrafs/%s", tc.ID.String()),
		},
	}
}

func newTelegrafResponses(tcs []*platform.TelegrafConfig) telegrafResponses {
	resp := telegrafResponses{
		TelegrafConfigs: make([]telegrafResponse, len(tcs)),
	}
	for i, c := range tcs {
		resp.TelegrafConfigs[i] = newTelegrafResponse(c)
	}
	return resp
}

func decodeGetTelegrafRequest(ctx context.Context, r *http.Request) (i platform.ID, err error) {
	params := httprouter.ParamsFromContext(ctx)
	id := params.ByName("id")
	if id == "" {
		return i, errors.InvalidDataf("url missing id")
	}

	if err := i.DecodeFromString(id); err != nil {
		return i, err
	}
	return i, nil
}

func (h *TelegrafHandler) handleGetTelegrafs(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	filter, err := decodeUserResourceMappingFilter(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	tcs, _, err := h.TelegrafService.FindTelegrafConfigs(ctx, *filter)
	if err != nil {
		if err == platform.ErrViewNotFound {
			err = errors.New(err.Error(), errors.NotFound)
		}
		EncodeError(ctx, err, w)
		return
	}
	if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponses(tcs)); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}

func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	id, err := decodeGetTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	tc, err := h.TelegrafService.FindTelegrafConfigByID(ctx, id)
	if err != nil {
		if err == platform.ErrViewNotFound {
			err = errors.New(err.Error(), errors.NotFound)
		}
		EncodeError(ctx, err, w)
		return
	}

	contentType := r.Header.Get("Content-Type")
	switch contentType {
	case "application/octet-stream":
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.toml\"", tc.Name))
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(tc.TOML()))
	case "application/toml":
		w.Header().Set("Content-Type", "application/toml; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(tc.TOML()))
	case "application/json":
		fallthrough
	default:
		if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc)); err != nil {
			EncodeError(ctx, err, w)
			return
		}
	}
}

func decodeUserResourceMappingFilter(ctx context.Context, r *http.Request) (*platform.UserResourceMappingFilter, error) {
	urm := new(platform.UserResourceMappingFilter)
	err := json.NewDecoder(r.Body).Decode(urm)
	return urm, err
}

func decodePostTelegrafRequest(ctx context.Context, r *http.Request) (*platform.TelegrafConfig, error) {
	tc := new(platform.TelegrafConfig)
	err := json.NewDecoder(r.Body).Decode(tc)
	return tc, err
}

func decodePutTelegrafRequest(ctx context.Context, r *http.Request) (*platform.TelegrafConfig, error) {
	tc := new(platform.TelegrafConfig)
	if err := json.NewDecoder(r.Body).Decode(tc); err != nil {
		return nil, err
	}
	params := httprouter.ParamsFromContext(ctx)
	id := params.ByName("id")
	if id == "" {
		return nil, errors.InvalidDataf("url missing id")
	}
	i := new(platform.ID)
	if err := i.DecodeFromString(id); err != nil {
		return nil, err
	}
	tc.ID = *i
	return tc, nil
}

// handlePostTelegraf is the HTTP handler for the POST /api/v2/telegrafs route.
func (h *TelegrafHandler) handlePostTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	tc, err := decodePostTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	now := time.Now()
	auth, err := pctx.GetAuthorizer(ctx)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := h.TelegrafService.CreateTelegrafConfig(ctx, tc, auth.GetUserID(), now); err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusCreated, newTelegrafResponse(tc)); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}

// handlePutTelegraf is the HTTP handler for the POST /api/v2/telegrafs route.
func (h *TelegrafHandler) handlePutTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	tc, err := decodePutTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	now := time.Now()
	auth, err := pctx.GetAuthorizer(ctx)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	tc, err = h.TelegrafService.UpdateTelegrafConfig(ctx, tc.ID, tc, auth.GetUserID(), now)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc)); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}

func (h *TelegrafHandler) handleDeleteTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	i, err := decodeGetTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err = h.TelegrafService.DeleteTelegrafConfig(ctx, i); err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusNoContent, nil); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}
<commit_msg>(fix/http) replace space with _<commit_after>package http

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/influxdata/platform"
	pctx "github.com/influxdata/platform/context"
	"github.com/influxdata/platform/kit/errors"
	"github.com/julienschmidt/httprouter"
)

// TelegrafHandler is the handler for the telegraf service
type TelegrafHandler struct {
	*httprouter.Router

	TelegrafService            platform.TelegrafConfigStore
	UserResourceMappingService platform.UserResourceMappingService
}

const (
	telegrafsPath            = "/api/v2/telegrafs"
	telegrafsIDPath          = "/api/v2/telegrafs/:id"
	telegrafsIDMembersPath   = "/api/v2/telegrafs/:id/members"
	telegrafsIDMembersIDPath = "/api/v2/telegrafs/:id/members/:userID"
	telegrafsIDOwnersPath    = "/api/v2/telegrafs/:id/owners"
	telegrafsIDOwnersIDPath  = "/api/v2/telegrafs/:id/owners/:userID"
)

// NewTelegrafHandler returns a new instance of TelegrafHandler.
func NewTelegrafHandler(mappingService platform.UserResourceMappingService) *TelegrafHandler {
	h := &TelegrafHandler{
		Router:                     httprouter.New(),
		UserResourceMappingService: mappingService,
	}
	h.HandlerFunc("POST", telegrafsPath, h.handlePostTelegraf)
	h.HandlerFunc("GET", telegrafsPath, h.handleGetTelegrafs)
	h.HandlerFunc("GET", telegrafsIDPath, h.handleGetTelegraf)
	h.HandlerFunc("DELETE", telegrafsIDPath, h.handleDeleteTelegraf)
	h.HandlerFunc("PUT", telegrafsIDPath, h.handlePutTelegraf)

	h.HandlerFunc("POST", telegrafsIDMembersIDPath, newPostMemberHandler(h.UserResourceMappingService, platform.TelegrafResourceType, platform.Member))
	h.HandlerFunc("GET", telegrafsIDMembersIDPath, newGetMembersHandler(h.UserResourceMappingService, platform.Member))
	h.HandlerFunc("DELETE", telegrafsIDMembersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Member))

	h.HandlerFunc("POST", telegrafsIDOwnersPath, newPostMemberHandler(h.UserResourceMappingService, platform.TelegrafResourceType, platform.Owner))
	h.HandlerFunc("GET", telegrafsIDOwnersPath, newGetMembersHandler(h.UserResourceMappingService, platform.Owner))
	h.HandlerFunc("DELETE", telegrafsIDOwnersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Owner))

	return h
}

type telegrafLinks struct {
	Self  string `json:"self"`
	Cells string `json:"cells"`
}

type link struct {
	Self string `json:"self"`
}

type telegrafResponse struct {
	*platform.TelegrafConfig
	Links link `json:"links"`
}

type telegrafResponses struct {
	TelegrafConfigs []telegrafResponse `json:"configurations"`
}

func newTelegrafResponse(tc *platform.TelegrafConfig) telegrafResponse {
	return telegrafResponse{
		TelegrafConfig: tc,
		Links: link{
			Self: fmt.Sprintf("/api/v2/telegrafs/%s", tc.ID.String()),
		},
	}
}

func newTelegrafResponses(tcs []*platform.TelegrafConfig) telegrafResponses {
	resp := telegrafResponses{
		TelegrafConfigs: make([]telegrafResponse, len(tcs)),
	}
	for i, c := range tcs {
		resp.TelegrafConfigs[i] = newTelegrafResponse(c)
	}
	return resp
}

func decodeGetTelegrafRequest(ctx context.Context, r *http.Request) (i platform.ID, err error) {
	params := httprouter.ParamsFromContext(ctx)
	id := params.ByName("id")
	if id == "" {
		return i, errors.InvalidDataf("url missing id")
	}

	if err := i.DecodeFromString(id); err != nil {
		return i, err
	}
	return i, nil
}

func (h *TelegrafHandler) handleGetTelegrafs(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	filter, err := decodeUserResourceMappingFilter(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	tcs, _, err := h.TelegrafService.FindTelegrafConfigs(ctx, *filter)
	if err != nil {
		if err == platform.ErrViewNotFound {
			err = errors.New(err.Error(), errors.NotFound)
		}
		EncodeError(ctx, err, w)
		return
	}
	if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponses(tcs)); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}

func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	id, err := decodeGetTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	tc, err := h.TelegrafService.FindTelegrafConfigByID(ctx, id)
	if err != nil {
		if err == platform.ErrViewNotFound {
			err = errors.New(err.Error(), errors.NotFound)
		}
		EncodeError(ctx, err, w)
		return
	}

	contentType := r.Header.Get("Content-Type")
	switch contentType {
	case "application/octet-stream":
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.toml\"", strings.Replace(strings.TrimSpace(tc.Name), " ", "_", -1)))
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(tc.TOML()))
	case "application/toml":
		w.Header().Set("Content-Type", "application/toml; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(tc.TOML()))
	case "application/json":
		fallthrough
	default:
		if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc)); err != nil {
			EncodeError(ctx, err, w)
			return
		}
	}
}

func decodeUserResourceMappingFilter(ctx context.Context, r *http.Request) (*platform.UserResourceMappingFilter, error) {
	urm := new(platform.UserResourceMappingFilter)
	err := json.NewDecoder(r.Body).Decode(urm)
	return urm, err
}

func decodePostTelegrafRequest(ctx context.Context, r *http.Request) (*platform.TelegrafConfig, error) {
	tc := new(platform.TelegrafConfig)
	err := json.NewDecoder(r.Body).Decode(tc)
	return tc, err
}

func decodePutTelegrafRequest(ctx context.Context, r *http.Request) (*platform.TelegrafConfig, error) {
	tc := new(platform.TelegrafConfig)
	if err := json.NewDecoder(r.Body).Decode(tc); err != nil {
		return nil, err
	}
	params := httprouter.ParamsFromContext(ctx)
	id := params.ByName("id")
	if id == "" {
		return nil, errors.InvalidDataf("url missing id")
	}
	i := new(platform.ID)
	if err := i.DecodeFromString(id); err != nil {
		return nil, err
	}
	tc.ID = *i
	return tc, nil
}

// handlePostTelegraf is the HTTP handler for the POST /api/v2/telegrafs route.
func (h *TelegrafHandler) handlePostTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	tc, err := decodePostTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	now := time.Now()
	auth, err := pctx.GetAuthorizer(ctx)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := h.TelegrafService.CreateTelegrafConfig(ctx, tc, auth.GetUserID(), now); err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusCreated, newTelegrafResponse(tc)); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}

// handlePutTelegraf is the HTTP handler for the POST /api/v2/telegrafs route.
func (h *TelegrafHandler) handlePutTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	tc, err := decodePutTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}
	now := time.Now()
	auth, err := pctx.GetAuthorizer(ctx)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	tc, err = h.TelegrafService.UpdateTelegrafConfig(ctx, tc.ID, tc, auth.GetUserID(), now)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc)); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}

func (h *TelegrafHandler) handleDeleteTelegraf(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	i, err := decodeGetTelegrafRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err = h.TelegrafService.DeleteTelegrafConfig(ctx, i); err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusNoContent, nil); err != nil {
		EncodeError(ctx, err, w)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/DailyBurn/ratchet"
	"github.com/DailyBurn/ratchet/stages"
)

// numberStarter implements the PipelineStarter interface
type numberStarter struct{}

func (s *numberStarter) Start(outputChan chan ratchet.Data, killChan chan error) {
	fmt.Println("[numberStarter] Start()")
	numbersData := []byte("[35,13,78,22,4,450,2,243,782,44]")
	fmt.Println("[numberStarter] Sending", numbersData, "to chan", outputChan)
	outputChan <- numbersData
	close(outputChan)
}

func (s *numberStarter) String() string {
	return "numberStarter"
}

// numberStats implements the PipelineStage interface
type numberStats struct{}

type numberStatsInput []int
type numberStatsOutput struct {
	Sum  int
	Mean int
}

func (s *numberStats) HandleData(data ratchet.Data, outputChan chan ratchet.Data, killChan chan error) {
	var numbers numberStatsInput
	err := ratchet.ParseDataIntoStructPtr(data, &numbers)
	if err != nil {
		killChan <- err
	}
	stats := numberStatsOutput{Sum: 0}
	for _, n := range numbers {
		stats.Sum += n
	}
	stats.Mean = (stats.Sum / len(numbers))

	output, _ := ratchet.NewDataFromStruct(stats)
	outputChan <- output
}

func (s *numberStats) Finish(outputChan chan ratchet.Data, killChan chan error) {
	close(outputChan)
}

func (s *numberStats) String() string {
	return "numberStats"
}

func main() {
	// Simple starter that sends a numbers array (defined in this example file).
	numberStarter := &numberStarter{}
	// Calculate some basic stats on the numbers (defined in this example file).
	numberStats := &numberStats{}
	// Prints resulting data to stdout (one of the standard ratchet stages).
	writeToStdout := stages.NewIoWriter(os.Stdout)

	pipeline := ratchet.NewPipeline(numberStarter, numberStats, writeToStdout)
	err := <-pipeline.Run()

	if err != nil {
		fmt.Println("pipeline returned err:", err)
	} else {
		fmt.Println("pipeline completed successfully")
	}
}
<commit_msg>Remove examples/<commit_after><|endoftext|>


<commit_before>package scamp

import (
  "sync"
)

type ClientChan chan *Client

type Client struct {
  conn *Connection
  serv *Service

  requests MessageChan
  openReplies map[int]MessageChan

  closeSplitReqsAndReps chan bool

  isClosed bool
  closedMutex sync.Mutex
}

func Dial(connspec string) (client *Client, err error){
  Trace.Printf("connecting to `%s`", connspec)

  conn,err := DialConnection(connspec)
  client = NewClient(conn)
  if err != nil {
    return
  }
  client.conn = conn

  return
}

func NewClient(conn *Connection) (client *Client){
  Trace.Printf("client allocated")
  client = new(Client)

  client.conn = conn
  client.requests = make(MessageChan)
  client.openReplies = make(map[int]MessageChan)

  client.closeSplitReqsAndReps = make(chan bool)

  conn.SetClient(client)

  go client.splitReqsAndReps()

  return
}

func (client *Client)SetService(serv *Service) {
  client.serv = serv
}

func (client *Client)Send(msg *Message) (responseChan MessageChan, err error){
  // Info.Printf("sending message `%d`", msg.RequestId)
  err = client.conn.Send(msg)
  if err != nil {
    return
  }

  if msg.MessageType == MESSAGE_TYPE_REQUEST {
    Trace.Printf("sending request so waiting for reply")
    responseChan = make(MessageChan)
    client.openReplies[msg.RequestId] = responseChan
  } else {
    Trace.Printf("sending reply so done with this message")
  }

  return
}

func (client *Client)Close() {
  client.closedMutex.Lock()
  defer client.closedMutex.Unlock()
  if client.isClosed {
    Trace.Printf("client already closed. skipping shutdown.")
    return
  }

  client.conn.Close()

  client.closeSplitReqsAndReps <- true

  // // Notify wrapper service that we're dead
  if client.serv != nil {
    client.serv.RemoveClient(client)
  }

  client.isClosed = true
}

func (client *Client)splitReqsAndReps() (err error) {
  var replyChan MessageChan

  forLoop:
  for {
    select {
    case message,ok := <-client.conn.msgs:
      if !ok {
        break forLoop
      }

      Trace.Printf("splitting incoming message to reqs and reps")

      if message.MessageType == MESSAGE_TYPE_REQUEST {
        // interesting things happen if there are outstanding messages
        // and the client closes
        client.requests <- message
      } else if message.MessageType == MESSAGE_TYPE_REPLY {
        replyChan = client.openReplies[message.RequestId]

        if replyChan == nil {
          Error.Printf("got an unexpected reply for requestId: %d. Skipping.", message.RequestId)
          continue
        }

        delete(client.openReplies, message.RequestId)
        replyChan <- message

      } else {
        Error.Printf("Could not handle msg, it's neither req or reply. Skipping.")
        continue
      }
    case <- client.closeSplitReqsAndReps:
      // Info.Printf("closing down SplitReqsAndReps")
      break forLoop
    }
  }

  Trace.Printf("done with SplitReqsAndReps")

  close(client.requests)
  for _,openReplyChan := range client.openReplies {
    close(openReplyChan)
  }

  return
}

func (client *Client)Incoming() (MessageChan) {
  return client.requests
}<commit_msg>Stefan recommend a lock on sending data<commit_after>package scamp

import (
  "sync"
)

type ClientChan chan *Client

type Client struct {
  conn *Connection
  serv *Service

  requests MessageChan
  openReplies map[int]MessageChan

  closeSplitReqsAndReps chan bool

  isClosed bool
  closedM sync.Mutex

  sendM sync.Mutex
}

func Dial(connspec string) (client *Client, err error){
  Trace.Printf("connecting to `%s`", connspec)

  conn,err := DialConnection(connspec)
  client = NewClient(conn)
  if err != nil {
    return
  }
  client.conn = conn

  return
}

func NewClient(conn *Connection) (client *Client){
  Trace.Printf("client allocated")
  client = new(Client)

  client.conn = conn
  client.requests = make(MessageChan)
  client.openReplies = make(map[int]MessageChan)

  client.closeSplitReqsAndReps = make(chan bool)

  conn.SetClient(client)

  go client.splitReqsAndReps()

  return
}

func (client *Client)SetService(serv *Service) {
  client.serv = serv
}

func (client *Client)Send(msg *Message) (responseChan MessageChan, err error){
  client.sendM.Lock()
  defer client.sendM.Unlock()

  // Info.Printf("sending message `%d`", msg.RequestId)
  err = client.conn.Send(msg)
  if err != nil {
    return
  }

  if msg.MessageType == MESSAGE_TYPE_REQUEST {
    Trace.Printf("sending request so waiting for reply")
    responseChan = make(MessageChan)
    client.openReplies[msg.RequestId] = responseChan
  } else {
    Trace.Printf("sending reply so done with this message")
  }

  return
}

func (client *Client)Close() {
  client.closedM.Lock()
  defer client.closedM.Unlock()
  if client.isClosed {
    Trace.Printf("client already closed. skipping shutdown.")
    return
  }

  client.conn.Close()

  client.closeSplitReqsAndReps <- true

  // // Notify wrapper service that we're dead
  if client.serv != nil {
    client.serv.RemoveClient(client)
  }

  client.isClosed = true
}

func (client *Client)splitReqsAndReps() (err error) {
  var replyChan MessageChan

  forLoop:
  for {
    select {
    case message,ok := <-client.conn.msgs:
      if !ok {
        break forLoop
      }

      Trace.Printf("splitting incoming message to reqs and reps")

      if message.MessageType == MESSAGE_TYPE_REQUEST {
        // interesting things happen if there are outstanding messages
        // and the client closes
        client.requests <- message
      } else if message.MessageType == MESSAGE_TYPE_REPLY {
        replyChan = client.openReplies[message.RequestId]

        if replyChan == nil {
          Error.Printf("got an unexpected reply for requestId: %d. Skipping.", message.RequestId)
          continue
        }

        delete(client.openReplies, message.RequestId)
        replyChan <- message

      } else {
        Error.Printf("Could not handle msg, it's neither req or reply. Skipping.")
        continue
      }
    case <- client.closeSplitReqsAndReps:
      // Info.Printf("closing down SplitReqsAndReps")
      break forLoop
    }
  }

  Trace.Printf("done with SplitReqsAndReps")

  close(client.requests)
  for _,openReplyChan := range client.openReplies {
    close(openReplyChan)
  }

  return
}

func (client *Client)Incoming() (MessageChan) {
  return client.requests
}<|endoftext|>


<commit_before>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

func TestScannerDirent(t *testing.T) {
	scanner, err := NewScanner(filepath.Join(testRoot, "d0/symlinks"))
	ensureError(t, err)

	var found bool

	for scanner.Scan() {
		if scanner.Name() != "toD1" {
			continue
		}
		found = true

		de, err := scanner.Dirent()
		ensureError(t, err)

		got, err := de.IsDirOrSymlinkToDir()
		ensureError(t, err)

		if want := true; got != want {
			t.Errorf("GOT: %v; WANT: %v", got, want)
		}
	}

	ensureError(t, scanner.Err())

	if got, want := found, true; got != want {
		t.Errorf("GOT: %v; WANT: %v", got, want)
	}
}

func TestScanDir(t *testing.T) {
	t.Run("dirents", func(t *testing.T) {
		actual, err := ReadDirents(filepath.Join(testRoot, "d0"), nil)

		ensureError(t, err)

		expected := Dirents{
			&Dirent{
				name:     maxName,
				modeType: os.FileMode(0),
			},
			&Dirent{
				name:     "d1",
				modeType: os.ModeDir,
			},
			&Dirent{
				name:     "f1",
				modeType: os.FileMode(0),
			},
			&Dirent{
				name:     "skips",
				modeType: os.ModeDir,
			},
			&Dirent{
				name:     "symlinks",
				modeType: os.ModeDir,
			},
		}

		ensureDirentsMatch(t, actual, expected)
	})

	t.Run("dirnames", func(t *testing.T) {
		actual, err := ReadDirnames(filepath.Join(testRoot, "d0"), nil)
		ensureError(t, err)
		expected := []string{maxName, "d1", "f1", "skips", "symlinks"}
		ensureStringSlicesMatch(t, actual, expected)
	})
}
<commit_msg>scanner test rewritten without readdirents<commit_after>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

func TestScannerDirent(t *testing.T) {
	scanner, err := NewScanner(filepath.Join(testRoot, "d0/symlinks"))
	ensureError(t, err)

	var found bool

	for scanner.Scan() {
		if scanner.Name() != "toD1" {
			continue
		}
		found = true

		de, err := scanner.Dirent()
		ensureError(t, err)

		got, err := de.IsDirOrSymlinkToDir()
		ensureError(t, err)

		if want := true; got != want {
			t.Errorf("GOT: %v; WANT: %v", got, want)
		}
	}

	ensureError(t, scanner.Err())

	if got, want := found, true; got != want {
		t.Errorf("GOT: %v; WANT: %v", got, want)
	}
}

func TestScanDir(t *testing.T) {
	var actual []*Dirent

	scanner, err := NewScanner(filepath.Join(testRoot, "d0"))
	ensureError(t, err)

	for scanner.Scan() {
		dirent, err := scanner.Dirent()
		ensureError(t, err)
		actual = append(actual, dirent)
	}
	ensureError(t, scanner.Err())

	expected := Dirents{
		&Dirent{
			name:     maxName,
			modeType: os.FileMode(0),
		},
		&Dirent{
			name:     "d1",
			modeType: os.ModeDir,
		},
		&Dirent{
			name:     "f1",
			modeType: os.FileMode(0),
		},
		&Dirent{
			name:     "skips",
			modeType: os.ModeDir,
		},
		&Dirent{
			name:     "symlinks",
			modeType: os.ModeDir,
		},
	}

	ensureDirentsMatch(t, actual, expected)
}
<|endoftext|>