<commit_before>package endly_test

import (
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/viant/endly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"github.com/viant/toolbox/url"
	"strings"
	"testing"

	"path"
)

const code = `
	package main
	import "fmt"

	func main() {
		fmt.Println("Hello WebDriver!\n")
	}
`

func TestSeleniumService_Start(t *testing.T) {

	var credentialFile, err = GetDummyCredential()
	var target = url.NewResource("scp://127.0.0.1:22/", credentialFile)
	assert.Nil(t, err)
	var manager = endly.NewManager()
	var useCases = []struct {
		baseDir     string
		DataURLs    []string
		DataPayload []byte
		target      *url.Resource
		request     *endly.SeleniumServerStartRequest
		Pid         int
	}{
		{
			"test/selenium/start/inactive/darwin",
			[]string{
				"https://github.com/mozilla/geckodriver/releases/download/v0.19.1/geckodriver-v0.19.1-linux64.tar.gz",
				"http://selenium-release.storage.googleapis.com/3.4/selenium-server-standalone-3.4.0.jar",
			},
			[]byte("test"),
			url.NewResource("scp://127.0.0.1:22/", credentialFile),
			&endly.SeleniumServerStartRequest{
				Target:     target,
				Sdk:        "jdk",
				SdkVersion: "1.8",
				Version:    "3.4",
				Port:       8117,
			},
			28811,
		},
		{
			"test/selenium/start/active/darwin",
			[]string{
				"https://github.com/mozilla/geckodriver/releases/download/v0.19.1/geckodriver-v0.19.1-linux64.tar.gz",
				"http://selenium-release.storage.googleapis.com/3.4/selenium-server-standalone-3.4.0.jar",
			},
			[]byte("test"),
			url.NewResource("scp://127.0.0.1:22/", credentialFile),
			&endly.SeleniumServerStartRequest{
				Target:     target,
				Sdk:        "jdk",
				SdkVersion: "1.8",
				Version:    "3.4",
				Port:       8117,
			},
			28866,
		},
	}

	for _, useCase := range useCases {
		execService, err := GetReplayService(useCase.baseDir)
		if assert.Nil(t, err) {
			context, err := OpenTestContext(manager, useCase.target, execService)
			var state = context.State()

			if len(useCase.DataURLs) > 0 {
				storageService := storage.NewMemoryService()
				state.Put(endly.UseMemoryService, true)
				for _, setupURL := range useCase.DataURLs {
					err = storageService.Upload(setupURL, bytes.NewReader(useCase.DataPayload))
				}

				assert.Nil(t, err)
			}
			service, err := context.Service(endly.SeleniumServiceID)
			if !assert.Nil(t, err) {
				break
			}

			defer context.Close()
			if assert.Nil(t, err) {
				serviceResponse := service.Run(context, useCase.request)

				var baseCase = useCase.baseDir
				assert.Equal(t, "", serviceResponse.Error, baseCase)
				response, ok := serviceResponse.Response.(*endly.SeleniumServerStartResponse)
				if !ok {
					assert.Fail(t, fmt.Sprintf("process serviceResponse was empty %v %T", baseCase, serviceResponse.Response))
					continue
				}
				var actual = response.Pid
				assert.Equal(t, actual, useCase.Pid, "PID "+baseCase)
			}
		}
	}

}

func StartSeleniumMockServer(port int) error {
	baseDir := toolbox.CallerDirectory(3)
	var sessionPath = path.Join(baseDir, "test/selenium/http/")

	return endly.StartHTTPServer(port, &endly.HTTPServerTrips{
		IndexKeys:     []string{endly.MethodKey, endly.URLKey, endly.BodyKey, endly.ContentTypeKey},
		BaseDirectory: sessionPath,
	})
}

