<commit_before>package pixelgl

import (
	"image"
	"image/color"
	"runtime"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/pkg/errors"
)

// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
	// Title at the top of the Window.
	Title string

	// Icon specifies the icon images available to be used by the window. This is usually displayed
	// in the top bar of the window or in the task bar of the desktop environment.
	//
	// If passed one image, it will use that image, if passed an array of images
	// those of or closest to the sizes desired by the system are selected.
	// The desired image sizes varies depending on platform and system settings. The selected
	// images will be rescaled as needed. Good sizes include 16x16, 32x32 and 48x48.
	//
	// Note: Setting this value doesn't have an effect on OSX. You'll need to set the icon
	// when bundling your application for release.
	Icon []pixel.Picture

	// Bounds specify the bounds of the Window in pixels.
	Bounds pixel.Rect

	// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
	// specified Monitor.
	Monitor *Monitor

	// Whether the Window is resizable.
	Resizable bool

	// Undecorated Window ommits the borders and decorations (close button, etc.).
	Undecorated bool

	// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
	// the monitor.
	VSync bool
}

// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
	window *glfw.Window

	bounds        pixel.Rect
	canvas        *Canvas
	vsync         bool
	cursorVisible bool

	// need to save these to correctly restore a fullscreen window
	restore struct {
		xpos, ypos, width, height int
	}

	prevInp, tempInp, currInp struct {
		mouse   pixel.Vec
		buttons [KeyLast + 1]bool
		scroll  pixel.Vec
	}
}

var currWin *Window

// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
	bool2int := map[bool]int{
		true:  glfw.True,
		false: glfw.False,
	}

	w := &Window{bounds: cfg.Bounds, cursorVisible: true}

	err := mainthread.CallErr(func() error {
		var err error

		glfw.WindowHint(glfw.ContextVersionMajor, 3)
		glfw.WindowHint(glfw.ContextVersionMinor, 3)
		glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
		glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

		glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
		glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])

		var share *glfw.Window
		if currWin != nil {
			share = currWin.window
		}
		_, _, width, height := intBounds(cfg.Bounds)
		w.window, err = glfw.CreateWindow(
			width,
			height,
			cfg.Title,
			nil,
			share,
		)
		if err != nil {
			return err
		}

		// enter the OpenGL context
		w.begin()
		w.end()

		return nil
	})
	if err != nil {
		return nil, errors.Wrap(err, "creating window failed")
	}

	w.SetVSync(cfg.VSync)

	w.initInput()
	w.SetMonitor(cfg.Monitor)

	w.canvas = NewCanvas(cfg.Bounds)
	w.Update()

	runtime.SetFinalizer(w, (*Window).Destroy)
	imgs := make([]image.Image, len(cfg.Icon))
	for i, v := range cfg.Icon {
		pic := pixel.PictureDataFromPicture(v)
		imgs[i] = pic.Image()
	}
	mainthread.Call(func() {
		w.window.SetIcon(imgs)
	})
	return w, nil
}

// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
	mainthread.Call(func() {
		w.window.Destroy()
	})
}

// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
	mainthread.Call(func() {
		_, _, oldW, oldH := intBounds(w.bounds)
		newW, newH := w.window.GetSize()
		w.bounds = w.bounds.ResizedMin(w.bounds.Size() + pixel.V(
			float64(newW-oldW),
			float64(newH-oldH),
		))
	})

	w.canvas.SetBounds(w.bounds)

	mainthread.Call(func() {
		w.begin()

		framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
		glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)

		glhf.Clear(0, 0, 0, 0)
		w.canvas.gf.Frame().Begin()
		w.canvas.gf.Frame().Blit(
			nil,
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
			0, 0, framebufferWidth, framebufferHeight,
		)
		w.canvas.gf.Frame().End()

		if w.vsync {
			glfw.SwapInterval(1)
		} else {
			glfw.SwapInterval(0)
		}
		w.window.SwapBuffers()
		w.end()
	})

	w.updateInput()
}

// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
	mainthread.Call(func() {
		w.window.SetShouldClose(closed)
	})
}

// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
	var closed bool
	mainthread.Call(func() {
		closed = w.window.ShouldClose()
	})
	return closed
}

// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
	mainthread.Call(func() {
		w.window.SetTitle(title)
	})
}

// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
	w.bounds = bounds
	mainthread.Call(func() {
		_, _, width, height := intBounds(bounds)
		w.window.SetSize(width, height)
	})
}

// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
	return w.bounds
}

func (w *Window) setFullscreen(monitor *Monitor) {
	mainthread.Call(func() {
		w.restore.xpos, w.restore.ypos = w.window.GetPos()
		w.restore.width, w.restore.height = w.window.GetSize()

		mode := monitor.monitor.GetVideoMode()

		w.window.SetMonitor(
			monitor.monitor,
			0,
			0,
			mode.Width,
			mode.Height,
			mode.RefreshRate,
		)
	})
}

func (w *Window) setWindowed() {
	mainthread.Call(func() {
		w.window.SetMonitor(
			nil,
			w.restore.xpos,
			w.restore.ypos,
			w.restore.width,
			w.restore.height,
			0,
		)
	})
}

// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
	if w.Monitor() != monitor {
		if monitor != nil {
			w.setFullscreen(monitor)
		} else {
			w.setWindowed()
		}
	}
}

// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
	var monitor *glfw.Monitor
	mainthread.Call(func() {
		monitor = w.window.GetMonitor()
	})
	if monitor == nil {
		return nil
	}
	return &Monitor{
		monitor: monitor,
	}
}

// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
	var focused bool
	mainthread.Call(func() {
		focused = w.window.GetAttrib(glfw.Focused) == glfw.True
	})
	return focused
}

// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
	w.vsync = vsync
}

// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
	return w.vsync
}

// SetCursorVisible sets the visibility of the mouse cursor inside the Window client area.
func (w *Window) SetCursorVisible(visible bool) {
	w.cursorVisible = visible
	mainthread.Call(func() {
		if visible {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		} else {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
		}
	})
}

// CursorVisible returns the visibility status of the mouse cursor.
func (w *Window) CursorVisible() bool {
	return w.cursorVisible
}

// Note: must be called inside the main thread.
func (w *Window) begin() {
	if currWin != w {
		w.window.MakeContextCurrent()
		glhf.Init()
		currWin = w
	}
}

// Note: must be called inside the main thread.
func (w *Window) end() {
	// nothing, really
}

// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return w.canvas.MakeTriangles(t)
}

// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
	return w.canvas.MakePicture(p)
}

// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
	w.canvas.SetMatrix(m)
}

// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
	w.canvas.SetColorMask(c)
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
	w.canvas.SetComposeMethod(cmp)
}

// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
	w.canvas.SetSmooth(smooth)
}

// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
	return w.canvas.Smooth()
}

// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
	w.canvas.Clear(c)
}

// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
	return w.canvas.Color(at)
}
<commit_msg>just align doc comment to 100 chars per line<commit_after>package pixelgl

import (
	"image"
	"image/color"
	"runtime"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/pkg/errors"
)

// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
	// Title at the top of the Window.
	Title string

	// Icon specifies the icon images available to be used by the window. This is usually
	// displayed in the top bar of the window or in the task bar of the desktop environment.
	//
	// If passed one image, it will use that image, if passed an array of images those of or
	// closest to the sizes desired by the system are selected. The desired image sizes varies
	// depending on platform and system settings. The selected images will be rescaled as
	// needed. Good sizes include 16x16, 32x32 and 48x48.
	//
	// Note: Setting this value doesn't have an effect on OSX. You'll need to set the icon when
	// bundling your application for release.
	Icon []pixel.Picture

	// Bounds specify the bounds of the Window in pixels.
	Bounds pixel.Rect

	// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
	// specified Monitor.
	Monitor *Monitor

	// Whether the Window is resizable.
	Resizable bool

	// Undecorated Window ommits the borders and decorations (close button, etc.).
	Undecorated bool

	// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
	// the monitor.
	VSync bool
}

// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
	window *glfw.Window

	bounds        pixel.Rect
	canvas        *Canvas
	vsync         bool
	cursorVisible bool

	// need to save these to correctly restore a fullscreen window
	restore struct {
		xpos, ypos, width, height int
	}

	prevInp, tempInp, currInp struct {
		mouse   pixel.Vec
		buttons [KeyLast + 1]bool
		scroll  pixel.Vec
	}
}

var currWin *Window

// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
	bool2int := map[bool]int{
		true:  glfw.True,
		false: glfw.False,
	}

	w := &Window{bounds: cfg.Bounds, cursorVisible: true}

	err := mainthread.CallErr(func() error {
		var err error

		glfw.WindowHint(glfw.ContextVersionMajor, 3)
		glfw.WindowHint(glfw.ContextVersionMinor, 3)
		glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
		glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

		glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
		glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])

		var share *glfw.Window
		if currWin != nil {
			share = currWin.window
		}
		_, _, width, height := intBounds(cfg.Bounds)
		w.window, err = glfw.CreateWindow(
			width,
			height,
			cfg.Title,
			nil,
			share,
		)
		if err != nil {
			return err
		}

		// enter the OpenGL context
		w.begin()
		w.end()

		return nil
	})
	if err != nil {
		return nil, errors.Wrap(err, "creating window failed")
	}

	w.SetVSync(cfg.VSync)

	w.initInput()
	w.SetMonitor(cfg.Monitor)

	w.canvas = NewCanvas(cfg.Bounds)
	w.Update()

	runtime.SetFinalizer(w, (*Window).Destroy)
	imgs := make([]image.Image, len(cfg.Icon))
	for i, v := range cfg.Icon {
		pic := pixel.PictureDataFromPicture(v)
		imgs[i] = pic.Image()
	}
	mainthread.Call(func() {
		w.window.SetIcon(imgs)
	})
	return w, nil
}

// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
	mainthread.Call(func() {
		w.window.Destroy()
	})
}

// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
	mainthread.Call(func() {
		_, _, oldW, oldH := intBounds(w.bounds)
		newW, newH := w.window.GetSize()
		w.bounds = w.bounds.ResizedMin(w.bounds.Size() + pixel.V(
			float64(newW-oldW),
			float64(newH-oldH),
		))
	})

	w.canvas.SetBounds(w.bounds)

	mainthread.Call(func() {
		w.begin()

		framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
		glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)

		glhf.Clear(0, 0, 0, 0)
		w.canvas.gf.Frame().Begin()
		w.canvas.gf.Frame().Blit(
			nil,
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
			0, 0, framebufferWidth, framebufferHeight,
		)
		w.canvas.gf.Frame().End()

		if w.vsync {
			glfw.SwapInterval(1)
		} else {
			glfw.SwapInterval(0)
		}
		w.window.SwapBuffers()
		w.end()
	})

	w.updateInput()
}

// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
	mainthread.Call(func() {
		w.window.SetShouldClose(closed)
	})
}

// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
	var closed bool
	mainthread.Call(func() {
		closed = w.window.ShouldClose()
	})
	return closed
}

// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
	mainthread.Call(func() {
		w.window.SetTitle(title)
	})
}

// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
	w.bounds = bounds
	mainthread.Call(func() {
		_, _, width, height := intBounds(bounds)
		w.window.SetSize(width, height)
	})
}

// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
	return w.bounds
}

func (w *Window) setFullscreen(monitor *Monitor) {
	mainthread.Call(func() {
		w.restore.xpos, w.restore.ypos = w.window.GetPos()
		w.restore.width, w.restore.height = w.window.GetSize()

		mode := monitor.monitor.GetVideoMode()

		w.window.SetMonitor(
			monitor.monitor,
			0,
			0,
			mode.Width,
			mode.Height,
			mode.RefreshRate,
		)
	})
}

func (w *Window) setWindowed() {
	mainthread.Call(func() {
		w.window.SetMonitor(
			nil,
			w.restore.xpos,
			w.restore.ypos,
			w.restore.width,
			w.restore.height,
			0,
		)
	})
}

// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
	if w.Monitor() != monitor {
		if monitor != nil {
			w.setFullscreen(monitor)
		} else {
			w.setWindowed()
		}
	}
}

// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
	var monitor *glfw.Monitor
	mainthread.Call(func() {
		monitor = w.window.GetMonitor()
	})
	if monitor == nil {
		return nil
	}
	return &Monitor{
		monitor: monitor,
	}
}

// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
	var focused bool
	mainthread.Call(func() {
		focused = w.window.GetAttrib(glfw.Focused) == glfw.True
	})
	return focused
}

// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
	w.vsync = vsync
}

// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
	return w.vsync
}

// SetCursorVisible sets the visibility of the mouse cursor inside the Window client area.
func (w *Window) SetCursorVisible(visible bool) {
	w.cursorVisible = visible
	mainthread.Call(func() {
		if visible {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		} else {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
		}
	})
}

// CursorVisible returns the visibility status of the mouse cursor.
func (w *Window) CursorVisible() bool {
	return w.cursorVisible
}

// Note: must be called inside the main thread.
func (w *Window) begin() {
	if currWin != w {
		w.window.MakeContextCurrent()
		glhf.Init()
		currWin = w
	}
}

// Note: must be called inside the main thread.
func (w *Window) end() {
	// nothing, really
}

// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return w.canvas.MakeTriangles(t)
}

// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
	return w.canvas.MakePicture(p)
}

// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
	w.canvas.SetMatrix(m)
}

// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
	w.canvas.SetColorMask(c)
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
	w.canvas.SetComposeMethod(cmp)
}

// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
	w.canvas.SetSmooth(smooth)
}

// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
	return w.canvas.Smooth()
}

// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
	w.canvas.Clear(c)
}

// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
	return w.canvas.Color(at)
}
<|endoftext|>


<commit_before>package object

import (
	"fmt"
	"monkey/ast"
)

type ObjectType string

const (
	INTEGER_OBJ      = "INTEGER"
	BOOLEAN_OBJ      = "BOOLEAN"
	NULL_OBJ         = "NULL"
	RETURN_VALUE_OBJ = "RETURN_VALUE"
	ERROR_OBJ        = "ERROR"
	FUNCTION_OBJ     = "FUNCTION"
	STRING_OBJ       = "STRING"
	BUILTIN_OBJ      = "BUILTIN"
	ARRAY_OBJ        = "ARRAY_OBJ"
)

type Object interface {
	Type() ObjectType
	Inspect() string
}

type BuiltinFunc func(args ...Object) Object

type Builtin struct {
	Fn BuiltinFunc
}

func (b *Builtin) Inspect() string  { return "builtin function" }
func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ }

type Function struct {
	Literal *ast.FunctionLiteral
	Scope   *Scope
}

func (f *Function) Inspect() string  { return f.Literal.String() }
func (r *Function) Type() ObjectType { return FUNCTION_OBJ }

type Array struct {
	Value   string
	Members []Object
}

func (a *Array) Inspect() string  { return a.Value }
func (a *Array) Type() ObjectType { return ARRAY_OBJ }

type ReturnValue struct{ Value Object }

func (rv *ReturnValue) Inspect() string  { return rv.Value.Inspect() }
func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }

type Integer struct{ Value int64 }

func (i *Integer) Inspect() string  { return fmt.Sprintf("%d", i.Value) }
func (i *Integer) Type() ObjectType { return INTEGER_OBJ }

type String struct{ Value string }

func (s *String) Inspect() string  { return s.Value }
func (s *String) Type() ObjectType { return STRING_OBJ }

type Boolean struct{ Value bool }

func (b *Boolean) Inspect() string  { return fmt.Sprintf("%v", b.Value) }
func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ }

type Null struct{}

func (n *Null) Inspect() string  { return "null" }
func (n *Null) Type() ObjectType { return NULL_OBJ }

type Error struct{ Message string }

func (e *Error) Inspect() string  { return "Err: " + e.Message }
func (e *Error) Type() ObjectType { return ERROR_OBJ }
<commit_msg>updated array Inspect() to return evaluated members<commit_after>package object

import (
	"bytes"
	"fmt"
	"monkey/ast"
	"strings"
)

type ObjectType string

const (
	INTEGER_OBJ      = "INTEGER"
	BOOLEAN_OBJ      = "BOOLEAN"
	NULL_OBJ         = "NULL"
	RETURN_VALUE_OBJ = "RETURN_VALUE"
	ERROR_OBJ        = "ERROR"
	FUNCTION_OBJ     = "FUNCTION"
	STRING_OBJ       = "STRING"
	BUILTIN_OBJ      = "BUILTIN"
	ARRAY_OBJ        = "ARRAY_OBJ"
)

type Object interface {
	Type() ObjectType
	Inspect() string
}

type BuiltinFunc func(args ...Object) Object

type Builtin struct {
	Fn BuiltinFunc
}

func (b *Builtin) Inspect() string  { return "builtin function" }
func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ }

type Function struct {
	Literal *ast.FunctionLiteral
	Scope   *Scope
}

func (f *Function) Inspect() string  { return f.Literal.String() }
func (r *Function) Type() ObjectType { return FUNCTION_OBJ }

type Array struct {
	Value   string
	Members []Object
}

