goon_model_gen 0.1.3 → 0.2.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.
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