Susumu Yata
null+****@clear*****
Fri May 12 19:17:20 JST 2017
Susumu Yata 2017-05-12 19:17:20 +0900 (Fri, 12 May 2017) New Revision: 09c35bc17c1a9d27caa32cc54018966d9ecee3a7 https://github.com/groonga/grnci/commit/09c35bc17c1a9d27caa32cc54018966d9ecee3a7 Message: Update v2. Added files: v2/error.go v2/libgrn/client.go v2/libgrn/libgrn.go Modified files: v2/argument.go v2/gqtp.go v2/http.go v2/request.go Renamed files: v2/response.go (from v2/respponse.go) Modified: v2/argument.go (+8 -4) =================================================================== --- v2/argument.go 2017-05-11 19:51:22 +0900 (96d9742) +++ v2/argument.go 2017-05-12 19:17:20 +0900 (0629675) @@ -5,7 +5,11 @@ import ( "fmt" ) -// Argument represents a named argument of a request. +// Argument stores a command argument. +// +// If Key != "", it is a named argument. +// Otherwise, it is an unnamed argument. +// Note that the order of unnamed arguments is important. type Argument struct { Key string Value string @@ -36,7 +40,7 @@ func checkArgumentKey(s string) error { continue } switch s[i] { - case '#', '@', '-', '_': + case '#', '@', '-', '_', '.', '[', ']': default: return fmt.Errorf("invalid format: s = %s", s) } @@ -45,8 +49,8 @@ func checkArgumentKey(s string) error { } // Check checks if arg is valid. -func (arg *Argument) Check() error { - if err := checkArgumentKey(arg.Key); err != nil { +func (a *Argument) Check() error { + if err := checkArgumentKey(a.Key); err != nil { return fmt.Errorf("checkArgumentKey failed: %v", err) } return nil Added: v2/error.go (+42 -0) 100644 =================================================================== --- /dev/null +++ v2/error.go 2017-05-12 19:17:20 +0900 (876af5f) @@ -0,0 +1,42 @@ +package grnci + +import "encoding/json" + +// Code is an error code. +type Code int + +// List of error codes. +const ( + CodeSuccess = Code(0) +) + +// String returns a string which briefly describes an error. +func (c Code) String() string { + switch c { + case CodeSuccess: + return "Success" + default: + return "Unknown error" + } +} + +// Error stores details of an error. +type Error struct { + Code Code + Message string + Data interface{} +} + +// NewError creates an error object. +func NewError(code Code, data interface{}) error { + return &Error{ + Code: code, + Data: data, + } +} + +// Error returns a string which describes an error. +func (e Error) Error() string { + b, _ := json.Marshal(e) + return string(b) +} Modified: v2/gqtp.go (+4 -0) =================================================================== --- v2/gqtp.go 2017-05-11 19:51:22 +0900 (3e846f9) +++ v2/gqtp.go 2017-05-12 19:17:20 +0900 (3075594) @@ -34,6 +34,10 @@ func (c *gqtpClient) Close() error { // Query sends a request and receives a response. func (c *gqtpClient) Query(req *Request) (*Response, error) { + if err := req.Check(); err != nil { + return nil, err + } + // TODO return nil, nil } Modified: v2/http.go (+36 -2) =================================================================== --- v2/http.go 2017-05-11 19:51:22 +0900 (e342b31) +++ v2/http.go 2017-05-12 19:17:20 +0900 (14b732a) @@ -2,8 +2,10 @@ package grnci import ( "fmt" + "io/ioutil" "net/http" "net/url" + "path" ) // httpClient is an HTTP client. @@ -34,6 +36,38 @@ func (c *httpClient) Close() error { // Query sends a request and receives a response. func (c *httpClient) Query(req *Request) (*Response, error) { - // TODO - return nil, nil + if err := req.Check(); err != nil { + return nil, err + } + + u := *c.url + u.Path = path.Join(u.Path, req.Cmd) + if len(req.Args) != 0 { + q := u.Query() + for _, arg := range req.Args { + q.Set(arg.Key, arg.Value) + } + u.RawQuery = q.Encode() + } + addr := u.String() + + var resp *http.Response + var err error + if req.Body == nil { + if resp, err = c.client.Get(addr); err != nil { + return nil, fmt.Errorf("c.client.Get failed: %v", err) + } + } else { + if resp, err = c.client.Post(addr, "application/json", req.Body); err != nil { + return nil, fmt.Errorf("c.client.Post failed: %v", err) + } + } + defer resp.Body.Close() + + // TODO: parse the response. + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("ioutil.ReadAll failed: %v", err) + } + return &Response{Bytes: respBytes}, nil } Added: v2/libgrn/client.go (+37 -0) 100644 =================================================================== --- /dev/null +++ v2/libgrn/client.go 2017-05-12 19:17:20 +0900 (9a85023) @@ -0,0 +1,37 @@ +package libgrn + +// #cgo pkg-config: groonga +// #include <groonga.h> +// #include <stdlib.h> +import "C" +import "github.com/groonga/grnci/v2" + +// Client is associated with a C.grn_ctx. +type Client struct { + ctx *grnCtx +} + +// Open opens a local Groonga DB. +func Open(path string) (*grnci.Client, error) { + return nil, nil +} + +// Create creates a local Groonga DB. +func Create(path string) (*grnci.Client, error) { + return nil, nil +} + +// Connect establishes a connection with a GQTP server. +func Connect(addr string) (*grnci.Client, error) { + return nil, nil +} + +// Close closes a client. +func (c *Client) Close() error { + return nil +} + +// Query sends a request and receives a response. +func (c *Client) Query(req *grnci.Request) (*grnci.Response, error) { + return nil, nil +} Added: v2/libgrn/libgrn.go (+196 -0) 100644 =================================================================== --- /dev/null +++ v2/libgrn/libgrn.go 2017-05-12 19:17:20 +0900 (4650603) @@ -0,0 +1,196 @@ +// Package libgrn provides Client using libgroonga. +package libgrn + +// #cgo pkg-config: groonga +// #include <groonga.h> +// #include <stdlib.h> +import "C" +import ( + "errors" + "fmt" + "sync" + "unsafe" +) + +var ( + // libCount is incremented by Init and decremented by Fin. + libCount int + // libMutex is used for exclusion control in Init and Fin. + libMutex sync.Mutex +) + +// Init initializes libgroonga. +// +// If an internal counter is zero, Init increments it and initializes libgroonga. +// Otherwise, Init only increments the internal counter. +// +// There is no need to call Init explicitly. +// libgrn calls Init when it creates a Client. +func Init() error { + libMutex.Lock() + defer libMutex.Unlock() + if libCount == 0 { + if rc := C.grn_init(); rc != C.GRN_SUCCESS { + return fmt.Errorf("C.grn_init failed: rc = %d", rc) + } + } + libCount++ + return nil +} + +// Fin finalizes libgroonga. +// +// If an internal counter is one, Fin decrements it and finalizes libgroonga. +// Otherwise, Fin only decrements the internal counter. +// +// There is no need to call Fin explicitly. +// libgrn calls Fin when it closes a Client. +func Fin() error { + libMutex.Lock() + defer libMutex.Unlock() + if libCount == 0 { + return fmt.Errorf("libCount = 0") + } + libCount-- + if libCount == 0 { + if rc := C.grn_fin(); rc != C.GRN_SUCCESS { + return fmt.Errorf("C.grn_fin failed: rc = %d", rc) + } + } + return nil +} + +// ctx is a Groonga context. +type grnCtx struct { + ctx *C.grn_ctx +} + +// newGrnCtx returns a new grnCtx. +func newGrnCtx() (*grnCtx, error) { + if err := Init(); err != nil { + return nil, fmt.Errorf("Init failed: %v", err) + } + ctx := C.grn_ctx_open(C.int(0)) + if ctx == nil { + Fin() + return nil, errors.New("C.grn_ctx_open failed") + } + return &grnCtx{ctx: ctx}, nil +} + +// Close closes a grnCtx. +func (c *grnCtx) Close() error { + if rc := C.grn_ctx_close(c.ctx); rc != C.GRN_SUCCESS { + return fmt.Errorf("C.grn_ctx_close failed: %s", rc) + } + if err := Fin(); err != nil { + return fmt.Errorf("Fin failed: %v", err) + } + return nil +} + +func (c *grnCtx) Err() error { + if c.ctx.rc == C.GRN_SUCCESS { + return nil + } + return fmt.Errorf("rc = %s: %s", c.ctx.rc, C.GoString(&c.ctx.errbuf[0])) +} + +// grnDB is a DB handle. +type grnDB struct { + obj *C.grn_obj + path string + count int + mutex sync.Mutex +} + +// createDB creates a new DB. +func createDB(ctx *grnCtx, path string) (*grnDB, error) { + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + obj := C.grn_db_create(ctx.ctx, cPath, nil) + if obj == nil { + return nil, fmt.Errorf("C.grn_db_create failed: %v", ctx.Err()) + } + if cAbsPath := C.grn_obj_path(ctx.ctx, obj); cAbsPath != nil { + path = C.GoString(cAbsPath) + } + return &grnDB{ + obj: obj, + path: path, + count: 1, + }, nil +} + +// openDB opens an existing DB. +func openDB(ctx *grnCtx, path string) (*grnDB, error) { + if ctx == nil { + return nil, errors.New("invalid argument: ctx = nil") + } + if path == "" { + return nil, errors.New("invalid argument: path = ") + } + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + obj := C.grn_db_open(ctx.ctx, cPath) + if obj == nil { + return nil, fmt.Errorf("C.grn_db_create failed: %v", ctx.Err()) + } + if cAbsPath := C.grn_obj_path(ctx.ctx, obj); cAbsPath != nil { + path = C.GoString(cAbsPath) + } + return &grnDB{ + obj: obj, + path: path, + count: 1, + }, nil +} + +// Close closes a DB. +func (db *grnDB) Close(ctx *grnCtx) error { + if db == nil { + return errors.New("invalid self: db = nil") + } + if db.obj == nil { + return errors.New("invalid self: obj = nil") + } + if ctx == nil { + return errors.New("invalid argument: ctx = nil") + } + db.mutex.Lock() + defer db.mutex.Unlock() + if db.count <= 0 { + return fmt.Errorf("underflow: cnt = %d", db.count) + } + db.count-- + if db.count == 0 { + if rc := C.grn_obj_close(ctx.ctx, db.obj); rc != C.GRN_SUCCESS { + return fmt.Errorf("C.grn_obj_close failed: rc = %s", rc) + } + db.obj = nil + } + return nil +} + +// Dup duplicates a DB handle. +func (db *grnDB) Dup() (*grnCtx, error) { + if db == nil { + return nil, errors.New("invalid self: db = nil") + } + if db.obj == nil { + return nil, errors.New("invalid self: obj = nil") + } + ctx, err := newGrnCtx() + if err != nil { + return nil, fmt.Errorf("newGrnCtx failed: %v", err) + } + C.grn_ctx_use(ctx.ctx, db.obj) + if err := ctx.Err(); err != nil { + ctx.Close() + return nil, fmt.Errorf("C.grn_ctx_use failed: %v", err) + } + db.mutex.Lock() + db.count++ + db.mutex.Unlock() + return ctx, nil +} Modified: v2/request.go (+1 -1) =================================================================== --- v2/request.go 2017-05-11 19:51:22 +0900 (dcd2fb4) +++ v2/request.go 2017-05-12 19:17:20 +0900 (3eaa7c5) @@ -32,7 +32,7 @@ func checkCmd(s string) error { // Check checks if req is valid. func (req *Request) Check() error { if err := checkCmd(req.Cmd); err != nil { - return fmt.Errorf("CheckCmd failed: %v", err) + return fmt.Errorf("checkCmd failed: %v", err) } for _, arg := range req.Args { if err := arg.Check(); err != nil { Renamed: v2/response.go (+1 -0) 76% =================================================================== --- v2/respponse.go 2017-05-11 19:51:22 +0900 (437d876) +++ v2/response.go 2017-05-12 19:17:20 +0900 (b37f90a) @@ -4,6 +4,7 @@ import ( "time" ) +// Response stores a response of Groonga. type Response struct { Bytes []byte Error error -------------- next part -------------- HTML����������������������������... Download