Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle re-exported types #2

Merged
merged 17 commits into from
Oct 2, 2022
Merged
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
31 changes: 20 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# Contributing

This project is not open to unsolicited code contributions (for the time being).
You are more than free to play around with the code though! The instructions below should be enough to get
you started. I suggest looking at [`ARCHITECTURE.md`](ARCHITECTURE.md) as well to get a sense of the overall project
structure.
You are more than free to play around with the code though! The instructions below should be enough to get you started. I suggest looking at [`ARCHITECTURE.md`](ARCHITECTURE.md) as well to get a sense of the overall project structure.

# Prerequisites

Expand All @@ -19,8 +17,7 @@ cargo nextest run
```

We primarily rely on end-to-end testing to check that `pavex`'s behaviour meets our expectations.
All tests are located in `libs/pavex_cli/tests` and are launched using a custom test runner that you can find
in `libs/pavex_test_runner`.
All tests are located in `libs/pavex_cli/tests` and are launched using a custom test runner that you can find in `libs/pavex_test_runner`.

In a nutshell:

Expand All @@ -30,9 +27,21 @@ In a nutshell:
- if the test is expected to pass, we check the generated code and the graph diagnostics;
- if the test is expected to fail, we check `stderr` to verify the quality of the error message returned to users.

For each, a runtime environment is created as a sub-folder of `ui_test_envs`, which is in turn generated at the root
of `pavex`'s workspace.
We use a consistent folder to leverage `cargo` caching and speed up successive test runs. It also allows you to easily
inspect the artifacts generated during the test run.
If you suspect that something funny is going on due to cross-run contamination, delete the `ui_test_envs` folder to get
a clean slate.
## Test runtime environment

For each test, a runtime environment is created as a sub-folder of `ui_test_envs`, which is in turn generated at the root of `pavex`'s workspace.
We use a consistent folder to leverage `cargo` caching and speed up successive test runs. It also allows you to easily inspect the artifacts generated during the test run.
If you suspect that something funny is going on due to cross-run contamination, delete the `ui_test_envs` folder to get a clean slate.

## Updating saved snapshots

The generated code or the graph diagnostics may not match our expectations.
The test runner will save the unexpected output in a file named like the expectation file with an additional `.snap` suffix. You can then choose to update the saved snapshot via our utility CLI:

```bash
# It must be run from the root folder of the workspace
cargo r --bin snaps
```

It will cycle through all `.snap` files and print the changeset with respect to our previous expectations.
You will then be prompted to decide if you want to update the saved snapshot to match the new value or if you prefer to keep it as it.
10 changes: 1 addition & 9 deletions examples/app_blueprint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ edition = "2021"
path = "src/bin.rs"
name = "bp"

[[bin]]
path = "src/app.rs"
name = "app"

[dependencies]
pavex_builder = { path = "../../libs/pavex_builder" }
http = "0.2"
hyper = { version = "0.14", features = ["server", "http1", "http2", "runtime"] }
matchit = "0.6.0"
tokio = { version = "1"}
anyhow = { version = "<=1.0.59"}
pavex_runtime = { path = "../../libs/pavex_runtime" }
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
),
],
component_lifecycles: {
(
registered_at: "app_blueprint",
import_path: "crate :: extract_path",
): RequestScoped,
(
registered_at: "app_blueprint",
import_path: "crate :: http_client",
): Singleton,
(
registered_at: "app_blueprint",
import_path: "crate :: extract_path",
): RequestScoped,
(
registered_at: "app_blueprint",
import_path: "crate :: logger",
Expand All @@ -45,7 +45,7 @@
import_path: "crate :: stream_file",
): [
(
line: 40,
line: 41,
column: 10,
file: "examples/app_blueprint/src/lib.rs",
),
Expand All @@ -56,25 +56,25 @@
registered_at: "app_blueprint",
import_path: "crate :: extract_path",
): (
line: 38,
column: 10,
file: "examples/app_blueprint/src/lib.rs",
line: 58,
column: 32,
file: "libs/pavex_builder/src/app.rs",
),
(
registered_at: "app_blueprint",
import_path: "crate :: logger",
): (
line: 39,
column: 10,
file: "examples/app_blueprint/src/lib.rs",
line: 58,
column: 32,
file: "libs/pavex_builder/src/app.rs",
),
(
registered_at: "app_blueprint",
import_path: "crate :: http_client",
): (
line: 37,
column: 10,
file: "examples/app_blueprint/src/lib.rs",
line: 58,
column: 32,
file: "libs/pavex_builder/src/app.rs",
),
},
)
65 changes: 0 additions & 65 deletions examples/app_blueprint/src/app.rs

This file was deleted.

5 changes: 3 additions & 2 deletions examples/app_blueprint/src/bin.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use app_blueprint::app_blueprint;
use std::error::Error;
use std::path::PathBuf;
use std::str::FromStr;

use app_blueprint::app_blueprint;

fn main() -> Result<(), Box<dyn Error>> {
let path = PathBuf::from_str("blueprint.json")?;
let path = PathBuf::from_str("blueprint.ron")?;
app_blueprint().persist(&path)?;

std::process::Command::new("../../target/debug/pavex_cli")
Expand Down
7 changes: 4 additions & 3 deletions examples/app_blueprint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use pavex_builder::{f, AppBlueprint, Lifecycle};
use std::path::PathBuf;

use pavex_builder::{f, AppBlueprint, Lifecycle};

pub struct Logger;

pub fn extract_path(_inner: http::Request<hyper::body::Body>) -> PathBuf {
pub fn extract_path(_inner: pavex_runtime::http::Request<pavex_runtime::hyper::Body>) -> PathBuf {
todo!()
}

Expand All @@ -15,7 +16,7 @@ pub fn stream_file(
_inner: PathBuf,
_logger: Logger,
_http_client: HttpClient,
) -> http::Response<hyper::body::Body> {
) -> pavex_runtime::http::Response<pavex_runtime::hyper::Body> {
todo!()
}

Expand Down
2 changes: 1 addition & 1 deletion libs/pavex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
pavex_builder = { path = "../pavex_builder" }
syn = { version = "1.0.96", features = ["full", "extra-traits", "visit"] }
rustdoc-types = "0.14.0"
rustdoc-types = "0.17.0"
serde_json = "1"
anyhow = "1.0.58"
fs-err = "2.7.0"
Expand Down
96 changes: 77 additions & 19 deletions libs/pavex/src/language/callable_path.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use std::fmt::{Display, Formatter};

use quote::quote;
use syn::ExprPath;
use syn::{ExprPath, GenericArgument, PathArguments, Type};

use pavex_builder::RawCallableIdentifiers;

/// A path that can be used in expression position (i.e. to refer to a function or a static method).
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub(crate) struct CallPath(pub ExprPath);
pub(crate) struct CallPath {
pub has_leading_colon: bool,
pub segments: Vec<CallPathSegment>,
}

impl AsRef<ExprPath> for CallPath {
fn as_ref(&self) -> &ExprPath {
&self.0
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub(crate) struct CallPathSegment {
pub ident: syn::Ident,
pub generic_arguments: Vec<CallPath>,
}

impl CallPath {
Expand All @@ -22,7 +24,48 @@ impl CallPath {
raw_identifiers: callable_identifiers.to_owned(),
parsing_error: e,
})?;
Ok(Self(callable_path))
Self::parse_from_path(callable_path.path)
}

pub(crate) fn parse_from_path(path: syn::Path) -> Result<Self, InvalidCallPath> {
let has_leading_colon = path.leading_colon.is_some();
let mut segments = Vec::with_capacity(path.segments.len());
for syn_segment in path.segments {
let generic_arguments = match syn_segment.arguments {
PathArguments::None => vec![],
PathArguments::AngleBracketed(syn_arguments) => {
let mut arguments = Vec::with_capacity(syn_arguments.args.len());
for syn_argument in syn_arguments.args {
let argument = match syn_argument {
GenericArgument::Type(p) => match p {
Type::Path(p) => Self::parse_from_path(p.path)?,
_ => unreachable!(),
},
GenericArgument::Lifetime(_)
| GenericArgument::Binding(_)
| GenericArgument::Constraint(_)
| GenericArgument::Const(_) => todo!(
"We can only handle generic type parameters for the time being."
),
};
arguments.push(argument)
}
arguments
}
PathArguments::Parenthesized(_) => {
todo!("We do not handle paranthesized generic parameters")
}
};
let segment = CallPathSegment {
ident: syn_segment.ident,
generic_arguments,
};
segments.push(segment)
}
Ok(Self {
has_leading_colon,
segments,
})
}

/// Return the first segment in the path.
Expand All @@ -31,21 +74,36 @@ impl CallPath {
/// return `my_module`.
pub fn leading_path_segment(&self) -> &proc_macro2::Ident {
// This unwrap never fails thanks to the validation done in `parse`
&self.0.path.segments.first().unwrap().ident
&self.segments.first().unwrap().ident
}
}

impl Display for CallPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let leading_colon = if self.has_leading_colon { "::" } else { "" };
write!(f, "{}", leading_colon)?;
let last_segment_index = self.segments.len().saturating_sub(1);
for (i, segment) in self.segments.iter().enumerate() {
write!(f, "{}", segment)?;
if i != last_segment_index {
write!(f, "::")?;
}
}
Ok(())
}
}

impl std::fmt::Display for CallPath {
impl Display for CallPathSegment {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let path = &self.0;
let s = quote! { #path }.to_string();
write!(
f,
"{}",
s.replace(" :: ", "::")
.replace("< ", "<")
.replace(" >", ">")
)
write!(f, "{}", self.ident)?;
let last_argument_index = self.generic_arguments.len().saturating_sub(1);
for (j, argument) in self.generic_arguments.iter().enumerate() {
write!(f, "{}", argument)?;
if j != last_argument_index {
write!(f, ", ")?;
}
}
Ok(())
}
}

Expand Down
10 changes: 5 additions & 5 deletions libs/pavex/src/language/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
pub(crate) use callable::Callable;
pub(crate) use callable_path::{CallPath, InvalidCallPath};
pub(crate) use resolved_path::{ParseError, ResolvedPath, ResolvedPathSegment, UnknownPath};
pub(crate) use resolved_type::ResolvedType;

mod callable;
mod callable_path;
mod resolved_path;
mod resolved_type;

// E.g. `["std", "path", "PathBuf"]`.
pub type ImportPath = Vec<String>;

pub(crate) use callable::Callable;
pub(crate) use callable_path::{CallPath, InvalidCallPath};
pub(crate) use resolved_path::{EncodedResolvedPath, ParseError, ResolvedPath, UnknownPath};
pub(crate) use resolved_type::ResolvedType;
Loading