Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions grpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub mod client;
pub mod credentials;
pub mod inmemory;
mod macros;
mod status;
pub use status::{ServerStatus, Status, StatusCode};
pub mod rt;
pub mod server;
pub mod service;
Expand Down
53 changes: 53 additions & 0 deletions grpc/src/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
mod server_status;
mod status_code;

pub use server_status::ServerStatus;
pub use status_code::StatusCode;

/// Represents a gRPC status.
#[derive(Debug, Clone)]
pub struct Status {
code: StatusCode,
message: String,
}

impl Status {
/// Create a new `Status` with the given code and message.
pub fn new(code: StatusCode, message: impl Into<String>) -> Self {
Status {
code,
message: message.into(),
}
}

/// Get the `StatusCode` of this `Status`.
pub fn code(&self) -> StatusCode {
self.code
}

/// Get the message of this `Status`.
pub fn message(&self) -> &str {
&self.message
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_status_new() {
let status = Status::new(StatusCode::Ok, "ok");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the "ok" message is duplicated in both the constructor call and the assertion.

To make the test less brittle, I’d suggest assigning the message to a variable and reusing it, e.g.:

let message = "ok";
let status = Status::new(StatusCode::Ok, message);

assert_eq!(status.code(), StatusCode::Ok);
assert_eq!(status.message(), message);

assert_eq!(status.code(), StatusCode::Ok);
assert_eq!(status.message(), "ok");
}

#[test]
fn test_status_debug() {
let status = Status::new(StatusCode::Ok, "ok");
let debug = format!("{:?}", status);
assert!(debug.contains("Status"));
assert!(debug.contains("Ok"));
assert!(debug.contains("ok"));
}
}
67 changes: 67 additions & 0 deletions grpc/src/status/server_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use super::status_code::StatusCode;
use super::Status;

/// Represents a gRPC status on the server.
///
/// This is a separate type from `Status` to prevent accidental conversion and
/// leaking of sensitive information from the server to the client.
#[derive(Debug, Clone)]
pub struct ServerStatus(Status);

impl std::ops::Deref for ServerStatus {
type Target = Status;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl ServerStatus {
/// Create a new `ServerStatus` with the given code and message.
pub fn new(code: StatusCode, message: impl Into<String>) -> Self {
ServerStatus(Status::new(code, message))
}

/// Converts the `ServerStatus` to a `Status` for client responses.
pub(crate) fn to_status(self) -> Status {

Check warning on line 26 in grpc/src/status/server_status.rs

View workflow job for this annotation

GitHub Actions / clippy

methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
self.0
}
}

impl From<Status> for ServerStatus {
fn from(status: Status) -> Self {
ServerStatus(status)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_server_status_new() {
let status = ServerStatus::new(StatusCode::Ok, "ok");
assert_eq!(status.code(), StatusCode::Ok);
assert_eq!(status.message(), "ok");
}

#[test]
fn test_server_status_deref() {
let status = ServerStatus::new(StatusCode::Ok, "ok");
assert_eq!(status.code(), StatusCode::Ok);
}

#[test]
fn test_server_status_from_status() {
let status = Status::new(StatusCode::Ok, "ok");
let server_status: ServerStatus = status.into();
assert_eq!(server_status.code(), StatusCode::Ok);
}

#[test]
fn test_server_status_to_status() {
let server_status = ServerStatus::new(StatusCode::Ok, "ok");
let status = server_status.to_status();
assert_eq!(status.code(), StatusCode::Ok);
}
}
22 changes: 22 additions & 0 deletions grpc/src/status/status_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// Represents a gRPC status code.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum StatusCode {
Ok = 0,
Cancelled = 1,
Unknown = 2,
InvalidArgument = 3,
DeadlineExceeded = 4,
NotFound = 5,
AlreadyExists = 6,
PermissionDenied = 7,
ResourceExhausted = 8,
FailedPrecondition = 9,
Aborted = 10,
OutOfRange = 11,
Unimplemented = 12,
Internal = 13,
Unavailable = 14,
DataLoss = 15,
Unauthenticated = 16,
}
Loading