envo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ module Envo
2
+ class CliParser
3
+ def self.opt?(opt)
4
+ opt =~ /^-/
5
+ end
6
+ def self.filter_opts_front(args)
7
+ front_opts = args.take_while { |a| opt?(a) }
8
+ args.shift(front_opts.size)
9
+ front_opts
10
+ end
11
+ def self.filter_opts_back(args)
12
+ back_opts = args.reverse.take_while { |a| opt?(a) }.reverse
13
+ args.pop(back_opts.size)
14
+ back_opts
15
+ end
16
+ def self.filter_opts(args)
17
+ filter_opts_front(args) + filter_opts_back(args)
18
+ end
19
+ def initialize(opts)
20
+ @known_cmds = {}
21
+ @known_opts = opts
22
+ end
23
+ def add_cmd(name, parse_func)
24
+ raise Envo::Error "cmd #{name} is already added to parser" if @known_cmds[name]
25
+ @known_cmds[name] = parse_func
26
+ end
27
+ def parse(argv)
28
+ result = ParseResult.new
29
+ cmd = nil
30
+ while !argv.empty?
31
+ arg = argv.shift
32
+ if CliParser.opt?(arg)
33
+ result.opts.merge! @known_opts.parse_cli(arg)
34
+ else
35
+ break cmd = arg
36
+ end
37
+ end
38
+
39
+ raise Envo::Error.new 'missing command' if !cmd
40
+ raise Envo::Error.new "unknown command '#{cmd}'" if !@known_cmds[cmd]
41
+
42
+ parsed_cmd = @known_cmds[cmd].(cmd, argv)
43
+
44
+ cmd_opts = {}
45
+ parsed_cmd.opts.each do |opt|
46
+ cmd_opts.merge! @known_opts.parse_cli(opt)
47
+ end
48
+ parsed_cmd.opts = cmd_opts
49
+
50
+ result.cmds << parsed_cmd
51
+ result
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,53 @@
1
+ module Envo
2
+ class CmdClean
3
+ Name = 'clean'
4
+ def self.register_help(help)
5
+ help.add_cmd "clean <name> ...", <<~EOF
6
+ cleans environent variables
7
+ unsets empty strings, non-existing paths, empty lists
8
+ removes duplicates from lists and non-existing paths from path lists
9
+ EOF
10
+ end
11
+
12
+ def self.register_cli_parser(parser)
13
+ parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
14
+ end
15
+
16
+ def self.register_script_parser(parser)
17
+ parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
18
+ end
19
+
20
+ def self.parse_cli(args)
21
+ opts = CliParser.filter_opts(args)
22
+ parse_tokens(args, opts)
23
+ end
24
+
25
+ def self.parse_tokens(tokens, opts)
26
+ ParsedCmd.new(CmdClean.new(tokens), opts)
27
+ end
28
+
29
+ def initialize(names)
30
+ raise Error.new 'clean: no names provided' if names.empty?
31
+ @names = names
32
+ end
33
+
34
+ attr_reader :names, :show_names
35
+
36
+ def execute(ctx)
37
+ @names.each do |name|
38
+ ename = ctx.expand_name(name)
39
+
40
+ if ctx.raw?
41
+ val = ctx.raw_get(ename)
42
+ ctx.unset(ename) if val && val.empty?
43
+ else
44
+ val = ctx.smart_get(ename)
45
+ if val.type != :empty
46
+ val.clean!
47
+ ctx.smart_set(ename, val)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,56 @@
1
+ module Envo
2
+ class CmdCopy
3
+ Name = 'copy'
4
+ def self.register_help(help)
5
+ help.add_cmd 'copy <source-name> <target-name>', <<~EOF
6
+ copy value of source to target
7
+ shorthand: 'cp'
8
+ EOF
9
+ end
10
+
11
+ def self.register_cli_parser(parser)
12
+ parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
13
+ parser.add_cmd('cp', ->(cmd, args) { parse_cli(args) })
14
+ end
15
+
16
+ def self.register_script_parser(parser)
17
+ parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
18
+ end
19
+
20
+ def self.parse_cli(args)
21
+ opts = CliParser.filter_opts(args)
22
+ parse_tokens(args, opts)
23
+ end
24
+
25
+ def self.parse_tokens(tokens, opts)
26
+ raise Envo::Error.new "copy: provide two names to copy. Use 'copy <source-name> <target-name>'" if tokens.size != 2
27
+ ParsedCmd.new(CmdCopy.new(tokens[0], tokens[1]), opts)
28
+ end
29
+
30
+ def initialize(source, target)
31
+ @source = source
32
+ @target = target
33
+ end
34
+
35
+ attr_accessor :source, :target
36
+
37
+ def execute(ctx)
38
+ esrc = ctx.expand_name(@source)
39
+ raw_src = ctx.raw_get(esrc)
40
+ raise Envo::Error.new "copy: no such var '#{esrc}'" if !raw_src && !ctx.force?
41
+ return if !raw_src
42
+
43
+ etarget = ctx.expand_name(@target)
44
+ raw_target = ctx.raw_get(etarget)
45
+
46
+ ok = !raw_target
47
+ ok ||= ctx.ask("'#{etarget}' already exists. Overwrite?")
48
+ raise Envo::Error.new "'#{etarget}' exists" if !ok
49
+
50
+ return if esrc == etarget # copy something over itself...
51
+ return if !raw_src && !raw_target # no point in doing anything if they don't exist
52
+
53
+ ctx.raw_set(etarget, raw_src)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,50 @@
1
+ module Envo
2
+ class CmdList
3
+ Name = 'list'
4
+ def self.register_help(help)
5
+ help.add_cmd "list <name>", "show value of list environment variable which is a list"
6
+ help.add_cmd "list <name> add <val>", <<~EOF
7
+ add value to list if it's not already inside
8
+ --front - adds value to front of list (moves it to front if it's already inside)
9
+ --back - adds value to back of list (moves it to back if it's already inside)
10
+ EOF
11
+ help.add_cmd "list <name> del <val|index>", <<~EOF
12
+ remove a value from a list
13
+ if the provided value is an integer, it's interpreted as an index in the list
14
+ EOF
15
+ end
16
+
17
+ def self.register_cli_parser(parser)
18
+ parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
19
+ end
20
+
21
+ def self.parse_cli(args)
22
+ opts = CliParser.filter_opts_front(args)
23
+ raise Envo::Error.new "list: missing name. Use 'list <name> <cmd> <args>'" if args.empty?
24
+ name = args.shift
25
+ return ParsedCmd.new(CmdShow.new([name], true), opts) if args.empty? # just list <name>
26
+
27
+ cmd = args.shift
28
+ return CmdListAdd.parse_cli_args(name, args, opts) if cmd == 'add'
29
+ return CmdListDel.parse_cli_args(name, args, opts) if cmd == 'del'
30
+
31
+ raise Envo::Error.new "list: unkonwn subcommand #{cmd}"
32
+ end
33
+
34
+ def self.register_script_parser(parser)
35
+ parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_script(tokens, opts) })
36
+ end
37
+
38
+ def self.parse_script(tokens, opts)
39
+ raise Envo::Error.new "list: missing name. Use 'list <name> <cmd> <args>'" if tokens.empty?
40
+ name = tokens.shift
41
+ return ParsedCmd.new(CmdShow.new([name], true), opts) if tokens.empty? # just list <name>
42
+
43
+ cmd = tokens.shift
44
+ return CmdListAdd.parse_script(name, tokens, opts) if cmd == 'add'
45
+ return CmdListDel.parse_tokens(name, tokens, opts) if cmd == 'del'
46
+
47
+ raise Envo::Error.new "list: unkonwn subcommand #{cmd}"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,102 @@
1
+ module Envo
2
+ class CmdListAdd
3
+ def self.register_help(help)
4
+ help.add_cmd 'la <name> <val>', "shorthand for 'list <name> add <val>'"
5
+ end
6
+
7
+ def self.register_cli_parser(parser)
8
+ parser.add_cmd('la', ->(cmd, args) { parse_cli_all(args) })
9
+ end
10
+
11
+ def self.register_script_parser(parser)
12
+ end
13
+
14
+ def self.parse_cli_all(args)
15
+ opts = CliParser.filter_opts_front(args)
16
+ raise Envo::Error.new "list-add: missing name. Use 'la <name> <val>'" if args.empty?
17
+ parse_cli_args(args[0], args[1..], opts)
18
+ end
19
+
20
+ def self.parse_script(name, tokens, opts)
21
+ pos = nil
22
+ opts.filter! do |opt|
23
+ case opt
24
+ when 'front', 'top'
25
+ pos = :front
26
+ false
27
+ when 'back', 'bottom'
28
+ pos = :back
29
+ false
30
+ else
31
+ true
32
+ end
33
+ end
34
+
35
+ ParsedCmd.new(CmdListAdd.new(name, tokens, pos), opts)
36
+ end
37
+
38
+ def self.parse_cli_args(name, args, opts)
39
+ opts += CliParser.filter_opts(args)
40
+ pos = nil
41
+ opts.filter! do |opt|
42
+ case opt
43
+ when '--front', '--top'
44
+ pos = :front
45
+ false
46
+ when '--back', '--bottom'
47
+ pos = :back
48
+ false
49
+ else
50
+ true
51
+ end
52
+ end
53
+
54
+ ParsedCmd.new(CmdListAdd.new(name, args, pos), opts)
55
+ end
56
+
57
+ def initialize(name, values, pos)
58
+ raise Envo::Error.new 'list-add: no values to add provided' if values.empty?
59
+ @name = name
60
+ @values = values
61
+ @pos = pos
62
+ end
63
+
64
+ attr_accessor :name, :values, :pos
65
+
66
+ def execute(ctx)
67
+ ename = ctx.expand_name(@name)
68
+
69
+ list = ctx.smart_get(ename)
70
+
71
+ ok = list.list?
72
+ if !ok
73
+ if list.type == :empty
74
+ ok ||= ctx.ask("#{ename} doesn't exist. Create?")
75
+ else
76
+ ok ||= ctx.ask("#{ename} is not a list, but a #{list.type}. Convert?")
77
+ end
78
+ end
79
+ raise Envo::Error.new "list-add: adding list item to a non-list" if !ok
80
+
81
+ list = list.to_list
82
+
83
+ ordered = @pos == :front ? values.reverse : values
84
+ ordered.each do |val|
85
+ val = ctx.expand_value(val)
86
+
87
+ ok = list.accept_item?(val)
88
+ ok ||= ctx.ask("Add #{val.type} to #{list.type}?")
89
+ raise Envo::Error.new "list-add: adding #{val.type} to #{list.type}" if !ok
90
+
91
+ idesc = val.invalid_description
92
+ ok = !idesc
93
+ ok ||= ctx.ask("Add #{idesc} to #{ename}?")
94
+ raise Envo::Error.new "list-add: adding #{idesc} to #{ename}" if !ok
95
+
96
+ list.insert(val.to_s, @pos)
97
+ end
98
+
99
+ ctx.smart_set(ename, list)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,78 @@
1
+ module Envo
2
+ class CmdListDel
3
+ def self.register_help(help)
4
+ help.add_cmd 'ld <name> <val|index>', "shorthand for 'list <name> del <val|index>'"
5
+ end
6
+
7
+ def self.register_cli_parser(parser)
8
+ parser.add_cmd('ld', ->(cmd, args) { parse_cli_all(args) })
9
+ end
10
+
11
+ def self.register_script_parser(parser)
12
+ end
13
+
14
+ def self.parse_cli_all(args)
15
+ opts = CliParser.filter_opts_front(args)
16
+ raise Envo::Error.new "list-del: missing name. Use 'ld <name> <val|index>'" if args.empty?
17
+ parse_cli_args(args[0], args[1..], opts)
18
+ end
19
+
20
+ def self.parse_cli_args(name, args, opts)
21
+ opts += CliParser.filter_opts(args)
22
+ parse_tokens(name, args, opts)
23
+ end
24
+
25
+ def self.parse_tokens(name, tokens, opts)
26
+ raise Envo::Error.new "list-del: provide one value or index to delete. Use 'list <name> del <val|index>'" if tokens.size != 1
27
+ ParsedCmd.new(CmdListDel.new(name, tokens[0]), opts)
28
+ end
29
+
30
+ def initialize(name, value)
31
+ @name = name
32
+ @value = value
33
+ end
34
+
35
+ attr_accessor :name, :value
36
+
37
+ def execute(ctx)
38
+ ename = ctx.expand_name(@name)
39
+
40
+ list = ctx.smart_get(ename)
41
+
42
+ if list.type == :empty
43
+ return if ctx.force?
44
+ raise Envo::Error.new "list-del: deleting an item from a non-exiting value"
45
+ end
46
+
47
+ ok = list.list?
48
+ ok ||= ctx.ask("#{ename} is not a list, but a #{list.type}. Convert?")
49
+ raise Envo::Error.new "list-del: deleting an item from a non-list" if !ok
50
+
51
+ list = list.to_list
52
+
53
+ val = ctx.expand_value(@value)
54
+ if val.list?
55
+ raise Envo::Error.new "list-del: can't delete a list from a list" if !ctx.force?
56
+ return
57
+ end
58
+ val = val.to_s
59
+
60
+ index = val.to_i
61
+ if !ctx.raw? && index.to_s == val
62
+ deleted = list.delete_at(index)
63
+ if !deleted
64
+ raise Envo::Error.new "list-del: no index #{val} in #{ename}" if !ctx.force?
65
+ return
66
+ end
67
+ else
68
+ deleted = list.delete(val)
69
+ if !deleted
70
+ raise Envo::Error.new "list-del: no item '#{val}' in #{ename}" if !deleted
71
+ return
72
+ end
73
+ end
74
+
75
+ ctx.smart_set(ename, list)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ module Envo
2
+ class CmdPath
3
+ Name = 'path'
4
+ def self.register_help(help)
5
+ help.add_cmd "path <args>", <<~EOF
6
+ shorthand for 'list @path <args>'
7
+ shorthand: 'p'
8
+ EOF
9
+ end
10
+
11
+ def self.register_cli_parser(parser)
12
+ parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
13
+ parser.add_cmd('p', ->(cmd, args) { parse_cli(args) })
14
+ end
15
+
16
+ def self.register_script_parser(parser)
17
+ parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_script(tokens, opts) })
18
+ end
19
+
20
+ def self.parse_cli(args)
21
+ CmdList.parse_cli(args.unshift('@path'))
22
+ end
23
+
24
+ def self.parse_script(tokens, opts)
25
+ CmdList.parse_script(tokens.unshift('@path'), opts)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ module Envo
2
+ class CmdReset
3
+ Name = 'reset'
4
+ def self.register_help(help)
5
+ help.add_cmd 'reset <name>[=[<val>]]', <<~EOF
6
+ set or unset value of an existing environment variable
7
+ produce an error if the variable doesn't exist
8
+ EOF
9
+ end
10
+
11
+ def self.register_cli_parser(parser)
12
+ parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
13
+ end
14
+
15
+ def self.register_script_parser(parser)
16
+ parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
17
+ end
18
+
19
+ def self.parse_cli(args)
20
+ opts = CliParser.filter_opts(args)
21
+ parse_tokens(args, opts)
22
+ end
23
+
24
+ def self.parse_tokens(args, opts)
25
+ raise Envo::Error.new "reset: missing name. Use 'reset <name> [= [<val>]]'" if args.empty? || args[0][0] == '='
26
+ if args.size == 1
27
+ arg = args[0]
28
+ cnt = arg.count('=')
29
+ return ParsedCmd.new(CmdReset.new(arg, nil), opts) if cnt == 0
30
+ if cnt == 1 && arg[-1] == '='
31
+ split = arg.split('=')
32
+ return ParsedCmd.new(CmdReset.new(split[0], nil), opts) if split.size == 1
33
+ end
34
+ elsif args.size == 2
35
+ return ParsedCmd.new(CmdReset.new(args[0], nil), opts) if args[1] == '='
36
+ end
37
+
38
+ helper = CmdSet.parse_cli(args).cmd
39
+ ParsedCmd.new(CmdReset.new(helper.name, helper), opts)
40
+ end
41
+
42
+ def self.parse_script(args)
43
+ puts "#{Name} parse_script"
44
+ end
45
+
46
+ def initialize(name, helper)
47
+ @name = name
48
+ @helper = helper
49
+ end
50
+
51
+ attr_accessor :name, :helper
52
+
53
+ def execute(ctx)
54
+ ename = ctx.expand_name(@name)
55
+
56
+ raise Envo::Error.new "reset: no such var '#{ename}'" if !ctx.raw_get(ename)
57
+
58
+ if !@helper
59
+ ctx.unset(ename)
60
+ else
61
+ @helper.name = @name
62
+ @helper.execute(ctx)
63
+ end
64
+ end
65
+ end
66
+ end