func (a *Array) Inspect() string {
	var out bytes.Buffer
	members := []string{}
	for _, m := range a.Members {
		members = append(members, m.Inspect())
	}
	out.WriteString("[")
	out.WriteString(strings.Join(members, ", "))
	out.WriteString("]")
	out.WriteString("\n")

	return out.String()
}
func (a *Array) Type() ObjectType { return ARRAY_OBJ }

type ReturnValue struct{ Value Object }

func (rv *ReturnValue) Inspect() string  { return rv.Value.Inspect() }
func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }

type Integer struct{ Value int64 }

func (i *Integer) Inspect() string  { return fmt.Sprintf("%d", i.Value) }
func (i *Integer) Type() ObjectType { return INTEGER_OBJ }

type String struct{ Value string }

func (s *String) Inspect() string  { return s.Value }
func (s *String) Type() ObjectType { return STRING_OBJ }

type Boolean struct{ Value bool }

func (b *Boolean) Inspect() string  { return fmt.Sprintf("%v", b.Value) }
func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ }

type Null struct{}

func (n *Null) Inspect() string  { return "null" }
func (n *Null) Type() ObjectType { return NULL_OBJ }

type Error struct{ Message string }

func (e *Error) Inspect() string  { return "Err: " + e.Message }
func (e *Error) Type() ObjectType { return ERROR_OBJ }
<|endoftext|>


<commit_before>//go:generate go run gen.go

package version

// Version tells us the app version string
const Version = "3.2.1"
<commit_msg>Bump to v3.3.0<commit_after>//go:generate go run gen.go

package version

// Version tells us the app version string
const Version = "3.3.0"
<|endoftext|>


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

package inode

import (
	"errors"
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket
	syncer gcsx.Syncer
	clock  timeutil.Clock

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	name         string
	attrs        fuseops.InodeAttributes
	gcsChunkSize uint64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount

	// The source object from which this inode derives.
	//
	// INVARIANT: src.Name == name
	//
	// GUARDED_BY(mu)
	src gcs.Object

	// The current content of this inode, or nil if the source object is still
	// authoritative.
	content gcsx.TempFile

	// Has Destroy been called?
	//
	// GUARDED_BY(mu)
	destroyed bool
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS. The initial lookup count is
// zero.
//
// REQUIRES: o != nil
// REQUIRES: o.Generation > 0
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	id fuseops.InodeID,
	o *gcs.Object,
	attrs fuseops.InodeAttributes,
	bucket gcs.Bucket,
	syncer gcsx.Syncer,
	clock timeutil.Clock) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket: bucket,
		syncer: syncer,
		clock:  clock,
		id:     id,
		name:   o.Name,
		attrs:  attrs,
		src:    *o,
	}

	f.lc.Init(id)

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) checkInvariants() {
	if f.destroyed {
		return
	}

	// Make sure the name is legal.
	name := f.Name()
	if len(name) == 0 || name[len(name)-1] == '/' {
		panic("Illegal file name: " + name)
	}

	// INVARIANT: src.Name == name
	if f.src.Name != name {
		panic(fmt.Sprintf("Name mismatch: %q vs. %q", f.src.Name, name))
	}

	// INVARIANT: content.CheckInvariants() does not panic
	f.content.CheckInvariants()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) clobbered(ctx context.Context) (b bool, err error) {
	// Stat the object in GCS.
	req := &gcs.StatObjectRequest{Name: f.name}
	o, err := f.bucket.StatObject(ctx, req)

	// Special case: "not found" means we have been clobbered.
	if _, ok := err.(*gcs.NotFoundError); ok {
		err = nil
		b = true
		return
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// We are clobbered iff the generation doesn't match our source generation.
	b = (o.Generation != f.src.Generation)

	return
}

// Ensure that f.content != nil
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) ensureContent(ctx context.Context) (err error) {
	err = errors.New("TODO")
	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuseops.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

// Return the object generation number from which this inode was branched.
//
// LOCKS_REQUIRED(f)
func (f *FileInode) SourceGeneration() int64 {
	return f.src.Generation
}

// If true, it is safe to serve reads directly from the object generation given
// by f.SourceGeneration(), rather than calling f.ReadAt. Doing so may be more
// efficient, because f.ReadAt may cause the entire object to be faulted in and
// requires the inode to be locked during the read.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) SourceGenerationIsAuthoritative() bool {
	return f.content == nil
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) IncrementLookupCount() {
	f.lc.Inc()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) DecrementLookupCount(n uint64) (destroy bool) {
	destroy = f.lc.Dec(n)
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Destroy() (err error) {
	f.destroyed = true

	f.content.Destroy()
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	attrs = f.attrs

	// Obtain default information from the source object.
	attrs.Mtime = f.src.Updated
	attrs.Size = uint64(f.src.Size)

	// If GCS is no longer authoritative, stat our local content to obtain size
	// and mtime.
	if f.content != nil {
		var sr gcsx.StatResult
		sr, err = f.content.Stat()
		if err != nil {
			err = fmt.Errorf("Stat: %v", err)
			return
		}

		attrs.Size = uint64(sr.Size)
		if sr.Mtime != nil {
			attrs.Mtime = *sr.Mtime
		}
	}

	// If the object has been clobbered, we reflect that as the inode being
	// unlinked.
	clobbered, err := f.clobbered(ctx)
	if err != nil {
		err = fmt.Errorf("clobbered: %v", err)
		return
	}

	if !clobbered {
		attrs.Nlink = 1
	}

	return
}

// Serve a read for this file with semantics matching fuseops.ReadFileOp.
//
// The caller may be better off reading directly from GCS when
// f.SourceGenerationIsAuthoritative() is true.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Read(
	ctx context.Context,
	dst []byte,
	offset int64) (n int, err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Read from the local content.
	n, err = f.content.ReadAt(dst, offset)

	// We don't return errors for EOF. Otherwise, propagate errors.
	if err == io.EOF {
		err = nil
	} else if err != nil {
		err = fmt.Errorf("ReadAt: %v", err)
		return
	}

	return
}

// Serve a write for this file with semantics matching fuseops.WriteFileOp.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Write(
	ctx context.Context,
	data []byte,
	offset int64) (err error) {
	// Write to the mutable content. Note that the mutable content guarantees
	// that it returns an error for short writes.
	_, err = f.content.WriteAt(ctx, data, offset)

	return
}

// Write out contents to GCS. If this fails due to the generation having been
// clobbered, treat it as a non-error (simulating the inode having been
// unlinked).
//
// After this method succeeds, SourceGeneration will return the new generation
// by which this inode should be known (which may be the same as before). If it
// fails, the generation will not change.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Sync(ctx context.Context) (err error) {
	// Write out the contents if they are dirty.
	rl, newObj, err := f.objectSyncer.SyncObject(
		ctx,
		&f.src,
		f.content)

	// Special case: a precondition error means we were clobbered, which we treat
	// as being unlinked. There's no reason to return an error in that case.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = nil
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("gcsproxy.Sync: %v", err)
		return
	}

	// If we wrote out a new object, we need to update our state.
	if newObj != nil {
		f.src = *newObj
		f.content = mutable.NewContent(
			gcsproxy.NewReadProxy(
				newObj,
				rl,
				f.gcsChunkSize,
				f.leaser,
				f.bucket),
			f.clock)
	}

	return
}

// Truncate the file to the specified size.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Truncate(
	ctx context.Context,
	size int64) (err error) {
	err = f.content.Truncate(ctx, size)
	return
}
<commit_msg>FileInode.Write<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package inode