func TestSeleniumService_Calls(t *testing.T) {

	StartSeleniumMockServer(8116)

	manager := endly.NewManager()
	service, err := manager.Service(endly.SeleniumServiceID)
	assert.Nil(t, err)
	assert.NotNil(t, service)

	context := manager.NewContext(toolbox.NewContext())
	var targetHost = "127.0.0.1:8116"
	var target = url.NewResource(fmt.Sprintf("http://%v/", targetHost))

	serviceResponse := service.Run(context, &endly.SeleniumOpenSessionRequest{
		RemoteSelenium: target,
		Browser:        "firefox",
	})

	if assert.Equal(t, "", serviceResponse.Error) {
		response, ok := serviceResponse.Response.(*endly.SeleniumOpenSessionResponse)
		if assert.True(t, ok) {
			assert.EqualValues(t, response.SessionID, targetHost)
		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebDriverCallRequest{
		SessionID: targetHost,
		Call: &endly.SeleniumMethodCall{
			Method:     "Get",
			Parameters: []interface{}{"http://play.golang.org/?simple=1"},
		},
	})

	if assert.Equal(t, "", serviceResponse.Error) {

		_, ok := serviceResponse.Response.(*endly.SeleniumServiceCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#dummay",
		},
		Call: &endly.SeleniumMethodCall{
			Method:     "Clear",
			Parameters: []interface{}{},
		},
	})
	response, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
	if assert.True(t, ok) {
		assert.Equal(t, "failed to lookup element: css selector #dummay", response.LookupError)
	}
	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#code",
		},
		Call: &endly.SeleniumMethodCall{
			Method:     "Clear",
			Parameters: []interface{}{},
		},
	})

	if assert.Equal(t, "", serviceResponse.Error) {
		_, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#code",
		},
		Call: &endly.SeleniumMethodCall{
			Method: "SendKeys",
			Parameters: []interface{}{
				code,
			},
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {
		_, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,
		Call: &endly.SeleniumMethodCall{
			Method:     "Click",
			Parameters: []interface{}{},
			Wait:       &endly.SeleniumWait{SleepInMs: 1},
		},
		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#run",
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {
		_, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#output",
		},
		Call: &endly.SeleniumMethodCall{
			Method:     "Text",
			Parameters: []interface{}{},
			Wait: &endly.SeleniumWait{
				Repeat:       20,
				SleepInMs:    100,
				ExitCriteria: "$value:/WebDriver/",
			},
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {
		callResponse, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {
			assert.True(t, strings.Contains(toolbox.AsString(callResponse.Result[0]), "Hello WebDriver!"))
		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebDriverCallRequest{
		SessionID: targetHost,
		Call: &endly.SeleniumMethodCall{
			Method:     "Close",
			Parameters: []interface{}{},
		},
	})

}

func TestSeleniumService_Run(t *testing.T) {

	StartSeleniumMockServer(8118)

	manager := endly.NewManager()
	service, err := manager.Service(endly.SeleniumServiceID)
	assert.Nil(t, err)
	assert.NotNil(t, service)

	context := manager.NewContext(toolbox.NewContext())
	var targetHost = "127.0.0.1:8118"
	var target = url.NewResource(fmt.Sprintf("http://%v/", targetHost))

	serviceResponse := service.Run(context, &endly.SeleniumRunRequest{
		RemoteSelenium: target,
		Browser:        "firefox",
		Actions: []*endly.SeleniumAction{
			{
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Get", nil, "http://play.golang.org/?simple=1"),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#code"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Clear", nil),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#code"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("SendKeys", nil, code),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#run"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Click", nil),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#output"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Text", &endly.SeleniumWait{
						Repeat:       20,
						SleepInMs:    100,
						ExitCriteria: "$value:/WebDriver/",
					}),
				},
			},
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {

		runResponse, ok := serviceResponse.Response.(*endly.SeleniumRunResponse)
		if assert.True(t, ok) {
			output, ok := runResponse.Data["#output"]
			if assert.True(t, ok) {
				outputMap := toolbox.AsMap(output)
				assert.EqualValues(t, "Hello WebDriver!\n\n\nProgram exited.", outputMap["Text"])
			}

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumCloseSessionRequest{
		SessionID: targetHost,
	})

}
<commit_msg>udpated test<commit_after>package endly_test

import (
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/viant/endly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"github.com/viant/toolbox/url"
	"strings"
	"testing"

	"path"
)

const code = `
	package main
	import "fmt"

	func main() {
		fmt.Println("Hello WebDriver!\n")
	}
`

func TestSeleniumService_Start(t *testing.T) {

	var credentialFile, err = GetDummyCredential()
	var target = url.NewResource("scp://127.0.0.1:22/", credentialFile)
	assert.Nil(t, err)
	var manager = endly.NewManager()
	var useCases = []struct {
		baseDir     string
		DataURLs    []string
		DataPayload []byte
		target      *url.Resource
		request     *endly.SeleniumServerStartRequest
		Pid         int
	}{
		{
			"test/selenium/start/inactive/darwin",
			[]string{
				"https://github.com/mozilla/geckodriver/releases/download/v0.19.1/geckodriver-v0.19.1-linux64.tar.gz",
				"http://selenium-release.storage.googleapis.com/3.4/selenium-server-standalone-3.4.0.jar",
			},
			[]byte("test"),
			url.NewResource("scp://127.0.0.1:22/", credentialFile),
			&endly.SeleniumServerStartRequest{
				Target:     target,
				Sdk:        "jdk",
				SdkVersion: "1.8",
				Version:    "3.4",
				Port:       8117,
			},
			28811,
		},
		{
			"test/selenium/start/active/darwin",
			[]string{
				"https://github.com/mozilla/geckodriver/releases/download/v0.19.1/geckodriver-v0.19.1-linux64.tar.gz",
				"http://selenium-release.storage.googleapis.com/3.4/selenium-server-standalone-3.4.0.jar",
			},
			[]byte("test"),
			url.NewResource("scp://127.0.0.1:22/", credentialFile),
			&endly.SeleniumServerStartRequest{
				Target:     target,
				Sdk:        "jdk",
				SdkVersion: "1.8",
				Version:    "3.4",
				Port:       8117,
			},
			28866,
		},
	}

	for _, useCase := range useCases {
		execService, err := GetReplayService(useCase.baseDir)
		if assert.Nil(t, err) {
			context, err := OpenTestContext(manager, useCase.target, execService)
			var state = context.State()

			if len(useCase.DataURLs) > 0 {
				storageService := storage.NewMemoryService()
				state.Put(endly.UseMemoryService, true)
				for _, setupURL := range useCase.DataURLs {
					err = storageService.Upload(setupURL, bytes.NewReader(useCase.DataPayload))
				}

				assert.Nil(t, err)
			}
			service, err := context.Service(endly.SeleniumServiceID)
			if !assert.Nil(t, err) {
				break
			}

			defer context.Close()
			if assert.Nil(t, err) {
				serviceResponse := service.Run(context, useCase.request)

				var baseCase = useCase.baseDir
				assert.Equal(t, "", serviceResponse.Error, baseCase)
				response, ok := serviceResponse.Response.(*endly.SeleniumServerStartResponse)
				if !ok {
					assert.Fail(t, fmt.Sprintf("process serviceResponse was empty %v %T", baseCase, serviceResponse.Response))
					continue
				}
				if response != nil {
					var actual= response.Pid
					assert.Equal(t, actual, useCase.Pid, "PID "+baseCase)
				}
			}
		}
	}

}

func StartSeleniumMockServer(port int) error {
	baseDir := toolbox.CallerDirectory(3)
	var sessionPath = path.Join(baseDir, "test/selenium/http/")

	return endly.StartHTTPServer(port, &endly.HTTPServerTrips{
		IndexKeys:     []string{endly.MethodKey, endly.URLKey, endly.BodyKey, endly.ContentTypeKey},
		BaseDirectory: sessionPath,
	})
}

func TestSeleniumService_Calls(t *testing.T) {

	StartSeleniumMockServer(8116)

	manager := endly.NewManager()
	service, err := manager.Service(endly.SeleniumServiceID)
	assert.Nil(t, err)
	assert.NotNil(t, service)

	context := manager.NewContext(toolbox.NewContext())
	var targetHost = "127.0.0.1:8116"
	var target = url.NewResource(fmt.Sprintf("http://%v/", targetHost))

	serviceResponse := service.Run(context, &endly.SeleniumOpenSessionRequest{
		RemoteSelenium: target,
		Browser:        "firefox",
	})

	if assert.Equal(t, "", serviceResponse.Error) {
		response, ok := serviceResponse.Response.(*endly.SeleniumOpenSessionResponse)
		if assert.True(t, ok) {
			assert.EqualValues(t, response.SessionID, targetHost)
		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebDriverCallRequest{
		SessionID: targetHost,
		Call: &endly.SeleniumMethodCall{
			Method:     "Get",
			Parameters: []interface{}{"http://play.golang.org/?simple=1"},
		},
	})

	if assert.Equal(t, "", serviceResponse.Error) {

		_, ok := serviceResponse.Response.(*endly.SeleniumServiceCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#dummay",
		},
		Call: &endly.SeleniumMethodCall{
			Method:     "Clear",
			Parameters: []interface{}{},
		},
	})
	response, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
	if assert.True(t, ok) {
		assert.Equal(t, "failed to lookup element: css selector #dummay", response.LookupError)
	}
	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#code",
		},
		Call: &endly.SeleniumMethodCall{
			Method:     "Clear",
			Parameters: []interface{}{},
		},
	})

	if assert.Equal(t, "", serviceResponse.Error) {
		_, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#code",
		},
		Call: &endly.SeleniumMethodCall{
			Method: "SendKeys",
			Parameters: []interface{}{
				code,
			},
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {
		_, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,
		Call: &endly.SeleniumMethodCall{
			Method:     "Click",
			Parameters: []interface{}{},
			Wait:       &endly.SeleniumWait{SleepInMs: 1},
		},
		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#run",
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {
		_, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebElementCallRequest{
		SessionID: targetHost,

		Selector: &endly.WebElementSelector{
			By:    "css selector",
			Value: "#output",
		},
		Call: &endly.SeleniumMethodCall{
			Method:     "Text",
			Parameters: []interface{}{},
			Wait: &endly.SeleniumWait{
				Repeat:       20,
				SleepInMs:    100,
				ExitCriteria: "$value:/WebDriver/",
			},
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {
		callResponse, ok := serviceResponse.Response.(*endly.SeleniumWebElementCallResponse)
		if assert.True(t, ok) {
			assert.True(t, strings.Contains(toolbox.AsString(callResponse.Result[0]), "Hello WebDriver!"))
		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumWebDriverCallRequest{
		SessionID: targetHost,
		Call: &endly.SeleniumMethodCall{
			Method:     "Close",
			Parameters: []interface{}{},
		},
	})

}

func TestSeleniumService_Run(t *testing.T) {

	StartSeleniumMockServer(8118)

	manager := endly.NewManager()
	service, err := manager.Service(endly.SeleniumServiceID)
	assert.Nil(t, err)
	assert.NotNil(t, service)

	context := manager.NewContext(toolbox.NewContext())
	var targetHost = "127.0.0.1:8118"
	var target = url.NewResource(fmt.Sprintf("http://%v/", targetHost))

	serviceResponse := service.Run(context, &endly.SeleniumRunRequest{
		RemoteSelenium: target,
		Browser:        "firefox",
		Actions: []*endly.SeleniumAction{
			{
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Get", nil, "http://play.golang.org/?simple=1"),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#code"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Clear", nil),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#code"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("SendKeys", nil, code),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#run"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Click", nil),
				},
			},
			{
				Selector: endly.NewWebElementSelector("", "#output"),
				Calls: []*endly.SeleniumMethodCall{
					endly.NewSeleniumMethodCall("Text", &endly.SeleniumWait{
						Repeat:       20,
						SleepInMs:    100,
						ExitCriteria: "$value:/WebDriver/",
					}),
				},
			},
		},
	})
	if assert.Equal(t, "", serviceResponse.Error) {

		runResponse, ok := serviceResponse.Response.(*endly.SeleniumRunResponse)
		if assert.True(t, ok) {
			output, ok := runResponse.Data["#output"]
			if assert.True(t, ok) {
				outputMap := toolbox.AsMap(output)
				assert.EqualValues(t, "Hello WebDriver!\n\n\nProgram exited.", outputMap["Text"])
			}

		}
	}

	serviceResponse = service.Run(context, &endly.SeleniumCloseSessionRequest{
		SessionID: targetHost,
	})

}
<|endoftext|>


<commit_before>// +build linux

package singlepoll

import (
	"context"
	"errors"
	"sync"
	"syscall"

	"github.com/gxed/eventfd"
	logging "github.com/ipfs/go-log"
)

var (
	ErrUnsupportedMode error = errors.New("only 'w' and 'r' modes are supported on this arch")
)

var (
	initOnce sync.Once
	workChan chan interface{}
	log      logging.EventLogger = logging.Logger("reuseport-poll")
)

type addPoll struct {
	fd     int
	events uint32
	ctx    context.Context
	wakeUp chan<- error
}

type ctxDone struct {
	fd int
}

func PollPark(reqctx context.Context, fd int, mode string) error {
	initOnce.Do(func() {
		workChan = make(chan interface{}, 128)
		go worker()
	})

	events := uint32(0)
	for _, c := range mode {
		switch c {
		case 'w':
			events |= syscall.EPOLLOUT
		case 'r':
			events |= syscall.EPOLLIN
		default:
			return ErrUnsupportedMode
		}
	}

	wakeUp := make(chan error)
	workChan <- addPoll{
		fd:     fd,
		events: events,
		ctx:    reqctx,
		wakeUp: wakeUp,
	}

	return <-wakeUp
}

func criticalError(msg string, err error) {
	log.Errorf("%s: %s.", msg, err.Error())
	log.Errorf("This is critical error, please report it at https://github.com/jbenet/go-reuseport/issues/new")
	log.Errorf("Bailing out. You are on your own. Good luck.")

	for {
		select {
		case <-backgroundctx.Done():
			return
		case unit := <-workChan:
			switch u := unit.(type) {
			case addPoll:
				u.wakeUp <- err
			default:
			}
		}
	}
}

func worker() {
	epfd, err := syscall.EpollCreate1(0)
	if err != nil {
		criticalError("EpollCreate1(0) failed", err)
	}
	evfd, err := eventfd.New()
	if err != nil {
		criticalError("eventfd.New() failed", err)
	}

	pool := make(map[int]addPoll)

	{
		event := syscall.EpollEvent{
			Events: syscall.EPOLLIN,
			Fd:     int32(evfd.Fd()),
		}
		syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, evfd.Fd(), &event)
	}
	go poller(epfd, evfd)

	remove := func(fd int) *addPoll {
		unit, ok := pool[fd]
		if ok {
			syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, unit.fd, nil)
			delete(pool, fd)
			close(unit.wakeUp)
		}
		return &unit
	}
	for {
		select {
		case <-backgroundctx.Done():
			evfd.WriteEvents(1)
			return
		case unit := <-workChan:
			switch u := unit.(type) {
			case addPoll:
				event := syscall.EpollEvent{
					Events: u.events | syscall.EPOLLONESHOT,
					Fd:     int32(u.fd),
				}

				// Make copies for *I* before we add it to Epoll group
				wrapWakeUp := make(chan error)
				wakeUp := u.wakeUp
				u.wakeUp = wrapWakeUp

				if _, ok := pool[u.fd]; ok {
					panic("duplicate fd") // safe guard against bad close calls
				}
				pool[u.fd] = u

				err := syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, u.fd, &event)
				if err != nil {
					delete(pool, u.fd)
					u.wakeUp <- err
				}

				// *I*
				reqCtx := u.ctx
				fd := u.fd

				go func() {
					select {
					case err := <-wrapWakeUp:
						wakeUp <- err
					case <-reqCtx.Done():
						workChan <- ctxDone{
							fd: fd,
						}
						<-wrapWakeUp
						wakeUp <- reqCtx.Err()
					}
				}()

			case []syscall.EpollEvent:
				for _, event := range u {
					remove(int(event.Fd))
				}
			case ctxDone:
				remove(u.fd)
			}
		}
	}

}

func poller(epfd int, evfd *eventfd.EventFD) {
	for {
		// do not reuse the array as we will be passing it over channel
		// 128 is quite arbitrary
		// to small and number of EpollWait calls would increase
		// to big and GC overhead increases
		events := make([]syscall.EpollEvent, 128)
		n, err := syscall.EpollWait(epfd, events, -1)
		if err != nil {
			log.Errorf("EpollWait returned error: %s. Continuing.", err.Error())
			continue
		}

		for i := 0; i < n; i++ {
			if int(events[i].Fd) == evfd.Fd() {
				syscall.Close(epfd)
				evfd.Close()
				return
			}
		}
		workChan <- events[:n]
	}
}
<commit_msg>EINTR on EpollWait is normal an can be ignored<commit_after>// +build linux

package singlepoll

import (
	"context"
	"errors"
	"sync"
	"syscall"

	"github.com/gxed/eventfd"
	logging "github.com/ipfs/go-log"
)

var (
	ErrUnsupportedMode error = errors.New("only 'w' and 'r' modes are supported on this arch")
)

var (
	initOnce sync.Once
	workChan chan interface{}
	log      logging.EventLogger = logging.Logger("reuseport-poll")
)

type addPoll struct {
	fd     int
	events uint32
	ctx    context.Context
	wakeUp chan<- error
}

type ctxDone struct {
	fd int
}

func PollPark(reqctx context.Context, fd int, mode string) error {
	initOnce.Do(func() {
		workChan = make(chan interface{}, 128)
		go worker()
	})

	events := uint32(0)
	for _, c := range mode {
		switch c {
		case 'w':
			events |= syscall.EPOLLOUT
		case 'r':
			events |= syscall.EPOLLIN
		default:
			return ErrUnsupportedMode
		}
	}

	wakeUp := make(chan error)
	workChan <- addPoll{
		fd:     fd,
		events: events,
		ctx:    reqctx,
		wakeUp: wakeUp,
	}

	return <-wakeUp
}

func criticalError(msg string, err error) {
	log.Errorf("%s: %s.", msg, err.Error())
	log.Errorf("This is critical error, please report it at https://github.com/jbenet/go-reuseport/issues/new")
	log.Errorf("Bailing out. You are on your own. Good luck.")

	for {
		select {
		case <-backgroundctx.Done():
			return
		case unit := <-workChan:
			switch u := unit.(type) {
			case addPoll:
				u.wakeUp <- err
			default:
			}
		}
	}
}

func worker() {
	epfd, err := syscall.EpollCreate1(0)
	if err != nil {
		criticalError("EpollCreate1(0) failed", err)
	}
	evfd, err := eventfd.New()
	if err != nil {
		criticalError("eventfd.New() failed", err)
	}

	pool := make(map[int]addPoll)

	{
		event := syscall.EpollEvent{
			Events: syscall.EPOLLIN,
			Fd:     int32(evfd.Fd()),
		}
		syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, evfd.Fd(), &event)
	}
	go poller(epfd, evfd)

	remove := func(fd int) *addPoll {
		unit, ok := pool[fd]
		if ok {
			syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, unit.fd, nil)
			delete(pool, fd)
			close(unit.wakeUp)
		}
		return &unit
	}
	for {
		select {
		case <-backgroundctx.Done():
			evfd.WriteEvents(1)
			return
		case unit := <-workChan:
			switch u := unit.(type) {
			case addPoll:
				event := syscall.EpollEvent{
					Events: u.events | syscall.EPOLLONESHOT,
					Fd:     int32(u.fd),
				}

				// Make copies for *I* before we add it to Epoll group
				wrapWakeUp := make(chan error)
				wakeUp := u.wakeUp
				u.wakeUp = wrapWakeUp

				if _, ok := pool[u.fd]; ok {
					panic("duplicate fd") // safe guard against bad close calls
				}
				pool[u.fd] = u

				err := syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, u.fd, &event)
				if err != nil {
					delete(pool, u.fd)
					u.wakeUp <- err
				}

				// *I*
				reqCtx := u.ctx
				fd := u.fd

				go func() {
					select {
					case err := <-wrapWakeUp:
						wakeUp <- err
					case <-reqCtx.Done():
						workChan <- ctxDone{
							fd: fd,
						}
						<-wrapWakeUp
						wakeUp <- reqCtx.Err()
					}
				}()

			case []syscall.EpollEvent:
				for _, event := range u {
					remove(int(event.Fd))
				}
			case ctxDone:
				remove(u.fd)
			}
		}
	}

}

