Skip to content

Commit b7130d3

Browse files
authored
Merge pull request #26 from stackhpc/improve-docs
docs: Improve Rust documentation
2 parents 14f238d + 7533fab commit b7130d3

File tree

9 files changed

+127
-8
lines changed

9 files changed

+127
-8
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ cd s3-active-storage-rs
113113
Next, use Cargo to build the package:
114114

115115
```sh
116-
cargo build
116+
cargo build --release
117117
```
118118

119119
The active storage server may be run using Cargo:
120120

121121
```sh
122-
cargo run
122+
cargo run --release
123123
```
124124

125125
Or installed to the system:
@@ -203,6 +203,19 @@ The proxy adds two custom headers `x-activestorage-dtype` and `x-activestrorage-
203203

204204
---
205205

206+
## Documentation
207+
208+
The source code is documented using [rustdoc](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html).
209+
Currently the `s3-active-storage` crate is not uploaded to https://crates.io, so we do not benefit from hosted documentation on https://docs.rs.
210+
It is however possible to build the documentation locally:
211+
212+
```sh
213+
cargo doc
214+
```
215+
216+
Cargo builds documentation for the `s3-active-storage` crate and all of its dependencies.
217+
The resulting documentation is available under `target/doc`, and may be viewed in a web browser using file:///path/to/s3-active-storage/target/doc/s3-active-storage/index.html.
218+
206219
## Contributing
207220

208221
See [CONTRIBUTING.md](CONTRIBUTING.md) for information about contributing to S3 active storage.

src/app.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//! Active Storage server API
2+
13
use crate::error::ActiveStorageError;
24
use crate::models;
35
use crate::operation;
@@ -22,10 +24,13 @@ use tower_http::normalize_path::NormalizePathLayer;
2224
use tower_http::trace::TraceLayer;
2325
use tower_http::validate_request::ValidateRequestHeaderLayer;
2426

27+
/// `x-activestorage-dtype` header definition
2528
static HEADER_DTYPE: header::HeaderName = header::HeaderName::from_static("x-activestorage-dtype");
29+
/// `x-activestorage-shape` header definition
2630
static HEADER_SHAPE: header::HeaderName = header::HeaderName::from_static("x-activestorage-shape");
2731

2832
impl IntoResponse for models::Response {
33+
/// Convert a [crate::models::Response] into a [axum::response::Response].
2934
fn into_response(self) -> Response {
3035
(
3136
[
@@ -42,6 +47,15 @@ impl IntoResponse for models::Response {
4247
}
4348
}
4449

50+
/// Returns a [axum::Router] for the Active Storage server API
51+
///
52+
/// The router is populated with all routes as well as the following middleware:
53+
///
54+
/// * a [tower_http::trace::TraceLayer] for tracing requests and responses
55+
/// * a [tower_http::validate_request::ValidateRequestHeaderLayer] for validating authorisation
56+
/// headers
57+
/// * a [tower_http::normalize_path::NormalizePathLayer] for trimming trailing slashes from
58+
/// requests
4559
pub fn router() -> Router {
4660
fn v1() -> Router {
4761
Router::new()
@@ -74,10 +88,19 @@ pub fn router() -> Router {
7488
.layer(NormalizePathLayer::trim_trailing_slash())
7589
}
7690

91+
/// TODO: Return an OpenAPI schema
7792
async fn schema() -> &'static str {
7893
"Hello, world!"
7994
}
8095

96+
/// Download an object from S3
97+
///
98+
/// Requests a byte range if `offset` or `size` is specified in the request.
99+
///
100+
/// # Arguments
101+
///
102+
/// * `auth`: Basic authentication credentials
103+
/// * `request_data`: RequestData object for the request
81104
async fn download_object(
82105
auth: &Authorization<Basic>,
83106
request_data: &models::RequestData,
@@ -89,9 +112,15 @@ async fn download_object(
89112
.await
90113
}
91114

92-
/// Handler for operations
115+
/// Handler for Active Storage operations
116+
///
117+
/// Downloads object data from S3 storage and executes the requested reduction operation.
93118
///
94-
/// Returns a `Result` with `models::Response` on success and `ActiveStorageError` on failure.
119+
/// This function is generic over any type implementing the [crate::operation::Operation] trait,
120+
/// allowing it to handle any operation conforming to that interface.
121+
///
122+
/// Returns a `Result` with [crate::models::Response] on success and
123+
/// [crate::error::ActiveStorageError] on failure.
95124
///
96125
/// # Arguments
97126
///
@@ -105,6 +134,13 @@ async fn operation_handler<T: operation::Operation>(
105134
T::execute(&request_data, &data)
106135
}
107136

137+
/// Handler for unknown operations
138+
///
139+
/// Returns an [crate::error::ActiveStorageError].
140+
///
141+
/// # Arguments
142+
///
143+
/// * `operation`: the unknown operation from the URL path
108144
async fn unknown_operation_handler(Path(operation): Path<String>) -> ActiveStorageError {
109145
ActiveStorageError::UnsupportedOperation { operation }
110146
}

src/array.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! This module provides functions and utilities for working with ndarray objects.
1+
//! Functions and utilities for working with [ndarray] objects.
22
33
use crate::error::ActiveStorageError;
44
use crate::models;

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ pub enum ActiveStorageError {
5656
UnsupportedOperation { operation: String },
5757
}
5858

59-
// Tell axum how to convert `ActiveStorageError` into a response.
6059
impl IntoResponse for ActiveStorageError {
60+
/// Convert from an `ActiveStorageError` into an [axum::response::Response].
6161
fn into_response(self) -> Response {
6262
ErrorResponse::from(self).into_response()
6363
}

src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
//! This crate provides an Active Storage Server. It implements simple reductions on S3 objects
2+
//! containing numeric binary data. By implementing these reductions in the storage system the
3+
//! volume of data that needs to be transferred to the end user is vastly reduced, leading to
4+
//! faster computations.
5+
//!
6+
//! The work is funded by the
7+
//! [ExCALIBUR project](https://www.metoffice.gov.uk/research/approach/collaboration/spf/excalibur)
8+
//! and is done in collaboration with the
9+
//! [University of Reading](http://www.reading.ac.uk/).
10+
//!
11+
//! This is a performant implementation of the Active Storage Server.
12+
//! The original Python functional prototype is available
13+
//! [here](https://github.com/stackhpc/s3-active-storage).
14+
//!
15+
//! The Active Storage Server is built on top of a number of open source components.
16+
//!
17+
//! * [Tokio](tokio), the most popular asynchronous Rust runtime.
18+
//! * [Axum](axum) web framework, built by the Tokio team. Axum performs well in [various](https://github.com/programatik29/rust-web-benchmarks/blob/master/result/hello-world.md) [benchmarks](https://web-frameworks-benchmark.netlify.app/result?l=rust)
19+
//! and is built on top of various popular components, including the [hyper] HTTP library.
20+
//! * [Serde](serde) performs (de)serialisation of JSON request and response data.
21+
//! * [AWS SDK for S3](aws-sdk-s3) is used to interact with S3-compatible object stores.
22+
//! * [ndarray] provides [NumPy](https://numpy.orgq)-like n-dimensional arrays used in numerical
23+
//! computation.
24+
125
use tokio::signal;
226
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
327

@@ -10,6 +34,7 @@ mod operations;
1034
mod s3_client;
1135
mod validated_json;
1236

37+
/// Application entry point
1338
#[tokio::main]
1439
async fn main() {
1540
init_tracing();
@@ -24,6 +49,10 @@ async fn main() {
2449
.unwrap();
2550
}
2651

52+
/// Initlialise tracing (logging)
53+
///
54+
/// Applies a filter based on the `RUST_LOG` environment variable, falling back to enable debug
55+
/// logging for this crate and tower_http if not set.
2756
fn init_tracing() {
2857
tracing_subscriber::registry()
2958
.with(
@@ -34,6 +63,9 @@ fn init_tracing() {
3463
.init();
3564
}
3665

66+
/// Graceful shutdown handler
67+
///
68+
/// Installs signal handlers to catch Ctrl-C or SIGTERM and trigger a graceful shutdown.
3769
async fn shutdown_signal() {
3870
let ctrl_c = async {
3971
signal::ctrl_c()

src/models.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1+
//! Data types and associated functions and methods
2+
13
use axum::body::Bytes;
24
use serde::{Deserialize, Serialize};
35
use strum_macros::Display;
46
use url::Url;
57
use validator::{Validate, ValidationError};
68

9+
/// Supported numerical data types
710
#[derive(Clone, Copy, Debug, Deserialize, Display, PartialEq)]
811
#[serde(rename_all = "lowercase")]
912
pub enum DType {
13+
/// [i32]
1014
Int32,
15+
/// [i64]
1116
Int64,
17+
/// [u64]
1218
Uint32,
19+
/// [u64]
1320
Uint64,
21+
/// [f64]
1422
Float32,
23+
/// [f64]
1524
Float64,
1625
}
1726

@@ -29,20 +38,29 @@ impl DType {
2938
}
3039
}
3140

41+
/// Array ordering
42+
///
43+
/// Defines an ordering for multi-dimensional arrays.
3244
#[derive(Debug, Deserialize, PartialEq)]
3345
pub enum Order {
46+
/// Row-major (C) ordering
3447
C,
48+
/// Column-major (Fortran) ordering
3549
F,
3650
}
3751

52+
/// A slice of a single dimension of an array
3853
// NOTE: In serde, structs can be deserialised from sequences or maps. This allows us to support
3954
// the [<start>, <end>, <stride>] API, with the convenience of named fields.
4055
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Validate)]
4156
#[serde(deny_unknown_fields)]
4257
#[validate(schema(function = "validate_slice"))]
4358
pub struct Slice {
59+
/// Start of the slice
4460
pub start: usize,
61+
/// End of the slice
4562
pub end: usize,
63+
/// Stride size
4664
#[validate(range(min = 1, message = "stride must be greater than 0"))]
4765
pub stride: usize,
4866
}
@@ -55,38 +73,50 @@ impl Slice {
5573
}
5674
}
5775

76+
/// Request data for operations
5877
#[derive(Debug, Deserialize, PartialEq, Validate)]
5978
#[serde(deny_unknown_fields)]
6079
#[validate(schema(function = "validate_request_data"))]
6180
pub struct RequestData {
81+
/// URL of the S3-compatible object store
6282
// TODO: Investigate using lifetimes to enable zero-copy: https://serde.rs/lifetimes.html
6383
pub source: Url,
84+
/// S3 bucket containing the object
6485
#[validate(length(min = 1, message = "bucket must not be empty"))]
6586
pub bucket: String,
87+
/// S3 object containing the data
6688
#[validate(length(min = 1, message = "object must not be empty"))]
6789
pub object: String,
90+
/// Data type
6891
pub dtype: DType,
92+
/// Offset in bytes of the numerical data within the object
6993
pub offset: Option<usize>,
94+
/// Size in bytes of the numerical data from the offset
7095
#[validate(range(min = 1, message = "size must be greater than 0"))]
7196
pub size: Option<usize>,
97+
/// Shape of the multi-dimensional array
7298
#[validate(
7399
length(min = 1, message = "shape length must be greater than 0"),
74100
custom = "validate_shape"
75101
)]
76102
pub shape: Option<Vec<usize>>,
103+
/// Order of the multi-dimensional array
77104
pub order: Option<Order>,
105+
/// Subset of the data to operate on
78106
#[validate]
79107
#[validate(length(min = 1, message = "selection length must be greater than 0"))]
80108
pub selection: Option<Vec<Slice>>,
81109
}
82110

111+
/// Validate an array shape
83112
fn validate_shape(shape: &[usize]) -> Result<(), ValidationError> {
84113
if shape.iter().any(|index| *index == 0) {
85114
return Err(ValidationError::new("shape indices must be greater than 0"));
86115
}
87116
Ok(())
88117
}
89118

119+
/// Validate an array slice
90120
fn validate_slice(slice: &Slice) -> Result<(), ValidationError> {
91121
if slice.end <= slice.start {
92122
let mut error = ValidationError::new("Selection end must be greater than start");
@@ -121,10 +151,10 @@ fn validate_shape_selection(
121151
Ok(())
122152
}
123153

154+
/// Validate request data
124155
fn validate_request_data(request_data: &RequestData) -> Result<(), ValidationError> {
125156
// Validation of multiple fields in RequestData.
126157
// TODO: More validation of shape & selection vs. size
127-
// TODO: More validation that selection fits in shape
128158
if let Some(size) = &request_data.size {
129159
let dtype_size = request_data.dtype.size_of();
130160
if size % dtype_size != 0 {
@@ -150,12 +180,16 @@ fn validate_request_data(request_data: &RequestData) -> Result<(), ValidationErr
150180

151181
/// Response containing the result of a computation and associated metadata.
152182
pub struct Response {
183+
/// Response data. May be a scalar or multi-dimensional array.
153184
pub body: Bytes,
185+
/// Data type of the response
154186
pub dtype: DType,
187+
/// Shape of the response
155188
pub shape: Vec<usize>,
156189
}
157190

158191
impl Response {
192+
/// Return a Response object
159193
pub fn new(body: Bytes, dtype: DType, shape: Vec<usize>) -> Response {
160194
Response { body, dtype, shape }
161195
}

src/operation.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//! Interface for Active Storage operations
2+
13
use crate::error::ActiveStorageError;
24
use crate::models;
35

src/s3_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! This module provides a simplified S3 client that supports downloading objects.
1+
//! A simplified S3 client that supports downloading objects.
22
//! It attempts to hide the complexities of working with the AWS SDK for S3.
33
44
use crate::error::ActiveStorageError;

src/validated_json.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//! Axum extractor that deserialises and validates JSON
2+
13
use crate::error::ActiveStorageError;
24

35
use async_trait::async_trait;

0 commit comments

Comments
 (0)