Skip to content
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
45 changes: 18 additions & 27 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,15 @@ pub fn stream_file(
how the application is supposed to behave at runtime.

```rust
use pavex::blueprint::{Blueprint, constructor::Lifecycle};
use pavex::blueprint::route::GET;
use pavex::f;

/// The blueprint for our application.
/// It lists all its routes and provides constructors for all the types
/// that will be needed to invoke `stream_file`, our request handler.
///
/// This will be turned into a ready-to-run web server by `pavex_cli`.
use pavex::Blueprint;

pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
bp.constructor(f!(crate::load_configuration), Lifecycle::Singleton);
bp.constructor(f!(crate::http_client), Lifecycle::Singleton);
bp.constructor(f!(crate::extract_path), Lifecycle::RequestScoped);
bp.constructor(f!(crate::logger), Lifecycle::Transient);
bp.route(GET, "/home", f!(crate::stream_file));
bp.singleton(LOAD_CONFIGURATION);
bp.singleton(HTTP_CLIENT);
bp.request_scoped(EXTRACT_PATH);
bp.transient(LOGGER);
bp.route(STREAM_FILE);
bp
}
```
Expand Down Expand Up @@ -187,25 +180,23 @@ You can get a structured representation of all the types in `library_name`.\
This is what Pavex does: for each registered route handler and constructor, it builds the documentation for the crate
it belongs to and extracts the relevant bits of information from `rustdoc`'s output.

If you are going through the source code, this is the process that converts a `RawCallableIdentifiers` into a `Callable`,
with `ResolvedPath` as an intermediate step.
If you are going through the source code, this is the process that converts a `RawCallableIdentifiers` into a `Callable`.

`Callable` looks like this:
`Callable` is an enum:

```rust
struct Callable {
pub output_fq_path: Type,
pub callable_fq_path: ResolvedPath,
pub inputs: Vec<Type>,
}

pub struct Type {
pub package_id: PackageId,
pub base_type: Vec<String>,
pub generic_arguments: Vec<Type>,
enum Callable {
FreeFunction(FreeFunction),
InherentMethod(InherentMethod),
TraitMethod(TraitMethod),
StructLiteralInit(StructLiteralInit),
EnumVariantInit(EnumVariantInit),
}
```

Each variant carries its inputs (as `Vec<Type>`) and output type. `Type` is a `rustdoc_types::Type` enum
with variants like `ResolvedPath`, `Generic`, `Primitive`, etc.

After this phase, we have a collection of `Callable` instances representing our constructors and handlers.\
It's a puzzle that we need to solve, starting from the handlers: how do we build instances of the types that they take
as inputs?
Expand Down
62 changes: 2 additions & 60 deletions compiler/pavexc/src/compiler/analyses/components/db/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,26 +105,13 @@ impl ComponentDb {
"All unassigned generic parameters must be used by the output type.\n\
`{}`, one of your constructors, breaks this rule: {free_parameters} {subject_verb} only used by its input parameters.",
callable));
let help = if db.registration(id).kind.is_blueprint() {
Some("Assign concrete type(s) to the problematic \
generic parameter(s) when registering the constructor against the blueprint: \n\
| bp.constructor(\n\
| f!(my_crate::my_constructor::<ConcreteType>), \n\
| ..\n\
| )".to_string())
} else {
None
};
let d = CompilerDiagnostic::builder(error)
.optional_source(source)
.optional_source(definition_snippet)
.help(
"Can you restructure your constructor to remove those generic parameters from its signature?"
.into(),
)
.optional_help(help)
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(d);
}
Expand Down Expand Up @@ -309,24 +296,16 @@ impl ComponentDb {
buffer
};
let verb = if parameters.len() == 1 { "does" } else { "do" };
let plural = if parameters.len() == 1 { "" } else { "s" };
let error = anyhow::anyhow!(e)
.context(
format!(
"I am not smart enough to figure out the concrete type for all the generic parameters in `{}`.\n\
There should no unassigned generic parameters in request handlers, but {free_parameters} {verb} \
not seem to have been assigned a concrete type.",
callable));
let d = CompilerDiagnostic::builder(error).optional_source(source)
let d = CompilerDiagnostic::builder(error)
.optional_source(source)
.optional_source(definition_snippet)
.help(
format!("Specify the concrete type{plural} for {free_parameters} when registering the request handler against the blueprint: \n\
| bp.route(\n\
| ..\n\
| f!(my_crate::my_handler::<ConcreteType>), \n\
| )"))
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(d);
}
Expand Down Expand Up @@ -417,7 +396,6 @@ impl ComponentDb {
buffer
};
let verb = if parameters.len() == 1 { "does" } else { "do" };
let plural = if parameters.len() == 1 { "" } else { "s" };
let error = anyhow::anyhow!(e)
.context(
format!(
Expand All @@ -428,13 +406,6 @@ impl ComponentDb {
let d = CompilerDiagnostic::builder(error)
.optional_source(source)
.optional_source(definition_snippet)
.help(
format!("Specify the concrete type{plural} for {free_parameters} when registering the wrapping middleware against the blueprint: \n\
| bp.wrap(\n\
| f!(my_crate::my_middleware::<ConcreteType>), \n\
| )"))
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(d);
}
Expand Down Expand Up @@ -495,7 +466,6 @@ impl ComponentDb {
buffer
};
let verb = if parameters.len() == 1 { "does" } else { "do" };
let plural = if parameters.len() == 1 { "" } else { "s" };
let error = anyhow::anyhow!(e)
.context(
format!(
Expand All @@ -505,13 +475,6 @@ impl ComponentDb {
let d = CompilerDiagnostic::builder(error)
.optional_source(source)
.optional_source(definition_snippet)
.help(
format!("Specify the concrete type{plural} for {free_parameters} when registering the pre-processing middleware against the blueprint: \n\
| bp.pre_process(\n\
| f!(my_crate::my_middleware::<ConcreteType>), \n\
| )"))
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(d);
}
Expand Down Expand Up @@ -584,7 +547,6 @@ impl ComponentDb {
buffer
};
let verb = if parameters.len() == 1 { "does" } else { "do" };
let plural = if parameters.len() == 1 { "" } else { "s" };
let error = anyhow::anyhow!(e)
.context(
format!(
Expand All @@ -595,13 +557,6 @@ impl ComponentDb {
let d = CompilerDiagnostic::builder(error)
.optional_source(source)
.optional_source(definition_snippet)
.help(
format!("Specify the concrete type{plural} for {free_parameters} when registering the post-processing middleware against the blueprint: \n\
| bp.post_process(\n\
| f!(my_crate::my_middleware::<ConcreteType>), \n\
| )"))
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(d);
}
Expand Down Expand Up @@ -717,11 +672,6 @@ impl ComponentDb {
get_snippet(callable, parameters, krate_collection);
let d = CompilerDiagnostic::builder(e).optional_source(source)
.optional_source(definition_snippet)
.help(
"Specify the concrete type(s) for the problematic \
generic parameter(s) when registering the error observer against the blueprint: `f!(my_crate::my_observer::<ConcreteType>)`".into())
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(d);
},
Expand Down Expand Up @@ -821,14 +771,6 @@ impl ComponentDb {
callable));
let diagnostic = CompilerDiagnostic::builder(error).optional_source(source)
.optional_source(definition_snippet)
.help(
"Specify the concrete type(s) for the problematic \
generic parameter(s) when registering the error handler against the blueprint: \n\
| .error_handler(\n\
| f!(my_crate::my_error_handler::<ConcreteType>)\n\
| )".into())
// ^ TODO: add a proper code snippet here, using the actual function that needs
// to be amended instead of a made signature
.build();
diagnostics.push(diagnostic);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
use pavexc_attr_parser::AnnotationProperties;

use super::{
AuxiliaryData, ConfigType, ImplInfo, annotated_item2type, cannot_resolve_callable_path,
AuxiliaryData, ConfigType, ImplInfo, annotated_item2type, callable_resolution_error,
invalid_config_type, validate_route_path,
};
use crate::compiler::analyses::user_components::{UserComponent, UserComponentId};
Expand Down Expand Up @@ -220,7 +220,7 @@ pub(crate) fn resolve_annotation_coordinates(
let callable = match outcome {
Ok(callable) => callable,
Err(e) => {
cannot_resolve_callable_path(
callable_resolution_error(
e.into(),
component_id,
aux,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ pub(super) fn invalid_config_type(
.unwrap();
write!(&mut error_msg, ".").unwrap();
}
Some("Set the generic parameters to concrete types when registering the type as configuration. E.g. `bp.config(t!(crate::MyType<std::string::String>))` for `struct MyType<T>(T)`.".to_string())
Some("Set the generic parameters to concrete types when registering the type as configuration.".to_string())
}
}
};
Expand All @@ -348,7 +348,7 @@ pub(super) fn invalid_config_type(
diagnostics.push(diagnostic);
}

pub(super) fn cannot_resolve_callable_path(
pub(super) fn callable_resolution_error(
e: CallableResolutionError,
id: UserComponentId,
db: &AuxiliaryData,
Expand All @@ -369,7 +369,7 @@ pub(super) fn cannot_resolve_callable_path(
def.annotated_source
});
let source = diagnostics.annotated(
TargetSpan::RawIdentifiers(&db.id2registration[id], kind),
db.registration_target(&id),
format!("The {kind} was registered here"),
);
let diagnostic = CompilerDiagnostic::builder(e.clone())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod diagnostic;
pub(super) use coordinates::resolve_annotation_coordinates;

use diagnostic::{
cannot_resolve_callable_path, const_generics_are_not_supported, invalid_config_type,
callable_resolution_error, const_generics_are_not_supported, invalid_config_type,
invalid_prebuilt_type, not_a_module, not_a_type_reexport, type_resolution_error,
unknown_module_path, unresolved_external_reexport,
};
Expand Down Expand Up @@ -187,7 +187,7 @@ pub(super) fn register_imported_components(
let callable = match outcome {
Ok(callable) => callable,
Err(e) => {
cannot_resolve_callable_path(
callable_resolution_error(
CallableResolutionError::from(e),
user_component_id,
aux,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,10 +643,10 @@ impl PathRouter {
// We are looking at a situation like the following:
//
// bp.nest_at("/path_prefix", {
// bp.fallback(f!(...));
// bp.fallback(MY_FALLBACK);
// bp.nest({
// bp.route(GET, "/yo", f!(...));
// bp.fallback(f!(...));
// bp.route(MY_ROUTE);
// bp.fallback(MY_FALLBACK);
// });
// });
//
Expand Down
4 changes: 2 additions & 2 deletions compiler/pavexc/src/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ pub(crate) use pavex_cli_diagnostic::{
pub(crate) use proc_macro_utils::ProcMacroSpanExt;
pub(crate) use registration::{Registration, RegistrationKind};
pub(crate) use registration_locations::{
bp_new_span, config_key_span, domain_span, f_macro_span, imported_sources_span,
nest_blueprint_span, prefix_span, registration_span, route_path_span,
bp_new_span, config_key_span, domain_span, imported_sources_span, nest_blueprint_span,
prefix_span, registration_span, route_path_span,
};
pub use sink::DiagnosticSink;
pub(crate) use sink::TargetSpan;
Expand Down
Loading
Loading