goon_model_gen 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/goon_model_gen/builder/converter_builder.rb +76 -0
  3. data/lib/goon_model_gen/cli.rb +24 -0
  4. data/lib/goon_model_gen/config.rb +4 -0
  5. data/lib/goon_model_gen/converter/abstract_conv.rb +20 -0
  6. data/lib/goon_model_gen/converter/conv_file.rb +20 -0
  7. data/lib/goon_model_gen/converter/loader.rb +85 -0
  8. data/lib/goon_model_gen/converter/mapping.rb +21 -0
  9. data/lib/goon_model_gen/converter/payload_conv.rb +25 -0
  10. data/lib/goon_model_gen/converter/result_conv.rb +24 -0
  11. data/lib/goon_model_gen/converter/type_ref.rb +14 -0
  12. data/lib/goon_model_gen/golang/builtin.rb +2 -1
  13. data/lib/goon_model_gen/golang/combination_type.rb +45 -0
  14. data/lib/goon_model_gen/golang/enum.rb +1 -1
  15. data/lib/goon_model_gen/golang/field.rb +51 -0
  16. data/lib/goon_model_gen/golang/file.rb +3 -4
  17. data/lib/goon_model_gen/golang/modifier.rb +4 -2
  18. data/lib/goon_model_gen/golang/named_slice.rb +12 -2
  19. data/lib/goon_model_gen/golang/package.rb +25 -5
  20. data/lib/goon_model_gen/golang/packages.rb +69 -17
  21. data/lib/goon_model_gen/golang/structs_loader.rb +67 -0
  22. data/lib/goon_model_gen/golang/type.rb +4 -2
  23. data/lib/goon_model_gen/templates/converter/payload/01_base_imports.go.erb +11 -0
  24. data/lib/goon_model_gen/templates/converter/payload/02_SliceToModelSlice.go.erb +45 -0
  25. data/lib/goon_model_gen/templates/converter/payload/03_ToModel.go.erb +21 -0
  26. data/lib/goon_model_gen/templates/converter/payload/04_AssignModel.go.erb +90 -0
  27. data/lib/goon_model_gen/templates/converter/result/01_base_imports.go.erb +18 -0
  28. data/lib/goon_model_gen/templates/converter/result/02_SliceToResultSlice.go.erb +33 -0
  29. data/lib/goon_model_gen/templates/converter/result/03_ToResult.go.erb +50 -0
  30. data/lib/goon_model_gen/version.rb +1 -1
  31. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23badabb9901481656de5b91833d0b04452ff998cf18036fbc8c4152963ed7d8
4
- data.tar.gz: 25c94a3b11c2411e8d5d68367dc9e5f81d737072be6e69d151597e92232066b8
3
+ metadata.gz: 4283b78918a93a63233070c2e5d0d1ef100d4b214e672b50640f5b70c6aa08b3
4
+ data.tar.gz: f0bc7f7a08b1311a4b12d754d1bce760fbe59ac39e6bdbf48e215d63c3b9cf2e
5
5
  SHA512:
6
- metadata.gz: 2c085ab7ad6374af508b4d2cc57fd750d9d2843d10d05906050aae9f31a4d27739df11118a542a21d9e8e3dd6500809e2bfd52c13f29c610b436adebc5cb0f28
7
- data.tar.gz: fc2f17df0f6446a511615215bf7f0937aa8c29639229f007a456385d53eeab3bc95433b2e439bd09ba30b4fac336db22c42f6c86c6453207983bc5fa23751db5
6
+ metadata.gz: 26af49ba18494c6722cd7421ab53598c70ad86a8ced5269ea1328989713678517f696f4886b6cac1fdad5c378e230d72a34e3c0cc399a0db04f7973a1227fb80
7
+ data.tar.gz: ad98807c4bfdfbf715b2b528ae5b193b3e205489d505ddca16956c74132113ef50329a8526aef636dff16498b25308a6c02a0f6710a6740488ee0d88b95e576f
@@ -0,0 +1,76 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/builder/abstract_builder"
4
+
5
+ require "goon_model_gen/source/struct"
6
+
7
+ require "goon_model_gen/golang/package"
8
+ require "goon_model_gen/golang/datastore_supported"
9
+
10
+
11
+ module GoonModelGen
12
+ module Builder
13
+ class ConverterBuilder < AbstractBuilder
14
+ attr_reader :loader
15
+ attr_reader :packages
16
+
17
+ # @param base_package_path [String]
18
+ # @param loader [Converter::Loader]
19
+ # @param packages [Golang::Packages]
20
+ def initialize(base_package_path, loader, packages)
21
+ super(base_package_path)
22
+ @package_suffix = "_conv"
23
+ @loader = loader
24
+ @packages = packages
25
+ end
26
+
27
+ # @param conv_file_path [Array<String>]
28
+ def build(conv_file_paths)
29
+ Golang::Packages.new.tap do |pkgs|
30
+ build_sentences = []
31
+ conv_file_paths.each do |conv_file_path|
32
+ conf_file = loader.process(conv_file_path)
33
+ procs = build_package(pkgs, conf_file)
34
+ build_sentences.concat(procs)
35
+ end
36
+ resolve_type_names(pkgs)
37
+ build_sentences.each(&:call)
38
+ end
39
+ end
40
+
41
+ # @param pkgs [Golang::Packages]
42
+ # @param conv_file [Converter::ConvFile]
43
+ # @return [Array<Proc>]
44
+ def build_package(pkgs, conv_file)
45
+ procs = []
46
+ pkgs.new_package(conv_file.converter_package_path).tap do |pkg|
47
+ {
48
+ 'converter/payload' => conv_file.payload_convs,
49
+ 'converter/result' => conv_file.result_convs,
50
+ }.each do |template_dir, convs|
51
+ convs.each do |conv|
52
+ conv_type = pkg.new_combination_type(conv.name).tap do |t|
53
+ m = conv.model
54
+ g = conv.gen_type
55
+ t.add(:model, m.name, m.package_path, m.package_base_path)
56
+ t.add(:gen_type, g.name, g.package_path, g.package_base_path)
57
+ t.memo['mappings'] = conv.mappings
58
+ unless conv.model.slice_with_ptr.nil?
59
+ t.memo['model_slice_with_ptr'] = conv.model.slice_with_ptr
60
+ end
61
+ end
62
+ procs << Proc.new{ build_sentences_with(template_dir, conv_type, nil) }
63
+ end
64
+ end
65
+ end
66
+ return procs
67
+ end
68
+
69
+ # @param pkgs [Golang::Packages]
70
+ def resolve_type_names(pkgs)
71
+ pkgs.resolve_type_names(Golang::DatastoreSupported.packages.dup.add(packages))
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -8,7 +8,10 @@ require "goon_model_gen/config"
8
8
  require "goon_model_gen/builder/model_builder"
