Returning strings: Writeables

Most languages have their own type to handle strings. To avoid unnecessary allocations, Diplomat supports DiplomatWriteable, a type with a Write implementation which can be used to write to appropriate string types on the other side.

For example, if we want to have methods that philosophically return a String or a Result<String>, we can do the following:

#![allow(unused)]
fn main() {
#[diplomat::bridge]
mod ffi {
    use diplomat_runtime::DiplomatWriteable;
    use std::fmt::Write;

    #[diplomat::opaque]
    #[derive(Debug)]
    pub struct Thingy(u8);

    impl Thingy {
        pub fn debug_output(&self, writeable: &mut DiplomatWriteable) {
            write!(writeable, "{:?}", self);
        }

        pub fn maybe_get_string(&self, writeable: &mut DiplomatWriteable) -> Result<(), ()> {
            write!(writeable, "integer is {}", self.0).map_err(|_| ())
        }
    }
}
}

On the JS side these will get converted to APIs that return strings (maybe_get_string will potentially throw in the case of an error, as is usual with DiplomatResult)

In C++ these become APIs that return std::string and diplomat::result<std::string, std::monostate> respectively.

Essentially, versions of the API returning std::string are generated, where the write!() operation will end up writing directly to the std::string with no additional intermediate Rust String allocations.