diff --git a/go.mod b/go.mod index a19a02f..658d7c8 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,19 @@ go 1.23 toolchain go1.23.0 require ( - github.com/AlecAivazis/survey/v2 v2.3.6 github.com/aws/aws-sdk-go-v2 v1.26.1 github.com/aws/aws-sdk-go-v2/config v1.25.1 github.com/aws/aws-sdk-go-v2/service/ec2 v1.134.0 github.com/aws/aws-sdk-go-v2/service/lambda v1.54.0 github.com/aws/smithy-go v1.20.2 + github.com/charmbracelet/bubbletea v0.27.0 + github.com/fatih/color v1.17.0 github.com/olekukonko/tablewriter v0.0.5 - github.com/rs/zerolog v1.29.0 + github.com/rs/zerolog v1.33.0 github.com/urfave/cli/v2 v2.25.0 go.uber.org/goleak v1.3.0 go.uber.org/mock v0.4.0 - golang.org/x/sync v0.5.0 + golang.org/x/sync v0.8.0 ) require ( @@ -31,16 +32,23 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.17.2 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.2 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.25.2 // indirect + github.com/charmbracelet/x/ansi v0.1.4 // indirect + github.com/charmbracelet/x/input v0.1.0 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.1.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect - github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.3.8 // indirect ) diff --git a/go.sum b/go.sum index 7fe9b3b..0ff76fb 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= -github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= -github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= -github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= @@ -34,75 +30,88 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.25.2 h1:vwyiRTnXLqsak/6WAQ+uTRhVqKI6 github.com/aws/aws-sdk-go-v2/service/sts v1.25.2/go.mod h1:4EqRHDCKP78hq3zOnmFXu5k0j4bXbRFfCh/zQ6KnEfQ= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/charmbracelet/bubbletea v0.27.0 h1:Mznj+vvYuYagD9Pn2mY7fuelGvP0HAXtZYGgRBCbHvU= +github.com/charmbracelet/bubbletea v0.27.0/go.mod h1:5MdP9XH6MbQkgGhnlxUqCNmBXf9I74KRQ8HIidRxV1Y= +github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= +github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= +github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= +github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= -github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8= github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/app/app.go b/internal/app/app.go index 817e05b..5d3053c 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -102,14 +102,20 @@ func (a *App) getAction() func(c *cli.Context) error { return err } - regionsLabel := "Select regions you want to search.\n" - targetRegions, continuation := io.GetCheckboxes(regionsLabel, allRegions) + regionsLabel := []string{"Select regions you want to search."} + targetRegions, continuation, err := io.GetCheckboxes(regionsLabel, allRegions) + if err != nil { + return err + } if !continuation { return nil } - runtimeLabel := "Select runtime values you want to search.\n" - targetRuntime, continuation := io.GetCheckboxes(runtimeLabel, allRuntime) + runtimeLabel := []string{"Select runtime values you want to search."} + targetRuntime, continuation, err := io.GetCheckboxes(runtimeLabel, allRuntime) + if err != nil { + return err + } if !continuation { return nil } diff --git a/internal/io/input.go b/internal/io/input.go index a8049b3..cf5305e 100644 --- a/internal/io/input.go +++ b/internal/io/input.go @@ -6,35 +6,45 @@ import ( "os" "strings" - "github.com/AlecAivazis/survey/v2" + tea "github.com/charmbracelet/bubbletea" + "github.com/fatih/color" ) -const CheckboxesPageSize = 50 - -func GetCheckboxes(label string, opts []string) ([]string, bool) { +func GetCheckboxes(headers []string, opts []string) ([]string, bool, error) { for { - var checkboxes []string - prompt := &survey.MultiSelect{ - Message: label, - Options: opts, - PageSize: CheckboxesPageSize, + ui := NewUI(opts, headers) + p := tea.NewProgram(ui) + if _, err := p.Run(); err != nil { + return nil, false, err + } + + checkboxes := []string{} + for c := range ui.Choices { + if _, ok := ui.Selected[c]; ok { + checkboxes = append(checkboxes, ui.Choices[c]) + } } - //nolint:errcheck - survey.AskOne(prompt, &checkboxes, survey.WithKeepFilter(true)) - if len(checkboxes) == 0 { - Logger.Warn().Msg("Select values!") + switch { + case ui.IsCanceled: + Logger.Warn().Msg("Canceled!") + case len(checkboxes) == 0: + Logger.Warn().Msg("Not selected!") + } + if len(checkboxes) == 0 || ui.IsCanceled { ok := GetYesNo("Do you want to finish?") if ok { Logger.Info().Msg("Finished...") - return checkboxes, false + return checkboxes, false, nil } continue } + fmt.Fprintf(os.Stderr, " %s\n", color.CyanString(strings.Join(checkboxes, ", "))) + ok := GetYesNo("OK?") if ok { - return checkboxes, true + return checkboxes, true, nil } } } diff --git a/internal/io/ui.go b/internal/io/ui.go new file mode 100644 index 0000000..b7810ce --- /dev/null +++ b/internal/io/ui.go @@ -0,0 +1,313 @@ +package io + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/fatih/color" +) + +const SelectionPageSize = 20 + +type UI struct { + Choices []string + Headers []string + Cursor int + Selected map[int]struct{} + Filtered *Filtered + Keyword string + IsEntered bool + IsCanceled bool +} + +type Filtered struct { + Choices map[int]struct{} + Prev *Filtered + Cursor int +} + +var _ tea.Model = (*UI)(nil) + +func NewUI(choices []string, headers []string) *UI { + return &UI{ + Choices: choices, + Headers: headers, + Selected: make(map[int]struct{}), + } +} + +func (u *UI) Init() tea.Cmd { + filtered := make(map[int]struct{}) + for i := range u.Choices { + filtered[i] = struct{}{} + } + u.Filtered = &Filtered{Choices: filtered} + + return nil +} + +func (u *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + //nolint:gocritic + switch msg := msg.(type) { + + case tea.KeyMsg: + + switch msg.Type { + + // Quit the selection + case tea.KeyEnter: + u.IsEntered = true + return u, tea.Quit + + // Quit the selection + case tea.KeyCtrlC: + u.IsCanceled = true + return u, tea.Quit + + case tea.KeyUp, tea.KeyShiftTab: + if len(u.Filtered.Choices) < 2 { + return u, nil + } + for range u.Choices { + if u.Cursor == 0 { + u.Cursor = len(u.Choices) - 1 + } else if u.Cursor > 0 { + u.Cursor-- + } + + if _, ok := u.Filtered.Choices[u.Cursor]; !ok { + continue + } + if u.Filtered.Cursor == 0 { + u.Filtered.Cursor = len(u.Filtered.Choices) - 1 + } else if u.Filtered.Cursor > 0 { + u.Filtered.Cursor-- + } + + f := u.Filtered + for f.Prev != nil { + f.Prev.Cursor = u.Filtered.Cursor + f = f.Prev + } + break + } + + case tea.KeyDown, tea.KeyTab: + if len(u.Filtered.Choices) < 2 { + return u, nil + } + for range u.Choices { + if u.Cursor < len(u.Choices)-1 { + u.Cursor++ + } else if u.Cursor == len(u.Choices)-1 { + u.Cursor = 0 + } + + if _, ok := u.Filtered.Choices[u.Cursor]; !ok { + continue + } + if u.Filtered.Cursor < len(u.Filtered.Choices)-1 { + u.Filtered.Cursor++ + } else if u.Filtered.Cursor == len(u.Filtered.Choices)-1 { + u.Filtered.Cursor = 0 + } + + f := u.Filtered + for f.Prev != nil { + f.Prev.Cursor = u.Filtered.Cursor + f = f.Prev + } + break + } + + // select or deselect an item + case tea.KeySpace: + if _, ok := u.Filtered.Choices[u.Cursor]; !ok { + return u, nil + } + _, ok := u.Selected[u.Cursor] + if ok { + delete(u.Selected, u.Cursor) + } else { + u.Selected[u.Cursor] = struct{}{} + } + + // select all items in filtered list + case tea.KeyRight: + for i := range u.Choices { + if _, ok := u.Filtered.Choices[i]; !ok { + continue + } + _, ok := u.Selected[i] + if !ok { + u.Selected[i] = struct{}{} + } + } + + // clear all selected items in filtered list + case tea.KeyLeft: + for i := range u.Choices { + if _, ok := u.Filtered.Choices[i]; !ok { + continue + } + _, ok := u.Selected[i] + if ok { + delete(u.Selected, i) + } + } + + // clear one character from the keyword + case tea.KeyBackspace: + u.backspace() + + // clear the keyword + case tea.KeyCtrlW: + for u.Keyword != "" { + u.backspace() + } + + // add a character to the keyword + case tea.KeyRunes: + str := msg.String() + if !msg.Paste { + for _, r := range str { + u.addCharacter(string(r)) + } + } else { + if strings.Contains(str, string('\n')) || strings.Contains(str, string('\r')) { + u.IsEntered = true + return u, tea.Quit + } + + runes := []rune(str) + for i, r := range runes { + // characters by paste key are enclosed by '[' and ']' + if i == 0 || i == len(runes)-1 { + continue + } + if r != ' ' && r != '\t' { + u.addCharacter(string(r)) + } + } + } + + } + } + + return u, nil +} + +func (u *UI) backspace() { + if len(u.Keyword) == 0 { + return + } + + keywordRunes := []rune(u.Keyword) + keywordRunes = keywordRunes[:len(keywordRunes)-1] + u.Keyword = string(keywordRunes) + u.Filtered = u.Filtered.Prev + cnt := 0 + for i := range u.Choices { + if _, ok := u.Filtered.Choices[i]; !ok { + continue + } + if cnt == u.Filtered.Cursor { + u.Cursor = i + break + } + cnt++ + } +} + +func (u *UI) addCharacter(c string) { + u.Keyword += c + u.Filtered = &Filtered{ + Choices: make(map[int]struct{}), + Prev: u.Filtered, + } + + tmpCursor := u.Cursor + for i, choice := range u.Choices { + lk := strings.ToLower(u.Keyword) + lc := strings.ToLower(choice) + contains := strings.Contains(lc, lk) + + fLen := len(u.Filtered.Choices) + if contains && fLen != 0 && fLen <= u.Filtered.Prev.Cursor { + u.Filtered.Cursor++ + u.Cursor = i + } + + switch { + case contains: + u.Filtered.Choices[i] = struct{}{} + tmpCursor = i + case u.Cursor == i && u.Cursor < len(u.Choices)-1: + u.Cursor++ + case u.Cursor == i: + u.Cursor = tmpCursor + } + } + + if len(u.Filtered.Choices) == 0 { + return + } + f := u.Filtered + for f.Prev != nil { + f.Prev.Cursor = u.Filtered.Cursor + f = f.Prev + } +} + +func (u *UI) View() string { + bold := color.New(color.Bold) + + s := color.CyanString("? ") + + for _, header := range u.Headers { + s += bold.Sprintln(header) + } + + if u.IsEntered && len(u.Selected) != 0 { + return s + } + + s += bold.Sprintln(u.Keyword) + + s += color.CyanString(" [Use arrows to move, space to select, to all, to none, type to filter]") + s += "\n" + + var contents []string + for i, choice := range u.Choices { + if _, ok := u.Filtered.Choices[i]; !ok { + continue + } + + cursor := " " // no cursor + if u.Cursor == i { + cursor = color.CyanString(bold.Sprint(">")) // cursor! + } + + checked := bold.Sprint("[ ]") // not selected + if _, ok := u.Selected[i]; ok { + checked = color.GreenString("[x]") // selected! + } + + contents = append(contents, fmt.Sprintf("%s %s %s\n", cursor, checked, choice)) + } + + if len(contents) > SelectionPageSize { + switch { + case u.Filtered.Cursor < SelectionPageSize/2: + contents = contents[:SelectionPageSize] + case u.Filtered.Cursor > len(contents)-SelectionPageSize/2: + contents = contents[len(contents)-SelectionPageSize:] + default: + contents = contents[u.Filtered.Cursor-SelectionPageSize/2 : u.Filtered.Cursor+SelectionPageSize/2] + } + } + + s += strings.Join(contents, "") + return s +}