func poller(epfd int, evfd *eventfd.EventFD) {
	for {
		// do not reuse the array as we will be passing it over channel
		// 128 is quite arbitrary
		// to small and number of EpollWait calls would increase
		// to big and GC overhead increases
		events := make([]syscall.EpollEvent, 128)
		n, err := syscall.EpollWait(epfd, events, -1)

		switch err {
		case nil:
			// everything is great
		case syscall.EINTR:
			// ignore
			continue
		default:
			// log
			log.Errorf("EpollWait returned error: %s. Continuing.", err.Error())
			continue
		}

		for i := 0; i < n; i++ {
			if int(events[i].Fd) == evfd.Fd() {
				syscall.Close(epfd)
				evfd.Close()
				return
			}
		}
		workChan <- events[:n]
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"gopkg.in/yaml.v2"
)

type config struct {
	Users     []string
	Orgs      []string
	Whitelist []string
	Token     string
}

func contains(list []string, s *string) bool {
	for _, item := range list {
		if item == *s {
			return true
		}
	}
	return false
}

func printIssue(issue *github.Issue) {
	if *issue.Number < 10 {
		fmt.Printf("    [%d]  - %s - %s\n", *issue.Number, *issue.Title, *issue.URL)
	} else {
		fmt.Printf("    [%d] - %s - %s\n", *issue.Number, *issue.Title, *issue.URL)
	}
}

func printPullRequest(pr *github.PullRequest) {
	fmt.Printf("    Pull request: %s (%s)", *pr.Title, *pr.CommitsURL)
}

func readConfig(file string) config {
	var config config

	raw, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	err = yaml.Unmarshal(raw, &config)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	return config
}

func buildClient(config config) *github.Client {
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: config.Token})
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	client := github.NewClient(tc)

	return client
}

func processRepositories(client *github.Client, ctx context.Context, entity string, repos []*github.Repository, whitelist []string) {
	fmt.Printf("Open issues for %s\n", entity)
	for _, repo := range repos {
		if whitelist != nil && !contains(whitelist, repo.Name) {
			continue
		}

		fmt.Printf("Repository: %s\n", *repo.Name)
		prOpt := &github.PullRequestListOptions{ListOptions: github.ListOptions{PerPage: 100}}
		prs, _, err := client.PullRequests.List(ctx, entity, *repo.Name, prOpt)

		if err != nil {
			fmt.Println("Error with PR's:", err)
		} else {
			if len(prs) != 0 {
				for _, pr := range prs {
					printPullRequest(pr)
				}
			}
		}

		issueOpt := &github.IssueListByRepoOptions{ListOptions: github.ListOptions{PerPage: 1000}}
		issues, _, err := client.Issues.ListByRepo(ctx, entity, *repo.Name, issueOpt)
		if err != nil {
			fmt.Println("Error with Issues:", err)
		} else {
			if len(issues) != 0 {
				for _, issue := range issues {
					printIssue(issue)
				}
			}
		}
	}
}

func processEntities(config config, client *github.Client) {
	ctx := context.Background()
	for _, org := range config.Orgs {
		opt := &github.RepositoryListByOrgOptions{Type: "all", ListOptions: github.ListOptions{PerPage: 1000}}
		repos, _, err := client.Repositories.ListByOrg(ctx, org, opt)

		if err != nil {
			fmt.Println("Error:", err)
		} else {
			processRepositories(client, ctx, org, repos, config.Whitelist)
		}
	}

	for _, user := range config.Users {
		opt := &github.RepositoryListOptions{Type: "all", ListOptions: github.ListOptions{PerPage: 1000}}
		repos, _, err := client.Repositories.List(ctx, user, opt)

		if err != nil {
			fmt.Println("Error:", err)
		} else {
			processRepositories(client, ctx, user, repos, config.Whitelist)
		}
	}
}

func main() {
	configPtr := flag.String("config", "config.yml", "Path to configuration yaml file")
	flag.Parse()
	config := readConfig(*configPtr)
	client := buildClient(config)
	processEntities(config, client)
}
<commit_msg>Skip repos where entity is not owner. Closes #4<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"gopkg.in/yaml.v2"
)

type config struct {
	Users     []string
	Orgs      []string
	Whitelist []string
	Token     string
}

func contains(list []string, s *string) bool {
	for _, item := range list {
		if item == *s {
			return true
		}
	}
	return false
}

func printIssue(issue *github.Issue) {
	if *issue.Number < 10 {
		fmt.Printf("    [%d]  - %s - %s\n", *issue.Number, *issue.Title, *issue.URL)
	} else {
		fmt.Printf("    [%d] - %s - %s\n", *issue.Number, *issue.Title, *issue.URL)
	}
}

func printPullRequest(pr *github.PullRequest) {
	fmt.Printf("    Pull request: %s (%s)", *pr.Title, *pr.CommitsURL)
}

func readConfig(file string) config {
	var config config

	raw, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	err = yaml.Unmarshal(raw, &config)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	return config
}

func buildClient(config config) *github.Client {
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: config.Token})
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	client := github.NewClient(tc)

	return client
}

func processRepositories(client *github.Client, ctx context.Context, entity string, repos []*github.Repository, whitelist []string) {
	fmt.Printf("Open issues for %s\n", entity)
	for _, repo := range repos {
		if whitelist != nil && !contains(whitelist, repo.Name) {
			continue
		}

		if *repo.Owner.Login != entity {
			continue
		}

		fmt.Printf("Repository: %s\n", *repo.Name)
		prOpt := &github.PullRequestListOptions{ListOptions: github.ListOptions{PerPage: 100}}
		prs, _, err := client.PullRequests.List(ctx, entity, *repo.Name, prOpt)

		if err != nil {
			fmt.Println("Error with PR's:", err)
		} else {
			if len(prs) != 0 {
				for _, pr := range prs {
					printPullRequest(pr)
				}
			}
		}

		issueOpt := &github.IssueListByRepoOptions{ListOptions: github.ListOptions{PerPage: 1000}}
		issues, _, err := client.Issues.ListByRepo(ctx, entity, *repo.Name, issueOpt)
		if err != nil {
			fmt.Println("Error with Issues:", err)
		} else {
			if len(issues) != 0 {
				for _, issue := range issues {
					printIssue(issue)
				}
			}
		}
	}
}

func processEntities(config config, client *github.Client) {
	ctx := context.Background()
	for _, org := range config.Orgs {
		opt := &github.RepositoryListByOrgOptions{Type: "all", ListOptions: github.ListOptions{PerPage: 1000}}
		repos, _, err := client.Repositories.ListByOrg(ctx, org, opt)

		if err != nil {
			fmt.Println("Error:", err)
		} else {
			processRepositories(client, ctx, org, repos, config.Whitelist)
		}
	}

	for _, user := range config.Users {
		opt := &github.RepositoryListOptions{Type: "all", ListOptions: github.ListOptions{PerPage: 1000}}
		repos, _, err := client.Repositories.List(ctx, user, opt)
		if err != nil {
			fmt.Println("Error:", err)
		} else {
			processRepositories(client, ctx, user, repos, config.Whitelist)
		}
	}
}

