Skip to content

Commit

Permalink
Add isIntialized to Messages.
Browse files Browse the repository at this point in the history
- Add isInitialized Message protocol
- Provide default isInitialized so the generator can emit a custom one as needed.
- Add isInitialized to ExtensionFields so Message can call to it.
- Add Internal.swift as a place to hold public code (because the generated code
  has to be able to call it), but that is not intended to be part of the
  "developer public" api.
  - Helpers for checking a map and repeated field for being initialized.
- Generate isInitialized - Walk the message fields to decided if
  directly/indirectly can have messages with required fields.

Re apple#98
  • Loading branch information
thomasvl committed Feb 13, 2017
1 parent 27c727f commit 592b7e6
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 2 deletions.
9 changes: 9 additions & 0 deletions Sources/SwiftProtobuf/ExtensionFieldValueSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,13 @@ public struct ExtensionFieldValueSet: Equatable, Sequence {
}
return nil
}

public var isInitialized: Bool {
for (_, v) in values {
if !v.isInitialized {
return false
}
}
return true
}
}
31 changes: 31 additions & 0 deletions Sources/SwiftProtobuf/ExtensionFields.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ public protocol AnyExtensionField: CustomDebugStringConvertible {

/// Fields know their own type, so can dispatch to a visitor
func traverse(visitor: Visitor) throws

/// Check if the field is initialized.
var isInitialized: Bool { get }
}

public extension AnyExtensionField {
// Default implementation for extensions fields. The message types below provide
// custom versions.
var isInitialized: Bool { return true }
}

///
Expand Down Expand Up @@ -247,6 +256,13 @@ public struct OptionalMessageExtensionField<M: Message & Equatable>:
value: v, fieldNumber: protobufExtension.protoFieldNumber)
}
}

public var isInitialized: Bool {
if let v = value {
return v.isInitialized
}
return true
}
}

public struct RepeatedMessageExtensionField<M: Message & Equatable>:
Expand Down Expand Up @@ -294,6 +310,10 @@ public struct RepeatedMessageExtensionField<M: Message & Equatable>:
value: value, fieldNumber: protobufExtension.protoFieldNumber)
}
}

public var isInitialized: Bool {
return Internal.areAllInitialized(value)
}
}

//
Expand Down Expand Up @@ -337,6 +357,13 @@ public struct OptionalGroupExtensionField<G: Message & Hashable>:
value: v, fieldNumber: protobufExtension.protoFieldNumber)
}
}

public var isInitialized: Bool {
if let v = value {
return v.isInitialized
}
return true
}
}

public struct RepeatedGroupExtensionField<G: Message & Hashable>:
Expand Down Expand Up @@ -384,4 +411,8 @@ public struct RepeatedGroupExtensionField<G: Message & Hashable>:
value: value, fieldNumber: protobufExtension.protoFieldNumber)
}
}

public var isInitialized: Bool {
return Internal.areAllInitialized(value)
}
}
42 changes: 42 additions & 0 deletions Sources/SwiftProtobuf/Internal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Sources/SwiftProtobuf/Message.swift - Message support
//
// Copyright (c) 2014 - 2017 Apple Inc. and the project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See LICENSE.txt for license information:
// https://github.com/apple/swift-protobuf/blob/master/LICENSE.txt
//
// -----------------------------------------------------------------------------
///
/// Internal helpers on Messages for the library. These are public
/// just so the generated code can call them, but shouldn't be called
/// by developers directly.
///
// -----------------------------------------------------------------------------


import Foundation
import Swift

public struct Internal {
private init() {}

public static func areAllInitialized<M: Message>(_ listOfMessages: [M]) -> Bool {
for msg in listOfMessages {
if !msg.isInitialized {
return false
}
}
return true
}

public static func areAllInitialized<K: Hashable, M: Message>(_ mapToMessages: [K: M]) -> Bool {
for (_, msg) in mapToMessages {
if !msg.isInitialized {
return false
}
}
return true
}

}
10 changes: 10 additions & 0 deletions Sources/SwiftProtobuf/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public protocol Message: CustomDebugStringConvertible {
var anyTypePrefix: String { get }
var anyTypeURL: String { get }

// Check if all required fields (if any) have values set on this message
// on any messages withing this message.
var isInitialized: Bool { get }

//
// General serialization/deserialization machinery
//
Expand Down Expand Up @@ -116,6 +120,12 @@ public protocol Message: CustomDebugStringConvertible {
}

public extension Message {

var isInitialized: Bool {
// The generated code will include a specialization as needed.
return true;
}

var hashValue: Int {
let visitor = HashVisitor()
try? traverse(visitor: visitor)
Expand Down
20 changes: 20 additions & 0 deletions Sources/protoc-gen-swift/MessageFieldGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ extension Google_Protobuf_FieldDescriptorProto {
let m = context.getMessageForPath(path: typeName)!
return m.options.mapEntry
}
// Returns true/false if the field will be hold a Message. map<> is the
func getFieldHoldsMessage(context: Context) -> Bool {
switch label {
case .required:
fallthrough
case .optional:
return type == .message || type == .group
case .repeated:
if type == .group { return true }
if type == .message {
let m = context.getMessageForPath(path: typeName)!
if m.options.mapEntry {
let valueField = m.field[1]
return valueField.type == .message
}
}
return false
}
}

func getSwiftBaseType(context: Context) -> String {
switch type {
Expand Down Expand Up @@ -283,6 +302,7 @@ struct MessageFieldGenerator {
var isGroup: Bool {return descriptor.isGroup}
var isMap: Bool {return descriptor.getIsMap(context: context)}
var isMessage: Bool {return descriptor.isMessage}
var fieldHoldsMessage: Bool {return descriptor.getFieldHoldsMessage(context: context)}
var isPacked: Bool {return descriptor.isPackable &&
(descriptor.options.hasPacked ? descriptor.options.packed : isProto3)}
var isRepeated: Bool {return descriptor.isRepeated}
Expand Down
Loading

0 comments on commit 592b7e6

Please sign in to comment.