9
9
  require "goon_model_gen/builder/store_builder"
10
10
  require "goon_model_gen/builder/validation_builder"
11
+ require "goon_model_gen/builder/converter_builder"
12
+ require "goon_model_gen/converter/loader"
11
13
  require "goon_model_gen/source/loader"
14
+ require "goon_model_gen/golang/structs_loader"
12
15
  require "goon_model_gen/generator"
13
16
 
14
17
  module GoonModelGen
@@ -58,6 +61,27 @@ module GoonModelGen
58
61
  end
59
62
  end
60
63
 
64
+ desc "converter FILE1...", "Generate store files from converter YAML files"
65
+ option :inspect, type: :boolean, desc: "Don't generate any file and show package objects if given"
66
+ def converter(*paths)
67
+ loader = Converter::Loader.new(cfg)
68
+ package_hash = Golang::StructsLoader.new.process(cfg.structs_json_path) # Golang::Packages
69
+ packages = Golang::Packages.wrap(package_hash.values.flatten)
70
+ converter_package = packages.find_or_new(cfg.converter_package_path)
71
+
72
+ b = Builder::ConverterBuilder.new(cfg.converter_package_path, loader, Golang::Packages.new.add(*packages))
73
+ conv_packages = b.build(paths)
74
+
75
+ if options[:inspect]
76
+ puts YAML.dump(conv_packages)
77
+ else
78
+ conv_packages.map(&:files).flatten.each do |f|
79
+ new_generator(f, packages).
80
+ run(converter_package: converter_package)
81
+ end
82
+ end
83
+ end
84
+
61
85
  no_commands do
62
86
  def cfg
63
87
  @cfg ||= Config.new.load_from(options[:config])
@@ -20,7 +20,10 @@ module GoonModelGen
20
20
  store_package_path
21
21
  converter_dir
22
22
  converter_package_path
23
+ goa_gen_dir
24
+ goa_gen_package_path
23
25
  structs_gen_dir
26
+ structs_json_path
24
27
  validator_package_path
25
28
  version_comment
26
29
  ].freeze
@@ -41,6 +44,7 @@ module GoonModelGen
41
44
  @converter_package_path ||= join_paths(@base_package_path, @converter_dir)
42
45
  @goa_gen_package_path ||= join_paths(@base_package_path, @goa_gen_dir)
43
46
  @structs_gen_dir ||= "./cmd/structs"
47
+ @structs_json_path ||= "./structs.json"
44
48
  @version_comment ||= false
45
49
  self
46
50
  end