func main() {
	configPtr := flag.String("config", "config.yml", "Path to configuration yaml file")
	flag.Parse()
	config := readConfig(*configPtr)
	client := buildClient(config)
	processEntities(config, client)
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/luci/luci-go/server/discovery"
	"github.com/luci/luci-go/server/middleware"
	"github.com/luci/luci-go/server/prpc"
	"golang.org/x/net/context"

	"github.com/nodirt/prpc-example/helloworld/proto"
)

// init registers HTTP routes.
func init() {
	// pRPC uses httprouter that implements http.Handler.
	router := httprouter.New()

	// Configure pRPC server.
	var server prpc.Server
	server.CustomAuthenticator = true // omit authentication.
	helloworld.RegisterGreeterServer(&server, &greeterService{})
	discovery.Enable(&server)
	server.InstallHandlers(router, base)

	// Plug the router into std HTTP stack.
	http.DefaultServeMux.Handle("/", router)
}

// base is the root of the middleware chain.
// This is the place where you can add a hook for all methods
// or configure the context.
func base(h middleware.Handler) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		h(context.Background(), w, r, p)
	}
}
<commit_msg>Add index page<commit_after>package server

import (
	"fmt"
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/luci/luci-go/server/discovery"
	"github.com/luci/luci-go/server/middleware"
	"github.com/luci/luci-go/server/prpc"
	"golang.org/x/net/context"

	"github.com/nodirt/prpc-example/helloworld/proto"
)

// init registers HTTP routes.
func init() {
	// pRPC uses httprouter that implements http.Handler.
	router := httprouter.New()

	// Configure pRPC server.
	var server prpc.Server
	server.CustomAuthenticator = true // omit authentication.
	helloworld.RegisterGreeterServer(&server, &greeterService{})
	discovery.Enable(&server)
	server.InstallHandlers(router, base)

	router.GET("/", index)

	// Plug the router into std HTTP stack.
	http.DefaultServeMux.Handle("/", router)
}

// base is the root of the middleware chain.
// This is the place where you can add a hook for all methods
// or configure the context.
func base(h middleware.Handler) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		h(context.Background(), w, r, p)
	}
}

var indexPage = `
<title>Helloworld</title>

Use rpc tool to talk to this server,
as described in
pPRC blog post.

`

func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintln(w, indexPage)
}
<|endoftext|>


<commit_before>package wiki

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"time"

	strip "github.com/grokify/html-strip-tags-go"

	httpdate "github.com/Songmu/go-httpdate"
	"github.com/cooper/quiki/wikifier"
)

// DisplayPage represents a page result to display.
type DisplayPage struct {

	// basename of the page, with the extension
	File string `json:"file,omitempty"`

	// basename of the page, without the extension
	Name string `json:"name,omitempty"`

	// absolute file path of the page
	Path string `json:"path,omitempty"`

	// the page content (HTML)
	Content wikifier.HTML `json:"content,omitempty"`

	// time when the page was last modified.
	// if Generated is true, this is the current time.
	// if FromCache is true, this is the modified date of the cache file.
	// otherwise, this is the modified date of the page file itself.
	Modified     *time.Time `json:"modified,omitempty"`
	ModifiedHTTP string     `json:"modified_http,omitempty"` // HTTP formatted for Last-Modified

	// CSS generated for the page from style{} blocks
	CSS string `json:"css,omitempty"`

	// true if this content was read from a cache file. opposite of Generated
	FromCache bool `json:"cached,omitempty"`

	// true if the content being served was just generated on the fly.
	// opposite of FromCache
	Generated bool `json:"generated,omitempty"`

	// true if this request resulted in the writing of a new cache file.
	// this can only be true if Generated is true
	CacheGenerated bool `json:"cache_gen,omitempty"`

	// true if this request resulted in the writing of a text file.
	// this can only be true if Generated is true
	TextGenerated bool `json:"text_gen,omitempty"`

	// true if the page has not yet been published for public viewing.
	// this only occurs when it is specified that serving drafts is OK,
	// since normally a draft page instead results in a DisplayError.
	Draft bool `json:"draft,omitempty"`

	// warnings produced by the parser
	Warnings []string `json:"warnings,omitempty"`

	// time when the page was created, as extracted from
	// the special @page.created variable
	Created     *time.Time `json:"created,omitempty"`
	CreatedHTTP string     `json:"created_http,omitempty"` // HTTP formatted

	// name of the page author, as extracted from the special @page.author
	// variable
	Author string `json:"author,omitempty"`

	// list of categories the page belongs to, without the '.cat' extension
	Categories []string `json:"categories,omitempty"`

	// page title as extracted from the special @page.title variable, including
	// any possible HTML-encoded formatting
	FmtTitle wikifier.HTML `json:"fmt_title,omitempty"`

	// like FmtTitle except that all text formatting has been stripped.
	// suitable for use in the <title> tag
	Title string `json:"title,omitempty"`
}

type pageJSONManifest struct {
	CSS        string   `json:"css,omitempty"`
	Categories []string `json:"categories,omitempty"`
	Warnings   []string `json:"warnings,omitempty"`
	Error      string   `json:"error,omitempty"`
	wikifier.PageInfo
}

// NewPage creates a Page given its filepath and configures it for
// use with this Wiki.
func (w *Wiki) NewPage(name string) *wikifier.Page {
	p := wikifier.NewPage(w.pathForPage(name, false, ""))
	p.Opt = &w.Opt
	return p
}

// DisplayPage returns the display result for a page.
func (w *Wiki) DisplayPage(name string) interface{} {
	return w.DisplayPageDraft(name, false)
}

// DisplayPageDraft returns the display result for a page.
//
// Unlike DisplayPage, if draftOK is true, the content is served even if it is
// marked as draft.
//
func (w *Wiki) DisplayPageDraft(name string, draftOK bool) interface{} {
	var r DisplayPage

	// create the page
	page := w.NewPage(name)

	// file does not exist
	if !page.Exists() {
		return DisplayError{
			Error:         "Page does not exist.",
			DetailedError: "Page '" + page.FilePath + "' does not exist.",
		}
	}

	// filename and path info
	path := page.Path()
	r.File = page.Name()
	r.Name = page.NameNE()
	r.Path = path

	// FIRST redirect check - symbolic link
	if page.IsSymlink() {
		return DisplayRedirect{Redirect: page.Redirect()}
	}

	// caching is enabled, so serve the cached copy if available
	if w.Opt.Page.EnableCache && page.CacheExists() {
		if errOrRedir := w.displayCachedPage(page, &r, draftOK); errOrRedir != nil {
			return errOrRedir
		}
		if r.FromCache {
			return r
		}
	}

	// Safe point - we will be generating the page right now.

	// parse the page
	//
	// if an error occurs, parse it again in variable-only mode.
	// then hopefully we can at least get the metadata and categories
	//
	err := page.Parse()
	if err != nil {
		page.VarsOnly = true
		page.Parse()
		// TODO: add page to categories
		// TODO: cache the error
		return DisplayError{Error: err.Error(), ParseError: true}
	}

	// if this is a draft and we're not serving drafts, pretend
	// that the page does not exist
	if !draftOK && page.Draft() {
		return DisplayError{Error: "Page has not yet been publised.", Draft: true}
	}

	// THIRD redirect check -
	// this is for pages we just parsed with @page.redirect
	if redir := page.Redirect(); redir != "" {
		w.writeRedirectCache(page)
		return DisplayRedirect{Redirect: redir}
	}

	// generate HTML and metadata
	create := page.Created()
	if !create.IsZero() {
		r.Created = &create
		r.CreatedHTTP = httpdate.Time2Str(create)
	}
	mod := page.Modified()
	r.Generated = true
	r.Title = page.Title()
	r.FmtTitle = page.FmtTitle()
	r.Author = page.Author()
	r.Draft = page.Draft()
	r.Modified = &mod
	r.ModifiedHTTP = httpdate.Time2Str(mod)
	r.Content = page.HTML()
	r.CSS = page.CSS()

	// TODO: should we include the page object?
	// TODO: warnings

	// update categories
	w.updatePageCategories(page)
	r.Categories = page.Categories()

	// only do these things if content was generated
	if !page.VarsOnly {

		// write cache file if enabled
		if dispErr := w.writePageCache(page, &r); dispErr != nil {
			return dispErr
		}

		// write search file if enabled
		if dispErr := w.writePageText(page, &r); dispErr != nil {
			return dispErr
		}
	}

	return r
}

func (w *Wiki) writeRedirectCache(page *wikifier.Page) {

	// caching isn't enabled
	if !page.Opt.Page.EnableCache || page.CachePath() == "" {
		return
	}

	// open the cache file for writing
	cacheFile, err := os.Create(page.CachePath())
	defer cacheFile.Close()
	if err != nil {
		return
	}

	// create manifest with just page info (includes redirect)
	j, err := json.Marshal(pageJSONManifest{PageInfo: page.Info()})
	if err != nil {
		return
	}

	cacheFile.Write(j)
}

func (w *Wiki) writePageCache(page *wikifier.Page, r *DisplayPage) interface{} {

	// caching isn't enabled
	if !page.Opt.Page.EnableCache || page.CachePath() == "" {
		return nil
	}

	// open the cache file for writing
	cacheFile, err := os.Create(page.CachePath())
	defer cacheFile.Close()
	if err != nil {
		return DisplayError{
			Error:         "Could not write page cache file.",
			DetailedError: "Open '" + page.CachePath() + "' for write error: " + err.Error(),
		}
	}

	// generate page info
	info := pageJSONManifest{
		CSS:        page.CSS(),
		Categories: []string{}, // TODO
		Warnings:   []string{}, // TODO
		Error:      "",         // TODO
		PageInfo:   page.Info(),
	}

	// encode as json
	j, err := json.Marshal(info)
	if err != nil {
		return DisplayError{
			Error:         "Could not write page cache file.",
			DetailedError: "JSON encode error: " + err.Error(),
		}
	}

	// save prefixing data
	cacheFile.Write(j)
	cacheFile.Write([]byte{'\n'})

	// save content
	content := string(r.Content)
	cacheFile.WriteString(content)
	if len(content) != 0 && content[len(content)-1] != '\n' {
		cacheFile.Write([]byte{'\n'})
	}

	// update result with real cache modified times
	mod := page.CacheModified()
	r.Modified = &mod
	r.ModifiedHTTP = httpdate.Time2Str(mod)
	r.CacheGenerated = true

	return nil // success
}

