Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore Option type #199

Open
vdrg opened this issue Sep 14, 2023 · 1 comment
Open

Explore Option type #199

vdrg opened this issue Sep 14, 2023 · 1 comment

Comments

@vdrg
Copy link
Contributor

vdrg commented Sep 14, 2023

Similar to Results, but has some additional considerations. For results, we have the Ok functions for each type, and we define "error functions", and a toXResult for errors. For Options, we have Some (similar to Ok), but we don't have errors. So we probably need a None function that returns a Pointer (or some other special None type just to be able to define functions for it) and the corresponding toXOption functions for it.

Preliminary implementation:

enum OptionType {
    None,
    Some
}

type Bytes32Option is bytes32;

type AddressOption is bytes32;

type BoolOption is bytes32;

library LibOptionPointer {
    function decode(Pointer self) internal pure returns (OptionType resultType, Pointer ptr) {
        bytes memory data;
        assembly {
            data := self
        }
        (resultType, ptr) = abi.decode(data, (OptionType, Pointer));
    }

    function isNone(Pointer self) internal pure returns (bool) {
        (OptionType _type,) = decode(self);
        return _type == OptionType.None;
    }

    function isSome(Pointer self) internal pure returns (bool) {
        (OptionType _type,) = decode(self);
        return _type == OptionType.Some;
    }

    function toValue(Pointer self) internal pure returns (bytes32) {
        (, Pointer ptr) = decode(self);
        return ptr.asBytes32();
    }

    function unwrap(Pointer self) internal pure returns (Pointer ptr) {
        if (isNone(self)) {
            revert("called `Option::unwrap()` on a `None` value");
        }

        (, ptr) = decode(self);
    }

    function expect(Pointer self, string memory err) internal pure returns (Pointer ptr) {
        if (isNone(self)) {
            revert(err);
        }

        (, ptr) = decode(self);
    }

    function asBoolOption(Pointer self) internal pure returns (BoolOption ptr) {
        assembly {
            ptr := self
        }
    }
}

library LibBytes32Option {
    function isNone(Bytes32Option self) internal pure returns (bool) {
        return LibOptionPointer.isNone(self.toPointer());
    }

    function isSome(Bytes32Option self) internal pure returns (bool) {
        return LibOptionPointer.isSome(self.toPointer());
    }

    function toValue(Bytes32Option self) internal pure returns (bytes32) {
        (, Pointer ptr) = LibOptionPointer.decode(self.toPointer());

        return ptr.asBytes32();
    }

    function unwrap(Bytes32Option self) internal pure returns (bytes32) {
        return LibOptionPointer.unwrap(self.toPointer()).asBytes32();
    }

    function expect(Bytes32Option self, string memory err) internal pure returns (bytes32) {
        return LibOptionPointer.expect(self.toPointer(), err).asBytes32();
    }

    function toPointer(Bytes32Option self) internal pure returns (Pointer) {
        return Pointer.wrap(Bytes32Option.unwrap(self));
    }
}

library LibAddressOption {
    function isNone(AddressOption self) internal pure returns (bool) {
        return LibOptionPointer.isNone(self.toPointer());
    }

    function isSome(AddressOption self) internal pure returns (bool) {
        return LibOptionPointer.isSome(self.toPointer());
    }

    function toValue(AddressOption self) internal pure returns (address) {
        (, Pointer ptr) = LibOptionPointer.decode(self.toPointer());

        return ptr.asAddress();
    }

    function unwrap(AddressOption self) internal pure returns (address) {
        return LibOptionPointer.unwrap(self.toPointer()).asAddress();
    }

    function expect(AddressOption self, string memory err) internal pure returns (address) {
        return LibOptionPointer.expect(self.toPointer(), err).asAddress();
    }

    function toPointer(AddressOption self) internal pure returns (Pointer) {
        return Pointer.wrap(AddressOption.unwrap(self));
    }
}

library LibBoolOption {
    function isNone(BoolOption self) internal pure returns (bool) {
        return LibOptionPointer.isNone(self.toPointer());
    }

    function isSome(BoolOption self) internal pure returns (bool) {
        return LibOptionPointer.isSome(self.toPointer());
    }

    function toValue(BoolOption self) internal pure returns (bool) {
        (, Pointer ptr) = LibOptionPointer.decode(self.toPointer());

        return ptr.asBool();
    }

    function unwrap(BoolOption self) internal pure returns (bool) {
        return LibOptionPointer.unwrap(self.toPointer()).asBool();
    }

    function expect(BoolOption self, string memory err) internal pure returns (bool) {
        return LibOptionPointer.expect(self.toPointer(), err).asBool();
    }

    function toPointer(BoolOption self) internal pure returns (Pointer) {
        return Pointer.wrap(BoolOption.unwrap(self));
    }
}

function encode(OptionType _type, Pointer _data) pure returns (Pointer result) {
    bytes memory data = abi.encode(_type, _data);
    assembly {
        result := data
    }
}

function None() pure returns (Pointer) {
    return encode(OptionType.None, Pointer.wrap(0));
}

function Some(bool value) pure returns (BoolOption opt) {
    Pointer _value;
    assembly {
        _value := value
    }
    return BoolOption.wrap(Pointer.unwrap(encode(OptionType.Some, _value)));
}

using LibBoolOption for BoolOption global;
using LibAddressOption for AddressOption global;
using LibBytes32Option for Bytes32Option global;
@vdrg
Copy link
Contributor Author

vdrg commented Sep 14, 2023

An improvement to this: the OptionType doesn't really need to be encoded, a Pointer(0) could represent None, and everything else is Some.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog
Development

No branches or pull requests

1 participant