mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1235 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			1235 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			Python
		
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| import os
 | |
| import sys
 | |
| import subprocess 
 | |
| import datetime
 | |
| import argparse
 | |
| import re
 | |
| 
 | |
| 
 | |
| git_repo_path = os.path.abspath(subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip())
 | |
| 
 | |
|         
 | |
| def lowerCamlCaseForUnderscoredText(name):
 | |
|     splits = name.split('_')
 | |
|     splits = [split.title() for split in splits]
 | |
|     splits[0] = splits[0].lower()
 | |
|     return ''.join(splits)
 | |
|         
 | |
| 
 | |
| # The generated code for "Apple Swift Protos" suppresses
 | |
| # adjacent capital letters in lowerCamlCase.
 | |
| def lowerCamlCaseForUnderscoredText_wrapped(name):
 | |
|     chars = []
 | |
|     lastWasUpper = False
 | |
|     for char in name:
 | |
|         if lastWasUpper:
 | |
|             char = char.lower()
 | |
|         chars.append(char)
 | |
|         lastWasUpper = (char.upper() == char)
 | |
|     result = ''.join(chars)
 | |
|     if result.endswith('Id'):
 | |
|         result = result[:-2] + 'ID'
 | |
|     return result
 | |
| 
 | |
| # Provides conext for writing an indented block surrounded by braces.
 | |
| #
 | |
| # e.g.
 | |
| #
 | |
| #     with BracedContext('class Foo', writer) as writer:
 | |
| #         with BracedContext('func bar() -> Bool', writer) as writer:
 | |
| #             return true
 | |
| #
 | |
| # Produces:
 | |
| #
 | |
| #    class Foo {
 | |
| #        func bar() -> Bool {
 | |
| #            return true
 | |
| #        }
 | |
| #    }
 | |
| #
 | |
| class BracedContext:
 | |
|     def __init__(self, line, writer):
 | |
|         self.writer = writer
 | |
|         writer.add('%s {' % line)
 | |
| 
 | |
|     def __enter__(self):
 | |
|         self.writer.push_indent()
 | |
|         return self.writer
 | |
| 
 | |
|     def __exit__(self, *args):
 | |
|         self.writer.pop_indent()
 | |
|         self.writer.add('}')
 | |
| 
 | |
| class WriterContext:
 | |
|     def __init__(self, proto_name, swift_name, parent=None):
 | |
|         self.proto_name = proto_name
 | |
|         self.swift_name = swift_name
 | |
|         self.parent = parent
 | |
|         self.name_map = {}
 | |
| 
 | |
| class LineWriter:
 | |
|     def __init__(self, args):
 | |
|         self.contexts = []
 | |
|         # self.indent = 0
 | |
|         self.lines = []
 | |
|         self.args = args
 | |
|         self.current_indent = 0
 | |
| 
 | |
|     def braced(self, line):
 | |
|         return BracedContext(line, self)
 | |
|         
 | |
|     def push_indent(self):
 | |
|         self.current_indent = self.current_indent + 1
 | |
|         
 | |
|     def pop_indent(self):
 | |
|         self.current_indent = self.current_indent - 1
 | |
|         if self.current_indent < 0:
 | |
|             raise Exception('Invalid indentation')
 | |
|         
 | |
|     def all_context_proto_names(self):
 | |
|         return [context.proto_name for context in self.contexts]
 | |
| 
 | |
|     def current_context(self):
 | |
|         return self.contexts[-1]
 | |
| 
 | |
|     def indent(self):
 | |
|         return self.current_indent
 | |
|         # return len(self.contexts)
 | |
|         
 | |
|     def push_context(self, proto_name, swift_name):
 | |
|         self.contexts.append(WriterContext(proto_name, swift_name))
 | |
|         self.push_indent()
 | |
|         
 | |
|     def pop_context(self):
 | |
|         self.contexts.pop()
 | |
|         self.pop_indent()
 | |
|     
 | |
|     def add(self, line):
 | |
|         self.lines.append(('    ' * self.indent()) + line)
 | |
|     
 | |
|     def add_raw(self, line):
 | |
|         self.lines.append(line)
 | |
|     
 | |
|     def extend(self, text):
 | |
|         for line in text.split('\n'):
 | |
|             self.add(line)
 | |
|         
 | |
|     def join(self):
 | |
|         lines = [line.rstrip() for line in self.lines]
 | |
|         return '\n'.join(lines)
 | |
|         
 | |
|     def rstrip(self):
 | |
|         lines = self.lines
 | |
|         while len(lines) > 0 and len(lines[-1].strip()) == 0:
 | |
|             lines = lines[:-1]
 | |
|         self.lines = lines
 | |
|         
 | |
|     def newline(self):
 | |
|         self.add('')
 | |
| 
 | |
| 
 | |
| class BaseContext(object):
 | |
|     def __init__(self):
 | |
|         self.parent = None
 | |
|         self.proto_name = None
 | |
|         
 | |
|     def inherited_proto_names(self):
 | |
|         if self.parent is None:
 | |
|             return []
 | |
|         if self.proto_name is None:
 | |
|             return []
 | |
|         return self.parent.inherited_proto_names() + [self.proto_name,]
 | |
| 
 | |
|     def derive_swift_name(self):
 | |
|         names = self.inherited_proto_names()
 | |
|         return self.args.wrapper_prefix + ''.join(names)
 | |
| 
 | |
|     def derive_wrapped_swift_name(self):
 | |
|         names = self.inherited_proto_names()
 | |
|         return self.args.proto_prefix + '_' + '.'.join(names)
 | |
|         
 | |
|     def children(self):
 | |
|         return []
 | |
|         
 | |
|     def descendents(self):
 | |
|         result = []
 | |
|         for child in self.children():
 | |
|             result.append(child)
 | |
|             result.extend(child.descendents())
 | |
|         return result
 | |
|         
 | |
|     def siblings(self):
 | |
|         result = []
 | |
|         if self.parent is not None:
 | |
|             result = self.parent.children()
 | |
|         return result
 | |
|         
 | |
|     def ancestors(self):
 | |
|         result = []
 | |
|         if self.parent is not None:
 | |
|             result.append(self.parent)
 | |
|             result.extend(self.parent.ancestors())
 | |
|         return result
 | |
|         
 | |
|     def context_for_proto_type(self, field):
 | |
|         candidates = []
 | |
|         candidates.extend(self.descendents())
 | |
|         candidates.extend(self.siblings())
 | |
|         for ancestor in self.ancestors():
 | |
|             if ancestor.proto_name is None:
 | |
|                 # Ignore the root context
 | |
|                 continue
 | |
|             candidates.append(ancestor)
 | |
|             candidates.extend(ancestor.siblings())
 | |
| 
 | |
|         for candidate in candidates:
 | |