@@ -0,0 +1,20 @@
1
+ require "goon_model_gen"
2
+
3
+ module GoonModelGen
4
+ module Converter
5
+ class AbstractConv
6
+ attr_accessor :file # ConvFile
7
+ attr_reader :name
8
+ attr_reader :model # TypeRef
9
+ attr_reader :gen_type # TypeRef Payload/Result
10
+ attr_reader :mappings
11
+
12
+ def initialize(name, model, gen_type, mappings)
13
+ @name = name
14
+ @model = model
15
+ @gen_type = gen_type
16
+ @mappings = mappings
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require "goon_model_gen"
2
+
3
+ module GoonModelGen
4
+ module Converter
5
+ class ConvFile
6
+ attr_reader :path
7
+ attr_reader :converter_package_path # String
8
+ attr_accessor :payload_convs, :result_convs # [XxxxConv]
9
+
10
+ def initialize(path, converter_package_path)
11
+ @path = path
12
+ @converter_package_path = converter_package_path
13
+ end
14
+
15
+ def basename
16
+ ::File.basename(path, '.*')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,85 @@
1
+ require "goon_model_gen"
2
+
3
+ require "erb"
4
+ require "yaml"
5
+
6
+ require "goon_model_gen/converter/conv_file"
7
+ require "goon_model_gen/converter/payload_conv"
8
+ require "goon_model_gen/converter/result_conv"
9
+ require "goon_model_gen/converter/type_ref"
10
+ require "goon_model_gen/converter/mapping"
11
+
12
+ require "active_support/core_ext/string"
13
+
14
+ module GoonModelGen
15
+ module Converter
16
+ class Loader
17
+ attr_reader :config
18
+
19
+ def initialize(config)
20
+ @config = config
21
+ end
22
+
23
+ def process(path)
24
+ erb = ERB.new(::File.read(path), nil, "-")
25
+ erb.filename = path
26
+ txt = erb.result
27
+ raw = YAML.load(txt)
28
+
29
+ converter_dir = raw['converter_dir'] || ::File.basename(path, '.*')
30
+ converter_package_path = raw['converter_package_path'] || File.join(config.converter_package_path, converter_dir)
31
+ ConvFile.new(path, converter_package_path).tap do |f|
32
+ f.payload_convs = load_conv_defs(f, PayloadConv, raw['payloads'] || {})
33
+ f.result_convs = load_conv_defs(f, ResultConv, raw['results'] || {})
34
+ end
35
+ end
36
+
37
+ def load_conv_defs(f, conv_class, hash)
38
+ hash.map do |(name, definition)|
39
+ model = load_model_for_conv(definition['model'])
40
+ gen_type = TypeRef.new(name, File.join(config.goa_gen_package_path, f.basename))
41
+ mappings = load_mappings(definition['mappings'], conv_class)
42
+ conv_class.new(name, model, gen_type, mappings).tap do |conv|
43
+ conv.file = f
44
+ end
45
+ end
46
+ end
47
+
48
+ def load_model_for_conv(obj)
49
+ pkg_name, pkg_path, pkg_base_path = nil, nil, nil
50
+ slice_with_ptr = nil
51
+ case obj
52
+ when Hash
53
+ pkg_name, pkg_path = obj['name'], obj['package_path']
54
+ slice_with_ptr = obj['slice_with_ptr']
55
+ when String
56
+ pkg_name = obj
57
+ pkg_base_path = config.model_package_path
58
+ else
59
+ raise "Unsupported model type for converter definition: #{obj.inspect}"
60
+ end
61
+ TypeRef.new(pkg_name, pkg_path).tap do |t|
62
+ t.package_base_path = pkg_base_path
63
+ t.slice_with_ptr = slice_with_ptr
64
+ end
65
+ end
66
+
67
+ def load_mappings(hash, conv_class)
68
+ hash.map do |(name, props)|
69
+ props ||= {}
70
+ props = {'arg' => props} if props.is_a?(String)
71
+ args = props['args'] || [props['arg'] || name]
72
+ func, requires_context, returns_error = *conv_class.load_func(props)
73
+ if func.nil? && (args.length > 1)
74
+ raise "Invalid argument length: #{args.length} for #{name}: #{args.inspect}"
75
+ end
76
+ Mapping.new(name, args, func, requires_context, returns_error).tap do |m|
77
+ m.allow_zero = props['allow_zero']
78
+ m.resolve_package_path(config)
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,21 @@
1
+ require "goon_model_gen"
2
+
3
+ module GoonModelGen
4
+ module Converter
5
+ class Mapping
6
+ attr_reader :name, :args, :func, :requires_context, :returns_error
7
+ attr_accessor :package_base_path, :package_name
8
+ attr_accessor :allow_zero # for int or uint only
9
+ def initialize(name, args, func, requires_context, returns_error)
10
+ @name, @args, @func, @requires_context, @returns_error = name, args, func, requires_context, returns_error
11
+ end
12
+
13
+ def resolve_package_path(config)
14
+ if func.present? && func.include?('.')
15
+ self.package_base_path = requires_context ? config.store_package_path : config.model_package_path
16
+ self.package_name = func.split('.', 2).first
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/converter/abstract_conv"
4
+
5
+ module GoonModelGen
6
+ module Converter
7
+ class PayloadConv < AbstractConv
8
+ class << self
9
+ # @return [String, boolean, boolean] func, requires_context, returns_error
10
+ def load_func(props)
11
+ if f = props['filter']
12
+ return f, false, false
13
+ elsif f = props['reader']
14
+ return f, false, true
15
+ elsif f = props['loader']
16
+ return f, true, true
17
+ else
18
+ return nil, nil, nil
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/converter/abstract_conv"
4
+
5
+ module GoonModelGen
6
+ module Converter
7
+ class ResultConv < AbstractConv
8
+
9
+ class << self
10
+ # @return [String, boolean, boolean] func, requires_context, returns_error
11
+ def load_func(props)
12
+ if f = props['filter']
13
+ return f, false, false
14
+ elsif f = props['writer']
15
+ return f, false, true
16
+ else
17
+ return nil, nil, nil
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ require "goon_model_gen"
2
+
3
+ module GoonModelGen
4
+ module Converter
5
+ class TypeRef
6
+ attr_reader :name, :package_path
7
+ attr_accessor :package_base_path
8
+ attr_accessor :slice_with_ptr
9
+ def initialize(name, package_path)
10
+ @name, @package_path = name, package_path
11
+ end
12
+ end
13
+ end
14
+ end
@@ -9,7 +9,8 @@ module GoonModelGen
9
9
  TYPE_NAMES =
10
10
  %w[bool byte complex128 complex64
11
11
  error float32 float64 int int16 int32 int64 int8
12
- rune string uint uint16 uint32 uint64 uint8 uintptr]
12
+ rune string uint uint16 uint32 uint64 uint8 uintptr
13
+ interface] # interface is not a type but is added to ease to treat any type
13
14
 
14
15
  class << self
15
16
  def package
@@ -0,0 +1,45 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/golang/type"
4
+
5
+ module GoonModelGen
6
+ module Golang
7
+ class CombinationType < Type
8
+ class ItemType
9
+ attr_reader :name, :package_path, :package_base_path
10
+ attr_reader :type
11
+ def initialize(name, package_path, package_base_path = nil)
12
+ @name, @package_path, @package_base_path = name, package_path, package_base_path
13
+ end
14
+
15
+ def to_hash
16
+ {name: name, package_path: package_path, package_base_path: package_base_path}
17
+ end
18
+
19
+ # @param pkgs [Packages]
20
+ def resolve(pkgs)
21
+ @type = pkgs.type_by(**to_hash) || raise("Type not found by #{to_hash.inspect}")
22
+ end
23
+ end
24
+
25
+ attr_reader :map
26
+
27
+ # @param name [String]
28
+ def initialize(name)
29
+ super(name)
30
+ @map = {}
31
+ end
32
+
33
+ def add(key, name, package_path, package_base_path = nil)
34
+ map[key] = ItemType.new(name, package_path, package_base_path)
35
+ end
36
+
37
+ # @param pkgs [Packages]
38
+ def resolve(pkgs)
39
+ map.each do |_, item|
40
+ item.resolve(pkgs)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -19,7 +19,7 @@ module GoonModelGen
19
19
 
20
20
  # @param pkgs [Packages]
21
21
  def resolve(pkgs)
22
- @base_type = pkgs.type_for(base_type_name) || raise("#{base_type_name.inspect} not found")
22
+ @base_type = pkgs.type_for(base_type_name) || raise("#{base_type_name.inspect} not found for #{name}")
23
23
  end
24
24
  end
25
25
  end
@@ -1,5 +1,11 @@
1
1
  require "goon_model_gen"
