diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c98294c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,8 @@ +For all prompts: +- if you need to understand what project does, feel free to walk over files and analyze them + +Common recommendations: +- dont write unnesesary comments if code is obvious + +Recommendations on golang code style: +- avoid iota diff --git a/example/golang/main.go b/example/golang/main.go index 30028a2..0321aad 100644 --- a/example/golang/main.go +++ b/example/golang/main.go @@ -32,7 +32,7 @@ func main() { cmdStr := fmt.Sprintf("node %s", path.Join(cwd, "..", "ts/index.js")) cmd := exec.Command(cmdStr) - kit, err := kittenipc.New(cmd, &api, kittenipc.Config{}) + kit, err := kittenipc.NewParent(cmd, &api) if err != nil { log.Panic(err) } diff --git a/lib/golang/lib.go b/lib/golang/lib.go index aa283fb..b29b4b5 100644 --- a/lib/golang/lib.go +++ b/lib/golang/lib.go @@ -2,6 +2,7 @@ package kittenipc import ( "bufio" + "flag" "fmt" "net" "os" @@ -16,14 +17,20 @@ import ( "github.com/samber/mo" ) +const ipcSocketArg = "--ipc-socket" + type StdioMode int -type Config struct { -} +type ipcMode int + +const ( + modeParent ipcMode = 1 + modeChild ipcMode = 2 +) type KittenIPC struct { + mode ipcMode cmd *exec.Cmd - cfg Config localApi any socketPath string @@ -36,12 +43,13 @@ type KittenIPC struct { mu sync.Mutex } -func New(cmd *exec.Cmd, localApi any, cfg Config) (*KittenIPC, error) { +func NewParent(cmd *exec.Cmd, localApi any) (*KittenIPC, error) { k := KittenIPC{ + mode: modeParent, cmd: cmd, - cfg: cfg, localApi: localApi, pendingCalls: make(map[int64]chan callResult), + errCh: make(chan error, 1), } k.socketPath = filepath.Join(os.TempDir(), fmt.Sprintf("kitten-ipc-%d.sock", os.Getpid())) @@ -49,7 +57,6 @@ func New(cmd *exec.Cmd, localApi any, cfg Config) (*KittenIPC, error) { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - const ipcSocketArg = "--ipc-socket" if slices.Contains(cmd.Args, ipcSocketArg) { return nil, fmt.Errorf("you should not use `%s` argument in your command", ipcSocketArg) } @@ -60,7 +67,33 @@ func New(cmd *exec.Cmd, localApi any, cfg Config) (*KittenIPC, error) { return &k, nil } +func NewChild(localApi any) (*KittenIPC, error) { + k := KittenIPC{ + mode: modeChild, + localApi: localApi, + pendingCalls: make(map[int64]chan callResult), + errCh: make(chan error, 1), + } + + socketPath := flag.String("ipc-socket", "", "Path to IPC socket") + flag.Parse() + + if *socketPath == "" { + return nil, fmt.Errorf("ipc socket path is missing") + } + k.socketPath = *socketPath + + return &k, nil +} + func (k *KittenIPC) Start() error { + if k.mode == modeParent { + return k.startParent() + } + return k.startChild() +} + +func (k *KittenIPC) startParent() error { _ = os.Remove(k.socketPath) listener, err := net.Listen("unix", k.socketPath) if err != nil { @@ -81,6 +114,16 @@ func (k *KittenIPC) Start() error { return nil } +func (k *KittenIPC) startChild() error { + conn, err := net.Dial("unix", k.socketPath) + if err != nil { + return fmt.Errorf("connect to parent socket: %w", err) + } + k.conn = conn + k.startRcvData() + return nil +} + func (k *KittenIPC) acceptConn() error { const acceptTimeout = time.Second * 10 @@ -289,7 +332,13 @@ func (k *KittenIPC) closeSock() error { } func (k *KittenIPC) Wait() error { + if k.mode == modeParent { + return k.waitParent() + } + return k.waitChild() +} +func (k *KittenIPC) waitParent() error { waitErrCh := make(chan error, 1) go func() { @@ -310,6 +359,14 @@ func (k *KittenIPC) Wait() error { return nil } +func (k *KittenIPC) waitChild() error { + err := <-k.errCh + if err != nil { + return fmt.Errorf("ipc error: %w", err) + } + return nil +} + func mergeErr(errs ...error) (ret error) { for _, err := range errs { if err != nil {