import (
	"errors"
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket
	syncer gcsx.Syncer
	clock  timeutil.Clock

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	name         string
	attrs        fuseops.InodeAttributes
	gcsChunkSize uint64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount

	// The source object from which this inode derives.
	//
	// INVARIANT: src.Name == name
	//
	// GUARDED_BY(mu)
	src gcs.Object

	// The current content of this inode, or nil if the source object is still
	// authoritative.
	content gcsx.TempFile

	// Has Destroy been called?
	//
	// GUARDED_BY(mu)
	destroyed bool
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS. The initial lookup count is
// zero.
//
// REQUIRES: o != nil
// REQUIRES: o.Generation > 0
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	id fuseops.InodeID,
	o *gcs.Object,
	attrs fuseops.InodeAttributes,
	bucket gcs.Bucket,
	syncer gcsx.Syncer,
	clock timeutil.Clock) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket: bucket,
		syncer: syncer,
		clock:  clock,
		id:     id,
		name:   o.Name,
		attrs:  attrs,
		src:    *o,
	}

	f.lc.Init(id)

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) checkInvariants() {
	if f.destroyed {
		return
	}

	// Make sure the name is legal.
	name := f.Name()
	if len(name) == 0 || name[len(name)-1] == '/' {
		panic("Illegal file name: " + name)
	}

	// INVARIANT: src.Name == name
	if f.src.Name != name {
		panic(fmt.Sprintf("Name mismatch: %q vs. %q", f.src.Name, name))
	}

	// INVARIANT: content.CheckInvariants() does not panic
	f.content.CheckInvariants()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) clobbered(ctx context.Context) (b bool, err error) {
	// Stat the object in GCS.
	req := &gcs.StatObjectRequest{Name: f.name}
	o, err := f.bucket.StatObject(ctx, req)

	// Special case: "not found" means we have been clobbered.
	if _, ok := err.(*gcs.NotFoundError); ok {
		err = nil
		b = true
		return
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// We are clobbered iff the generation doesn't match our source generation.
	b = (o.Generation != f.src.Generation)

	return
}

// Ensure that f.content != nil
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) ensureContent(ctx context.Context) (err error) {
	err = errors.New("TODO")
	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuseops.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

// Return the object generation number from which this inode was branched.
//
// LOCKS_REQUIRED(f)
func (f *FileInode) SourceGeneration() int64 {
	return f.src.Generation
}

// If true, it is safe to serve reads directly from the object generation given
// by f.SourceGeneration(), rather than calling f.ReadAt. Doing so may be more
// efficient, because f.ReadAt may cause the entire object to be faulted in and
// requires the inode to be locked during the read.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) SourceGenerationIsAuthoritative() bool {
	return f.content == nil
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) IncrementLookupCount() {
	f.lc.Inc()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) DecrementLookupCount(n uint64) (destroy bool) {
	destroy = f.lc.Dec(n)
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Destroy() (err error) {
	f.destroyed = true

	f.content.Destroy()
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	attrs = f.attrs

	// Obtain default information from the source object.
	attrs.Mtime = f.src.Updated
	attrs.Size = uint64(f.src.Size)

	// If GCS is no longer authoritative, stat our local content to obtain size
	// and mtime.
	if f.content != nil {
		var sr gcsx.StatResult
		sr, err = f.content.Stat()
		if err != nil {
			err = fmt.Errorf("Stat: %v", err)
			return
		}

		attrs.Size = uint64(sr.Size)
		if sr.Mtime != nil {
			attrs.Mtime = *sr.Mtime
		}
	}

	// If the object has been clobbered, we reflect that as the inode being
	// unlinked.
	clobbered, err := f.clobbered(ctx)
	if err != nil {
		err = fmt.Errorf("clobbered: %v", err)
		return
	}

	if !clobbered {
		attrs.Nlink = 1
	}

	return
}

// Serve a read for this file with semantics matching fuseops.ReadFileOp.
//
// The caller may be better off reading directly from GCS when
// f.SourceGenerationIsAuthoritative() is true.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Read(
	ctx context.Context,
	dst []byte,
	offset int64) (n int, err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Read from the local content.
	n, err = f.content.ReadAt(dst, offset)

	// We don't return errors for EOF. Otherwise, propagate errors.
	if err == io.EOF {
		err = nil
	} else if err != nil {
		err = fmt.Errorf("ReadAt: %v", err)
		return
	}

	return
}

// Serve a write for this file with semantics matching fuseops.WriteFileOp.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Write(
	ctx context.Context,
	data []byte,
	offset int64) (err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Write to the mutable content. Note that io.WriterAt guarantees it returns
	// an error for short writes.
	_, err = f.content.WriteAt(data, offset)

	return
}

// Write out contents to GCS. If this fails due to the generation having been
// clobbered, treat it as a non-error (simulating the inode having been
// unlinked).
//
// After this method succeeds, SourceGeneration will return the new generation
// by which this inode should be known (which may be the same as before). If it
// fails, the generation will not change.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Sync(ctx context.Context) (err error) {
	// Write out the contents if they are dirty.
	rl, newObj, err := f.objectSyncer.SyncObject(
		ctx,
		&f.src,
		f.content)

	// Special case: a precondition error means we were clobbered, which we treat
	// as being unlinked. There's no reason to return an error in that case.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = nil
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("gcsproxy.Sync: %v", err)
		return
	}

	// If we wrote out a new object, we need to update our state.
	if newObj != nil {
		f.src = *newObj
		f.content = mutable.NewContent(
			gcsproxy.NewReadProxy(
				newObj,
				rl,
				f.gcsChunkSize,
				f.leaser,
				f.bucket),
			f.clock)
	}

	return
}

// Truncate the file to the specified size.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Truncate(
	ctx context.Context,
	size int64) (err error) {
	err = f.content.Truncate(ctx, size)
	return
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Alibaba Group Holding Limited
 */
package openapi

import (
	"github.com/aliyun/aliyun-cli/i18n"
	"github.com/aliyun/aliyun-cli/cli"
	"fmt"
)

const (
	flagOutputTableRows = "output-table-rows"
	flagOutputTableCols = "output-table-cols"

	flagWaitForExpr = "wait-for-expr"
	flagWaitForTarget = "wait-for-target"
	flagWaitTimeout = "wait-time-out"
	flagWaitInterval = "wait-interval"
)

func AddFlags(fs *cli.FlagSet) {
	fs.Add(SecureFlag)
	fs.Add(ForceFlag)
	fs.Add(EndpointFlag)
	fs.Add(VersionFlag)
	fs.Add(HeaderFlag)
	fs.Add(BodyFlag)
	fs.Add(BodyFileFlag)
	fs.Add(PagerFlag)
	fs.Add(AcceptFlag)

	fs.Add(OutputTableRowFlag)
	fs.Add(OutputTableColsFlag)

	fs.Add(WaitForExprFlag)
	fs.Add(WaitForTargetFlag)
	fs.Add(WaitTimeoutFlag)
	fs.Add(WaitIntervalFlag)
}

var (
	SecureFlag = cli.Flag{Category: "caller",
	Name: "secure", AssignedMode: cli.AssignedNone,
	Usage: i18n.T(
		"use `--secure` to force https",
		"使用 `--secure` 开关强制使用https方式调用")}

    ForceFlag = cli.Flag{Category: "caller",
	Name: "force", AssignedMode: cli.AssignedNone,
	Usage: i18n.T(
		"use `--force` to skip api and parameters check",
		"添加 `--force` 开关可跳过API与参数的合法性检查")}

    EndpointFlag = cli.Flag{Category: "caller",
	Name: "endpoint", AssignedMode: cli.AssignedOnce,
	Usage: i18n.T(
		"use `--endpoint <endpoint>` to assign endpoint",
		"使用 `--endpoint <endpoint>` 来指定接入点地址")}

    VersionFlag = cli.Flag{Category: "caller",
	Name: "version", AssignedMode: cli.AssignedOnce,
	Usage: i18n.T(
		"use `--version <YYYY-MM-DD>` to assign product api version",
		"使用 `--version <YYYY-MM-DD>` 来指定访问的API版本")}

    HeaderFlag = cli.Flag{Category: "caller",
	Name: "header", AssignedMode: cli.AssignedRepeatable,
	Usage: i18n.T(
		"use `--header X-foo=bar` to add custom HTTP header, repeatable",
		"使用 `--header X-foo=bar` 来添加特定的HTTP头, 可多次添加")}

    BodyFlag = cli.Flag{Category: "caller",
	Name: "body", AssignedMode: cli.AssignedOnce,
	Usage: i18n.T(
		"use `--body $(cat foo.json)` to assign http body in RESTful call",
		"使用 `--body $(cat foo.json)` 来指定在RESTful调用中的HTTP包体")}

    BodyFileFlag = cli.Flag{Category: "caller",
	Name: "body-file",AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		"assign http body in Restful call with local file",
		"使用 `--body-file foo.json` 来指定输入包体")}

    PagerFlag = cli.Flag{Category: "caller",
	Name: "all-pages", AssignedMode: cli.AssignedDefault, Hidden: true,
	Usage: i18n.T(
		"use `--all-pages` to merge pages for pageable APIs",
		"使用 `--all-pages` 在访问分页的API时合并分页")}

    AcceptFlag = cli.Flag{Category: "caller",
	Name: "accept", AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		"add `--accept {json|xml}` to add Accept header",
		"使用 `--accept {json|xml}` 来指定Accept头")}

    OutputTableRowFlag = cli.Flag{Category: "caller",
	Name: flagOutputTableRows, AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		fmt.Sprintf("use `--%s` to assign fields of table row", flagOutputTableRows),
		fmt.Sprintf("使用 `--%s` 指定表格行的内容", flagOutputTableRows))}

    OutputTableColsFlag = cli.Flag{Category: "caller",
	Name: flagOutputTableCols, AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		fmt.Sprintf("use `--%s` to assign table column names", flagOutputTableCols),
		fmt.Sprintf("使用 `--%s` 指定表格的列名", flagOutputTableCols))}

    WaitForExprFlag = cli.Flag{Category: "caller",
	Name: flagWaitForExpr, AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		fmt.Sprintf("use `--%s` ", flagWaitForExpr),
		fmt.Sprintf("使用 `--%s` ", flagWaitForExpr))}

    WaitForTargetFlag = cli.Flag{Category: "caller",
	Name: flagWaitForTarget, AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		fmt.Sprintf("use `--%s` ", flagWaitForTarget),
		fmt.Sprintf("使用 `--%s` ", flagWaitForTarget))}

    WaitTimeoutFlag = cli.Flag{Category: "caller",
	Name: flagWaitTimeout, AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		fmt.Sprintf("use `--%s` ", flagWaitTimeout),
		fmt.Sprintf("使用 `--%s` ", flagWaitTimeout))}

    WaitIntervalFlag = cli.Flag{Category: "caller",
	Name: flagWaitInterval, AssignedMode: cli.AssignedOnce, Hidden: true,
	Usage: i18n.T(
		fmt.Sprintf("use `--%s` ", flagWaitInterval),
		fmt.Sprintf("使用 `--%s` ", flagWaitInterval))}
)
<commit_msg>flag description<commit_after>/*
 * Copyright (C) 2017-2018 Alibaba Group Holding Limited
 */