|             if candidate.proto_name == field.proto_type:
 | |
|                 return candidate
 | |
|         
 | |
|         return None                
 | |
|         
 | |
|     
 | |
|     def base_swift_type_for_field(self, field):
 | |
|     
 | |
|         if field.proto_type == 'string':
 | |
|             return 'String'
 | |
|         elif field.proto_type == 'uint64':
 | |
|             return 'UInt64'            
 | |
|         elif field.proto_type == 'uint32':
 | |
|             return 'UInt32'
 | |
|         elif field.proto_type == 'fixed64':
 | |
|             return 'UInt64'
 | |
|         elif field.proto_type == 'bool':
 | |
|             return 'Bool'
 | |
|         elif field.proto_type == 'bytes':
 | |
|             return 'Data'
 | |
|         else:
 | |
|             matching_context = self.context_for_proto_type(field)
 | |
|             if matching_context is not None:
 | |
|                 return matching_context.swift_name
 | |
|             else:
 | |
|                 # Failure
 | |
|                 return field.proto_type
 | |
|     
 | |
|     def swift_type_for_field(self, field, suppress_optional=False):
 | |
|         base_type = self.base_swift_type_for_field(field)
 | |
|         
 | |
|         if field.rules == 'optional':
 | |
|             if suppress_optional:
 | |
|                 return base_type
 | |
|             can_be_optional = self.can_field_be_optional(field)
 | |
|             if can_be_optional:
 | |
|                 return '%s?' % base_type
 | |
|             else:
 | |
|                 return base_type
 | |
|         elif field.rules == 'required':
 | |
|             return base_type
 | |
|         elif field.rules == 'repeated':
 | |
|             return '[%s]' % base_type
 | |
|         else:
 | |
|             raise Exception('Unknown field type')
 | |
|         
 | |
|     def is_field_primitive(self, field):
 | |
|         return field.proto_type in ('uint64',
 | |
|             'uint32',
 | |
|             'fixed64',
 | |
|             'bool', )
 | |
|         
 | |
|     def can_field_be_optional(self, field):
 | |
|         if self.is_field_primitive(field):
 | |
|             return not field.is_required
 | |
| 
 | |
|         # if field.proto_type == 'uint64':
 | |
|         #     return False
 | |
|         # elif field.proto_type == 'uint32':
 | |
|         #     return False
 | |
|         # elif field.proto_type == 'fixed64':
 | |
|         #     return False
 | |
|         # elif field.proto_type == 'bool':
 | |
|         #     return False
 | |
|         # elif self.is_field_an_enum(field):
 | |
|         if self.is_field_an_enum(field):
 | |
|             return False
 | |
|         else:
 | |
|             return True
 | |
|         
 | |
|     def is_field_an_enum(self, field):
 | |
|         matching_context = self.context_for_proto_type(field)
 | |
|         if matching_context is not None:
 | |
|             if type(matching_context) is EnumContext:
 | |
|                 return True
 | |
|         return False
 | |
|         
 | |
|     def is_field_a_proto(self, field):
 | |
|         matching_context = self.context_for_proto_type(field)
 | |
|         if matching_context is not None:
 | |
|             if type(matching_context) is MessageContext:
 | |
|                 return True
 | |
|         return False
 | |
|         
 | |
|     def default_value_for_field(self, field):
 | |
|         if field.rules == 'repeated':
 | |
|             return '[]'
 | |
|         
 | |
|         if field.default_value is not None and len(field.default_value) > 0:
 | |
|             return field.default_value
 | |
| 
 | |
|         if field.rules == 'optional':
 | |
|             can_be_optional = self.can_field_be_optional(field)
 | |
|             if can_be_optional:
 | |
|                 return 'nil'
 | |
|         
 | |
|         if field.proto_type == 'uint64':
 | |
|             return '0'
 | |
|         elif field.proto_type == 'uint32':
 | |
|             return '0'
 | |
|         elif field.proto_type == 'fixed64':
 | |
|             return '0'
 | |
|         elif field.proto_type == 'bool':
 | |
|             return 'false'
 | |
|         elif self.is_field_an_enum(field):
 | |
|             # TODO: Assert that rules is empty.
 | |
|             enum_context = self.context_for_proto_type(field)
 | |
|             return enum_context.default_value()
 | |
|             
 | |
|         return None        
 | |
|         
 | |
| 
 | |
| class FileContext(BaseContext):
 | |
|     def __init__(self, args):
 | |
|         BaseContext.__init__(self)
 | |
|         
 | |
|         self.args = args
 | |
|         
 | |
|         self.messages = []
 | |
|         self.enums = []
 | |
|         
 | |
|     def children(self):
 | |
|         return self.enums + self.messages
 | |
|         
 | |
|     def prepare(self):
 | |
|         for child in self.children():
 | |
|             child.prepare()
 | |
|         
 | |
|     def generate(self, writer):
 | |
|         writer.extend('''//
 | |
| //  Copyright (c) 2018 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| import Foundation
 | |
| ''')
 | |
| 
 | |
|         writer.extend('''
 | |
| // WARNING: This code is generated. Only edit within the markers.
 | |
| '''.strip())
 | |
|         writer.newline()        
 | |
|         
 | |
|         writer.invalid_protobuf_error_name = '%sError' % self.args.wrapper_prefix
 | |
|         writer.extend(('''
 | |
| public enum %s: Error {
 | |
|     case invalidProtobuf(description: String)
 | |
| }
 | |
| ''' % writer.invalid_protobuf_error_name).strip())
 | |
|         writer.newline()        
 | |
|         
 | |
|         for child in self.children():
 | |
|             child.generate(writer)
 | |
| 
 | |
| 
 | |
| class MessageField:
 | |
|     def __init__(self, name, index, rules, proto_type, default_value, sort_index, is_required):
 | |
|         self.name = name
 | |
|         self.index = index
 | |
|         self.rules = rules
 | |
|         self.proto_type = proto_type
 | |
|         self.default_value = default_value
 | |
|         self.sort_index = sort_index
 | |
|         self.is_required = is_required
 | |
|             
 | |
|     def has_accessor_name(self):
 | |
|         name = 'has' + self.name_swift[0].upper() + self.name_swift[1:]
 | |
|         if name == 'hasId':
 | |
|             # TODO: I'm not sure why "Apple Swift Proto" code formats the
 | |
|             # the name in this way.
 | |
|             name = 'hasID'
 | |
|         elif name == 'hasUrl':
 | |
|             # TODO: I'm not sure why "Apple Swift Proto" code formats the
 | |
|             # the name in this way.
 | |
|             name = 'hasURL'
 | |
|         return name
 | |
|         
 | |
| class MessageContext(BaseContext):
 | |
|     def __init__(self, args, parent, proto_name):
 | |
