1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (C) 2023 Soni L.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Branded Option. for when you need bro's in your ption's.
//!
//! Possibly unsound. Designed for use with `selfref`.
//!
//! # Examples
//!
//! ```rust
//! use broption::BOption;
//!
//! // define a resource that needs a separate initialization step.
//! struct Foo<'bro> {
//!   name: BOption<'bro, Box<str>>,
//! }
//!
//! // define a context wrapper for our resource.
//! struct Ctx;
//! impl broption::Wrapper for Ctx {
//!   type Kind<'bro> = Foo<'bro>;
//! }
//!
//! // create an "initialization context".
//! let initialized = BOption::factory::<Ctx, _>(|factory| {
//!   // create an uninitialized resource.
//!   let mut maybeinit = Foo {
//!     name: factory.new_none(),
//!   };
//!   // initialize the resource.
//!   factory.init(&mut maybeinit.name, Box::from("hello"));
//!   // return the hopefully-initialized resource.
//!   maybeinit
//! });
//! // use the initialized resource
//! assert_eq!(&**initialized.name, "hello");
//! ```
//!
//! Failing to correctly initialize the resource panics:
//!
//! ```rust,should_panic
//! use broption::BOption;
//!
//! // define a resource that needs a separate initialization step.
//! struct Foo<'bro> {
//!   name: BOption<'bro, Box<str>>,
//! }
//!
//! // define a context wrapper for our resource.
//! struct Ctx;
//! impl broption::Wrapper for Ctx {
//!   type Kind<'bro> = Foo<'bro>;
//! }
//!
//! // create an "initialization context".
//! let initialized = BOption::factory::<Ctx, _>(|factory| {
//!   // create an uninitialized resource.
//!   let mut maybeinit = Foo {
//!     name: factory.new_none(),
//!   };
//!   // return the uninitialized resource.
//!   maybeinit
//! });
//! ```

use core::marker::PhantomData;
use core::ops::Deref;
use core::ops::DerefMut;

#[repr(transparent)]
pub struct BOption<'id, T>(Option<T>, PhantomData<fn(&'id ())->&'id ()>);

impl<T: Copy> Copy for BOption<'static, T> {}
impl<T: Clone> Clone for BOption<'static, T> {
    fn clone(&self) -> Self {
        let Self(option, id) = self;
        Self(option.clone(), *id)
    }
}

pub trait Wrapper {
    type Kind<'a>;
}

pub struct Factory<'id> {
    count: usize,
    id: PhantomData<fn(&'id ())->&'id ()>,
}

impl<'id> Factory<'id> {
    pub fn new_none<T>(&mut self) -> BOption<'id, T> {
        self.count += 1;
        BOption(None, self.id)
    }

    pub fn init<'a, T>(
        &mut self,
        boption: &'a mut BOption<'id, T>,
        value: T,
    ) -> &'a mut T {
        let was_none = boption.0.is_none();
        let value = boption.0.insert(value);
        if was_none {
            self.count -= 1;
        }
        value
    }
}

impl BOption<'static, ()> {
    pub fn factory<T, F>(f: F) -> T::Kind<'static>
    where
        T: Wrapper,
        F: for<'id> FnOnce(&mut Factory<'id>) -> T::Kind<'id>,
    {
        let mut factory = Factory {
            count: 0,
            id: PhantomData, // inferred 'static
        };
        let res = f(&mut factory);
        assert_eq!(factory.count, 0);
        res
    }
}

impl<T> BOption<'static, T> {
    pub fn new_init(t: T) -> Self {
        Self(Some(t), PhantomData)
    }
}

impl<T> Deref for BOption<'static, T> {
    type Target = T;
    fn deref(&self) -> &T {
        // SAFETY: the safety of this is very non-local.
        // you can't create a BOption without going through Factory, which keeps
        // track of uninitialized BOptions.
        // you can't swap a Factory with another Factory since you can't get the
        // invariant 'id to converge.
        // finally, you can only gain access to a 'static BOption if it's
        // already initialized. oh and you can only swap it for other
        // initialized BOptions.
        unsafe {
            self.0.as_ref().unwrap_unchecked()
        }
    }
}

impl<T> DerefMut for BOption<'static, T> {
    fn deref_mut(&mut self) -> &mut T {
        // SAFETY: the safety of this is very non-local.
        // you can't create a BOption without going through Factory, which keeps
        // track of uninitialized BOptions.
        // you can't swap a Factory with another Factory since you can't get the
        // invariant 'id to converge.
        // finally, you can only gain access to a 'static BOption if it's
        // already initialized. oh and you can only swap it for other
        // initialized BOptions.
        unsafe {
            self.0.as_mut().unwrap_unchecked()
        }
    }
}