2
2
 
3
+ require "goon_model_gen/golang/type"
4
+ require "goon_model_gen/golang/struct"
5
+ require "goon_model_gen/golang/named_slice"
6
+ require "goon_model_gen/golang/builtin"
7
+ require "goon_model_gen/golang/modifier"
8
+
3
9
  module GoonModelGen
4
10
  module Golang
5
11
  class Field
@@ -37,6 +43,51 @@ module GoonModelGen
37
43
  (type.package.path == pkg.path) ? type.name : type.qualified_name
38
44
  "#{ name } #{ type_exp } `#{ tags_string }`"
39
45
  end
46
+
47
+ def ptr?
48
+ case type
49
+ when Modifier then (type.prefix == "*")
50
+ when Type then false
51
+ else raise "Unsupported type class #{type.inspect}"
52
+ end
53
+ end
54
+
55
+ def slice?
56
+ case type
57
+ when Modifier then (type.prefix == "[]")
58
+ when NamedSlice then true
59
+ when Type then false
60
+ else raise "Unsupported type class #{type.inspect}"
61
+ end
62
+ end
63
+
64
+ def struct?
65
+ case type
66
+ when Modifier then false
67
+ when Struct then true
68
+ when Type then false
69
+ else raise "Unsupported type class #{type.inspect}"
70
+ end
71
+ end
72
+
73
+ def value_ptr?
74
+ case type
75
+ when Modifier then
76
+ return false unless type.prefix == '*'
77
+ case type.target
78
+ when Builtin then type.target.name != 'interface'
79
+ else false
80
+ end
81
+ when Type then false
82
+ else raise "Unsupported type class #{type.inspect}"
83
+ end
84
+ end
85
+
86
+ # @param pkg2alias [Hash<String,String>]
87
+ # @return [string]
88
+ def short_desc(pkg2alias = nil)
89
+ "#{name}: #{type.qualified_name(pkg2alias)}"
90
+ end
40
91
  end
41
92
  end
42
93
  end
@@ -5,14 +5,13 @@ require "goon_model_gen/golang/sentence"
5
5
  module GoonModelGen
6
6
  module Golang
7
7
  class File
8
- attr_reader :package, :name
8
+ attr_reader :name
9
9
  attr_reader :sentences
10
+ attr_accessor :package
10
11
  attr_accessor :custom_suffix # false/true
11
12
 
12
- # @param package [Package]
13
13
  # @param name [string]
14
- def initialize(package, name)
15
- @package = package
14
+ def initialize(name)
16
15
  @name = name
17
16
  @sentences = []
18
17
  end
@@ -27,8 +27,10 @@ module GoonModelGen
27
27
  prefix + target.name
28
28
  end
29
29
 
30
- def qualified_name
31
- prefix + target.qualified_name
30
+ # @param pkg2alias [Hash<String,String>]
31
+ # @return [string]
32
+ def qualified_name(pkg2alias = nil)
33
+ prefix + target.qualified_name(pkg2alias)
32
34
  end
33
35
 
34
36
  # @param pkgs [Packages]
@@ -6,18 +6,28 @@ module GoonModelGen
6
6
  module Golang
7
7
  class NamedSlice < Type
8
8
  attr_reader :base_type_name
9
+ attr_reader :base_type_package_path
9
10
  attr_reader :base_type
10
11
 
11
12
  # @param name [String]
12
13
  # @param base_type_name [String]
13
- def initialize(name, base_type_name)
14
+ # @param base_type_package_path [String]
15
+ def initialize(name, base_type_name, base_type_package_path = nil)
14
16
  super(name)
15
17
  @base_type_name = base_type_name
18
+ @base_type_package_path = base_type_package_path
16
19
  end
17
20
 
18
21
  # @param pkgs [Packages]
19
22
  def resolve(pkgs)
20
- @base_type = pkgs.type_for(base_type_name) || raise("#{base_type_name.inspect} not found")
23
+ @base_type =
24
+ base_type_package_path.present? ?
25
+ pkgs.type_for(base_type_name, base_type_package_path) :
26
+ pkgs.type_for(base_type_name) || raise("#{base_type_name.inspect} not found")
27
+ end
28
+
29
+ def ptr_slice?
30
+ base_type.is_a?(GoonModelGen::Golang::Modifier) && (base_type.prefix == '*')
21
31
  end
22
32
  end
23
33
  end
@@ -3,6 +3,7 @@ require "goon_model_gen"
3
3
  require "goon_model_gen/golang/struct"
4
4
  require "goon_model_gen/golang/enum"
5
5
  require "goon_model_gen/golang/named_slice"
6
+ require "goon_model_gen/golang/combination_type"
6
7
  require "goon_model_gen/golang/file"
7
8
 
8
9
  module GoonModelGen
@@ -20,13 +21,25 @@ module GoonModelGen
20
21
  end
21
22
 
22
23
  def basename
23
- @basename ||= path ? ::File.basename(path, '.*') : nil
24
+ @basename ||= (path ? ::File.basename(path, '.*') : nil)
24
25
  end
25
26
 
26
27
  def name
27
28
  @name ||= basename ? basename.gsub(/[\-\_]/, '').downcase : nil
28
29
  end
29
30
 
31
+ def merge!(other)
32
+ other.types.each{|t| add(t) unless types.any?{|oldt| oldt.name == t.name } }
33
+ other.files.each{|f| add_file(f) unless files.any?{|oldf| oldf.name == f.name } }
34
+ end
35
+
36
+ # @param file [File]
37
+ def add_file(file)
38
+ files.push(file)
39
+ file.package = self
40
+ end
41
+
42
+ # @param type [Type]
30
43
  def add(type)
31
44
  types.push(type)
32
45
  type.package = self
@@ -45,10 +58,17 @@ module GoonModelGen
45
58
  end
46
59
 
47
60
  # @param name [string]
61
+ # @param base_type_package_path [String]
48
62
  # @param base_type_name [String]
49
63
  # @return [Slice]