func (w *Wiki) writePageText(page *wikifier.Page, r *DisplayPage) interface{} {

	// search optimization isn't enabled
	if !page.Opt.Search.Enable || page.SearchPath() == "" || r.Content == "" {
		return nil
	}

	// open the text file for writing
	textFile, err := os.Create(page.SearchPath())
	defer textFile.Close()
	if err != nil {
		return DisplayError{
			Error:         "Could not write page text file.",
			DetailedError: "Open '" + page.SearchPath() + "' for write error: " + err.Error(),
		}
	}

	// save the content with HTML tags stripped
	textFile.WriteString(strip.StripTags(string(r.Content)))

	r.TextGenerated = true
	return nil // success
}

func (w *Wiki) displayCachedPage(page *wikifier.Page, r *DisplayPage, draftOK bool) interface{} {
	cacheModify := page.CacheModified()
	timeStr := httpdate.Time2Str(cacheModify)

	// the page's file is more recent than the cache file.
	// discard the outdated cached copy
	if page.Modified().After(cacheModify) {
		os.Remove(page.CachePath())
		return nil // OK
	}

	content := "<!-- cached page dated " + timeStr + " -->\n"

	// open cache file for reading
	cacheContent, err := ioutil.ReadFile(page.CachePath())
	if err != nil {
		return DisplayError{
			Error:         "Could not read page cache file.",
			DetailedError: "Open '" + page.CachePath() + "' for read error: " + err.Error(),
		}
	}

	// find the first line
	var jsonData []byte
	firstNL := bytes.IndexByte(cacheContent, '\n')
	if firstNL != -1 && firstNL < len(cacheContent) {
		jsonData = cacheContent[:firstNL+1]
		cacheContent = cacheContent[firstNL:]
	}

	// the rest is the html content
	content += string(cacheContent)

	// decode the manifest
	var info pageJSONManifest
	if err := json.Unmarshal(jsonData, &info); err != nil {
		return DisplayError{
			Error:         "Could not read page cache file.",
			DetailedError: "JSON encode error: " + err.Error(),
		}
	}

	// if this is a draft and we're not serving drafts, pretend
	// that the page does not exist
	if !draftOK && info.Draft {
		return DisplayError{Error: "Page has not yet been publised.", Draft: true}
	}

	// cached error
	if info.Error != "" {
		return DisplayError{Error: info.Error}
	}

	// cached redirect
	if info.Redirect != "" {
		return DisplayRedirect{Redirect: info.Redirect}
	}

	// update result with stuff from cache
	if info.Created != nil {
		r.Created = info.Created
		r.CreatedHTTP = httpdate.Time2Str(*info.Created)
	}
	r.Draft = info.Draft
	r.Author = info.Author
	r.Title = info.Title
	r.FmtTitle = info.FmtTitle
	r.FromCache = true
	r.CSS = info.CSS
	r.Content = wikifier.HTML(content)
	r.Modified = &cacheModify
	r.ModifiedHTTP = httpdate.Time2Str(cacheModify)

	return nil // success
}
<commit_msg>only generate css once<commit_after>package wiki

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"time"

	strip "github.com/grokify/html-strip-tags-go"

	httpdate "github.com/Songmu/go-httpdate"
	"github.com/cooper/quiki/wikifier"
)

// DisplayPage represents a page result to display.
type DisplayPage struct {

	// basename of the page, with the extension
	File string `json:"file,omitempty"`

	// basename of the page, without the extension
	Name string `json:"name,omitempty"`

	// absolute file path of the page
	Path string `json:"path,omitempty"`

	// the page content (HTML)
	Content wikifier.HTML `json:"content,omitempty"`

	// time when the page was last modified.
	// if Generated is true, this is the current time.
	// if FromCache is true, this is the modified date of the cache file.
	// otherwise, this is the modified date of the page file itself.
	Modified     *time.Time `json:"modified,omitempty"`
	ModifiedHTTP string     `json:"modified_http,omitempty"` // HTTP formatted for Last-Modified

	// CSS generated for the page from style{} blocks
	CSS string `json:"css,omitempty"`

	// true if this content was read from a cache file. opposite of Generated
	FromCache bool `json:"cached,omitempty"`

	// true if the content being served was just generated on the fly.
	// opposite of FromCache
	Generated bool `json:"generated,omitempty"`

	// true if this request resulted in the writing of a new cache file.
	// this can only be true if Generated is true
	CacheGenerated bool `json:"cache_gen,omitempty"`

	// true if this request resulted in the writing of a text file.
	// this can only be true if Generated is true
	TextGenerated bool `json:"text_gen,omitempty"`

	// true if the page has not yet been published for public viewing.
	// this only occurs when it is specified that serving drafts is OK,
	// since normally a draft page instead results in a DisplayError.
	Draft bool `json:"draft,omitempty"`

	// warnings produced by the parser
	Warnings []string `json:"warnings,omitempty"`

	// time when the page was created, as extracted from
	// the special @page.created variable
	Created     *time.Time `json:"created,omitempty"`
	CreatedHTTP string     `json:"created_http,omitempty"` // HTTP formatted

	// name of the page author, as extracted from the special @page.author
	// variable
	Author string `json:"author,omitempty"`

	// list of categories the page belongs to, without the '.cat' extension
	Categories []string `json:"categories,omitempty"`

	// page title as extracted from the special @page.title variable, including
	// any possible HTML-encoded formatting
	FmtTitle wikifier.HTML `json:"fmt_title,omitempty"`

	// like FmtTitle except that all text formatting has been stripped.
	// suitable for use in the <title> tag
	Title string `json:"title,omitempty"`
}

type pageJSONManifest struct {
	CSS        string   `json:"css,omitempty"`
	Categories []string `json:"categories,omitempty"`
	Warnings   []string `json:"warnings,omitempty"`
	Error      string   `json:"error,omitempty"`
	wikifier.PageInfo
}

// NewPage creates a Page given its filepath and configures it for
// use with this Wiki.
func (w *Wiki) NewPage(name string) *wikifier.Page {
	p := wikifier.NewPage(w.pathForPage(name, false, ""))
	p.Opt = &w.Opt
	return p
}

// DisplayPage returns the display result for a page.
func (w *Wiki) DisplayPage(name string) interface{} {
	return w.DisplayPageDraft(name, false)
}

// DisplayPageDraft returns the display result for a page.
//
// Unlike DisplayPage, if draftOK is true, the content is served even if it is
// marked as draft.
//
func (w *Wiki) DisplayPageDraft(name string, draftOK bool) interface{} {
	var r DisplayPage

	// create the page
	page := w.NewPage(name)

	// file does not exist
	if !page.Exists() {
		return DisplayError{
			Error:         "Page does not exist.",
			DetailedError: "Page '" + page.FilePath + "' does not exist.",
		}
	}

	// filename and path info
	path := page.Path()
	r.File = page.Name()
	r.Name = page.NameNE()
	r.Path = path

	// FIRST redirect check - symbolic link
	if page.IsSymlink() {
		return DisplayRedirect{Redirect: page.Redirect()}
	}

	// caching is enabled, so serve the cached copy if available
	if w.Opt.Page.EnableCache && page.CacheExists() {
		if errOrRedir := w.displayCachedPage(page, &r, draftOK); errOrRedir != nil {
			return errOrRedir
		}
		if r.FromCache {
			return r
		}
	}

	// Safe point - we will be generating the page right now.

	// parse the page
	//
	// if an error occurs, parse it again in variable-only mode.
	// then hopefully we can at least get the metadata and categories
	//
	err := page.Parse()
	if err != nil {
		page.VarsOnly = true
		page.Parse()
		// TODO: add page to categories
		// TODO: cache the error
		return DisplayError{Error: err.Error(), ParseError: true}
	}

	// if this is a draft and we're not serving drafts, pretend
	// that the page does not exist
	if !draftOK && page.Draft() {
		return DisplayError{Error: "Page has not yet been publised.", Draft: true}
	}

	// THIRD redirect check -
	// this is for pages we just parsed with @page.redirect
	if redir := page.Redirect(); redir != "" {
		w.writeRedirectCache(page)
		return DisplayRedirect{Redirect: redir}
	}

	// generate HTML and metadata
	create := page.Created()
	if !create.IsZero() {
		r.Created = &create
		r.CreatedHTTP = httpdate.Time2Str(create)
	}
	mod := page.Modified()
	r.Generated = true
	r.Title = page.Title()
	r.FmtTitle = page.FmtTitle()
	r.Author = page.Author()
	r.Draft = page.Draft()
	r.Modified = &mod
	r.ModifiedHTTP = httpdate.Time2Str(mod)
	r.Content = page.HTML()
	r.CSS = page.CSS()

	// TODO: should we include the page object?
	// TODO: warnings

	// update categories
	w.updatePageCategories(page)
	r.Categories = page.Categories()

	// only do these things if content was generated
	if !page.VarsOnly {

		// write cache file if enabled
		if dispErr := w.writePageCache(page, &r); dispErr != nil {
			return dispErr
		}

		// write search file if enabled
		if dispErr := w.writePageText(page, &r); dispErr != nil {
			return dispErr
		}
	}

	return r
}

func (w *Wiki) writeRedirectCache(page *wikifier.Page) {

	// caching isn't enabled
	if !page.Opt.Page.EnableCache || page.CachePath() == "" {
		return
	}

	// open the cache file for writing
	cacheFile, err := os.Create(page.CachePath())
	defer cacheFile.Close()
	if err != nil {
		return
	}

	// create manifest with just page info (includes redirect)
	j, err := json.Marshal(pageJSONManifest{PageInfo: page.Info()})
	if err != nil {
		return
	}

	cacheFile.Write(j)
}

