Skip to content

Commit

Permalink
Add doc comments throughout Rust SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
daviddrysdale committed Sep 25, 2019
1 parent 8c7f72b commit da56a8a
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 18 deletions.
75 changes: 65 additions & 10 deletions rust/oak/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mod tests;
#[macro_use]
extern crate assert_matches;

/// Result type that uses a [`proto::status::Status`] type for error values.
pub type GrpcResult<T> = Result<T, proto::status::Status>;

/// Trait to allow repeated writing of responses for server-streaming gRPC methods.
Expand Down Expand Up @@ -64,9 +65,12 @@ where
}
}

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
pub type Handle = u64;

/// Map an OakStatus to the nearest available std::io::Result.
/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
match status {
Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
Expand Down Expand Up @@ -133,6 +137,10 @@ mod wasm {
}
}

/// Create a new unidirectional channel.
///
/// On success, returns [`Handle`] values for the write and read halves
/// (respectively).
pub fn channel_create() -> Result<(Handle, Handle), OakStatus> {
let mut write: Handle = 0;
let mut read: Handle = 0;
Expand All @@ -145,17 +153,24 @@ pub fn channel_create() -> Result<(Handle, Handle), OakStatus> {
}
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
match OakStatus::from_i32(unsafe { wasm::channel_close(handle) }) {
Some(s) => s,
None => OakStatus::OAK_STATUS_UNSPECIFIED,
}
}

/// Determine the [`Handle`] for a pre-defined channel, identified by its
/// `port_name`.
pub fn channel_find(port_name: &str) -> Handle {
unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

/// Number of bytes needed per-handle for channel readiness notifications.
///
/// The notification space consists of the channel handle (as a little-endian
/// u64) followed by a single byte indicating the channel readiness.
pub const SPACE_BYTES_PER_HANDLE: usize = 9;

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
Expand All @@ -178,9 +193,12 @@ fn prep_handle_space(space: &mut [u8]) {
}
}

// Convenience wrapper around wait_on_channels host function. This version is
// easier to use in Rust but is less efficient (because the notification space
// is re-created on each invocation).
/// Wait for one or more of the provided handles to become ready for reading
/// from.
///
/// This is a convenience wrapper around the [`wasm::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
let mut space = new_handle_space(handles);
unsafe {
Expand All @@ -200,6 +218,8 @@ pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
}
}

/// Convenience wrapper for the send half of a channel, to allow use of the
/// [`std::io::Write`] trait.
pub struct SendChannelHalf {
handle: Handle,
}
Expand All @@ -225,8 +245,6 @@ impl SendChannelHalf {
}
}

// Implement the Write trait on a send channel for convenience, particularly for
// the logging channel.
impl Write for SendChannelHalf {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self.write_message(buf, &[]) {
Expand All @@ -239,12 +257,20 @@ impl Write for SendChannelHalf {
}
}

/// Return an instance of the [`std::io::Write`] trait that emits messages to
/// the Node's logging channel.
///
/// Assumes that the Node has a pre-configured channel to the logging
/// pseudo-Node that is identified by the default port name (`"log"`).
pub fn logging_channel() -> impl Write {
let logging_channel = SendChannelHalf::new(channel_find("log"));
// Only flush logging channel on newlines.
std::io::LineWriter::new(logging_channel)
}

/// Convenience wrapper for the receive half of a channel.
///
/// This helps when the underlying [`Handle`] is known to be for a receive half.
pub struct ReceiveChannelHalf {
handle: Handle,
}
Expand Down Expand Up @@ -312,15 +338,41 @@ impl ReceiveChannelHalf {
}
}

/// Trait encapsulating the operations required for an Oak Node.
/// Trait for Oak Nodes that act as a gRPC services.
///
/// An `OakNode` instance is normally passed to [`grpc_event_loop`], to allow
/// repeated invocation of its `invoke()` method.
pub trait OakNode {
/// Construct the (single) instance of the node.
///
/// This method may choose to initialize logging by invoking
/// [`oak_log::init()`].
///
/// [`oak_log::init()`]: ../oak_log/fn.init.html
fn new() -> Self
where
Self: Sized;

/// Process a single gRPC method invocation.
///
/// The method name is provided by `method` and the incoming serialized gRPC
/// request is held in `req`. Response messages should be written to `out`,
/// as serialized [`GrpcResponse`] messages encapsulating the service
/// response.
///
/// [`GrpcResponse`]: proto::grpc_encap::GrpcResponse
fn invoke(&mut self, method: &str, req: &[u8], out: &mut SendChannelHalf);
}

/// Perform a gRPC event loop on the given channels, invoking the given node.
/// Perform a gRPC event loop for a Node.
///
/// Invoking the given `node`'s [`invoke`] method for each incoming request that
/// arrives on the inbound channel as a serialized [`GrpcRequest`] message,
/// giving the [`invoke`] method the outbound channel for encapsulated responses
/// to be written to.
///
/// [`invoke`]: OakNode::invoke
/// [`GrpcRequest`]: proto::grpc_encap::GrpcRequest
pub fn grpc_event_loop<T: OakNode>(
mut node: T,
grpc_in_handle: Handle,
Expand Down Expand Up @@ -376,8 +428,11 @@ pub fn grpc_event_loop<T: OakNode>(
}
}

