Skip to content

Commit

Permalink
Begin work on OCI example.
Browse files Browse the repository at this point in the history
  • Loading branch information
CathalMullan committed Aug 31, 2024
1 parent 0c49426 commit 72af8af
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 85 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ corpus
artifacts
coverage

# OCI
junit.xml
report.html

# Nix
result
result-dev
Expand All @@ -31,4 +35,3 @@ result-bin

# Dir Env
.direnv
g
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Fleshed out hyper example.

### Fixed

- Router display no longer relies on generic being displayable.
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/hyper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ publish = false
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
repository.workspace = true
license.workspace = true
keywords.workspace = true
Expand All @@ -28,3 +27,4 @@ bytes = "1.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0"
regex = "1.10"
7 changes: 7 additions & 0 deletions examples/hyper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `hyper` example

This is a mini implementation of an [Open Container Initiative (OCI) Distribution Specification](https://github.com/opencontainers/distribution-spec/blob/main/spec.md) registry.

## Inspirations:
- https://github.com/mcronce/oci-registry
- https://github.com/Trow-Registry/trow
90 changes: 7 additions & 83 deletions examples/hyper/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,28 @@
#![allow(clippy::unused_async)]

use bytes::Bytes;
use http_body_util::{combinators::BoxBody, BodyExt, Full};
use hyper::{body::Incoming, header, service::service_fn, Request, Response, StatusCode};
use hyper::{body::Incoming, service::service_fn, Request};
use hyper_util::{
rt::{TokioExecutor, TokioIo},
server::conn::auto::Builder,
};
use routes::router;
use std::{
convert::Infallible,
future::Future,
net::{IpAddr, Ipv4Addr, SocketAddr},
pin::Pin,
sync::Arc,
};
use tokio::{net::TcpListener, task::JoinSet};
use wayfind::{Parameter, Path, Router};

type BoxFuture<'a> = Pin<
Box<
dyn Future<Output = Result<Response<BoxBody<Bytes, Infallible>>, anyhow::Error>>
+ Send
+ 'a,
>,
>;
use wayfind::Path;

type HandlerFn =
Arc<dyn for<'a> Fn(&'a str, &'a [Parameter<'_, 'a>]) -> BoxFuture<'a> + Send + Sync>;
pub mod routes;

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let mut router: Router<HandlerFn> = Router::new();
router.insert(
"/",
Arc::new(move |path, parameters| Box::pin(index_route(path, parameters))),
)?;
router.insert(
"/hello/{name}",
Arc::new(move |path, parameters| Box::pin(hello_route(path, parameters))),
)?;
router.insert(
"{*catch_all}",
Arc::new(move |path, parameters| Box::pin(not_found(path, parameters))),
)?;

let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1337);
let listener = TcpListener::bind(&socket).await?;
println!("Listening on http://{socket}");

let router = router()?;
let router = Arc::new(router);
println!("Router: {router}");

let mut join_set = JoinSet::new();

loop {
Expand Down Expand Up @@ -81,53 +55,3 @@ async fn main() -> Result<(), anyhow::Error> {
});
}
}

async fn index_route(
_: &'_ str,
_: &'_ [Parameter<'_, '_>],
) -> Result<Response<BoxBody<Bytes, Infallible>>, anyhow::Error> {
let json = serde_json::json!({
"hello": "world"
});

let body = Full::new(Bytes::from(json.to_string()));
let response = Response::builder()
.header(header::CONTENT_TYPE, "application/json")
.body(body.boxed())?;

Ok(response)
}

async fn hello_route(
_: &'_ str,
parameters: &'_ [Parameter<'_, '_>],
) -> Result<Response<BoxBody<Bytes, Infallible>>, anyhow::Error> {
let name = parameters[0].value;
let json = serde_json::json!({
"hello": name,
});

let body = Full::new(Bytes::from(json.to_string()));
let response = Response::builder()
.header(header::CONTENT_TYPE, "application/json")
.body(body.boxed())?;

Ok(response)
}