50
- def new_named_slice(name, base_type_name)
51
- NamedSlice.new(name, "[]#{base_type_name}").tap{|s| add(s) }
64
+ def new_named_slice(name, base_type_name, base_type_package_path = nil)
65
+ NamedSlice.new(name, base_type_name, base_type_package_path).tap{|s| add(s) }
66
+ end
67
+
68
+ # @param name [string]
69
+ # @return [CombinationType]
70
+ def new_combination_type(name)
71
+ CombinationType.new(name).tap{|s| add(s) }
52
72
  end
53
73
 
54
74
  # @param name [string]
@@ -60,8 +80,8 @@ module GoonModelGen
60
80
  # @param name [string]
61
81
  # @return [File]
62
82
  def new_file(name)
63
- File.new(self, name).tap do |f|
64
- files.push(f)
83
+ File.new(name).tap do |f|
84
+ add_file(f)
65
85
  end
66
86
  end
67
87
 
@@ -9,6 +9,17 @@ module GoonModelGen
9
9
  module Golang
10
10
  class Packages < Array
11
11
 
12
+ class << self
13
+ def wrap(obj)
14
+ case obj
15
+ when Packages then obj
16
+ when Package then Packages.new.add(obj)
17
+ when Array then Packages.new.add(*obj)
18
+ else raise "Unsupported obj for #{self.name}.wrap #{obj.inspect}"
19
+ end
20
+ end
21
+ end
22
+
12
23
  def name_to_type_map
13
24
  each_with_object({}) do |pkg, d|
14
25
  d.update(pkg.name_to_type_map)
@@ -20,8 +31,12 @@ module GoonModelGen
20
31
  end
21
32
 
22
33
  def add(*packages)
23
- packages.each do |i|
24
- self << i
34
+ packages.flatten.each do |i|
35
+ if pkg = find_by_path(i.path)
36
+ pkg.merge!(i)
37
+ else
38
+ self << i
39
+ end
25
40
  end
26
41
  self
27
42
  end
@@ -30,8 +45,20 @@ module GoonModelGen
30
45
  Package.new(path).tap{|pkg| add(pkg)}
31
46
  end
32
47
 
33
- def detect_by(basename)
34
- detect{|pkg| pkg.basename == basename}
48
+ def detect_by(name)
49
+ detect{|pkg| pkg.name == name}
50
+ end
51
+
52
+ def find_by_path(path)
53
+ detect{|pkg| pkg.path == path}
54
+ end
55
+
56
+ def find_or_new(path)
57
+ find_by_path(path) || new_package(path)
58
+ end
59
+
60
+ def select_by(name)
61
+ select{|pkg| pkg.name == name}
35
62
  end
36
63
 
37
64
  def resolve_type_names(extra_packages = [])
@@ -43,25 +70,50 @@ module GoonModelGen
43
70
  end
44
71
 
45
72
  # @param type_name [string]
73
+ # @param package_path [string]
46
74
  # @param [Type]
47
- def lookup(type_name)
48
- if type_name.include?('.')
49
- pkg_name, name = type_name.split('.', 2)
50
- pkg = detect_by(pkg_name)
51
- return pkg ? pkg.lookup(name) : nil
52
- else
53
- each do |pkg|
54
- t = pkg.lookup(type_name)
55
- return t if t
56
- end
57
- return nil
75
+ def lookup(type_name, package_path = nil)
76
+ lookup_by(name: type_name, package_path: package_path)
77
+ end
78
+
79
+ # @param name [String]
80
+ # @param package_path [String]
81
+ # @param package_base_path [String]
82
+ # @param [Type]
83
+ def lookup_by(name: nil, package_path: nil, package_base_path: nil)
84
+ pkg_name, type_name = name.include?('.') ? name.split('.', 2) : [nil, name]
85
+ if package_path.present?
86
+ pkg = find_by_path(package_path) || raise("Package not found #{package_path.inspect} for type #{name.inspect}")
87
+ return pkg.lookup(type_name)
88
+ end
89
+
90
+ pkgs =
91
+ package_base_path.blank? ? self :
92
+ self.class.wrap(select{|pkg| pkg.path ? pkg.path.include?(package_base_path) : false})
93
+ if pkg_name.present?
94
+ pkgs = pkgs.select_by(pkg_name)
95
+ raise("Package not found #{pkg_name.inspect} for type #{name.inspect}") if pkgs.empty?
96
+ return self.class.wrap(pkgs).lookup(type_name)
58
97
  end
98
+
99
+ each do |pkg|
100
+ t = pkg.lookup(type_name)
101
+ return t if t
102
+ end
103
+ return nil
59
104
  end
60
105
 
61
- def type_for(expression)
106
+ def type_for(expression, package_path = nil)
62
107
  return nil if expression.blank?
63
108
  Modifier.parse(expression) do |name|
64
- lookup(name)
109
+ lookup(name, package_path)
110
+ end
111
+ end
112
+
113
+ def type_by(name: nil, package_path: nil, package_base_path: nil)
114
+ return nil if name.blank?
115
+ Modifier.parse(name) do |type_name|
116
+ lookup_by(name: type_name, package_path: package_path, package_base_path: package_base_path)
65
117
  end
66
118
  end
67
119
  end