package openapi

import (
	"github.com/aliyun/aliyun-cli/i18n"
	"github.com/aliyun/aliyun-cli/cli"
	"fmt"
)

const (
	flagOutputTableRows = "output-table-rows"
	flagOutputTableCols = "output-table-cols"

	flagWaitForExpr = "wait-for-expr"
	flagWaitForTarget = "wait-for-target"
	flagWaitTimeout = "wait-time-out"
	flagWaitInterval = "wait-interval"
)

func AddFlags(fs *cli.FlagSet) {
	fs.Add(SecureFlag)
	fs.Add(ForceFlag)
	fs.Add(EndpointFlag)
	fs.Add(VersionFlag)
	fs.Add(HeaderFlag)
	fs.Add(BodyFlag)
	fs.Add(BodyFileFlag)
	fs.Add(PagerFlag)
	fs.Add(AcceptFlag)

	fs.Add(OutputTableRowFlag)
	fs.Add(OutputTableColsFlag)

	fs.Add(WaitForExprFlag)
	fs.Add(WaitForTargetFlag)
	fs.Add(WaitTimeoutFlag)
	fs.Add(WaitIntervalFlag)
}

var (
	SecureFlag = cli.Flag{Category: "caller",
		Name: "secure", AssignedMode: cli.AssignedNone,
		Usage: i18n.T(
			"use `--secure` to force https",
			"使用 `--secure` 开关强制使用https方式调用")}

	ForceFlag = cli.Flag{Category: "caller",
		Name: "force", AssignedMode: cli.AssignedNone,
		Usage: i18n.T(
			"use `--force` to skip api and parameters check",
			"添加 `--force` 开关可跳过API与参数的合法性检查")}

	EndpointFlag = cli.Flag{Category: "caller",
		Name: "endpoint", AssignedMode: cli.AssignedOnce,
		Usage: i18n.T(
			"use `--endpoint <endpoint>` to assign endpoint",
			"使用 `--endpoint <endpoint>` 来指定接入点地址")}

	VersionFlag = cli.Flag{Category: "caller",
		Name: "version", AssignedMode: cli.AssignedOnce,
		Usage: i18n.T(
			"use `--version <YYYY-MM-DD>` to assign product api version",
			"使用 `--version <YYYY-MM-DD>` 来指定访问的API版本")}

	HeaderFlag = cli.Flag{Category: "caller",
		Name: "header", AssignedMode: cli.AssignedRepeatable,
		Usage: i18n.T(
			"use `--header X-foo=bar` to add custom HTTP header, repeatable",
			"使用 `--header X-foo=bar` 来添加特定的HTTP头, 可多次添加")}

	BodyFlag = cli.Flag{Category: "caller",
		Name: "body", AssignedMode: cli.AssignedOnce,
		Usage: i18n.T(
			"use `--body $(cat foo.json)` to assign http body in RESTful call",
			"使用 `--body $(cat foo.json)` 来指定在RESTful调用中的HTTP包体")}

	BodyFileFlag = cli.Flag{Category: "caller",
		Name: "body-file",AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			"assign http body in Restful call with local file",
			"使用 `--body-file foo.json` 来指定输入包体")}

	PagerFlag = cli.Flag{Category: "caller",
		Name: "all-pages", AssignedMode: cli.AssignedDefault, Hidden: true,
		Usage: i18n.T(
			"use `--all-pages` to merge pages for pageable APIs",
			"使用 `--all-pages` 在访问分页的API时合并分页")}

	AcceptFlag = cli.Flag{Category: "caller",
		Name: "accept", AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			"add `--accept {json|xml}` to add Accept header",
			"使用 `--accept {json|xml}` 来指定Accept头")}

	OutputTableRowFlag = cli.Flag{Category: "caller",
		Name: flagOutputTableRows, AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			fmt.Sprintf("use `--%s` to assign fields of table row", flagOutputTableRows),
			fmt.Sprintf("使用 `--%s` 指定表格行的内容", flagOutputTableRows))}

	OutputTableColsFlag = cli.Flag{Category: "caller",
		Name: flagOutputTableCols, AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			fmt.Sprintf("use `--%s` to assign table column names", flagOutputTableCols),
			fmt.Sprintf("使用 `--%s` 指定表格的列名", flagOutputTableCols))}

	WaitForExprFlag = cli.Flag{Category: "caller",
		Name: flagWaitForExpr, AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			fmt.Sprintf("use `--%s` to assign jmes expr", flagWaitForExpr),
			fmt.Sprintf("使用 `--%s` 指定jmes表达式", flagWaitForExpr))}

	WaitForTargetFlag = cli.Flag{Category: "caller",
		Name: flagWaitForTarget, AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			fmt.Sprintf("use `--%s` to assign target", flagWaitForTarget),
			fmt.Sprintf("使用 `--%s` 指定目标的值", flagWaitForTarget))}

	WaitTimeoutFlag = cli.Flag{Category: "caller",
		Name: flagWaitTimeout, AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			fmt.Sprintf("use `--%s` to set timeout(seconds)", flagWaitTimeout),
			fmt.Sprintf("使用 `--%s` 设定等待超时时间(秒)", flagWaitTimeout))}

	WaitIntervalFlag = cli.Flag{Category: "caller",
		Name: flagWaitInterval, AssignedMode: cli.AssignedOnce, Hidden: true,
		Usage: i18n.T(
			fmt.Sprintf("use `--%s` to set interval(seconds)", flagWaitInterval),
			fmt.Sprintf("使用 `--%s` 去指定请求间隔时间(秒)", flagWaitInterval))}
)
<|endoftext|>


<commit_before>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
// Copyright 2017 Charles University, Faculty of Arts,
//                Institute of the Czech National Corpus
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proc

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/czcorpus/vert-tagextract/db"
	"github.com/czcorpus/vert-tagextract/db/colgen"
	_ "github.com/mattn/go-sqlite3" // sqlite3 driver load
	"github.com/tomachalek/vertigo"
)

// TTEConfProvider defines an object able to
// provide configuration data for TTExtractor factory.
type TTEConfProvider interface {
	GetCorpus() string
	GetAtomStructure() string
	GetStackStructEval() bool
	GetStructures() map[string][]string
	GetPoSTagColumn() int
}

// TTExtractor handles writing parsed data
// to a sqlite3 database. Parsed values are
// received pasivelly by implementing vertigo.LineProcessor
type TTExtractor struct {
	lineCounter        int
	atomCounter        int
	tokenInAtomCounter int
	corpusID           string
	database           *sql.DB
	transaction        *sql.Tx
	docInsert          *sql.Stmt
	attrAccum          attrAccumulator
	atomStruct         string
	structures         map[string][]string
	attrNames          []string
	colgenFn           colgen.AlignedColGenFn
	currAtomAttrs      map[string]interface{}
	posTagColumn       int
	posTags            map[string]int
}

// NewTTExtractor is a factory function to
// instantiate proper TTExtractor.
func NewTTExtractor(database *sql.DB, conf TTEConfProvider,
	colgenFn colgen.AlignedColGenFn) *TTExtractor {
	ans := &TTExtractor{
		database:     database,
		corpusID:     conf.GetCorpus(),
		atomStruct:   conf.GetAtomStructure(),
		structures:   conf.GetStructures(),
		colgenFn:     colgenFn,
		posTagColumn: conf.GetPoSTagColumn() - 1, // internally we exclude "word" as a separate stuff
		posTags:      make(map[string]int),
	}
	if conf.GetStackStructEval() {
		ans.attrAccum = newStructStack()

	} else {
		ans.attrAccum = newDefaultAccum()
	}
	return ans
}

