Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る(Go言語のアプリを統合する)

More than 1 year has passed since last update.

概要

Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る シリーズの第4弾です。

Go言語で作られたアプリやあるライブラリのサンプルなどをこのJavaScript環境に統合することを考えます。アプリによっては出来なかったり、手直しが必要なものがあります。

もともとこのコマンドシェルを作ったの自分が使いたいものを一つのバイナリでかつJavaScriptをコマンド替わりに使おうということです。なので便利な既存のGo製のツールやサンプルを統合しましょう。

前提としてgo moduleで話を進めますので、depなどを使っている方には申し訳ありません。

統合するアプリ

事前準備

モジュール名をmyshellとします。アプリのルートディレクトリもmyshellとします。

> cd myshell
> go mod init myshell
go: creating new go.mod: module myshell

> mkdir cmd

いままで作成したmain.gomyshell/cmd/main.goと配置します。

> cd cmd
> go run main.go
go: finding github.com/dop251/goja latest
go: finding golang.org/x/text/unicode/norm latest
go: finding golang.org/x/text/collate latest
go: finding golang.org/x/text/cases latest
go: finding golang.org/x/text/language latest
go: finding golang.org/x/text/unicode latest
> print(10)
10
undefined
> exit

となりまして無事コンパイルできました。

これまで作成したexcelwebのソースを分けるときはには注意してください。mainパッケージで作成したのでjs.Runtimeにアクセスできましたが違うパッケージにするとアクセスできなくなります。

jidサンプルの統合

https://github.com/simeji/jid/blob/master/cmd/jid/jid.go
のソースを取り込みます。
取り込みますがJavaScriptとの相互使用はしません。あくまでコマンドとしてアプリの起動のみです。
当然アプリなのでmainがあるのでそのままではダメです。
まず、関数名などが被らないように固有のパッケージ名にします。今回はmyshell/myjidというパッケージ名します。ダウンロードしたソースjid.gomyshell/myjid/jid.goに配置します。

以下に変更したソースを示します。 //myjidが付いている箇所が変更箇所です。os.exitは全てreturnにしました。flag.BoolVar同じパラメータで一度しか実行できないので最初の起動のみにしました。このサンプルのJSONデータは標準入力でしたがJavaScriptからJSON文字列で渡すようにしました。*goja.Runtimeも渡せばJavaScriptと相互使用ができるでしょう。mainMainJidとしました。

main.go
import  "myshell/myjid"

func initialSetting() *jsRuntime {

    rt.Set("jid",myjid.MainJid)

}

jid.go
package myjid //myjid

import (
    "flag"
    "fmt"
    "strings"

    "github.com/simeji/jid"
)

const VERSION = "0.7.6"

var isJidExec = false //myjid

func MainJid(json string) { //myjid
    //content := os.Stdin  //myjid
    content := strings.NewReader(json) //myjid
    var qm bool
    var help bool
    var version bool
    var mono bool
    var pretty bool
    qs := "."

    if !isJidExec { //myjid
        flag.BoolVar(&qm, "q", false, "Output query mode")
        flag.BoolVar(&help, "h", false, "print a help")
        flag.BoolVar(&help, "help", false, "print a help")
        flag.BoolVar(&version, "version", false, "print the version and exit")
        flag.BoolVar(&mono, "M", false, "monochrome output mode")
        flag.BoolVar(&pretty, "p", false, "pretty print json result")
        flag.Parse()
        isJidExec = true //myjid
    } //myjid
    if help {
        flag.Usage()
        fmt.Println(getHelpString())
        //os.Exit(0) //myjid
        return //myjid
    }

    if version {
        fmt.Println(fmt.Sprintf("jid version v%s", VERSION))
        //os.Exit(0)  //myjid
        return //myjid
    }
    args := flag.Args()
    if len(args) > 0 {
        qs = args[0]
    }

    ea := &jid.EngineAttribute{
        DefaultQuery: qs,
        Monochrome:   mono,
        PrettyResult: pretty,
    }

    e, err := jid.NewEngine(content, ea)

    if err != nil {
        fmt.Println(err)
        //os.Exit(1) //myjid
        return //myjid
    }
    //os.Exit(run(e, qm)) //myjid
    run(e, qm) //myjid
}