func (w *Wiki) writePageCache(page *wikifier.Page, r *DisplayPage) interface{} {

	// caching isn't enabled
	if !page.Opt.Page.EnableCache || page.CachePath() == "" {
		return nil
	}

	// open the cache file for writing
	cacheFile, err := os.Create(page.CachePath())
	defer cacheFile.Close()
	if err != nil {
		return DisplayError{
			Error:         "Could not write page cache file.",
			DetailedError: "Open '" + page.CachePath() + "' for write error: " + err.Error(),
		}
	}

	// generate page info
	info := pageJSONManifest{
		CSS:        r.CSS,
		Categories: r.Categories,
		Warnings:   []string{}, // TODO
		Error:      "",         // TODO
		PageInfo:   page.Info(),
	}

	// encode as json
	j, err := json.Marshal(info)
	if err != nil {
		return DisplayError{
			Error:         "Could not write page cache file.",
			DetailedError: "JSON encode error: " + err.Error(),
		}
	}

	// save prefixing data
	cacheFile.Write(j)
	cacheFile.Write([]byte{'\n'})

	// save content
	content := string(r.Content)
	cacheFile.WriteString(content)
	if len(content) != 0 && content[len(content)-1] != '\n' {
		cacheFile.Write([]byte{'\n'})
	}

	// update result with real cache modified times
	mod := page.CacheModified()
	r.Modified = &mod
	r.ModifiedHTTP = httpdate.Time2Str(mod)
	r.CacheGenerated = true

	return nil // success
}

func (w *Wiki) writePageText(page *wikifier.Page, r *DisplayPage) interface{} {

	// search optimization isn't enabled
	if !page.Opt.Search.Enable || page.SearchPath() == "" || r.Content == "" {
		return nil
	}

	// open the text file for writing
	textFile, err := os.Create(page.SearchPath())
	defer textFile.Close()
	if err != nil {
		return DisplayError{
			Error:         "Could not write page text file.",
			DetailedError: "Open '" + page.SearchPath() + "' for write error: " + err.Error(),
		}
	}

	// save the content with HTML tags stripped
	textFile.WriteString(strip.StripTags(string(r.Content)))

	r.TextGenerated = true
	return nil // success
}

func (w *Wiki) displayCachedPage(page *wikifier.Page, r *DisplayPage, draftOK bool) interface{} {
	cacheModify := page.CacheModified()
	timeStr := httpdate.Time2Str(cacheModify)

	// the page's file is more recent than the cache file.
	// discard the outdated cached copy
	if page.Modified().After(cacheModify) {
		os.Remove(page.CachePath())
		return nil // OK
	}

	content := "<!-- cached page dated " + timeStr + " -->\n"

	// open cache file for reading
	cacheContent, err := ioutil.ReadFile(page.CachePath())
	if err != nil {
		return DisplayError{
			Error:         "Could not read page cache file.",
			DetailedError: "Open '" + page.CachePath() + "' for read error: " + err.Error(),
		}
	}

	// find the first line
	var jsonData []byte
	firstNL := bytes.IndexByte(cacheContent, '\n')
	if firstNL != -1 && firstNL < len(cacheContent) {
		jsonData = cacheContent[:firstNL+1]
		cacheContent = cacheContent[firstNL:]
	}

	// the rest is the html content
	content += string(cacheContent)

	// decode the manifest
	var info pageJSONManifest
	if err := json.Unmarshal(jsonData, &info); err != nil {
		return DisplayError{
			Error:         "Could not read page cache file.",
			DetailedError: "JSON encode error: " + err.Error(),
		}
	}

	// if this is a draft and we're not serving drafts, pretend
	// that the page does not exist
	if !draftOK && info.Draft {
		return DisplayError{Error: "Page has not yet been publised.", Draft: true}
	}

	// cached error
	if info.Error != "" {
		return DisplayError{Error: info.Error}
	}

	// cached redirect
	if info.Redirect != "" {
		return DisplayRedirect{Redirect: info.Redirect}
	}

	// update result with stuff from cache
	if info.Created != nil {
		r.Created = info.Created
		r.CreatedHTTP = httpdate.Time2Str(*info.Created)
	}
	r.Draft = info.Draft
	r.Author = info.Author
	r.Title = info.Title
	r.FmtTitle = info.FmtTitle
	r.FromCache = true
	r.CSS = info.CSS
	r.Content = wikifier.HTML(content)
	r.Modified = &cacheModify
	r.ModifiedHTTP = httpdate.Time2Str(cacheModify)

	return nil // success
}
<|endoftext|>


<commit_before>// +build windows,amd64

package winapi

import (
	"fmt"

	"github.com/google/cabbie/search"
	"github.com/google/cabbie/session"
	"github.com/google/cabbie/updatehistory"
	"github.com/scjalliance/comshim"

	so "github.com/iamacarpet/go-win64api/shared"
)

var updateResultStatus []string = []string{
	"Pending",
	"Completed", // Was "In Progress", swap to "Completed" to match Update UI in OS
	"Completed",
	"Completed With Errors",
	"Failed",
	"Aborted",
}

func UpdatesPending() (*so.WindowsUpdate, error) {
	retData := &so.WindowsUpdate{}

	comshim.Add(1)
	defer comshim.Done()

	reqUpdates, _, err := listUpdates(false)
	if err != nil {
		return nil, fmt.Errorf("Error getting Windows Update info: %s", err.Error())
	}
	retData.NumUpdates = len(reqUpdates)

	history, err := updateHistory()
	if err != nil {
		return nil, fmt.Errorf("Error getting update history: %s", err.Error())
	}

	for _, e := range history.Entries {
		retData.UpdateHistory = append(retData.UpdateHistory, &so.WindowsUpdateHistory{
			EventDate:  e.Date,
			Status:     updateResultStatus[int(e.ResultCode)],
			UpdateName: e.Title,
		})
	}

	if retData.NumUpdates > 0 {
		retData.UpdatesReq = true
	}

	return retData, nil
}

func listUpdates(hidden bool) ([]string, []string, error) {
	// Set search criteria
	c := search.BasicSearch + " OR Type='Driver' OR " + search.BasicSearch + " AND Type='Software'"
	if hidden {
		c += " and IsHidden=1"
	} else {
		c += " and IsHidden=0"
	}

	// Start Windows update session
	s, err := session.New()
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create new Windows Update session: %v", err)
	}
	defer s.Close()

	q, err := search.NewSearcher(s, c, []string{}, 1)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create a new searcher object: %v", err)
	}
	defer q.Close()

	uc, err := q.QueryUpdates()
	if err != nil {
		return nil, nil, fmt.Errorf("error encountered when attempting to query for updates: %v", err)
	}
	defer uc.Close()

	var reqUpdates, optUpdates []string
	for _, u := range uc.Updates {
		// Add to optional updates list if the update does not match the required categories.
		if !u.InCategories([]string{"Critical Updates", "Definition Updates", "Security Updates"}) {
			optUpdates = append(optUpdates, u.Title)
			continue
		}
		// Skip virus updates as they always exist.
		if !u.InCategories([]string{"Definition Updates"}) {
			reqUpdates = append(reqUpdates, u.Title)
		}
	}
	return reqUpdates, optUpdates, nil
}

func updateHistory() (*updatehistory.History, error) {
	// Start Windows update session
	s, err := session.New()
	if err != nil {
		return nil, err
	}
	defer s.Close()

	// Create Update searcher interface
	searcher, err := search.NewSearcher(s, "", []string{}, 1)
	if err != nil {
		return nil, err
	}
	defer searcher.Close()

	return updatehistory.Get(searcher)
}
<commit_msg>Windows Update: fix status<commit_after>// +build windows,amd64

package winapi

import (
	"fmt"

	"github.com/google/cabbie/search"
	"github.com/google/cabbie/session"
	"github.com/google/cabbie/updatehistory"
	"github.com/scjalliance/comshim"

	so "github.com/iamacarpet/go-win64api/shared"
)

var updateResultStatus []string = []string{
	"Completed", // Was "Pending", swap to "Completed" to match Update UI in OS
	"Completed", // Was "In Progress", swap to "Completed" to match Update UI in OS
	"Completed",
	"Completed With Errors",
	"Failed",
	"Aborted",
}

func UpdatesPending() (*so.WindowsUpdate, error) {
	retData := &so.WindowsUpdate{}

	comshim.Add(1)
	defer comshim.Done()

	reqUpdates, _, err := listUpdates(false)
	if err != nil {
		return nil, fmt.Errorf("Error getting Windows Update info: %s", err.Error())
	}
	retData.NumUpdates = len(reqUpdates)

	history, err := updateHistory()
	if err != nil {
		return nil, fmt.Errorf("Error getting update history: %s", err.Error())
	}

	for _, e := range history.Entries {
		retData.UpdateHistory = append(retData.UpdateHistory, &so.WindowsUpdateHistory{
			EventDate:  e.Date,
			Status:     updateResultStatus[int(e.ResultCode)],
			UpdateName: e.Title,
		})
	}

	if retData.NumUpdates > 0 {
		retData.UpdatesReq = true
	}

	return retData, nil
}

func listUpdates(hidden bool) ([]string, []string, error) {
	// Set search criteria
	c := search.BasicSearch + " OR Type='Driver' OR " + search.BasicSearch + " AND Type='Software'"
	if hidden {
		c += " and IsHidden=1"
	} else {
		c += " and IsHidden=0"
	}

	// Start Windows update session
	s, err := session.New()
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create new Windows Update session: %v", err)
	}
	defer s.Close()

	q, err := search.NewSearcher(s, c, []string{}, 1)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create a new searcher object: %v", err)
	}
	defer q.Close()

	uc, err := q.QueryUpdates()
	if err != nil {
		return nil, nil, fmt.Errorf("error encountered when attempting to query for updates: %v", err)
	}
	defer uc.Close()

	var reqUpdates, optUpdates []string
	for _, u := range uc.Updates {
		// Add to optional updates list if the update does not match the required categories.
		if !u.InCategories([]string{"Critical Updates", "Definition Updates", "Security Updates"}) {
			optUpdates = append(optUpdates, u.Title)
			continue
		}
		// Skip virus updates as they always exist.
		if !u.InCategories([]string{"Definition Updates"}) {
			reqUpdates = append(reqUpdates, u.Title)
		}
	}
	return reqUpdates, optUpdates, nil
}

func updateHistory() (*updatehistory.History, error) {
	// Start Windows update session
	s, err := session.New()
	if err != nil {
		return nil, err
	}
	defer s.Close()

	// Create Update searcher interface
	searcher, err := search.NewSearcher(s, "", []string{}, 1)
	if err != nil {
		return nil, err
	}
	defer searcher.Close()

	return updatehistory.Get(searcher)
}
<|endoftext|>


<commit_before>
package ws