// ProcToken is a part of vertigo.LineProcessor implementation.
// It is called by Vertigo parser when a token line is encountered.
func (tte *TTExtractor) ProcToken(tk *vertigo.Token) {
	tte.lineCounter++
	tte.tokenInAtomCounter++
	if tte.posTagColumn > -1 {
		if tte.posTagColumn < len(tk.Attrs) {
			tte.posTags[tk.Attrs[tte.posTagColumn]]++

		} else {
			log.Printf("WARNING: cannot fetch PoS tag from line %d (%s)", tte.lineCounter, tk.Attrs)
		}
	}
}

// ProcStructClose is a part of vertigo.LineProcessor implementation.
// It is called by Vertigo parser when a closing structure tag is
// encountered.
func (tte *TTExtractor) ProcStructClose(st *vertigo.StructureClose) {
	tte.attrAccum.end(st.Name)
	tte.lineCounter++

	if st.Name == tte.atomStruct {
		tte.currAtomAttrs["poscount"] = tte.tokenInAtomCounter

		values := make([]interface{}, len(tte.attrNames))
		for i, n := range tte.attrNames {
			if tte.currAtomAttrs[n] != nil {
				values[i] = tte.currAtomAttrs[n]

			} else {
				values[i] = "" // liveattrs plug-in does not like NULLs
			}
		}
		_, err := tte.docInsert.Exec(values...)
		if err != nil {
			log.Fatalf("Failed to insert data: %s", err)
		}
		tte.currAtomAttrs = make(map[string]interface{})
	}
}

// acceptAttr tests whether a structural attribute
// [structName].[attrName] is configured (see _example/*.json) to be imported
func (tte *TTExtractor) acceptAttr(structName string, attrName string) bool {
	tmp := tte.structures[structName]
	for _, v := range tmp {
		if v == attrName {
			return true
		}
	}
	return false
}

// ProcStruct is a part of vertigo.LineProcessor implementation.
// It si called by Vertigo parser when an opening structure tag
// is encountered.
func (tte *TTExtractor) ProcStruct(st *vertigo.Structure) {
	tte.attrAccum.begin(st)
	if st.Name == tte.atomStruct {
		tte.tokenInAtomCounter = 0
		attrs := make(map[string]interface{})
		tte.attrAccum.forEachAttr(func(s string, k string, v string) {
			if tte.acceptAttr(s, k) {
				attrs[fmt.Sprintf("%s_%s", s, k)] = v
			}
		})
		attrs["wordcount"] = 0 // This value is currently unused
		attrs["poscount"] = 0  // This value is updated once we hit the closing tag
		attrs["corpus_id"] = tte.corpusID
		if tte.colgenFn != nil {
			attrs["item_id"] = tte.colgenFn(attrs)
		}
		tte.currAtomAttrs = attrs
		tte.atomCounter++
	}
	tte.lineCounter++
}

func (tte *TTExtractor) calcNumAttrs() int {
	ans := 0
	for _, items := range tte.structures {
		ans += len(items)
	}
	return ans
}

func (tte *TTExtractor) generateAttrList() []string {
	attrNames := make([]string, tte.calcNumAttrs()+4)
	i := 0
	for s, items := range tte.structures {
		for _, item := range items {
			attrNames[i] = fmt.Sprintf("%s_%s", s, item)
			i++
		}
	}
	attrNames[i] = "wordcount"
	attrNames[i+1] = "poscount"
	attrNames[i+2] = "corpus_id"
	if tte.colgenFn != nil {
		attrNames[i+3] = "item_id"

	} else {
		attrNames = attrNames[:i+3]
	}
	return attrNames
}

func (tte *TTExtractor) insertPosTags() {
	ins := db.PrepareInsert(tte.transaction, "postag", []string{"value", "corpus_id", "count"})
	for value, count := range tte.posTags {
		ins.Exec(value, tte.corpusID, count)
	}
}

// Run starts the parsing and metadata extraction
// process. The method expects a proper database
// schema to be ready (see database.go for details).
// The whole process runs within a transaction which
// makes sqlite3 inserts a few orders of magnitude
// faster.
func (tte *TTExtractor) Run(conf *vertigo.ParserConf) {
	log.Print("Starting to process the vertical file...")
	tte.database.Exec("PRAGMA synchronous = OFF")
	tte.database.Exec("PRAGMA journal_mode = MEMORY")
	var err error
	tte.transaction, err = tte.database.Begin()
	if err != nil {
		log.Fatalf("Failed to start a database transaction: %s", err)
	}

	tte.attrNames = tte.generateAttrList()
	tte.docInsert = db.PrepareInsert(tte.transaction, "item", tte.attrNames)

	parserErr := vertigo.ParseVerticalFile(conf, tte)
	if parserErr != nil {
		tte.transaction.Rollback()
		log.Fatalf("Failed to parse vertical file: %s", parserErr)

	} else {
		log.Print("...DONE")
		if tte.posTagColumn > 0 {
			log.Print("Saving PoS tags into the database...")
			tte.insertPosTags()
			log.Print("...DONE")
		}
		err = tte.transaction.Commit()
		if err != nil {
			log.Fatal("Failed to commit database transaction: ", err)
		}
	}
}
<commit_msg>Fix postag idx testing<commit_after>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
// Copyright 2017 Charles University, Faculty of Arts,
//                Institute of the Czech National Corpus
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proc

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/czcorpus/vert-tagextract/db"
	"github.com/czcorpus/vert-tagextract/db/colgen"
	_ "github.com/mattn/go-sqlite3" // sqlite3 driver load
	"github.com/tomachalek/vertigo"
)

// TTEConfProvider defines an object able to
// provide configuration data for TTExtractor factory.
type TTEConfProvider interface {
	GetCorpus() string
	GetAtomStructure() string
	GetStackStructEval() bool
	GetStructures() map[string][]string
	GetPoSTagColumn() int
}

// TTExtractor handles writing parsed data
// to a sqlite3 database. Parsed values are
// received pasivelly by implementing vertigo.LineProcessor
type TTExtractor struct {
	lineCounter        int
	atomCounter        int
	tokenInAtomCounter int
	corpusID           string
	database           *sql.DB
	transaction        *sql.Tx
	docInsert          *sql.Stmt
	attrAccum          attrAccumulator
	atomStruct         string
	structures         map[string][]string
	attrNames          []string
	colgenFn           colgen.AlignedColGenFn
	currAtomAttrs      map[string]interface{}
	posTagColumn       int
	posTags            map[string]int
}

// NewTTExtractor is a factory function to
// instantiate proper TTExtractor.
func NewTTExtractor(database *sql.DB, conf TTEConfProvider,
	colgenFn colgen.AlignedColGenFn) *TTExtractor {
	ans := &TTExtractor{
		database:     database,
		corpusID:     conf.GetCorpus(),
		atomStruct:   conf.GetAtomStructure(),
		structures:   conf.GetStructures(),
		colgenFn:     colgenFn,
		posTagColumn: conf.GetPoSTagColumn() - 1, // internally we exclude "word" as a separate stuff
		posTags:      make(map[string]int),
	}
	if conf.GetStackStructEval() {
		ans.attrAccum = newStructStack()

	} else {
		ans.attrAccum = newDefaultAccum()
	}
	return ans
}

// ProcToken is a part of vertigo.LineProcessor implementation.
// It is called by Vertigo parser when a token line is encountered.
func (tte *TTExtractor) ProcToken(tk *vertigo.Token) {
	tte.lineCounter++
	tte.tokenInAtomCounter++
	if tte.posTagColumn > -1 {
		if tte.posTagColumn < len(tk.Attrs) {
			tte.posTags[tk.Attrs[tte.posTagColumn]]++

		} else {
			log.Printf("WARNING: cannot fetch PoS tag from line %d (%s)", tte.lineCounter, tk.Attrs)
		}
	}
}

// ProcStructClose is a part of vertigo.LineProcessor implementation.
// It is called by Vertigo parser when a closing structure tag is
// encountered.
func (tte *TTExtractor) ProcStructClose(st *vertigo.StructureClose) {
	tte.attrAccum.end(st.Name)
	tte.lineCounter++

	if st.Name == tte.atomStruct {
		tte.currAtomAttrs["poscount"] = tte.tokenInAtomCounter

		values := make([]interface{}, len(tte.attrNames))
		for i, n := range tte.attrNames {
			if tte.currAtomAttrs[n] != nil {
				values[i] = tte.currAtomAttrs[n]

			} else {
				values[i] = "" // liveattrs plug-in does not like NULLs
			}
		}
		_, err := tte.docInsert.Exec(values...)
		if err != nil {
			log.Fatalf("Failed to insert data: %s", err)
		}
		tte.currAtomAttrs = make(map[string]interface{})
	}
}

