Expand description
This crate is intended for implementing some design patterns, based on fieldx
crate. At the moment it is only Application/Agent and Parent/Child patterns. Both are basically the same thing
essentially where Application/Parent is a reference counted object and agents/children hold references to it. The
difference is in children accessor method names which are app and parent, correspondingly.
Unfortunately, lack of time doesn’t allow me to create comprehensive documentation for this crate. Consider the tests as the starting points. But here is, briefly, an idea of how it looks:
use fieldx_plus::{fx_plus, agent_build};
use thiserror::Error;
#[derive(Error, Debug)]
enum AppError {
#[error("{0}")]
AdHoc(String),
#[error("Application object is unexpectedly gone")]
AppIsGone
}
#[fx_plus(app, sync)]
struct Application {
#[fieldx(lazy, get)]
service: NetService,
}
impl Application {
fn build_service(&self) -> NetService {
agent_build!(
self, NetService {
port: 4242,
name: "app service",
}
).unwrap()
}
pub fn config(&self, key: &str) -> u32 {
42
}
pub fn run() {
let app = Application::new();
app.service().launch().expect("Something went wrong...");
}
}
#[fx_plus(agent(Application, unwrap(or(AppError, AppError::AppIsGone))), sync)]
struct NetService {
port: u16,
#[fieldx(into, get)]
name: String,
}
impl NetService {
pub fn launch(&self) -> Result<(), AppError> {
let app = self.app()?;
let cfg = app.config("foo");
println!("Launching '{}' service.", self.name());
Ok(())
}
}
fn main() {
Application::run();
}Here is a quick breakdown for it:
fx_plus is an extender to fieldx::fxstruct attribute. As such, it takes all the arguments, fxstruct takes and
adds of couple of its own. But be aware that it overrides some of fxstruct arguments:
- with
apporparentfxstruct(rc)is enforced - with
agentorchildit setsfxstruct(no_new, builder); setting additional parameters with thebuilderare allowed, though:fx_plus(child, builder(into))
With any of the four serde gets disabled if serde feature is enabled.
Since application/parent struct is always rc there is a chance that its reference counter can drop to 0 while an
agent/child with weak reference attempts to call .app() or .parent(), which internally attempt to upgrade the
reference. agent(unwrap) argument controls how this situation is handled:
arguments:
child(unwrap)results in simple.unwrap()applied the upgrade callunwrap(expect("Error message")commands to use.expect("Error message"); with this and the above variant we always get just the app/parent objectunwrap(or(ErrorType, <expression>))would produceapp/parentmethods that returnErrorType; the particular value returned depends on the<expression>. Say,ErrorType::ParentIsGonecan be used to return a specific error codeunwrap(or_else(ErrorType, <expr>))can be used to invoke a method onself.<expr>can either be just method name or something likemap_to_err("argument", 42)in which case themap_to_errmethod will get the arguments specified.
Helper macros agent_build, agent_builder, child_build, and child_builder are wrappers around builder
pattern. I.e. agent_build!(self.app(), Agent { foo: 42, bar: "baz" }) is actually a shortcut for
Agent::builder().app(self.app()).foo(42).bar("baz").build(). agent_builder is the same but without the final
build() call.
Modules§
Macros§
Traits§
- Agent
- This trait is used to declare agents that can access the application. Technically, it is identical to the
Childtrait, but it is used to distinguish agents from children because an agent can also be a child of a parent that is different from the application itself. Besides, specialized method names improve code readability and intent clarity. - Application
- This trait is used to declare application structs. For now it is just a marker trait, but it may be extended in the future to include application-specific methods or properties.
- Child
- This trait is used to declare child structs in parent-child relationships.
- Parent
- This trait is used to declare parent structs in parent-child relationships.