import (
  "github.com/mhoc/jarvis/config"
  "github.com/mhoc/jarvis/log"
  "github.com/mhoc/jarvis/util"
  "golang.org/x/net/websocket"
)

var allReceivers = make([]chan map[string]interface{}, 0)
var msgReceivers = make([]chan util.IncomingSlackMessage, 0)

func StartReading() {
  log.Info("Beginning read loop on websocket")
  for {
    frame := make(map[string]interface{})
    websocket.JSON.Receive(wsConnection, &frame)
    if len(frame) == 0 {
      continue
    }
    if sender, in := frame["user"]; in && sender == config.JarvisUserId() {
      log.Trace("Ignoring message sent by jarvis")
      continue
    }
    go Dispatch(frame)
  }
}

func Dispatch(msg map[string]interface{}) {
  DispatchAll(msg)
  if msg["type"] == "message" {
    msgStruct := util.IncomingSlackMessage{
      Type: msg["type"].(string),
      Channel: msg["channel"].(string),
      User: msg["user"].(string),
      Text: msg["text"].(string),
      Timestamp: msg["ts"].(string),
    }
    DispatchMessage(msgStruct)
  }
}

func DispatchAll(msg map[string]interface{}) {
  for _, receiver := range allReceivers {
    select {
    case receiver <- msg:
    default:
    }
  }
}

func DispatchMessage(msg util.IncomingSlackMessage) {
  for _, receiver := range msgReceivers {
    select {
    case receiver <- msg:
    default:
    }
  }
}

func SubscribeToAll(c chan map[string]interface{}) {
  allReceivers = append(allReceivers, c)
}

func SubscribeToMessages(c chan util.IncomingSlackMessage) {
  msgReceivers = append(msgReceivers, c)
}
<commit_msg>Small change<commit_after>
package ws

import (
  "github.com/mhoc/jarvis/config"
  "github.com/mhoc/jarvis/log"
  "github.com/mhoc/jarvis/util"
  "golang.org/x/net/websocket"
)

var allReceivers = make([]chan map[string]interface{}, 0)
var msgReceivers = make([]chan util.IncomingSlackMessage, 0)

func StartReading() {
  log.Info("Beginning read loop on websocket")
  for {
    frame := make(map[string]interface{})
    err := websocket.JSON.Receive(wsConnection, &frame)
    if err != nil {
      log.Warn("Websocket read threw an error. Generally this doesn't indicate catastrophic failure.")
      log.Warn(err.Error())
    }
    if len(frame) == 0 {
      continue
    }
    if sender, in := frame["user"]; in && sender == config.JarvisUserId() {
      log.Trace("Ignoring message sent by jarvis")
      continue
    }
    go Dispatch(frame)
  }
}

func Dispatch(msg map[string]interface{}) {
  DispatchAll(msg)
  if msg["type"] == "message" {
    msgStruct := util.IncomingSlackMessage{
      Type: msg["type"].(string),
      Channel: msg["channel"].(string),
      User: msg["user"].(string),
      Text: msg["text"].(string),
      Timestamp: msg["ts"].(string),
    }
    DispatchMessage(msgStruct)
  }
}

func DispatchAll(msg map[string]interface{}) {
  for _, receiver := range allReceivers {
    select {
    case receiver <- msg:
    default:
    }
  }
}

func DispatchMessage(msg util.IncomingSlackMessage) {
  for _, receiver := range msgReceivers {
    select {
    case receiver <- msg:
    default:
    }
  }
}

func SubscribeToAll(c chan map[string]interface{}) {
  allReceivers = append(allReceivers, c)
}

func SubscribeToMessages(c chan util.IncomingSlackMessage) {
  msgReceivers = append(msgReceivers, c)
}
<|endoftext|>


<commit_before>// Package logger encargado de respaldar en archivos de texto mensajes y mostrar en consola
package logger

import (
	"fmt"
	"os"
	"os/user"
	"strings"
	"time"
)

// Logger estructura encargada de almacenar la instancia para almacenamiento de mensajes
type Logger struct {
	FileName string
	FilePath string
}

// openFile open/create file for loggin
func openFile(filePath, fileName string) (*os.File, error) {

	if !strings.HasSuffix(filePath, "/") {
		filePath += "/"
	}

	// Creamos directorio
	err := os.MkdirAll(filePath, 0777)
	if err != nil {
		return nil, err
	}

	// Abrimos archivo
	file, err := os.OpenFile(filePath+fileName, os.O_RDWR|os.O_APPEND, 0660)
	if err != nil {
		// Creamos archivo
		file, err = os.Create(filePath + fileName)
		if err != nil {
			return nil, err
		}
		initText := "#version: 0.1\n"
		initText += "#creation: " + time.Now().String() + "\n"
		initText += "#config: [datetime][userid:username][alert/error/info/etc] message\n"

		// Cerramos archivo
		defer file.Close()

		// Escribimos encabezado
		if _, err := file.WriteString(initText); err != nil {
			return nil, err
		}
	}

	return file, nil
}

// WriteLine write message in log file
func (log *Logger) WriteLine(message string, kind ...string) (int, error) {

	// Abrimos archivo
	file, err := openFile(log.FilePath, log.FileName)
	if err != nil {
		return 0, nil
	}
	// Cerramos archivo
	defer file.Close()

	// Obtenemos la información del usuario del Sistema Operativo
	user, err := user.Current()
	if err != nil {
		return 0, err
	}

	userMessage := user.Uid + ":" + user.Username
	timeMessage := time.Now().Format("02/01/2006 15:04:05.99999")

	kinds := "info"
	if len(kind) > 0 {
		kinds = strings.Join(kind, ",")
	}

	// Guardamos mensaje
	n, err := file.WriteString(fmt.Sprintf("[%s][%s][%s] %s\n", timeMessage, userMessage, kinds, message))
	if err != nil {
		return 0, err
	}

	// Imprimitmos mensaje
	fmt.Printf("[%s][%s][%s] %s\n", timeMessage, userMessage, kind, message)

	return n, nil
}

// New create instance for loggin messages
func New(filePath, fileName string) (*Logger, error) {
	log := &Logger{fileName, filePath}
	if _, err := openFile(filePath, fileName); err != nil {
		return nil, err
	}
	return log, nil
}
<commit_msg>check filePath directory slash<commit_after>// Package logger encargado de respaldar en archivos de texto mensajes y mostrar en consola
package logger

import (
	"fmt"
	"os"
	"os/user"
	"strings"
	"time"
)

// Logger estructura encargada de almacenar la instancia para almacenamiento de mensajes
type Logger struct {
	FileName string
	FilePath string
}

// openFile open/create file for loggin
func openFile(filePath, fileName string) (*os.File, error) {

	if strings.Contains(filePath, "\\") && !strings.HasSuffix(filePath, "\\") {
		filePath += "\\"
	} else if strings.Contains(filePath, "/") && !strings.HasSuffix(filePath, "/") {
		filePath += "/"
	}

	// Creamos directorio
	err := os.MkdirAll(filePath, 0777)
	if err != nil {
		return nil, err
	}

	// Abrimos archivo
	file, err := os.OpenFile(filePath+fileName, os.O_RDWR|os.O_APPEND, 0660)
	if err != nil {
		// Creamos archivo
		file, err = os.Create(filePath + fileName)
		if err != nil {
			return nil, err
		}
		initText := "#version: 0.1\n"
		initText += "#creation: " + time.Now().String() + "\n"
		initText += "#config: [datetime][userid:username][alert/error/info/etc] message\n"

		// Cerramos archivo
		defer file.Close()

		// Escribimos encabezado
		if _, err := file.WriteString(initText); err != nil {
			return nil, err
		}
	}

	return file, nil
}

// WriteLine write message in log file
func (log *Logger) WriteLine(message string, kind ...string) (int, error) {

	// Abrimos archivo
	file, err := openFile(log.FilePath, log.FileName)
	if err != nil {
		return 0, nil
	}
	// Cerramos archivo
	defer file.Close()

	// Obtenemos la información del usuario del Sistema Operativo
	user, err := user.Current()
	if err != nil {
		return 0, err
	}

	userMessage := user.Uid + ":" + user.Username
	timeMessage := time.Now().Format("02/01/2006 15:04:05.99999")

	kinds := "info"
	if len(kind) > 0 {
		kinds = strings.Join(kind, ",")
	}

	// Guardamos mensaje
	n, err := file.WriteString(fmt.Sprintf("[%s][%s][%s] %s\n", timeMessage, userMessage, kinds, message))
	if err != nil {
		return 0, err
	}

	// Imprimitmos mensaje
	fmt.Printf("[%s][%s][%s] %s\n", timeMessage, userMessage, kind, message)

	return n, nil
}

// New create instance for loggin messages
func New(filePath, fileName string) (*Logger, error) {
	log := &Logger{fileName, filePath}
	if _, err := openFile(filePath, fileName); err != nil {
		return nil, err
	}
	return log, nil
}
<|endoftext|>


<commit_before>package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"time"
)

// Logger stuct contains the log details
type Logger struct {
	Date   time.Time
	Level  string
	Src    Source
	Output *string
}

// Source struct contains the source details
type Source struct {
	Func string
	Line int
}

var log = new(Logger)

func (l *Logger) caller() {
	var ok bool
	var pc uintptr
	pc, _, l.Src.Line, ok = runtime.Caller(3)
	if !ok {
		l.Src.Func = "???"
		l.Src.Line = 0
	} else {
		l.Src.Func = runtime.FuncForPC(pc).Name()
	}

}

func (l *Logger) message(format string, args []interface{}) *string {
	m := fmt.Sprintf(format, args...)
	return &m
}

func (l *Logger) now() {
	l.Date = time.Now()
}

func (l *Logger) print(level string, format string, args ...interface{}) {
	l.now()
	l.caller()
	l.Output = l.message(format, args)
	l.Level = level

	data, err := json.Marshal(l)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(data))
}

// Info function logs a message using INFO as log level
func Info(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("info", "%s", s)
}

// Warning function logs a message using WARNING as log level
func Warning(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("warning", "%s", s)
}

// Fatal function logs a message using FATAL as log level followed by a call to os.Exit(1)
func Fatal(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("fatal", "%s", s)
	os.Exit(1)
}

// Infof function logs a message with arguments using INFO as log level
func Infof(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("info", s)
}

// Warningf function logs a message with arguments using WARNING as log level
func Warningf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("warning", s)
}

