Susumu Yata
null+****@clear*****
Mon May 15 19:04:47 JST 2017
Susumu Yata 2017-05-15 19:04:47 +0900 (Mon, 15 May 2017) New Revision: e6ff6ab6b1ac3306d287e1cf57a01abc244133d2 https://github.com/groonga/grnci/commit/e6ff6ab6b1ac3306d287e1cf57a01abc244133d2 Message: Update v2. Added files: v2/libgrn/handle.go Modified files: v2/address.go v2/argument.go v2/client.go v2/http.go v2/libgrn/client.go v2/libgrn/libgrn.go v2/request.go v2/response.go Modified: v2/address.go (+1 -1) =================================================================== --- v2/address.go 2017-05-12 19:17:20 +0900 (6ff2d8d) +++ v2/address.go 2017-05-15 19:04:47 +0900 (cfa5634) @@ -22,7 +22,7 @@ type Address struct { Fragment string } -// String reassembles the address fields except Raw into an address string. +// String assembles the address fields except Raw into an address string. func (a *Address) String() string { var url string if a.Scheme != "" { Modified: v2/argument.go (+4 -1) =================================================================== --- v2/argument.go 2017-05-12 19:17:20 +0900 (0629675) +++ v2/argument.go 2017-05-15 19:04:47 +0900 (8862bcd) @@ -35,7 +35,10 @@ func checkArgumentKey(s string) error { if s == "" { return errors.New("invalid format: s = ") } - for i := 0; i < len(s); i++ { + if !isAlpha(s[0]) { + return fmt.Errorf("invalid format: s = %s", s) + } + for i := 1; i < len(s); i++ { if isAlnum(s[i]) { continue } Modified: v2/client.go (+12 -2) =================================================================== --- v2/client.go 2017-05-12 19:17:20 +0900 (254bcb5) +++ v2/client.go 2017-05-15 19:04:47 +0900 (9f46eaa) @@ -14,12 +14,12 @@ type iClient interface { // Client is a Groonga client. type Client struct { - iClient + impl iClient } // NewClient returns a new Client using an existing client. func NewClient(c iClient) *Client { - return &Client{iClient: c} + return &Client{impl: c} } // NewClientByAddress returns a new Client to access a Groonga server. @@ -76,3 +76,13 @@ func NewHTTPClient(addr string, client *http.Client) (*Client, error) { } return NewClient(c), nil } + +// Close closes a client. +func (c *Client) Close() error { + return c.impl.Close() +} + +// Query sends a request and receives a response. +func (c *Client) Query(req *Request) (*Response, error) { + return c.impl.Query(req) +} Modified: v2/http.go (+3 -3) =================================================================== --- v2/http.go 2017-05-12 19:17:20 +0900 (14b732a) +++ v2/http.go 2017-05-15 19:04:47 +0900 (14e2fbf) @@ -41,10 +41,10 @@ func (c *httpClient) Query(req *Request) (*Response, error) { } u := *c.url - u.Path = path.Join(u.Path, req.Cmd) - if len(req.Args) != 0 { + u.Path = path.Join(u.Path, req.Command) + if len(req.Arguments) != 0 { q := u.Query() - for _, arg := range req.Args { + for _, arg := range req.Arguments { q.Set(arg.Key, arg.Value) } u.RawQuery = q.Encode() Modified: v2/libgrn/client.go (+0 -10) =================================================================== --- v2/libgrn/client.go 2017-05-12 19:17:20 +0900 (9a85023) +++ v2/libgrn/client.go 2017-05-15 19:04:47 +0900 (a8075b4) @@ -11,16 +11,6 @@ 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 Added: v2/libgrn/handle.go (+134 -0) 100644 =================================================================== --- /dev/null +++ v2/libgrn/handle.go 2017-05-15 19:04:47 +0900 (0af4fa1) @@ -0,0 +1,134 @@ +package libgrn + +// #cgo pkg-config: groonga +// #include <groonga.h> +// #include <stdlib.h> +import "C" +import ( + "fmt" + "io/ioutil" + "unsafe" + + "github.com/groonga/grnci/v2" +) + +// Handle is associated with a C.grn_ctx. +type Handle struct { + ctx *grnCtx + db *grnDB +} + +// Open opens a local DB and returns a handle. +func Open(path string) (*Handle, error) { + ctx, err := newGrnCtx() + if err != nil { + return nil, err + } + db, err := openGrnDB(ctx, path) + if err != nil { + ctx.Close() + return nil, err + } + return &Handle{ + ctx: ctx, + db: db, + }, nil +} + +// Create creates a local DB and returns a hendle. +func Create(path string) (*Handle, error) { + ctx, err := newGrnCtx() + if err != nil { + return nil, err + } + db, err := createGrnDB(ctx, path) + if err != nil { + ctx.Close() + return nil, err + } + return &Handle{ + ctx: ctx, + db: db, + }, nil +} + +// Close closes a handle. +func (h *Handle) Close() error { + if err := h.db.Close(h.ctx); err != nil { + return err + } + if err := h.ctx.Close(); err != nil { + return err + } + return nil +} + +// Dup duplicates a handle. +func (h *Handle) Dup() (*Handle, error) { + ctx, err := h.db.Dup() + if err != nil { + return nil, err + } + return &Handle{ + ctx: ctx, + db: h.db, + }, nil +} + +// send sends data. +func (h *Handle) send(data []byte) error { + var p *C.char + if len(data) != 0 { + p = (*C.char)(unsafe.Pointer(&data[0])) + } + rc := C.grn_rc(C.grn_ctx_send(h.ctx.ctx, p, C.uint(len(data)), C.int(0))) + if (rc != C.GRN_SUCCESS) || (h.ctx.ctx.rc != C.GRN_SUCCESS) { + return fmt.Errorf("C.grn_ctx_send failed: rc = %d", rc) + } + return nil +} + +// recv receives the response to sent data. +func (h *Handle) recv() ([]byte, error) { + var resp *C.char + var respLen C.uint + var respFlags C.int + rc := C.grn_rc(C.grn_ctx_recv(h.ctx.ctx, &resp, &respLen, &respFlags)) + if (rc != C.GRN_SUCCESS) || (h.ctx.ctx.rc != C.GRN_SUCCESS) { + return nil, fmt.Errorf("C.grn_ctx_recv failed: rc = %d", rc) + } + return C.GoBytes(unsafe.Pointer(resp), C.int(respLen)), nil +} + +// Query sends a request and receives a response. +// +// TODO: error handling +func (h *Handle) Query(req *grnci.Request) (*grnci.Response, error) { + cmd, err := req.Assemble() + if err != nil { + return nil, err + } + if err := h.send([]byte(cmd)); err != nil { + respBytes, _ := h.recv() + resp, _ := grnci.NewResponse(respBytes) + return resp, err + } + respBytes, err := h.recv() + if (req.Body == nil) || (err != nil) { + resp, _ := grnci.NewResponse(respBytes) + return resp, err + } + if len(respBytes) != 0 { + resp, _ := grnci.NewResponse(respBytes) + return resp, fmt.Errorf("unexpected response") + } + body, _ := ioutil.ReadAll(req.Body) + if err := h.send(body); err != nil { + respBytes, _ := h.recv() + resp, _ := grnci.NewResponse(respBytes) + return resp, err + } + respBytes, _ = h.recv() + resp, _ := grnci.NewResponse(respBytes) + return resp, nil +} Modified: v2/libgrn/libgrn.go (+5 -26) =================================================================== --- v2/libgrn/libgrn.go 2017-05-12 19:17:20 +0900 (4650603) +++ v2/libgrn/libgrn.go 2017-05-15 19:04:47 +0900 (b77537f) @@ -104,8 +104,8 @@ type grnDB struct { mutex sync.Mutex } -// createDB creates a new DB. -func createDB(ctx *grnCtx, path string) (*grnDB, error) { +// createGrnDB creates a new DB. +func createGrnDB(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) @@ -122,14 +122,8 @@ func createDB(ctx *grnCtx, path string) (*grnDB, error) { }, 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 = ") - } +// openGrnDB opens an existing DB. +func openGrnDB(ctx *grnCtx, path string) (*grnDB, error) { cPath := C.CString(path) defer C.free(unsafe.Pointer(cPath)) obj := C.grn_db_open(ctx.ctx, cPath) @@ -148,19 +142,10 @@ func openDB(ctx *grnCtx, path string) (*grnDB, error) { // 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) + return fmt.Errorf("underflow: count = %d", db.count) } db.count-- if db.count == 0 { @@ -174,12 +159,6 @@ func (db *grnDB) Close(ctx *grnCtx) error { // 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) Modified: v2/request.go (+48 -9) =================================================================== --- v2/request.go 2017-05-12 19:17:20 +0900 (3eaa7c5) +++ v2/request.go 2017-05-15 19:04:47 +0900 (f3bc332) @@ -8,13 +8,13 @@ import ( // Request stores a Groonga command with arguments. type Request struct { - Cmd string - Args []Argument - Body io.Reader + Command string + Arguments []Argument + Body io.Reader } -// checkCmd checks if s is valid as a command name. -func checkCmd(s string) error { +// checkCommand checks if s is valid as a command name. +func checkCommand(s string) error { if s == "" { return errors.New("invalid name: s = ") } @@ -30,14 +30,53 @@ 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) +func (r *Request) Check() error { + if err := checkCommand(r.Command); err != nil { + return fmt.Errorf("checkCommand failed: %v", err) } - for _, arg := range req.Args { + for _, arg := range r.Arguments { if err := arg.Check(); err != nil { return fmt.Errorf("arg.Check failed: %v", err) } } return nil } + +// Assemble assembles Command and Arguments into a command string. +// +// The command string format is +// Command --Arguments[i].Key 'Arguments[i].Value' ... +func (r *Request) Assemble() (string, error) { + if err := r.Check(); err != nil { + return "", err + } + size := len(r.Command) + for _, arg := range r.Arguments { + if len(arg.Key) != 0 { + size += len(arg.Key) + 3 + } + size += len(arg.Value)*2 + 3 + } + buf := make([]byte, 0, size) + buf = append(buf, r.Command...) + for _, arg := range r.Arguments { + buf = append(buf, ' ') + if len(arg.Key) != 0 { + buf = append(buf, "--"...) + buf = append(buf, arg.Key...) + } + buf = append(buf, '\'') + for i := 0; i < len(arg.Value); i++ { + switch arg.Value[i] { + case '\'': + buf = append(buf, '\'') + case '\\': + buf = append(buf, '\'') + } + buf = append(buf, arg.Value[i]) + } + buf = append(buf, '\'') + + } + return string(buf), nil +} Modified: v2/response.go (+4 -0) =================================================================== --- v2/response.go 2017-05-12 19:17:20 +0900 (b37f90a) +++ v2/response.go 2017-05-15 19:04:47 +0900 (c5323e6) @@ -11,3 +11,7 @@ type Response struct { Time time.Time Elapsed time.Duration } + +func NewResponse([]byte) (*Response, error) { + return nil, nil +} -------------- next part -------------- HTML����������������������������...다운로드