@@ -0,0 +1,67 @@
1
+ require "goon_model_gen"
2
+
3
+ require "erb"
4
+ require "yaml"
5
+
6
+ require "goon_model_gen/golang/packages"
7
+ require "goon_model_gen/golang/datastore_supported"
8
+
9
+ require "active_support/core_ext/string"
10
+
11
+ module GoonModelGen
12
+ module Golang
13
+ class StructsLoader
14
+
15
+ # @param path [String]
16
+ # @return [Hash<String,Packages>]
17
+ def process(path)
18
+ erb = ERB.new(::File.read(path), nil, "-")
19
+ erb.filename = path
20
+ txt = erb.result
21
+ raw = YAML.load(txt)
22
+
23
+ r = raw.each_with_object({}) do |(key, types), d|
24
+ d[key] = build_packages(types)
25
+ end
26
+
27
+ whole_packages = Golang::DatastoreSupported.packages.dup
28
+ r.values.each do |pkgs|
29
+ whole_packages.add(*pkgs)
30
+ end
31
+
32
+ r.values.each do |pkgs|
33
+ pkgs.resolve_type_names(whole_packages)
34
+ end
35
+ return r
36
+ end
37
+
38
+ def build_packages(types)
39
+ Packages.new.tap do |pkgs|
40
+ types.each do |type_hash|
41
+ next if type_hash['PkgPath'].blank? || type_hash['Name'].blank?
42
+ pkg = pkgs.find_or_new(type_hash['PkgPath'])
43
+ if type_hash['Fields']
44
+ pkg.new_struct(type_hash['Name']).tap do |s|
45
+ type_hash['Fields'].each do |f|
46
+ t = f['Type']
47
+ tags = (f['Tag'] || {}).each_with_object({}){|(k,v), d| d[k] = v.split(',')}
48
+ s.new_field(f['Name'], t['Representation'], tags)
49
+ end
50
+ end
51
+ elsif type_hash['Kind'] == 'slice'
52
+ base_type_hash = base_type_hash_from(type_hash['Elem'])
53
+ pkg.new_named_slice(type_hash['Name'], base_type_hash['Name'], base_type_hash['PkgPath'])
54
+ else
55
+ pkg.new_enum(type_hash['Name'], type_hash['Kind'], {})
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def base_type_hash_from(type_hash)
62
+ type_hash['Elem'].nil? ? type_hash : base_type_name_from(type_hash['Elem'])
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -16,10 +16,12 @@ module GoonModelGen
16
16
  raise NotImplementedError, "#{self.type.name} doesn't implement resolve method"
17
17
  end
18
18
 
19
+ # @param pkg2alias [Hash<String,String>]
19
20
  # @return [string]
20
- def qualified_name
21
+ def qualified_name(pkg2alias = nil)
21
22
  if package && package.name
22
- "#{package.name}.#{name}"
23
+ pkg_name = (pkg2alias && package.path ? pkg2alias[package.path] : nil) || package.name
24
+ "#{pkg_name}.#{name}"
23
25
  else
24
26
  name
25
27
  end