// Fatalf function logs a message with arguments using FATAL as log level followed by a call to os.Exit(1)
func Fatalf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("fatal", s)
	os.Exit(1)
}
<commit_msg>Add debug level<commit_after>package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"time"
)

// Logger stuct contains the log details
type Logger struct {
	Date   time.Time
	Level  string
	Src    Source
	Output *string
}

// Source struct contains the source details
type Source struct {
	Func string
	Line int
}

var log = new(Logger)

func (l *Logger) caller() {
	var ok bool
	var pc uintptr
	pc, _, l.Src.Line, ok = runtime.Caller(3)
	if !ok {
		l.Src.Func = "???"
		l.Src.Line = 0
	} else {
		l.Src.Func = runtime.FuncForPC(pc).Name()
	}

}

func (l *Logger) message(format string, args []interface{}) *string {
	m := fmt.Sprintf(format, args...)
	return &m
}

func (l *Logger) now() {
	l.Date = time.Now()
}

func (l *Logger) print(level string, format string, args ...interface{}) {
	l.now()
	l.caller()
	l.Output = l.message(format, args)
	l.Level = level

	data, err := json.Marshal(l)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(data))
}

// Info function logs a message using INFO as log level
func Info(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("info", "%s", s)
}

// Debug function logs a message using DEBUG as log level if DEBUG environment variable is enabled
func Debug(args ...interface{}) {
	debug := os.Getenv("DEBUG")
	if debug != "" {
		s := fmt.Sprint(args...)
		log.print("debug", "%s", s)
	}
}

// Warning function logs a message using WARNING as log level
func Warning(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("warning", "%s", s)
}

// Fatal function logs a message using FATAL as log level followed by a call to os.Exit(1)
func Fatal(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("fatal", "%s", s)
	os.Exit(1)
}

// Infof function logs a message with arguments using INFO as log level
func Infof(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("info", s)
}

// Debugf function logs a message with arguments using DEBUG as log level if DEBUG environment variable is enabled
func Debugf(format string, args ...interface{}) {
	debug := os.Getenv("DEBUG")
	if debug != "" {
		s := fmt.Sprintf(format, args...)
		log.print("debug", s)
	}
}

// Warningf function logs a message with arguments using WARNING as log level
func Warningf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("warning", s)
}

// Fatalf function logs a message with arguments using FATAL as log level followed by a call to os.Exit(1)
func Fatalf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("fatal", s)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package scene

import (
	"github.com/pankona/gomo-simra/simra"
	"math"
)

// modeler represents a model
type modeler interface {
	getPosition() (x float32, y float32)
	setPosition(x float32, y float32)
	getRotate() float32
	setRotate(radian float32)
	setDirection(radian float64)
	getDirection() float64
	setSpeed(s float64)
	getSpeed() float64
	move()
}

type modelEventListener interface {
	onDead()
}

type models struct {
	ball        modeler
	obstacles   [2]modeler
	backgrounds [2]modeler
	listeners   []modelEventListener
	isDead      bool
}

func (models *models) restart() {
	models.isDead = false
}

func (models *models) registerBall(ball modeler) {
	ball.setSpeed(1)
	ball.setDirection(90)
	models.ball = ball
}

func (models *models) registerObstacle(obstacle modeler, index int) {
	obstacle.setSpeed(3)
	obstacle.setDirection(180)
	models.obstacles[index] = obstacle
}

func (models *models) registerBackground(bg modeler, index int) {
	bg.setSpeed(3)
	models.backgrounds[index] = bg
}

func (models *models) addEventListener(listener modelEventListener) {
	models.listeners = append(models.listeners, listener)
}

var degree float32

// Progress progresses the time of models 1 frame
func (models *models) Progress(isKeyTouching bool) {
	ball := models.ball

	if !models.isDead {
		degree++
		if degree >= 360 {
			degree = 0
		}

		ball.setRotate(float32(degree) * math.Pi / 180)

		if isKeyTouching {
			dx := ball.getSpeed() * math.Cos(ball.getDirection()*math.Pi/180)
			dy := ball.getSpeed() * math.Sin(ball.getDirection()*math.Pi/180)
			dy += 9.8 / 60 * 2
			ball.setSpeed(math.Sqrt(dx*dx + dy*dy))
			ball.setDirection(math.Atan2(dy, dx) * 180 / math.Pi)
		}
		models.move()
	}
}

func (models *models) move() {
	ball := models.ball
	ball.move()
	obstacles := models.obstacles
	obstacles[0].move()
	obstacles[1].move()
	backgrounds := models.backgrounds
	backgrounds[0].move()
	backgrounds[1].move()
}

// OnCollision is called at collision detected
func (models *models) OnCollision(c1, c2 simra.Collider) {
	if _, ok := c1.(*Ball); ok {
		if _, ok := c2.(*Obstacle); ok {
			// onDead
			if !models.isDead {
				models.isDead = true
				for _, v := range models.listeners {
					models.isDead = true
					v.onDead()
				}
			}
		}
	}
}
<commit_msg>[#90] improve comments in OnCollision<commit_after>package scene

import (
	"math"

	"github.com/pankona/gomo-simra/simra"
)

// modeler represents a model
type modeler interface {
	getPosition() (x float32, y float32)
	setPosition(x float32, y float32)
	getRotate() float32
	setRotate(radian float32)
	setDirection(radian float64)
	getDirection() float64
	setSpeed(s float64)
	getSpeed() float64
	move()
}

type modelEventListener interface {
	onDead()
}

type models struct {
	ball        modeler
	obstacles   [2]modeler
	backgrounds [2]modeler
	listeners   []modelEventListener
	isDead      bool
}

func (models *models) restart() {
	models.isDead = false
}

func (models *models) registerBall(ball modeler) {
	ball.setSpeed(1)
	ball.setDirection(90)
	models.ball = ball
}

func (models *models) registerObstacle(obstacle modeler, index int) {
	obstacle.setSpeed(3)
	obstacle.setDirection(180)
	models.obstacles[index] = obstacle
}

func (models *models) registerBackground(bg modeler, index int) {
	bg.setSpeed(3)
	models.backgrounds[index] = bg
}

func (models *models) addEventListener(listener modelEventListener) {
	models.listeners = append(models.listeners, listener)
}

var degree float32

// Progress progresses the time of models 1 frame
func (models *models) Progress(isKeyTouching bool) {
	ball := models.ball

	if !models.isDead {
		degree++
		if degree >= 360 {
			degree = 0
		}

		ball.setRotate(float32(degree) * math.Pi / 180)

		if isKeyTouching {
			dx := ball.getSpeed() * math.Cos(ball.getDirection()*math.Pi/180)
			dy := ball.getSpeed() * math.Sin(ball.getDirection()*math.Pi/180)
			dy += 9.8 / 60 * 2
			ball.setSpeed(math.Sqrt(dx*dx + dy*dy))
			ball.setDirection(math.Atan2(dy, dx) * 180 / math.Pi)
		}
		models.move()
	}
}

func (models *models) move() {
	ball := models.ball
	ball.move()
	obstacles := models.obstacles
	obstacles[0].move()
	obstacles[1].move()
	backgrounds := models.backgrounds
	backgrounds[0].move()
	backgrounds[1].move()
}

// OnCollision is called at collision detected
func (models *models) OnCollision(c1, c2 simra.Collider) {
	if _, ok := c1.(*Ball); ok {
		if _, ok := c2.(*Obstacle); ok {
			// collision indicates a miss. this will be decrease a life.
			if !models.isDead {
				models.isDead = true
				for _, v := range models.listeners {
					models.isDead = true
					v.onDead()
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Martini Authors
// Copyright 2014 The Macaron Authors
//
// 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 macaron

import (
	"fmt"
	"log"
	"net/http"
	"runtime"
	"time"
)

var ColorLog = true

func init() {
	ColorLog = runtime.GOOS != "windows"
}

// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out.
func Logger() Handler {
	return func(ctx *Context, log *log.Logger) {
		start := time.Now()

		log.Printf("Started %s %s for %s", ctx.Req.Method, ctx.Req.RequestURI, ctx.RemoteAddr())

		rw := ctx.Resp.(ResponseWriter)
		ctx.Next()

		content := fmt.Sprintf("Completed %s %v %s in %v", ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start))
		if ColorLog {
			switch rw.Status() {
			case 200, 201, 202:
				content = fmt.Sprintf("\033[1;32m%s\033[0m", content)
			case 301, 302:
				content = fmt.Sprintf("\033[1;37m%s\033[0m", content)
			case 304:
				content = fmt.Sprintf("\033[1;33m%s\033[0m", content)
			case 401, 403:
				content = fmt.Sprintf("\033[4;31m%s\033[0m", content)
			case 404:
				content = fmt.Sprintf("\033[1;31m%s\033[0m", content)
			case 500:
				content = fmt.Sprintf("\033[1;36m%s\033[0m", content)
			}
		}
		log.Println(content)
	}
}
<commit_msg>Print date and time in log for requests.<commit_after>// Copyright 2013 Martini Authors
// Copyright 2014 The Macaron Authors
//
// 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 macaron

import (
	"fmt"
	"log"
	"net/http"
	"runtime"
	"time"
)

var (
	ColorLog      = true
	LogTimeFormat = "2006-01-02 15:04:05"
)

func init() {
	ColorLog = runtime.GOOS != "windows"
}

// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out.
func Logger() Handler {
	return func(ctx *Context, log *log.Logger) {
		start := time.Now()

		log.Printf("%s: Started %s %s for %s", time.Now().Format(LogTimeFormat), ctx.Req.Method, ctx.Req.RequestURI, ctx.RemoteAddr())

		rw := ctx.Resp.(ResponseWriter)
		ctx.Next()

		content := fmt.Sprintf("%s: Completed %s %v %s in %v", time.Now().Format(LogTimeFormat), ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start))
		if ColorLog {
			switch rw.Status() {
			case 200, 201, 202:
				content = fmt.Sprintf("\033[1;32m%s\033[0m", content)
			case 301, 302:
				content = fmt.Sprintf("\033[1;37m%s\033[0m", content)
			case 304:
				content = fmt.Sprintf("\033[1;33m%s\033[0m", content)
			case 401, 403:
				content = fmt.Sprintf("\033[4;31m%s\033[0m", content)
			case 404:
				content = fmt.Sprintf("\033[1;31m%s\033[0m", content)
			case 500:
				content = fmt.Sprintf("\033[1;36m%s\033[0m", content)
			}
		}
		log.Println(content)
	}
}
<|endoftext|>