envo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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