|         BaseContext.__init__(self)
 | |
| 
 | |
|         self.args = args
 | |
|         self.parent = parent
 | |
|         
 | |
|         self.proto_name = proto_name
 | |
| 
 | |
|         self.messages = []
 | |
|         self.enums = []
 | |
|         
 | |
|         self.field_map = {}
 | |
|     
 | |
|     def fields(self): 
 | |
|         fields = self.field_map.values()
 | |
|         fields = sorted(fields, key=lambda f: f.sort_index)
 | |
|         return fields
 | |
|     
 | |
|     def field_indices(self):
 | |
|         return [field.index for field in self.fields()]
 | |
| 
 | |
|     def field_names(self):
 | |
|         return [field.name for field in self.fields()]
 | |
| 
 | |
|     def children(self):
 | |
|         return self.enums + self.messages
 | |
|         
 | |
|     def prepare(self):
 | |
|         self.swift_name = self.derive_swift_name()
 | |
|         self.swift_builder_name = "%sBuilder" % self.swift_name
 | |
|         
 | |
|         for child in self.children():
 | |
|             child.prepare()
 | |
|         
 | |
|     def generate(self, writer):
 | |
|         for child in self.messages:
 | |
|             child.generate(writer)
 | |
|     
 | |
|         writer.add('// MARK: - %s' % self.swift_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         writer.add('@objc public class %s: NSObject {' % self.swift_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         writer.push_context(self.proto_name, self.swift_name)
 | |
|         
 | |
|         for child in self.enums:
 | |
|             child.generate(writer)
 | |
| 
 | |
|         wrapped_swift_name = self.derive_wrapped_swift_name()
 | |
| 
 | |
|         # Prepare fields
 | |
|         explict_fields = []
 | |
|         implict_fields = []
 | |
|         for field in self.fields():
 | |
|             field.type_swift = self.swift_type_for_field(field)
 | |
|             field.type_swift_not_optional = self.swift_type_for_field(field, suppress_optional=True)
 | |
|             field.name_swift = lowerCamlCaseForUnderscoredText_wrapped(field.name)
 | |
|             
 | |
|             is_explicit = False
 | |
|             if field.is_required:
 | |
|                 is_explicit = True
 | |
|             elif self.is_field_a_proto(field):
 | |
|                 is_explicit = True
 | |
|             if is_explicit:
 | |
|                 explict_fields.append(field)
 | |
|             else:
 | |
|                 implict_fields.append(field)
 | |
| 
 | |
|         self.generate_builder(writer)
 | |
|         
 | |
|         writer.add('fileprivate let proto: %s' % wrapped_swift_name )
 | |
|         writer.newline()
 | |
|         
 | |
|         # Property Declarations
 | |
|         if len(explict_fields) > 0:
 | |
|             for field in explict_fields:
 | |
|                 type_name = field.type_swift_not_optional if field.is_required else field.type_swift
 | |
|                 writer.add('@objc public let %s: %s' % (field.name_swift, type_name))
 | |
|                 
 | |
|                 if (not field.is_required) and field.rules != 'repeated' and (not self.is_field_a_proto(field)):
 | |
|                     writer.add('@objc public var %s: Bool {' % field.has_accessor_name() )
 | |
|                     writer.push_indent()
 | |
|                     writer.add('return proto.%s' % field.has_accessor_name() )
 | |
|                     writer.pop_indent()
 | |
|                     writer.add('}')
 | |
|                 writer.newline()
 | |
| 
 | |
|         if len(implict_fields) > 0:
 | |
|             for field in implict_fields:
 | |
|                 if field.rules == 'optional':
 | |
|                     can_be_optional = (not self.is_field_primitive(field)) and (not self.is_field_an_enum(field))
 | |
|                     if can_be_optional:
 | |
|                         writer.add('@objc public var %s: %s? {' % (field.name_swift, field.type_swift_not_optional))
 | |
|                         writer.push_indent()
 | |
|                         writer.add('guard proto.%s else {' % field.has_accessor_name() )
 | |
|                         writer.push_indent()
 | |
|                         writer.add('return nil')
 | |
|                         writer.pop_indent()
 | |
|                         writer.add('}')
 | |
|                         if self.is_field_an_enum(field):
 | |
|                             enum_context = self.context_for_proto_type(field)
 | |
|                             writer.add('return %s.%sWrap(proto.%s)' % ( enum_context.parent.swift_name, enum_context.swift_name, field.name_swift, ) )
 | |
|                         else:
 | |
|                             writer.add('return proto.%s' % field.name_swift )
 | |
|                         writer.pop_indent()
 | |
|                         writer.add('}')
 | |
|                     else:
 | |
|                         writer.add('@objc public var %s: %s {' % (field.name_swift, field.type_swift_not_optional))
 | |
|                         writer.push_indent()
 | |
|                         if self.is_field_an_enum(field):
 | |
|                             enum_context = self.context_for_proto_type(field)
 | |
|                             writer.add('return %s.%sWrap(proto.%s)' % ( enum_context.parent.swift_name, enum_context.swift_name, field.name_swift, ) )
 | |
|                         else:
 | |
|                             writer.add('return proto.%s' % field.name_swift )
 | |
|                         writer.pop_indent()
 | |
|                         writer.add('}')
 | |
| 
 | |
|                     writer.add('@objc public var %s: Bool {' % field.has_accessor_name() )
 | |
|                     writer.push_indent()
 | |
|                     writer.add('return proto.%s' % field.has_accessor_name() )
 | |
|                     writer.pop_indent()
 | |
|                     writer.add('}')
 | |
|                     writer.newline()
 | |
|                 elif field.rules == 'repeated':
 | |
|                     writer.add('@objc public var %s: %s {' % (field.name_swift, field.type_swift_not_optional))
 | |
|                     writer.push_indent()
 | |
|                     writer.add('return proto.%s' % field.name_swift )
 | |
|                     writer.pop_indent()
 | |
|                     writer.add('}')
 | |
|                     writer.newline()
 | |
|                 else:
 | |
|                     writer.add('@objc public var %s: %s {' % (field.name_swift, field.type_swift_not_optional))
 | |
|                     writer.push_indent()
 | |
|                     if self.is_field_an_enum(field):
 | |
|                         enum_context = self.context_for_proto_type(field)
 | |
|                         writer.add('return %sUnwrap(proto.%s)' % ( enum_context.swift_name, field.name_swift, ) )
 | |
|                     else:
 | |
|                         writer.add('return proto.%s' % field.name_swift )
 | |
|                     writer.pop_indent()
 | |
|                     writer.add('}')
 | |
|                     writer.newline()
 | |
|                 
 | |
|         
 | |
|         # Initializer
 | |
|         initializer_parameters = []
 | |
|         initializer_parameters.append('proto: %s' % wrapped_swift_name)        
 | |
|         initializer_prefix = 'private init('
 | |
|         for field in explict_fields:
 | |
|             type_name = field.type_swift_not_optional if field.is_required else field.type_swift
 | |
|             parameter = '%s: %s' % (field.name_swift, type_name)
 | |
|             parameter = '\n' + ' ' * len(initializer_prefix) + parameter
 | |
|             initializer_parameters.append(parameter)
 | |
|         initializer_parameters = ', '.join(initializer_parameters)
 | |
|         writer.extend('%s%s) {' % ( initializer_prefix, initializer_parameters, ) )
 | |
|         writer.push_indent()
 | |
|         writer.add('self.proto = proto')
 | |
|         for field in explict_fields:
 | |
|             writer.add('self.%s = %s' % (field.name_swift, field.name_swift))
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|  
 | |
|         # serializedData() func
 | |
|         writer.extend(('''
 | |
| @objc
 | |
| public func serializedData() throws -> Data {
 | |
|     return try self.proto.serializedData()
 | |
| }
 | |
| ''').strip())
 | |
|         writer.newline()
 | |
| 
 | |
|         # parseData() func
 | |
|         writer.add('@objc public class func parseData(_ serializedData: Data) throws -> %s {' % self.swift_name)
 | |
|         writer.push_indent()
 | |
|         writer.add('let proto = try %s(serializedData: serializedData)' % ( wrapped_swift_name, ) )
 | |
|         writer.add('return try parseProto(proto)')        
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
| 
 | |
|         # parseData() func
 | |
|         writer.add('fileprivate class func parseProto(_ proto: %s) throws -> %s {' % ( wrapped_swift_name, self.swift_name, ) )
 | |
|         writer.push_indent()
 | |
|         
 | |
|         for field in explict_fields:
 | |
|             if field.is_required:
 | |
|             # if self.can_field_be_optional(field):
 | |
|                 writer.add('guard proto.%s else {' % field.has_accessor_name() )
 | |
|                 writer.push_indent()
 | |
|                 writer.add('throw %s.invalidProtobuf(description: "\(logTag) missing required field: %s")' % ( writer.invalid_protobuf_error_name, field.name_swift, ) )   
 | |
|                 writer.pop_indent()
 | |
|                 writer.add('}')
 | |
|             
 | |
|                 if self.is_field_an_enum(field):
 | |
|                     # TODO: Assert that rules is empty.
 | |
|                     enum_context = self.context_for_proto_type(field)
 | |
|                     writer.add('let %s = %sWrap(proto.%s)' % ( field.name_swift, enum_context.swift_name, field.name_swift, ) )
 | |
|                 elif self.is_field_a_proto(field):
 | |
|                     writer.add('let %s = try %s.parseProto(proto.%s)' % (field.name_swift, self.base_swift_type_for_field(field), field.name_swift)),
 | |
|                 else:
 | |
|                     writer.add('let %s = proto.%s' % ( field.name_swift, field.name_swift, ) )
 | |
|                 writer.newline()
 | |
|                 continue
 | |
|             
 | |
|             default_value = self.default_value_for_field(field)
 | |
|             if default_value is None:
 | |
|                 writer.add('var %s: %s' % (field.name_swift, field.type_swift))
 | |
|             else:
 | |
|                 writer.add('var %s: %s = %s' % (field.name_swift, field.type_swift, default_value))
 | |
|                 
 | |
|             if field.rules == 'repeated':
 | |
|                 if self.is_field_an_enum(field):
 | |
|                     enum_context = self.context_for_proto_type(field)
 | |
|                     writer.add('%s = proto.%s.map { %sWrap($0) }' % ( field.name_swift, field.name_swift, enum_context.swift_name, ) )
 | |
|                 elif self.is_field_a_proto(field):
 | |
|                     writer.add('%s = try proto.%s.map { try %s.parseProto($0) }' % ( field.name_swift, field.name_swift, self.base_swift_type_for_field(field), ) )
 | |
|                 else:
 | |
|                     writer.add('%s = proto.%s' % ( field.name_swift, field.name_swift, ) )
 | |
|             else:
 | |
|                 writer.add('if proto.%s {' % field.has_accessor_name() )
 | |
|                 writer.push_indent()
 | |
|             
 | |
|                 if self.is_field_an_enum(field):
 | |
|                     # TODO: Assert that rules is empty.
 | |
|                     enum_context = self.context_for_proto_type(field)
 | |
|                     writer.add('%s = %sWrap(proto.%s)' % ( field.name_swift, enum_context.swift_name, field.name_swift, ) )
 | |
|                 elif self.is_field_a_proto(field):
 | |
|                     writer.add('%s = try %s.parseProto(proto.%s)' % (field.name_swift, self.base_swift_type_for_field(field), field.name_swift)),
 | |
|                 else:
 | |
|                     writer.add('%s = proto.%s' % ( field.name_swift, field.name_swift, ) )
 | |
|                 
 | |
|                 writer.pop_indent()
 | |
|                 writer.add('}')
 | |
|             writer.newline()
 | |
| 
 | |
|         writer.add('// MARK: - Begin Validation Logic for %s -' % self.swift_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         # Preserve existing validation logic.
 | |
|         if self.swift_name in args.validation_map:
 | |
|             validation_block = args.validation_map[self.swift_name]
 | |
|             if validation_block:
 | |
|                 writer.add_raw(validation_block)
 | |
|                 writer.newline()
 | |
|         
 | |
|         writer.add('// MARK: - End Validation Logic for %s -' % self.swift_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         initializer_prefix = 'let result = %s(' % self.swift_name
 | |
|         initializer_arguments = []
 | |
|         initializer_arguments.append('proto: proto')
 | |
|         for field in explict_fields:
 | |
|             argument = '%s: %s' % (field.name_swift, field.name_swift)
 | |
|             argument = '\n' + ' ' * len(initializer_prefix) + argument
 | |
|             initializer_arguments.append(argument)
 | |
|         initializer_arguments = ', '.join(initializer_arguments)
 | |
|         writer.extend('%s%s)' % ( initializer_prefix, initializer_arguments, ) )
 | |
|         writer.add('return result')        
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
| 
 | |
|         # description
 | |
|         writer.add('@objc public override var debugDescription: String {')
 | |
|         writer.push_indent()
 | |
|         writer.add('return "\(proto)"')
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|             
 | |
|         writer.pop_context()
 | |
| 
 | |
|         writer.rstrip()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|         self.generate_debug_extension(writer)
 | |
| 
 | |
|     def generate_debug_extension(self, writer):
 | |
|         writer.add('#if DEBUG') 
 | |
|         writer.newline() 
 | |
|         with writer.braced('extension %s' % self.swift_name) as writer:
 | |
|             with writer.braced('@objc public func serializedDataIgnoringErrors() -> Data?') as writer:
 | |
|                 writer.add('return try! self.serializedData()')
 | |
| 
 | |
|         writer.newline()
 | |
|  
 | |
|         with writer.braced('extension %s.%s' % ( self.swift_name, self.swift_builder_name )) as writer:
 | |
|             with writer.braced('@objc public func buildIgnoringErrors() -> %s?' % self.swift_name) as writer:
 | |
|                 writer.add('return try! self.build()')
 | |
| 
 | |
|         writer.newline()
 | |
|         writer.add('#endif')
 | |
|         writer.newline()
 | |
|         
 | |
|     def generate_builder(self, writer):
 | |
|     
 | |
|         wrapped_swift_name = self.derive_wrapped_swift_name()
 | |
|         
 | |
|         writer.add('// MARK: - %s' % self.swift_builder_name)
 | |
|         writer.newline()
 | |
| 
 | |
|         # Required Fields
 | |
|         required_fields = [field for field in self.fields() if field.is_required]
 | |
|         required_init_params = []
 | |
|         required_init_args = []
 | |
|         if len(required_fields) > 0:
 | |
|             for field in required_fields:
 | |
|                 if field.rules == 'repeated':
 | |
|                     param_type = '[' + self.base_swift_type_for_field(field) + ']'
 | |
|                 else:
 | |
|                     param_type = self.base_swift_type_for_field(field)
 | |
|                 required_init_params.append('%s: %s' % ( field.name_swift, param_type) )
 | |
|                 required_init_args.append('%s: %s' % ( field.name_swift, field.name_swift) )
 | |
|         
 | |
|         # Convenience accessor.
 | |
|         with writer.braced('@objc public class func builder(%s) -> %s' % ( 
 | |
|                 ', '.join(required_init_params),
 | |
|                 self.swift_builder_name,
 | |
|                 )) as writer:
 | |
|             writer.add('return %s(%s)' % (self.swift_builder_name, ', '.join(required_init_args), ))
 | |
|         writer.newline()
 | |
|         
 | |
|         # asBuilder()        
 | |
|         writer.add('// asBuilder() constructs a builder that reflects the proto\'s contents.')
 | |
|         with writer.braced('@objc public func asBuilder() -> %s' % (
 | |
|                 self.swift_builder_name,
 | |
|                 )) as writer:
 | |
|             writer.add('let builder = %s(%s)' % (self.swift_builder_name, ', '.join(required_init_args), ))
 | |
| 
 | |
|             for field in self.fields():
 | |
|                 if field.is_required:
 | |
|                     continue
 | |
| 
 | |
|                 accessor_name = field.name_swift
 | |
|                 accessor_name = 'set' + accessor_name[0].upper() + accessor_name[1:]
 | |
| 
 | |
|                 can_be_optional = (not self.is_field_primitive(field)) and (not self.is_field_an_enum(field))
 | |
|                 if field.rules == 'repeated':
 | |
|                     writer.add('builder.%s(%s)' % ( accessor_name, field.name_swift, ))
 | |
|                 elif can_be_optional:
 | |
|                     writer.add('if let _value = %s {' % field.name_swift )
 | |
|                     writer.push_indent()
 | |
|                     writer.add('builder.%s(_value)' % ( accessor_name, ))
 | |
|                     writer.pop_indent()
 | |
|                     writer.add('}')
 | |
|                 else:
 | |
|                     writer.add('if %s {' % field.has_accessor_name() )
 | |
|                     writer.push_indent()
 | |
|                     writer.add('builder.%s(%s)' % ( accessor_name, field.name_swift, ))
 | |
|                     writer.pop_indent()
 | |
|                     writer.add('}')
 | |
| 
 | |
|             writer.add('return builder')
 | |
|         writer.newline()
 | |
|         
 | |
|         writer.add('@objc public class %s: NSObject {' % self.swift_builder_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         writer.push_context(self.proto_name, self.swift_name)
 | |
|         
 | |
|         writer.add('private var proto = %s()' % wrapped_swift_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         # Initializer
 | |
|         writer.add('@objc fileprivate override init() {}')
 | |
|         writer.newline()
 | |
|         
 | |
|         # Required-Field Initializer
 | |
|         if len(required_fields) > 0:
 | |
|             # writer.add('// Initializer for required fields')
 | |
|             writer.add('@objc fileprivate init(%s) {' % ', '.join(required_init_params))
 | |
|             writer.push_indent()
 | |
|             writer.add('super.init()')
 | |
|             writer.newline()
 | |
|             for field in required_fields:
 | |
|                 accessor_name = field.name_swift
 | |
|                 accessor_name = 'set' + accessor_name[0].upper() + accessor_name[1:]
 | |
|                 writer.add('%s(%s)' % ( accessor_name, field.name_swift, ) )
 | |
|             writer.pop_indent()
 | |
|             writer.add('}')
 | |
|             writer.newline()
 | |
|                 
 | |
|         # Setters
 | |
|         for field in self.fields():
 | |
|             if field.rules == 'repeated':
 | |
|                 # Add
 | |
|                 accessor_name = field.name_swift
 | |
|                 accessor_name = 'add' + accessor_name[0].upper() + accessor_name[1:]
 | |
|                 writer.add('@objc public func %s(_ valueParam: %s) {' % ( accessor_name, self.base_swift_type_for_field(field), ))
 | |
|                 writer.push_indent()
 | |
|                 writer.add('var items = proto.%s' % ( field.name_swift, ) )
 | |
|                 
 | |
|                 if self.is_field_an_enum(field):
 | |
|                     enum_context = self.context_for_proto_type(field)
 | |
|                     writer.add('items.append(%sUnwrap(valueParam))' % enum_context.swift_name )
 | |
|                 elif self.is_field_a_proto(field):
 | |
|                     writer.add('items.append(valueParam.proto)')
 | |
|                 else:
 | |
|                     writer.add('items.append(valueParam)')
 | |
|                 writer.add('proto.%s = items' % ( field.name_swift, ) )
 | |
|                 writer.pop_indent()
 | |
|                 writer.add('}')
 | |
|                 writer.newline()
 | |
|                 
 | |
|                 # Set
 | |
|                 accessor_name = field.name_swift
 | |
|                 accessor_name = 'set' + accessor_name[0].upper() + accessor_name[1:]
 | |
|                 writer.add('@objc public func %s(_ wrappedItems: [%s]) {' % ( accessor_name, self.base_swift_type_for_field(field), ))
 | |
|                 writer.push_indent()
 | |
|                 if self.is_field_an_enum(field):
 | |
|                     enum_context = self.context_for_proto_type(field)
 | |
|                     writer.add('proto.%s = wrappedItems.map { %sUnwrap($0) }' % ( field.name_swift, enum_context.swift_name, ) )
 | |
|                 elif self.is_field_a_proto(field):
 | |
|                     writer.add('proto.%s = wrappedItems.map { $0.proto }' % ( field.name_swift, ) )
 | |
|                 else:
 | |
|                     writer.add('proto.%s = wrappedItems' % ( field.name_swift, ) )
 | |
|                 writer.pop_indent()
 | |
|                 writer.add('}')
 | |
|                 writer.newline()
 | |
|             else:
 | |
|                 accessor_name = field.name_swift
 | |
|                 accessor_name = 'set' + accessor_name[0].upper() + accessor_name[1:]
 | |
|                 writer.add('@objc public func %s(_ valueParam: %s) {' % ( accessor_name, self.base_swift_type_for_field(field), ))
 | |
|                 writer.push_indent()
 | |
| 
 | |
|                 if self.is_field_an_enum(field):
 | |
|                     enum_context = self.context_for_proto_type(field)
 | |
|                     writer.add('proto.%s = %sUnwrap(valueParam)' % ( field.name_swift, enum_context.swift_name, ) )
 | |
|                 elif self.is_field_a_proto(field):
 | |
|                     writer.add('proto.%s = valueParam.proto' % ( field.name_swift, ) )
 | |
|                 else:
 | |
|                     writer.add('proto.%s = valueParam' % ( field.name_swift, ) )
 | |
|                 
 | |
|                 writer.pop_indent()
 | |
|                 writer.add('}')
 | |
|                 writer.newline()
 | |
|  
 | |
|         # build() func
 | |
|         writer.add('@objc public func build() throws -> %s {' % self.swift_name)
 | |
|         writer.push_indent()
 | |
|         writer.add('return try %s.parseProto(proto)' % self.swift_name)
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
| 
 | |
|         # buildSerializedData() func
 | |
|         writer.add('@objc public func buildSerializedData() throws -> Data {')
 | |
|         writer.push_indent()
 | |
|         writer.add('return try %s.parseProto(proto).serializedData()' % self.swift_name)
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
| 
 | |
|         # description
 | |
|         if self.args.add_description:
 | |
|             writer.add('@objc public override var description: String {')
 | |
|             writer.push_indent()
 | |
|             writer.add('var fields = [String]()')
 | |
|             for field in self.fields():
 | |
|                 writer.add('fields.append("%s: \(proto.%s)")' % ( field.name_swift, field.name_swift, ) )
 | |
|             writer.add('return "[" + fields.joined(separator: ", ") + "]"')
 | |
|             writer.pop_indent()
 | |
|             writer.add('}')
 | |
|             writer.newline()
 | |
|         
 | |
|         writer.pop_context()
 | |
| 
 | |
|         writer.rstrip()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|                 
 | |
| class EnumContext(BaseContext):
 | |
|     def __init__(self, args, parent, proto_name):
 | |
|         BaseContext.__init__(self)
 | |
| 
 | |
|         self.args = args
 | |
|         self.parent = parent        
 | |
|         self.proto_name = proto_name
 | |
|         
 | |
|         # self.item_names = set()
 | |
|         # self.item_indices = set()
 | |
|         self.item_map = {}
 | |
|     
 | |
|     def derive_wrapped_swift_name(self):
 | |
|         # return BaseContext.derive_wrapped_swift_name(self) + 'Enum'
 | |
|         result = BaseContext.derive_wrapped_swift_name(self)
 | |
|         if self.proto_name == 'Type':
 | |
|             result = result + 'Enum'
 | |
|         return result
 | |
|     
 | |
|     def item_names(self):
 | |
|         return self.item_map.values()
 | |
|     
 | |
|     def item_indices(self):
 | |
|         return self.item_map.keys()
 | |
| 
 | |
|     def prepare(self):
 | |
|         self.swift_name = self.derive_swift_name()
 | |
|         
 | |
|         for child in self.children():
 | |
|             child.prepare()
 | |
| 
 | |
|     def case_pairs(self):
 | |
|         indices = [int(index) for index in self.item_indices()]
 | |
|         indices = sorted(indices)
 | |
|         result = []
 | |
|         for index in indices:
 | |
|             index_str = str(index)
 | |
|             item_name = self.item_map[index_str]
 | |
|             case_name = lowerCamlCaseForUnderscoredText(item_name)
 | |
|             result.append( (case_name, index_str,) )
 | |
|         return result
 | |
|         
 | |
|     def default_value(self):
 | |
|         for case_name, case_index in self.case_pairs():
 | |
|             return '.' + case_name
 | |
| 
 | |
|     def generate(self, writer):
 | |
|         
 | |
|         writer.add('// MARK: - %s' % self.swift_name)
 | |
|         writer.newline()
 | |
|         
 | |
|         writer.add('@objc public enum %s: Int32 {' % self.swift_name)
 | |
|         
 | |
|         writer.push_context(self.proto_name, self.swift_name)
 | |
| 
 | |
|         for case_name, case_index in self.case_pairs():
 | |
|             if case_name == 'default':
 | |
|                 case_name = '`default`'
 | |
|             writer.add('case %s = %s' % (case_name, case_index,))
 | |
|         
 | |
|         writer.pop_context()
 | |
| 
 | |
|         writer.rstrip()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|         
 | |
|         wrapped_swift_name = self.derive_wrapped_swift_name()
 | |
|         writer.add('private class func %sWrap(_ value: %s) -> %s {' % ( self.swift_name, wrapped_swift_name, self.swift_name, ) )
 | |
|         writer.push_indent()
 | |
|         writer.add('switch value {')
 | |
|         for case_name, case_index in self.case_pairs():
 | |
|             writer.add('case .%s: return .%s' % (case_name, case_name,))
 | |
|         writer.add('}')
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|         writer.add('private class func %sUnwrap(_ value: %s) -> %s {' % ( self.swift_name, self.swift_name, wrapped_swift_name, ) )
 | |
|         writer.push_indent()
 | |
|         writer.add('switch value {')
 | |
|         for case_name, case_index in self.case_pairs():
 | |
|             writer.add('case .%s: return .%s' % (case_name, case_name,))
 | |
|         writer.add('}')
 | |
|         writer.pop_indent()
 | |
|         writer.add('}')
 | |
|         writer.newline()
 | |
|         
 | |
|  
 | |
| class LineParser:
 | |
|     def __init__(self, text):
 | |
|         self.lines = text.split('\n')
 | |
|         self.lines.reverse()
 | |
|         self.next_line_comments = []
 | |
|     
 | |
|     def next(self):
 | |
|         # lineParser = LineParser(text.split('\n'))
 | |
|     
 | |
|         self.next_line_comments = []
 | |
|         while len(self.lines) > 0:
 | |
|             line = self.lines.pop()
 | |
|             line = line.strip()
 | |
|             # if not line:
 | |
|             #     continue
 | |
| 
 | |
|             comment_index = line.find('//')
 | |
|             if comment_index >= 0:
 | |
|                 comment = line[comment_index + len('//'):].strip()
 | |
|                 line = line[:comment_index].strip()
 | |
|                 if not line:
 | |
|                     if comment:
 | |
|                         self.next_line_comments.append(comment)
 | |
|             else:
 | |
|                 if not line:
 | |
|                     self.next_line_comments = []
 | |
|                 
 | |
|             if not line:
 | |
|                 continue
 | |
|         
 | |
|             # if args.verbose:
 | |
|             #     print 'line:', line
 | |
|             
 | |
|             return line
 | |
|         raise StopIteration()
 | |
|         
 | |
| 
 | |
| def parse_enum(args, proto_file_path, parser, parent_context, enum_name):
 | |
| 
 | |
|     # if args.verbose:
 | |
|     #     print '# enum:', enum_name
 | |
|     
 | |
|     context = EnumContext(args, parent_context, enum_name)
 | |
|     
 | |
|     while True:
 | |
|         try:
 | |
|             line = parser.next()
 | |
|         except StopIteration:
 | |
|             raise Exception('Incomplete enum: %s' % proto_file_path)
 | |
|     
 | |
|         if line == '}':
 | |
|             # if args.verbose:
 | |
|             #     print
 | |
|             parent_context.enums.append(context)
 | |
|             return
 | |
| 
 | |
|         item_regex = re.compile(r'^(.+?)\s*=\s*(\d+?)\s*;$')
 | |
|         item_match = item_regex.search(line)
 | |
|         if item_match:
 | |
|             item_name = item_match.group(1).strip()
 | |
|             item_index = item_match.group(2).strip()
 | |
|         
 | |
|             # if args.verbose:
 | |
|             #     print '\t enum item[%s]: %s' % (item_index, item_name)
 | |
|             
 | |
|             if item_name in context.item_names():
 | |
|                 raise Exception('Duplicate enum name[%s]: %s' % (proto_file_path, item_name))
 | |
|             
 | |
|             if item_index in context.item_indices():
 | |
|                 raise Exception('Duplicate enum index[%s]: %s' % (proto_file_path, item_name))
 | |
|             
 | |
|             context.item_map[item_index] = item_name
 | |
|                 
 | |
|             continue
 | |
|     
 | |
|         raise Exception('Invalid enum syntax[%s]: %s' % (proto_file_path, line))
 | |
|         
 | |
| 
 | |
| def optional_match_group(match, index):
 | |
|     group = match.group(index)
 | |
|     if group is None:
 | |
|         return None
 | |
|     return group.strip()
 | |
| 
 | |
| 
 | |
| def parse_message(args, proto_file_path, parser, parent_context, message_name):
 | |
| 
 | |
|     # if args.verbose:
 | |
|     #     print '# message:', message_name
 | |
|     
 | |
|     context = MessageContext(args, parent_context, message_name)
 | |
|     
 | |
|     sort_index = 0
 | |
|     while True:
 | |
|         try:
 | |
|             line = parser.next()
 | |
|         except StopIteration:
 | |
|             raise Exception('Incomplete message: %s' % proto_file_path)
 | |
|     
 | |
|         field_comments = parser.next_line_comments
 | |
|         
 | |
|         if line == '}':
 | |
|             # if args.verbose:
 | |
|             #     print
 | |
|             parent_context.messages.append(context)
 | |
|             return
 | |
| 
 | |
|         enum_regex = re.compile(r'^enum\s+(.+?)\s+\{$')
 | |
|         enum_match = enum_regex.search(line)
 | |
|         if enum_match:
 | |
|             enum_name = enum_match.group(1).strip()        
 | |
|             parse_enum(args, proto_file_path, parser, context, enum_name)
 | |
|             continue
 | |
|         
 | |
|         message_regex = re.compile(r'^message\s+(.+?)\s+\{$')
 | |
|         message_match = message_regex.search(line)
 | |
|         if message_match:
 | |
|             message_name = message_match.group(1).strip()
 | |
|             parse_message(args, proto_file_path, parser, context, message_name)
 | |
|             continue
 | |
| 
 | |
|         # Examples:
 | |
|         #
 | |
|         # optional bytes  id          = 1;
 | |
|         # optional bool              isComplete = 2 [default = false];
 | |
|         item_regex = re.compile(r'^(optional|required|repeated)?\s*([\w\d]+?)\s+([\w\d]+?)\s*=\s*(\d+?)\s*(\[default = (true|false)\])?;$')
 | |
|         item_match = item_regex.search(line)
 | |
|         if item_match:
 | |
|             # print 'item_rules:', item_match.groups()
 | |
|             item_rules = optional_match_group(item_match, 1)
 | |
|             item_type = optional_match_group(item_match, 2)
 | |
|             item_name = optional_match_group(item_match, 3)
 | |
|             item_index = optional_match_group(item_match, 4)
 | |
|             # item_defaults_1 = optional_match_group(item_match, 5)
 | |
|             item_default = optional_match_group(item_match, 6)
 | |
|     
 | |
|             # print 'item_rules:', item_rules
 | |
|             # print 'item_type:', item_type
 | |
|             # print 'item_name:', item_name
 | |
|             # print 'item_index:', item_index
 | |
|             # print 'item_default:', item_default
 | |
|             
 | |
|             message_field = {
 | |
|                 'rules': item_rules,
 | |
|                 'type': item_type,
 | |
|                 'name': item_name,
 | |
|                 'index': item_index,
 | |
|                 'default': item_default,
 | |
|                 'field_comments': field_comments,
 | |
|             }
 | |
|             # print 'message_field:', message_field
 | |
|         
 | |
|             # if args.verbose:
 | |
|             #     print '\t message field[%s]: %s' % (item_index, str(message_field))
 | |
|             
 | |
|             if item_name in context.field_names():
 | |
|                 raise Exception('Duplicate message field name[%s]: %s' % (proto_file_path, item_name))
 | |
|             # context.field_names.add(item_name)
 | |
|             
 | |
|             if item_index in context.field_indices():
 | |
|                 raise Exception('Duplicate message field index[%s]: %s' % (proto_file_path, item_name))
 | |
|             # context.field_indices.add(item_index)
 | |
|             
 | |
|             is_required = '@required' in field_comments
 | |
|             # if is_required:
 | |
|             #     print 'is_required:', item_name
 | |
|             context.field_map[item_index] = MessageField(item_name, item_index, item_rules, item_type, item_default, sort_index, is_required)
 | |
|             
 | |
|             sort_index = sort_index + 1
 | |
|             
 | |
|             continue
 | |
| 
 | |
|         raise Exception('Invalid message syntax[%s]: %s' % (proto_file_path, line))
 | |
|     
 | |
| 
 | |
| def preserve_validation_logic(args, proto_file_path, dst_file_path):
 | |
|     args.validation_map = {}
 | |
|     if os.path.exists(dst_file_path):
 | |
|         with open(dst_file_path, 'rt') as f:
 | |
|             old_text = f.read()
 | |
|         validation_start_regex = re.compile(r'// MARK: - Begin Validation Logic for ([^ ]+) -')
 | |
|         for match in validation_start_regex.finditer(old_text):
 | |
|             # print 'match'
 | |
|             name = match.group(1)
 | |
|             # print '\t name:', name
 | |
|             start = match.end(0)
 | |
|             # print '\t start:', start
 | |
|             end_marker = '// MARK: - End Validation Logic for %s -' % name
 | |
|             end = old_text.find(end_marker)
 | |
|             # print '\t end:', end
 | |
|             if end < start:
 | |
|                 raise Exception('Malformed validation: %s, %s' % ( proto_file_path, name, ) )
 | |
|             validation_block = old_text[start:end]
 | |
|             # print '\t validation_block:', validation_block
 | |
|             
 | |
|             # Strip trailing whitespace.
 | |
|             validation_lines = validation_block.split('\n')
 | |
|             validation_lines = [line.rstrip() for line in validation_lines]
 | |
|             # Strip leading empty lines.
 | |
|             while len(validation_lines) > 0 and validation_lines[0] == '':
 | |
|                 validation_lines = validation_lines[1:]
 | |
|             # Strip trailing empty lines.
 | |
|             while len(validation_lines) > 0 and validation_lines[-1] == '':
 | |
|                 validation_lines = validation_lines[:-1]
 | |
|             validation_block = '\n'.join(validation_lines)
 | |
|             
 | |
|             if len(validation_block) > 0:
 | |
|                 if args.verbose:
 | |
|                     print('Preserving validation logic for:', name)
 | |
|             
 | |
|             args.validation_map[name] = validation_block
 | |
|             
 | |
|             
 | |
| def process_proto_file(args, proto_file_path, dst_file_path):
 | |
|     with open(proto_file_path, 'rt') as f:
 | |
|         text = f.read()
 | |
|     
 | |
|     multiline_comment_regex = re.compile(r'/\*.*?\*/', re.MULTILINE|re.DOTALL)
 | |
|     text = multiline_comment_regex.sub('', text)
 | |
|     
 | |
|     syntax_regex = re.compile(r'^syntax ')
 | |
|     package_regex = re.compile(r'^package\s+(.+);')
 | |
|     option_regex = re.compile(r'^option ')
 | |
|     
 | |
|     parser = LineParser(text)
 | |
|     
 | |
|     # lineParser = LineParser(text.split('\n'))
 | |
|     
 | |
|     context = FileContext(args)
 | |
|     
 | |
|     while True:
 | |
|         try:
 | |
|             line = parser.next()
 | |
|         except StopIteration:
 | |
|             break
 | |
| 
 | |
|         if syntax_regex.search(line):
 | |
|             if args.verbose:
 | |
|                 print('# Ignoring syntax')
 | |
|             continue
 | |
|         
 | |
|         if option_regex.search(line):
 | |
|             if args.verbose:
 | |
|                 print('# Ignoring option')
 | |
|             continue
 | |
|         
 | |
|         package_match = package_regex.search(line)
 | |
|         if package_match:
 | |
|             if args.package:
 | |
|                 raise Exception('More than one package statement: %s' % proto_file_path)
 | |
|             args.package = package_match.group(1).strip()
 | |
|             
 | |
|             if args.verbose:
 | |
|                 print('# package:', args.package)
 | |
|             continue
 | |
|         
 | |
|         message_regex = re.compile(r'^message\s+(.+?)\s+\{$')
 | |
|         message_match = message_regex.search(line)
 | |
|         if message_match:
 | |
|             message_name = message_match.group(1).strip()
 | |
|             parse_message(args, proto_file_path, parser, context, message_name)
 | |
|             continue
 | |
|     
 | |
|         raise Exception('Invalid syntax[%s]: %s' % (proto_file_path, line))
 | |
|     
 | |
|     preserve_validation_logic(args, proto_file_path, dst_file_path)
 | |
|     
 | |
|     writer = LineWriter(args)
 | |
|     context.prepare()
 | |
|     context.generate(writer)
 | |
|     output = writer.join()
 | |
|     with open(dst_file_path, 'wt') as f:
 | |
|         f.write(output)
 | |
|     
 | |
|     
 | |
| if __name__ == "__main__":
 | |
|     
 | |
|     parser = argparse.ArgumentParser(description='Protocol Buffer Swift Wrapper Generator.')
 | |
|     # parser.add_argument('--all', action='store_true', help='process all files in or below current dir')
 | |
|     # parser.add_argument('--path', help='used to specify a path to a file.')
 | |
|     parser.add_argument('--proto-dir', help='dir path of the proto schema file.')
 | |
|     parser.add_argument('--proto-file', help='filename of the proto schema file.')
 | |
|     parser.add_argument('--wrapper-prefix', help='name prefix for generated wrappers.')
 | |
|     parser.add_argument('--proto-prefix', help='name prefix for proto bufs.')
 | |
|     parser.add_argument('--dst-dir', help='path to the destination directory.')
 | |
|     parser.add_argument('--add-log-tag', action='store_true', help='add log tag properties.')
 | |
|     parser.add_argument('--add-description', action='store_true', help='add log tag properties.')
 | |
|     parser.add_argument('--verbose', action='store_true', help='enables verbose logging')
 | |
|     args = parser.parse_args()
 | |
|     
 | |
|     if args.verbose:
 | |
|         print('args:', args)
 | |
|     
 | |
|     proto_file_path = os.path.abspath(os.path.join(args.proto_dir, args.proto_file))
 | |
|     if not os.path.exists(proto_file_path):
 | |
|         raise Exception('File does not exist: %s' % proto_file_path)
 | |
|     
 | |
|     dst_dir_path = os.path.abspath(args.dst_dir)
 | |
|     if not os.path.exists(dst_dir_path):
 | |
|         raise Exception('Destination does not exist: %s' % dst_dir_path)
 | |
|     
 | |
|     dst_file_path = os.path.join(dst_dir_path, "%s.swift" % args.wrapper_prefix)
 | |
|     
 | |
|     if args.verbose:
 | |
|         print('dst_file_path:', dst_file_path)
 | |
|     
 | |
|     args.package = None
 | |
|     process_proto_file(args, proto_file_path, dst_file_path)
 | |
|     
 | |
|     # print 'complete.'
 | |
|     
 |