// acceptAttr tests whether a structural attribute
// [structName].[attrName] is configured (see _example/*.json) to be imported
func (tte *TTExtractor) acceptAttr(structName string, attrName string) bool {
	tmp := tte.structures[structName]
	for _, v := range tmp {
		if v == attrName {
			return true
		}
	}
	return false
}

// ProcStruct is a part of vertigo.LineProcessor implementation.
// It si called by Vertigo parser when an opening structure tag
// is encountered.
func (tte *TTExtractor) ProcStruct(st *vertigo.Structure) {
	tte.attrAccum.begin(st)
	if st.Name == tte.atomStruct {
		tte.tokenInAtomCounter = 0
		attrs := make(map[string]interface{})
		tte.attrAccum.forEachAttr(func(s string, k string, v string) {
			if tte.acceptAttr(s, k) {
				attrs[fmt.Sprintf("%s_%s", s, k)] = v
			}
		})
		attrs["wordcount"] = 0 // This value is currently unused
		attrs["poscount"] = 0  // This value is updated once we hit the closing tag
		attrs["corpus_id"] = tte.corpusID
		if tte.colgenFn != nil {
			attrs["item_id"] = tte.colgenFn(attrs)
		}
		tte.currAtomAttrs = attrs
		tte.atomCounter++
	}
	tte.lineCounter++
}

func (tte *TTExtractor) calcNumAttrs() int {
	ans := 0
	for _, items := range tte.structures {
		ans += len(items)
	}
	return ans
}

func (tte *TTExtractor) generateAttrList() []string {
	attrNames := make([]string, tte.calcNumAttrs()+4)
	i := 0
	for s, items := range tte.structures {
		for _, item := range items {
			attrNames[i] = fmt.Sprintf("%s_%s", s, item)
			i++
		}
	}
	attrNames[i] = "wordcount"
	attrNames[i+1] = "poscount"
	attrNames[i+2] = "corpus_id"
	if tte.colgenFn != nil {
		attrNames[i+3] = "item_id"

	} else {
		attrNames = attrNames[:i+3]
	}
	return attrNames
}

func (tte *TTExtractor) insertPosTags() {
	ins := db.PrepareInsert(tte.transaction, "postag", []string{"value", "corpus_id", "count"})
	for value, count := range tte.posTags {
		ins.Exec(value, tte.corpusID, count)
	}
}

// Run starts the parsing and metadata extraction
// process. The method expects a proper database
// schema to be ready (see database.go for details).
// The whole process runs within a transaction which
// makes sqlite3 inserts a few orders of magnitude
// faster.
func (tte *TTExtractor) Run(conf *vertigo.ParserConf) {
	log.Print("Starting to process the vertical file...")
	tte.database.Exec("PRAGMA synchronous = OFF")
	tte.database.Exec("PRAGMA journal_mode = MEMORY")
	var err error
	tte.transaction, err = tte.database.Begin()
	if err != nil {
		log.Fatalf("Failed to start a database transaction: %s", err)
	}

	tte.attrNames = tte.generateAttrList()
	tte.docInsert = db.PrepareInsert(tte.transaction, "item", tte.attrNames)

	parserErr := vertigo.ParseVerticalFile(conf, tte)
	if parserErr != nil {
		tte.transaction.Rollback()
		log.Fatalf("Failed to parse vertical file: %s", parserErr)

	} else {
		log.Print("...DONE")
		if tte.posTagColumn > -1 {
			log.Print("Saving PoS tags into the database...")
			tte.insertPosTags()
			log.Print("...DONE")
		}
		err = tte.transaction.Commit()
		if err != nil {
			log.Fatal("Failed to commit database transaction: ", err)
		}
	}
}
<|endoftext|>


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

package pathutil_test

import (
	"go/build"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/neovim/go-client/nvim"
	"github.com/zchee/nvim-go/src/nvimutil"
	"github.com/zchee/nvim-go/src/pathutil"
)

var (
	testCwd, _     = os.Getwd()
	projectRoot, _ = filepath.Abs(filepath.Join(testCwd, "../../../"))
	testdata, _    = filepath.Abs(filepath.Join("../testdata"))
	testGoPath     = filepath.Join("testdata", "go")
	testGbPath     = filepath.Join(testdata, "gb")
)

func TestChdir(t *testing.T) {
	type args struct {
		v   *nvim.Nvim
		dir string
	}
	tests := []struct {
		name    string
		args    args
		wantCwd string
	}{
		{
			name: "nvim-go (gb)",
			args: args{
				v:   nvimutil.TestNvim(t, projectRoot),
				dir: filepath.Join(projectRoot, "src", "nvim-go"),
			},
			wantCwd: filepath.Join(projectRoot, "src", "nvim-go"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			defer func() {
				if testCwd != filepath.Join(projectRoot, "src/nvim-go/pathutil") || testCwd == tt.args.dir {
					t.Errorf("%q. Chdir(%v, %v) = %v, want %v", tt.name, tt.args.v, tt.args.dir, testCwd, tt.wantCwd)
				}
			}()
			defer pathutil.Chdir(tt.args.v, tt.args.dir)()
			var cwd interface{}
			tt.args.v.Eval("getcwd()", &cwd)
			if cwd.(string) != tt.wantCwd {
				t.Errorf("%q. Chdir(%v, %v) = %v, want %v", tt.name, tt.args.v, tt.args.dir, cwd, tt.wantCwd)
			}
		})
	}
}

func TestTrimGoPath(t *testing.T) {
	type args struct {
		p string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "typical go package full path",
			args: args{p: filepath.Join(testGoPath, "src/github.com/zchee/nvim-go")},
			want: "github.com/zchee/nvim-go",
		},
		{
			name: ".goimportsignore file on GOPATH",
			args: args{p: filepath.Join(testGoPath, ".goimportsignore")},
			want: ".goimportsignore",
		},
		{
			name: ".a file on GOPATH",
			args: args{p: filepath.Join(testGoPath, ".a")},
			want: ".a",
		},
		{
			name: "just GOPATH only",
			args: args{p: testGoPath},
			want: "",
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			defer setBuildContext(t, testGoPath)()
			if got := pathutil.TrimGoPath(tt.args.p); got != tt.want {
				t.Errorf("TrimGoPath(%v) = %v, want %v", tt.args.p, got, tt.want)
			}
		})
	}
}

func TestJoinGoPath(t *testing.T) {
	type args struct {
		p string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "astdump",
			args: args{p: "astdump"},
			want: filepath.Join(testGoPath, "src", "astdump"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			defer setBuildContext(t, testGoPath)()
			if got := pathutil.JoinGoPath(tt.args.p); got != tt.want {
				t.Errorf("JoinGoPath(%v) = %v, want %v", tt.args.p, got, tt.want)
			}
		})
	}
}

func TestShortFilePath(t *testing.T) {
	type args struct {
		p   string
		cwd string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "filename only",
			args: args{
				p:   filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
				cwd: filepath.Join(testCwd, "nvim-go/pathutil"),
			},
			want: "./pathutil_test.go",
		},
		{
			name: "with directory",
			args: args{
				p:   filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
				cwd: filepath.Join(testCwd, "nvim-go"),
			},
			want: "./pathutil/pathutil_test.go",
		},
		{
			name: "not shorten",
			args: args{
				p:   filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
				cwd: filepath.Join(testCwd, "nvim-go/command"),
			},
			want: filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.ShortFilePath(tt.args.p, tt.args.cwd); got != tt.want {
				t.Errorf("ShortFilePath(%v, %v) = %v, want %v", tt.args.p, tt.args.cwd, got, tt.want)
			}
		})
	}
}

func TestRel(t *testing.T) {
	type args struct {
		f   string
		cwd string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "own filepath and directory",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: testCwd,
			},
			want: "pathutil_test.go",
		},
		{
			name: "own filepath and project root",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: projectRoot,
			},
			want: "src/nvim-go/pathutil/pathutil_test.go",
		},
		{
			name: "Use different directory",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: filepath.Join(testCwd, "../commands"),
			},
			want: "../pathutil/pathutil_test.go",
		},
		{
			name: "Fail the filepath.Rel()",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: filepath.Join("foo", "bar", "baz"),
			},
			want: filepath.Join(testCwd, "pathutil_test.go"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.Rel(tt.args.cwd, tt.args.f); got != tt.want {
				t.Errorf("Rel(%v, %v) = %v, want %v", tt.args.f, tt.args.cwd, got, tt.want)
			}
		})
	}
}

