Skip to main content

Crate fieldx_plus

Crate fieldx_plus 

Source
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 app or parent fxstruct(rc) is enforced
  • with agent or child it sets fxstruct(no_new, builder); setting additional parameters with the builder are 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 call
  • unwrap(expect("Error message") commands to use .expect("Error message"); with this and the above variant we always get just the app/parent object
  • unwrap(or(ErrorType, <expression>)) would produce app/parent methods that return ErrorType; the particular value returned depends on the <expression>. Say, ErrorType::ParentIsGone can be used to return a specific error code
  • unwrap(or_else(ErrorType, <expr>)) can be used to invoke a method on self. <expr> can either be just method name or something like map_to_err("argument", 42) in which case the map_to_err method 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§

traits

Macros§

agent_build
agent_builder
child_build
child_builder

Traits§

Agent
This trait is used to declare agents that can access the application. Technically, it is identical to the Child trait, 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.

Attribute Macros§

fx_plus