async fn not_found(
path: &'_ str,
_: &'_ [Parameter<'_, '_>],
) -> Result<Response<BoxBody<Bytes, Infallible>>, anyhow::Error> {
let json = serde_json::json!({
"error": "route_not_found",
"route": path,
});

let body = Full::new(Bytes::from(json.to_string()));
let response = Response::builder()
.status(StatusCode::NOT_FOUND)
.body(body.boxed())?;

Ok(response)
}
32 changes: 32 additions & 0 deletions examples/hyper/src/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#![allow(clippy::unused_async)]

use bytes::Bytes;
use http_body_util::combinators::BoxBody;
use hyper::Response;
use index::index_route;
use std::{convert::Infallible, future::Future, pin::Pin, sync::Arc};
use wayfind::{errors::InsertError, Parameter, Router};

pub mod index;

type BoxFuture<'a> = Pin<
Box<
dyn Future<Output = Result<Response<BoxBody<Bytes, Infallible>>, anyhow::Error>>
+ Send
+ 'a,
>,
>;

type HandlerFn =
Arc<dyn for<'a> Fn(&'a str, &'a [Parameter<'_, 'a>]) -> BoxFuture<'a> + Send + Sync>;

pub fn router() -> Result<Router<HandlerFn>, InsertError> {
let mut router: Router<HandlerFn> = Router::new();

router.insert(
"/",
Arc::new(move |path, parameters| Box::pin(index_route(path, parameters))),
)?;

Ok(router)
}
21 changes: 21 additions & 0 deletions examples/hyper/src/routes/index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use bytes::Bytes;
use http_body_util::{combinators::BoxBody, BodyExt, Full};
use hyper::{header, Response};
use std::convert::Infallible;
use wayfind::Parameter;

pub async fn index_route(
_: &'_ str,
_: &'_ [Parameter<'_, '_>],
) -> Result<Response<BoxBody<Bytes, Infallible>>, anyhow::Error> {
let json = serde_json::json!({
"hello": "world"
});

let body = Full::new(Bytes::from(json.to_string()));
let response = Response::builder()
.header(header::CONTENT_TYPE, "application/json")
.body(body.boxed())?;

Ok(response)
}
4 changes: 4 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
(self: super: {
cargo-codspeed = pkgs.callPackage ./nix/pkgs/cargo-codspeed {};
cargo-insta = pkgs.callPackage ./nix/pkgs/cargo-insta {};
oci-distribution-spec-conformance = pkgs.callPackage ./nix/pkgs/oci-distribution-spec-conformance {};
})
];
};
Expand Down Expand Up @@ -70,6 +71,9 @@
# Release
cargo-semver-checks

# OCI
oci-distribution-spec-conformance

# Nix
alejandra
statix
Expand Down
38 changes: 38 additions & 0 deletions nix/pkgs/oci-distribution-spec-conformance/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
lib,
buildGoModule,
fetchFromGitHub,
}:
buildGoModule rec {
pname = "oci-distribution-spec-conformance";
version = "1.1.0";

src = fetchFromGitHub {
owner = "opencontainers";
repo = "distribution-spec";
rev = "v${version}";
hash = "sha256-GL28YUwDRicxS65E7SDR/Q3tJOWN4iwgq4AGBjwVPzA=";
};

sourceRoot = "source/conformance";
vendorHash = "sha256-5gn9RpjCALZB/GFjlJHDqPs2fIHl7NJr5QjPmsLnnO4=";

CGO_ENABLED = 0;

postInstall = ''
go test -c ./... -o oci-distribution-spec-conformance
mkdir -p $out/bin
mv oci-distribution-spec-conformance $out/bin
'';

doCheck = false;

meta = with lib; {
description = " OCI Distribution Specification Conformance Tests";
mainProgram = "oci-distribution-spec-conformance";
homepage = "https://opencontainers.org";
changelog = "https://github.com/opencontainers/distribution-spec/releases/tag/v${version}";
license = licenses.asl20;
platforms = platforms.all;
};
}

0 comments on commit 72af8af

Please sign in to comment.