func TestExpandGoRoot(t *testing.T) {
	goroot := runtime.GOROOT()

	type args struct {
		p string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "exist $GOROOT",
			args: args{p: "$GOROOT/src/go/ast/ast.go"},
			want: filepath.Join(goroot, "src/go/ast/ast.go"),
		},
		{
			name: "not exist $GOROOT",
			args: args{p: "src/go/ast/ast.go"},
			want: "src/go/ast/ast.go",
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.ExpandGoRoot(tt.args.p); got != tt.want {
				t.Errorf("ExpandGoRoot(%v) = %v, want %v", tt.args.p, got, tt.want)
			}
		})
	}
}

func TestIsDir(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "true (own parent directory)",
			args: args{filename: testCwd},
			want: true,
		},
		{
			name: "false (own file path)",
			args: args{filename: filepath.Join(testCwd, "pathutil_test.go")},
			want: false,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsDir(tt.args.filename); got != tt.want {
				t.Errorf("IsDir(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}

func TestIsExist(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "exist (own file)",
			args: args{filename: "./pathutil_test.go"},
			want: true,
		},
		{
			name: "not exist",
			args: args{filename: "./not_exist.go"},
			want: false,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsExist(tt.args.filename); got != tt.want {
				t.Errorf("IsExist(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}

func TestIsNotExist(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "exist (own file)",
			args: args{filename: "./pathutil_test.go"},
			want: false,
		},
		{
			name: "not exist",
			args: args{filename: "./not_exist.go"},
			want: true,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsNotExist(tt.args.filename); got != tt.want {
				t.Errorf("IsExist(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}

func TestIsGoFile(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "go file",
			args: args{filename: "pathutil.go"},
			want: true,
		},
		{
			name: "not go file",
			args: args{filename: "test.c"},
			want: false,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsGoFile(tt.args.filename); got != tt.want {
				t.Errorf("IsGoFile(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}
<commit_msg>test/pathutil: remove global variable and fix TestChdir testcase<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pathutil_test

import (
	"go/build"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/neovim/go-client/nvim"
	"github.com/zchee/nvim-go/src/nvimutil"
	"github.com/zchee/nvim-go/src/pathutil"
)

func TestChdir(t *testing.T) {
	testCwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	type args struct {
		v   *nvim.Nvim
		dir string
	}
	tests := []struct {
		name    string
		args    args
		wantCwd string
	}{
		{
			name: "gb/gsftp",
			args: args{
				v:   nvimutil.TestNvim(t, testGbRoot),
				dir: filepath.Join(testGbRoot, "src", "gsftp"),
			},
			wantCwd: filepath.Join(testGbRoot, "src", "gsftp"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			defer func() {
				if testCwd == tt.args.dir {
					t.Errorf("%q. Chdir(%v, %v) = %v, want %v", tt.name, tt.args.v, tt.args.dir, testCwd, tt.wantCwd)
				}
			}()
			defer pathutil.Chdir(tt.args.v, tt.args.dir)()
			var cwd interface{}
			tt.args.v.Eval("getcwd()", &cwd)
			if cwd.(string) != testCwd {
				t.Errorf("%q. Chdir(%v, %v) = %v, want %v", tt.name, tt.args.v, tt.args.dir, cwd, tt.wantCwd)
			}
		})
	}
}

func TestTrimGoPath(t *testing.T) {
	type args struct {
		p string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "typical go package full path",
			args: args{p: filepath.Join(testGoPath, "src/github.com/zchee/nvim-go")},
			want: "github.com/zchee/nvim-go",
		},
		{
			name: ".goimportsignore file on GOPATH",
			args: args{p: filepath.Join(testGoPath, ".goimportsignore")},
			want: ".goimportsignore",
		},
		{
			name: ".a file on GOPATH",
			args: args{p: filepath.Join(testGoPath, ".a")},
			want: ".a",
		},
		{
			name: "just GOPATH only",
			args: args{p: testGoPath},
			want: "",
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			defer setBuildContext(t, testGoPath)()
			if got := pathutil.TrimGoPath(tt.args.p); got != tt.want {
				t.Errorf("TrimGoPath(%v) = %v, want %v", tt.args.p, got, tt.want)
			}
		})
	}
}

func TestJoinGoPath(t *testing.T) {
	type args struct {
		p string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "astdump",
			args: args{p: "astdump"},
			want: filepath.Join(testGoPath, "src", "astdump"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			defer setBuildContext(t, testGoPath)()
			if got := pathutil.JoinGoPath(tt.args.p); got != tt.want {
				t.Errorf("JoinGoPath(%v) = %v, want %v", tt.args.p, got, tt.want)
			}
		})
	}
}

func TestShortFilePath(t *testing.T) {
	testCwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	type args struct {
		p   string
		cwd string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "filename only",
			args: args{
				p:   filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
				cwd: filepath.Join(testCwd, "nvim-go/pathutil"),
			},
			want: "./pathutil_test.go",
		},
		{
			name: "with directory",
			args: args{
				p:   filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
				cwd: filepath.Join(testCwd, "nvim-go"),
			},
			want: "./pathutil/pathutil_test.go",
		},
		{
			name: "not shorten",
			args: args{
				p:   filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
				cwd: filepath.Join(testCwd, "nvim-go/command"),
			},
			want: filepath.Join(testCwd, "nvim-go/pathutil/pathutil_test.go"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.ShortFilePath(tt.args.p, tt.args.cwd); got != tt.want {
				t.Errorf("ShortFilePath(%v, %v) = %v, want %v", tt.args.p, tt.args.cwd, got, tt.want)
			}
		})
	}
}

func TestRel(t *testing.T) {
	testCwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	type args struct {
		f   string
		cwd string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "own filepath and directory",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: testCwd,
			},
			want: "pathutil_test.go",
		},
		{
			name: "own filepath and project root",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: filepath.Join(build.Default.GOPATH, "src", "github.com", "zchee", "nvim-go"),
			},
			want: "src/pathutil/pathutil_test.go",
		},
		{
			name: "Use different directory",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: filepath.Join(testCwd, "../commands"),
			},
			want: "../pathutil/pathutil_test.go",
		},
		{
			name: "Fail the filepath.Rel()",
			args: args{
				f:   filepath.Join(testCwd, "pathutil_test.go"),
				cwd: filepath.Join("foo", "bar", "baz"),
			},
			want: filepath.Join(testCwd, "pathutil_test.go"),
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.Rel(tt.args.cwd, tt.args.f); got != tt.want {
				t.Errorf("Rel(%v, %v) = got %v, want %v", tt.args.f, tt.args.cwd, got, tt.want)
			}
		})
	}
}

func TestExpandGoRoot(t *testing.T) {
	goroot := runtime.GOROOT()

	type args struct {
		p string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "exist $GOROOT",
			args: args{p: "$GOROOT/src/go/ast/ast.go"},
			want: filepath.Join(goroot, "src/go/ast/ast.go"),
		},
		{
			name: "not exist $GOROOT",
			args: args{p: "src/go/ast/ast.go"},
			want: "src/go/ast/ast.go",
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.ExpandGoRoot(tt.args.p); got != tt.want {
				t.Errorf("ExpandGoRoot(%v) = %v, want %v", tt.args.p, got, tt.want)
			}
		})
	}
}

func TestIsDir(t *testing.T) {
	testCwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "true (own parent directory)",
			args: args{filename: testCwd},
			want: true,
		},
		{
			name: "false (own file path)",
			args: args{filename: filepath.Join(testCwd, "pathutil_test.go")},
			want: false,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsDir(tt.args.filename); got != tt.want {
				t.Errorf("IsDir(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}

func TestIsExist(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "exist (own file)",
			args: args{filename: "./pathutil_test.go"},
			want: true,
		},
		{
			name: "not exist",
			args: args{filename: "./not_exist.go"},
			want: false,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsExist(tt.args.filename); got != tt.want {
				t.Errorf("IsExist(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}

func TestIsNotExist(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "exist (own file)",
			args: args{filename: "./pathutil_test.go"},
			want: false,
		},
		{
			name: "not exist",
			args: args{filename: "./not_exist.go"},
			want: true,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsNotExist(tt.args.filename); got != tt.want {
				t.Errorf("IsExist(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}

func TestIsGoFile(t *testing.T) {
	type args struct {
		filename string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "go file",
			args: args{filename: "pathutil.go"},
			want: true,
		},
		{
			name: "not go file",
			args: args{filename: "test.c"},
			want: false,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := pathutil.IsGoFile(tt.args.filename); got != tt.want {
				t.Errorf("IsGoFile(%v) = %v, want %v", tt.args.filename, got, tt.want)
			}
		})
	}
}
<|endoftext|>