@@ -0,0 +1,11 @@
1
+ <%-
2
+ model = type.map[:model].type
3
+ gen_type = type.map[:gen_type].type
4
+
5
+ import model.package
6
+ import :gen, gen_type.package
7
+
8
+ mappings = type.memo['mappings'] || []
9
+ requires_context = mappings.any?(&:requires_context)
10
+ import 'context' if requires_context
11
+ -%>
@@ -0,0 +1,45 @@
1
+ <%-
2
+ import converter_package
3
+
4
+ model = type.map[:model].type
5
+ gen_type = type.map[:gen_type].type
6
+ mappings = type.memo['mappings'] || []
7
+ requires_context = mappings.any?(&:requires_context)
8
+ arg_def_prefix = requires_context ? 'ctx context.Context, ' : nil
9
+ arg_prefix = requires_context ? 'ctx, ' : nil
10
+
11
+ model_slice = model.package.types.detect do |t|
12
+ t.is_a?(GoonModelGen::Golang::NamedSlice) &&
13
+ (
14
+ (t.base_type == model) ||
15
+ (t.ptr_slice? && (t.base_type.target == model))
16
+ )
17
+ end
18
+
19
+ if model_slice
20
+ element_is_ptr = model_slice.ptr_slice?
21
+ plural_model_type_name = model_slice.qualified_name
22
+ else
23
+ element_is_ptr =
24
+ !type.memo['model_slice_with_ptr'].nil? ? type.memo['model_slice_with_ptr'] :
25
+ model.fields.any?{|f| !f.tags['goon'].nil? }
26
+ plural_model_type_name = (element_is_ptr ? '[]*' : '[]') + model.qualified_name
27
+ end
28
+ -%>
29
+
30
+ func <%= gen_type.name %>SliceToModelSlice(<%= arg_def_prefix %>payloads *[]*<%= gen_type.qualified_name(dependencies) %>) (*<%= plural_model_type_name %>, error) {
31
+ if payloads == nil {
32
+ return nil, <%= converter_package.name %>.NoPayloadGiven
33
+ }
34
+
35
+ s := <%= plural_model_type_name %>{}
36
+ for _, payload := range *payloads {
37
+ m, err := <%= gen_type.name %>ToModel(<%= arg_prefix %>payload)
38
+ if err != nil {
39
+ return nil, err
40
+ }
41
+ s = append(s, <%= element_is_ptr ? '' : '*' %>m)
42
+ }
43
+
44
+ return &s, nil
45
+ }
@@ -0,0 +1,21 @@
1
+ <%-
2
+ import converter_package
3
+
4
+ model = type.map[:model].type
5
+ gen_type = type.map[:gen_type].type
6
+ mappings = type.memo['mappings'] || []
7
+ requires_context = mappings.any?(&:requires_context)
8
+ arg_def_prefix = requires_context ? 'ctx context.Context, ' : nil
9
+ arg_prefix = requires_context ? 'ctx, ' : nil
10
+ -%>
11
+
12
+ func <%= gen_type.name %>ToModel(<%= arg_def_prefix %>payload *<%= gen_type.qualified_name(dependencies) %>) (*<%= model.qualified_name %>, error) {
13
+ if payload == nil {
14
+ return nil, <%= converter_package.name %>.NoPayloadGiven
15
+ }
16
+ var m <%= model.qualified_name %>
17
+ if err := <%= gen_type.name %>AssignModel(<%= arg_prefix %>payload, &m); err != nil {
18
+ return nil, err
19
+ }
20
+ return &m, nil
21
+ }
@@ -0,0 +1,90 @@
1
+ <%-
2
+ model = type.map[:model].type
3
+ gen_type = type.map[:gen_type].type
4
+ mappings = type.memo['mappings'] || []
5
+ requires_context = mappings.any?(&:requires_context)
6
+ arg_def_prefix = requires_context ? 'ctx context.Context, ' : nil
7
+ arg_prefix = requires_context ? 'ctx, ' : nil
8
+ -%>
9
+
10
+ func <%= gen_type.name %>AssignModel(<%= arg_def_prefix %>payload *<%= gen_type.qualified_name(dependencies) %>, m *<%= model.qualified_name %>) error {
11
+ if payload == nil {
12
+ return converters.NoPayloadGiven
13
+ }
14
+ if m == nil {
15
+ return converters.NoModelGiven
16
+ }
17
+
18
+ <%-
19
+ mappings.each do |mapping|
20
+ import GoonModelGen::Golang::Packages.wrap(packages).detect_by(mapping.package_name) if mapping.package_name.present?
21
+
22
+ mfield = model.fields.detect{|f| f.name == mapping.name} || raise("Field not found #{mapping.name} of #{model.package.path}.#{model.name}")
23
+ argFields = mapping.args.map do |arg|
24
+ gen_type.fields.detect{|f| f.name == arg} ||
25
+ raise("Field not found #{arg.inspect} of #{gen_type.package.path}.#{gen_type.name}")
26
+ end
27
+ -%>
28
+ // <%= mfield.short_desc(dependencies) %> <== <%= argFields.map{|f| f.short_desc(dependencies) }.join(', ') %>
29
+ <%- if mapping.args.length == 1 -%>
30
+ <%-
31
+ pfield = argFields.first
32
+
33
+ assignable_condition = nil
34
+ if pfield.ptr?
35
+ assignable_condition = "payload.#{ pfield.name } != nil"
36
+ if !mapping.allow_zero && pfield.value_ptr? && pfield.type.target.name =~ /\Aint\z|\Aint8\z|\Aint16\z|\Aint32\z|\Aint64\z|\Auint\z|\Auint8\z|\Auint16\z|\Auint32\z|\Auint64\z/
37
+ assignable_condition << " && *payload.#{ pfield.name } != 0"
38
+ end
39
+ end
40
+ if mapping.func.nil? || !mapping.returns_error
41
+ arg = '%spayload.%s' % [pfield.ptr? ? '*' : '', pfield.name]
42
+ right_side = mapping.func.blank? ? arg :
43
+ '%s(%s%s)' % [mapping.func, mapping.requires_context ? 'ctx, ' : '', arg]
44
+ -%>
45
+ <%- if assignable_condition -%>
46
+ if <%= assignable_condition %> {
47
+ m.<%= mfield.name %> = <%= right_side %>
48
+ }
49
+ <%- else -%>
50
+ m.<%= mfield.name %> = <%= right_side %>
51
+ <%- end -%>
52
+ <%-
53
+ else
54
+ arg = '%spayload.%s' % [pfield.ptr? ? '*' : pfield.slice? ? '&' :'', pfield.name]
55
+ right_side = '%s(%s%s)' % [mapping.func, mapping.requires_context ? 'ctx, ' : '', arg]
56
+ -%>
57
+ <%- if assignable_condition -%>
58
+ if <%= assignable_condition %> {
59
+ <%- end -%>
60
+ if v, err := <%= right_side %>; err != nil {
61
+ return err
62
+ } else {
63
+ m.<%= mfield.name %> = <%= (pfield.slice? || mapping.requires_context) ? '*' : '' %>v
64
+ }
65
+ <%- if assignable_condition -%>
66
+ }
67
+ <%- end -%>
68
+ <%- end -%>
69
+
70
+ <%- else -%>
71
+ <%- arg_str = argFields.map{|f| "payload.#{f.name}"}.join(", ") %>
72
+ <%- if !mapping.returns_error # => requries_context is false -%>
73
+ m.<%= mfield.name %> = <%= mapping.func %>(<%= arg_str %>)
74
+ <%- else
75
+ arg_str = (mapping.requries_context ? 'ctx, ' : '') + arg_str
76
+ -%>
77
+ if v, err := <%= mapping.func %>(<%= arg_str %>); err != nil {
78
+ return err
79
+ } else {
80
+ m.<%= mfield.name %> = v
81
+ }
82
+ <%- end -%>
83
+
84
+ <%- end -%>
85
+
86
+ <%-
87
+ end
88
+ -%>
89
+ return nil
90
+ }
@@ -0,0 +1,18 @@
1
+ <%-
2
+ model = type.map[:model].type
3
+ gen_type = type.map[:gen_type].type
4
+ mappings = type.memo['mappings'] || []
5
+
6
+ import model.package
7
+ import :gen, gen_type.package
8
+
9
+ if mappings.any?{|m| m.args.length > 1}
10
+ raise "Multiple arguments for result type mapping is not supported now"
11
+ end
12
+ if mappings.any?(&:returns_error)
13
+ raise "returns_error (by writer) for result type mapping is not supported now"
14
+ end
15
+ if mappings.any?(&:requires_context)
16
+ raise "requires_context (by saver) for result type mapping is not supported now"
17
+ end
18
+ -%>
@@ -0,0 +1,33 @@
1
+ <%-
2
+ model = type.map[:model].type
3
+ gen_type = type.map[:gen_type].type
4
+
5
+ model_slice = model.package.types.detect do |t|
6
+ t.is_a?(GoonModelGen::Golang::NamedSlice) &&
7
+ (
8
+ (t.base_type == model) ||
9
+ (t.ptr_slice? && (t.base_type.target == model))
10
+ )
11
+ end
12
+
13
+ if model_slice
14
+ element_is_ptr = model_slice.ptr_slice?
15
+ plural_model_type_name = model_slice.qualified_name
16
+ else
17
+ element_is_ptr =
18
+ !type.memo['model_slice_with_ptr'].nil? ? type.memo['model_slice_with_ptr'] :
19
+ model.fields.any?{|f| !f.tags['goon'].nil? }
20
+ plural_model_type_name = (element_is_ptr ? '[]*' : '[]') + model.qualified_name
21
+ end
22
+ -%>
23
+
24
+ func <%= model.name %>SliceToResultSlice(s *<%= plural_model_type_name %>) *[]*<%= gen_type.qualified_name(dependencies) %> {
25
+ if s == nil {
26
+ return nil
27
+ }
28
+ r := []*<%= gen_type.qualified_name(dependencies) %>{}
29
+ for _, m := range *s {
30
+ r = append(r, <%= model.name %>ToResult(<%= element_is_ptr ? '' : '&' %>m))
31
+ }
32
+ return &r
33
+ }
@@ -0,0 +1,50 @@
1
+ <%-
2
+ model = type.map[:model].type
3
+ gen_type = type.map[:gen_type].type
4
+ mappings = type.memo['mappings'] || []
5
+ -%>
6
+
7
+
8
+ func <%= model.name %>ToResult(m *<%= model.qualified_name %>) *<%= gen_type.qualified_name(dependencies) %> {
9
+ if m == nil {
10
+ return nil
11
+ }
12
+ r := &<%= gen_type.qualified_name(dependencies) %>{}
13
+
14
+ <%-
15
+ mappings.each do |mapping|
16
+ import GoonModelGen::Golang::Packages.wrap(packages).detect_by(mapping.package_name) if mapping.package_name.present?
17
+
18
+ rfield = gen_type.fields.detect{|f| f.name == mapping.name} || raise("Field not found #{mapping.name} of #{gen_type.package.path}.#{gen_type.name}")
19
+ arg_fields = mapping.args.map do |arg|
20
+ model.fields.detect{|f| f.name == arg} ||
21
+ raise("Field not found #{arg.inspect} of #{model.package.path}.#{model.name}")
22
+ end
23
+ -%>
24
+ // <%= rfield.short_desc(dependencies) %> <== <%= arg_fields.map{|f| f.short_desc(dependencies) }.join(', ') %>
25
+ <%-
26
+ mfield = arg_fields.first
27
+ arg = "m.#{mfield.name}"
28
+ if mfield.struct? || mfield.slice?
29
+ arg = "&#{arg}"
30
+ raise "Struct field or Slice field requires func for mapping" if mapping.func.blank?
31
+ end
32
+ right_side_main = mapping.func ? "#{mapping.func}(#{arg})" : arg
33
+ right_side = (rfield.slice? ? '*' : '') + right_side_main
34
+ -%>
35
+ <%- if rfield.value_ptr? -%>
36
+ {
37
+ v := <%= right_side_main %>
38
+ r.<%= rfield.name %> = &v
39
+ }
40
+
41
+ <%- else -%>
42
+ r.<%= rfield.name %> = <%= right_side %>
43
+
44
+ <%- end -%>
45
+ <%-
46
+ end
47
+ -%>
48
+
49
+ return r
50
+ }
@@ -1,3 +1,3 @@
1
1
  module GoonModelGen
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goon_model_gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - akm
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-08 00:00:00.000000000 Z
11
+ date: 2019-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -144,14 +144,23 @@ files:
144
144
  - goon_model_gen.gemspec