func run(e jid.EngineInterface, qm bool) int {

    result := e.Run()
    if result.GetError() != nil {
        return 2
    }
    if qm {
        fmt.Printf("%s", result.GetQueryString())
    } else {
        fmt.Printf("%s", result.GetContent())
    }
    return 0
}

func getHelpString() string {
    return `

============ Load JSON from a file ==============

$ jid < file.json

============ With a JSON filter mode =============

TAB / CTRL-I
  Show available items and choice them

CTRL-W
  Delete from the cursor to the start of the word

CTRL-U
  Delete whole query

CTRL-F / Right Arrow
  Move cursor a character to the right

CTRL-B / Left Arrow
  Move cursor a character to the left

CTRL-A
  To the first character of the 'Filter'

CTRL-E
  To the end of the 'Filter'

CTRL-J
  Scroll json buffer 1 line downwards

CTRL-K
  Scroll json buffer 1 line upwards

CTRL-G
  Scroll json buffer to bottom

CTRL-T
  Scroll json buffer to top

CTRL-N
  Scroll json buffer 'Page Down'

CTRL-P
  Scroll json buffer 'Page Up'

CTRL-L
  Change view mode whole json or keys (only object)

ESC
  Hide a candidate box

`
}

実行してみます。Windowsの場合はchcp 65001にしてUTF-8にしてください。そうしないとjidで日本語表示が乱れます。

chcp 65001
go run main.go
> doc = [
...>     {a:1, b:"1111111"},
...>     {a:2, b:"あいうえお"}
...> ]
[{"a":1,"b":"1111111"},{"a":2,"b":"あいうえお"}]
> json=JSON.stringify(doc)
[{"a":1,"b":"1111111"},{"a":2,"b":"あいうえお"}]
> jid(json)

doc変数はJavaScriptのオブジェクトなのでjidには直接渡せませんのでJSON.stringifyで文字列にしてからjidに渡します。jidの画面に切り替わります。文字列を直接渡すときは必ずaなどの項目名は"a"と引用符を付けてください。

[Filter]> .[
[
  {
    "a": 1,
    "b": "1111111"
  },
  {
    "a": 2,
    "b": "あ い う え お "
  }
]
[Filter]> .[1]
{
  "a": 2,
  "b": "あ い う え お "
}
[Filter]> .[1].b
 "あ い う え お "

終了時にはCTRL+Uを押して初期状態に戻して.qで終了します。あまり詳しくないので他の操作があるかもしれません。

termdashdemoデモを統合

パッケージはmyshell/mytermdashとします。

https://github.com/mum4k/termdash/blob/master/termdashdemo/termdashdemo.go
をダウンロードしてmyshell/mytermdash/termdashdemo.goと配置します。

これに関してはソースの量が多いので変更箇所のみ掲載します。

main.go
import  "myshell/mytermdash"

func initialSetting() *jsRuntime {

    rt.Set("dash",mytermdash.MainTermdash)

}
termdashdemo.go
package main
  // を以下に変更
package mytermdash
termdashdemo.go
func main() 
  // を以下に変更
func MainTermdash() 

これだけの変更で実行できます。

go run main.go
> dash()

以下の画面に切り替わります。ESCを押せば終了します。中央のtextlinechartなどのボタンをマウスでクリックしてみてください。画面が変わります。

image.png

まとめ

パッケージを分けることによって関数や変数がバッティングしないようにします。そしてmainを適当な名称に変更してJavaScriptに関係付けます。オリジナルのアプリがコマンドライン引数を必要とする場合はmainの引数として渡すなりの工夫が必要になります。

いろいろなコマンドを作成したり、既存のアプリを統合したりして自分が使いやすいツールとして作成していきます。Go言語には有用なパッケージが多数存在しますのでどんなツールになるか楽しみです。

また、JavaScriptのRuntimeを複数作って切り替えることも考えていますが、使い道があるかは分かりません。

Go言語パッケージの一覧は下記が有用です。

次回の投稿はこのシリーズの最後として、最初の投稿に書いたgojaの簡単な改造を説明します。

h6591
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away