/// Install a panic hook so that panics are logged to the logging channel, if one is set.
/// See https://doc.rust-lang.org/std/panic/struct.PanicInfo.html.
/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
std::panic::set_hook(Box::new(|panic_info| {
let msg = panic_info
Expand Down
3 changes: 3 additions & 0 deletions rust/oak/src/proto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Auto-generated code for processing protocol buffer message definitions and
//! gRPC service definitions.
#[allow(clippy::all)]
pub mod grpc_encap;
pub mod oak_api;
Expand Down
14 changes: 14 additions & 0 deletions rust/oak/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// limitations under the License.
//

//! Helper library for accessing Oak storage services.
extern crate protobuf;

use crate::{GrpcResult, ReceiveChannelHalf, SendChannelHalf};
Expand All @@ -23,19 +25,25 @@ use proto::storage_channel::{
};
use protobuf::Message;

/// Local representation of the connection to an external storage service.
pub struct Storage {
write_channel: crate::SendChannelHalf,
wait_space: Vec<u8>,
read_channel: crate::ReceiveChannelHalf,
}

impl Default for Storage {
/// Create a default `Storage` instance assuming the standard port names
/// (`"storage_in"`, `"storage_out"`) for pre-defined channels for storage
/// communication.
fn default() -> Storage {
Storage::new("storage_in", "storage_out")
}
}

impl Storage {
/// Create a `Storage` instance using the given port names for pre-defined
/// channels for storage communication.
pub fn new(in_port_name: &str, out_port_name: &str) -> Storage {
let handle = crate::channel_find(in_port_name);
let handles = vec![handle];
Expand Down Expand Up @@ -82,6 +90,8 @@ impl Storage {
response
}

/// Read the value associated with the given `name` from the storage
/// instance identified by `name`.
pub fn read(&mut self, storage_name: &[u8], name: &[u8]) -> GrpcResult<Vec<u8>> {
let mut read_request = StorageChannelReadRequest::new();
read_request.datum_name = name.to_owned();
Expand All @@ -103,6 +113,8 @@ impl Storage {
}
}

/// Set the value associated with the given `name` from the storage instance
/// identified by `name`.
pub fn write(&mut self, storage_name: &[u8], name: &[u8], value: &[u8]) -> GrpcResult<()> {
let mut write_request = StorageChannelWriteRequest::new();
write_request.datum_name = name.to_owned();
Expand All @@ -121,6 +133,8 @@ impl Storage {
}
}

/// Delete the value associated with the given `name` from the storage
/// instance identified by `name`.
pub fn delete(&mut self, storage_name: &[u8], name: &[u8]) -> GrpcResult<()> {
let mut delete_request = StorageChannelDeleteRequest::new();
delete_request.datum_name = name.to_owned();
Expand Down
31 changes: 25 additions & 6 deletions rust/oak_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
//
// Copyright 2019 The Project Oak 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
//
// http://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.
//

//! Macro to derive standard boilerplate code for an Oak Node.
extern crate oak;
extern crate proc_macro;
extern crate protobuf;
Expand All @@ -6,14 +24,15 @@ extern crate syn;
use proc_macro::TokenStream;
use quote::quote;

/// Implements the necessary bindings to make the annotated struct act as an Oak
/// Node that processes gRPC method invocations, assuming the default port names
/// are used for gRPC channels.
/// Implements the necessary bindings to make the annotated struct act as a
/// gRPC-processing Oak Node.
///
/// May only be used on struct objects.
/// May only be used on struct objects. Assumes that the default pre-defined
/// port names (`"grpc_in"`, `"grpc_out"`) are used to identify the gRPC
/// channels.
///
/// At most one struct may be annotated with this, as it produces global symbols that would
/// otherwise conflict if implemented multiple times.
/// At most one struct may be annotated with this, as it produces global symbols
/// that would otherwise conflict if implemented multiple times.
///
/// ```rust
/// extern crate oak;
Expand Down
21 changes: 20 additions & 1 deletion rust/oak_log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
// limitations under the License.
//

//! A logger that send output to an Oak logging channel.
//! A logger that sends output to an Oak logging channel, for use with
//! the [log facade].
//!
//! [log facade]: https://crates.io/crates/log
extern crate log;
extern crate oak;
Expand Down Expand Up @@ -52,10 +55,26 @@ impl Log for OakChannelLogger {
fn flush(&self) {}
}

/// Initialize Node-wide default logging.
///
/// Uses the default level (`Debug`) and the default pre-defined port name
/// (`"log"`) for the logging channel from the current Node.
///
/// # Panics
///
/// Panics if a logger has already been set.
pub fn init_default() {
init(Level::Debug, oak::channel_find("log")).unwrap();
}

/// Initialize Node-wide logging via a channel.
///
/// Initialize logging at the given level, using the send channel half
/// identified by the given port name.
///
/// # Errors
///
/// An error is returned if a logger has already been set.
pub fn init(level: Level, channel_handle: oak::Handle) -> Result<(), SetLoggerError> {
log::set_boxed_logger(Box::new(OakChannelLogger { channel_handle }))?;
log::set_max_level(level.to_level_filter());
Expand Down
Loading

0 comments on commit da56a8a

Please sign in to comment.