145
145
  - lib/goon_model_gen.rb
146
146
  - lib/goon_model_gen/builder/abstract_builder.rb
147
+ - lib/goon_model_gen/builder/converter_builder.rb
147
148
  - lib/goon_model_gen/builder/model_builder.rb
148
149
  - lib/goon_model_gen/builder/store_builder.rb
149
150
  - lib/goon_model_gen/builder/validation_builder.rb
150
151
  - lib/goon_model_gen/cli.rb
151
152
  - lib/goon_model_gen/config.rb
153
+ - lib/goon_model_gen/converter/abstract_conv.rb
154
+ - lib/goon_model_gen/converter/conv_file.rb
155
+ - lib/goon_model_gen/converter/loader.rb
156
+ - lib/goon_model_gen/converter/mapping.rb
157
+ - lib/goon_model_gen/converter/payload_conv.rb
158
+ - lib/goon_model_gen/converter/result_conv.rb
159
+ - lib/goon_model_gen/converter/type_ref.rb
152
160
  - lib/goon_model_gen/generator.rb
153
161
  - lib/goon_model_gen/golang.rb
154
162
  - lib/goon_model_gen/golang/builtin.rb
163
+ - lib/goon_model_gen/golang/combination_type.rb
155
164
  - lib/goon_model_gen/golang/datastore_supported.rb
156
165
  - lib/goon_model_gen/golang/enum.rb
157
166
  - lib/goon_model_gen/golang/field.rb
@@ -163,6 +172,7 @@ files:
163
172
  - lib/goon_model_gen/golang/predeclared_type.rb
164
173
  - lib/goon_model_gen/golang/sentence.rb
165
174
  - lib/goon_model_gen/golang/struct.rb
175
+ - lib/goon_model_gen/golang/structs_loader.rb
166
176
  - lib/goon_model_gen/golang/type.rb
167
177
  - lib/goon_model_gen/source/context.rb
168
178
  - lib/goon_model_gen/source/contextual.rb
@@ -173,6 +183,13 @@ files:
173
183
  - lib/goon_model_gen/source/named_slice.rb
174
184
  - lib/goon_model_gen/source/struct.rb
175
185
  - lib/goon_model_gen/source/type.rb
186
+ - lib/goon_model_gen/templates/converter/payload/01_base_imports.go.erb
187
+ - lib/goon_model_gen/templates/converter/payload/02_SliceToModelSlice.go.erb
188
+ - lib/goon_model_gen/templates/converter/payload/03_ToModel.go.erb
189
+ - lib/goon_model_gen/templates/converter/payload/04_AssignModel.go.erb
190
+ - lib/goon_model_gen/templates/converter/result/01_base_imports.go.erb
191
+ - lib/goon_model_gen/templates/converter/result/02_SliceToResultSlice.go.erb
192
+ - lib/goon_model_gen/templates/converter/result/03_ToResult.go.erb
176
193
  - lib/goon_model_gen/templates/dsl.rb
177
194
  - lib/goon_model_gen/templates/model/enum/01_base.go.erb
178
195
  - lib/goon_model_gen/templates/model/enum/02_All.go.erb