Code Monkey home page Code Monkey logo

optionalstruct's Introduction

OptionalStruct

Crates.io

Quick-start

#[optional_struct]
#[derive(Bar)]
struct Foo {
	meow: u32,
	woof: String,
}

// Generated:
#[derive(Clone, Debug, PartialEq, Bar)]
struct OptionalFoo {
	meow: Option<u32>,
	woof: Option<String>,
}

impl OptionalFoo {
	pub fn apply_to(self, foo: &mut OptionalFoo) {
        if let Some(meow) = self.meow {
            foo.meow = meow;
        }

        if let Some(woof) = self.woof {
            foo.woof = woof;
        }
	}
}

fn main() {
    let mut f = Foo {
        meow: 4,
        woof: "I am hungry rn".to_owned(),
    };

    let opt_f = OptionalFoo {
        meow: Some(10),
        woof: None,
    };

    opt_f.apply_to(&mut f);

    assert_eq!(f.meow, 10);
    assert_eq!(&f.woof, "I am hungry rn");
}

Goal

Since rust does not have default arguments, and some tools are strict when deserializing data (e.g. serde), missing configuration values can be quite frustrating to deal with. For example:

#[derive(Deserialize)]
struct Config {
    log_file: PathBuf,
}

If we read the configuration from a file, and the log_file is not specified, serde will e.g. fail to create the struct. While serde offers ways to set the default value for a field with e.g.

#[derive(Deserialize)]
struct Config {
    #[serde(default = "get_next_log_filename")]
    log_file: PathBuf,
}

there are obvious limitations. This crate aims to fill this gap by allowing optional values, and providing an easy way to apply values obtained from different sources to construct our configuration.

With optional_struct, one can define the required configuration as it shall be used and only use the generated struct to handle configuration/missing values/default values.

How

The macro optional_struct generates a structure containing the same fields as the one it was tagged on, but wrapped by an Option. A function on the new structure allows applying its values to the original one (if the Options are not None). This can be called multiple times, to apply configuration from different source, while giving the caller complete control over how to set the values, since the generated struct can be easily manipulated and passed around before constructing the final configuration.

Features

  1. Rename the generated struct:
#[optional_struct(HeyU)]
struct Config();

let me = HeyU();
  1. Handle recursive types:
#[optional_struct]
struct Foo {
    // Replaces Option<Bar> with Option<OptionalBar>
    #[optional_rename(OptionalBar)]
    bar: Bar,
}

#[optional_struct]
struct bar ();
  1. Handle Options in the original struct (by ignoring them):
#[optional_struct]
struct Foo {
    bar: Option<u8>,
}

let opt_f = OptionalFoo { bar: Some(1) }
  1. Force wrapping (or not) of fields:
#[optional_struct]
struct Foo {
    #[optional_skip_wrap]
    bar: char,

    // Useless here since we wrap by default
    #[optional_wrap]
    baz: bool,
}

let opt_f = OptionalFoo { bar: Some(1) }
  1. Change the default wrapping behavior:
#[optional_struct(OptionalFoo, false)]
struct Foo {
    bar: u8,
    #[optional_wrap]
    baz: i8,
}

let opt_f = OptionalFoo { bar: 1, baz: None };

apply_to

The simplest way to understand how this function is generated is to look at the Applyable helper trait we define in this crate:

pub trait Applyable<T> {
    fn apply_to(self, t: &mut T);
}

impl<T> Applyable<T> for Option<T> {
    fn apply_to(self, t: &mut T) {
        if let Some(s) = self {
            *t = s;
        }
    }
}

impl<T> Applyable<T> for T {
    fn apply_to(self, t: &mut T) {
        *t = self;
    }
}

Each structure generated by optional_struct implements Applyable<Foo> for OptionalFoo. This implementation itself calls apply_to recursively on all fields of the structure. Besides those implementations, the ones defined just above tell you how this function behaves: values are applied when they exist, options are simply overridden.

TODOs / open questions

  1. Allow not overridding Option<T> -> Option<T> if the original Option is set but not the applied one.

  2. Allow wrapping behavior to be set without manually writing the struct name.

  3. Automatically find out which fields have an associated optional_struct structure (and avoid the optional_rename). This might require better support for associated types? Could not get it to work with only constant generics :(

  4. Make generated fields public by default? For now the fields have the same visibility as the original struct, and this is generated locally, so this seems to be a minor detail?

  5. Control which derive attributes are tagged onto the generated struct. Right now, they are copied verbatim, and then some are added (PartialEq / Default / Clone / Debug). Problem: conflicting implementation, missing impl. (e.g. serde's Ser/Deser), invalid impl (e.g. if Default is missing on some skip_wrap field) etc.

optionalstruct's People

Contributors

lesurp avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.