aboutsummaryrefslogtreecommitdiff
path: root/pw_rpc/java/main/dev/pigweed/pw_rpc/Method.java
blob: c306147f278fafe87de0ac770b49e6ed434a0737 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package dev.pigweed.pw_rpc;

import com.google.auto.value.AutoValue;
import com.google.common.base.Ascii;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;

/**
 * Information about an RPC service method.
 *
 * <p>If protobuf descriptors are available, this information can be extracted from the generated
 * protobuf descriptors. The "lite" Java protobuf library does not provide descriptors, so RPC
 * services and methods must be manually declared and provided to an RPC client.
 */
@AutoValue
public abstract class Method {
  public abstract Service service();

  public abstract String name();

  public abstract Type type();

  abstract Parser<? extends MessageLite> requestParser();

  abstract Parser<? extends MessageLite> responseParser();

  public final Class<? extends MessageLite> request() {
    return getProtobufClass(requestParser());
  }

  public final Class<? extends MessageLite> response() {
    return getProtobufClass(responseParser());
  }

  final int id() {
    return Ids.calculate(name());
  }

  public final boolean isServerStreaming() {
    return type().isServerStreaming();
  }

  public final boolean isClientStreaming() {
    return type().isClientStreaming();
  }

  public final String fullName() {
    return createFullName(service().name(), name());
  }

  static String createFullName(String serviceName, String methodName) {
    return serviceName + '/' + methodName;
  }

  static Builder builder() {
    return new AutoValue_Method.Builder();
  }

  @Override
  public final String toString() {
    return fullName();
  }

  /** Builds Method instances. */
  @AutoValue.Builder
  public abstract static class Builder {
    abstract Builder setService(Service value);

    abstract Builder setType(Type type);

    abstract Builder setName(String value);

    abstract Builder setRequestParser(Parser<? extends MessageLite> parser);

    abstract Builder setResponseParser(Parser<? extends MessageLite> parser);

    abstract Method build();
  }

  /** Decodes a response payload according to the method's response type. */
  final MessageLite decodeResponsePayload(ByteString data) throws InvalidProtocolBufferException {
    return responseParser().parseFrom(data);
  }

  private static Class<? extends MessageLite> getProtobufClass(
      Parser<? extends MessageLite> parser) {
    try {
      return parser.parseFrom(ByteString.EMPTY).getClass();
    } catch (InvalidProtocolBufferException e) {
      throw new AssertionError("Failed to parse zero bytes as a protobuf! "
              + "It was assumed that zero bytes is always a valid protobuf.",
          e);
    }
  }

  /** Which type of RPC this is: unary or server/client/bidirectional streaming. */
  public enum Type {
    UNARY(/* isServerStreaming= */ false, /* isClientStreaming= */ false),
    SERVER_STREAMING(/* isServerStreaming= */ true, /* isClientStreaming= */ false),
    CLIENT_STREAMING(/* isServerStreaming= */ false, /* isClientStreaming= */ true),
    BIDIRECTIONAL_STREAMING(/* isServerStreaming= */ true, /* isClientStreaming= */ true);

    private final boolean isServerStreaming;
    private final boolean isClientStreaming;

    Type(boolean isServerStreaming, boolean isClientStreaming) {
      this.isServerStreaming = isServerStreaming;
      this.isClientStreaming = isClientStreaming;
    }

    public final boolean isServerStreaming() {
      return isServerStreaming;
    }

    public final boolean isClientStreaming() {
      return isClientStreaming;
    }

    public final String sentenceName() {
      return Ascii.toLowerCase(name()).replace('_', ' ');
    }
  }
}