plusplus 0.2.0

Classes and object-oriented programming for Rust!
Documentation
# Rust++: Object-Oriented Programming for Rust!!
[<img alt="crates.io" src="https://img.shields.io/crates/v/plusplus.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/plusplus)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-plusplus-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/plusplus)

This crate provides a `class!` macro that allows you to write Rust in an object-oriented style.
As an example:

```rust
use plusplus::{class, ClassInConstruction, Downcast, DowncastTo, InConstruction};

// define a class
class!{
    // classes can derive traits (though right now only `Clone` is supported)
    #[derive(Clone)]
    pub class ObjectZero {
        string: String;

        // a class constructor. Classes returned from Rust++ class constructors must be
        // parameterized with the `<InConstruction>` type parameter.
        pub fn new() -> ObjectZero<InConstruction> {
            // classes are initialized in the constructor with the `init_class!` syntax.
            //
            // this takes all the class's fields as an argument, plus a `superclass` field for
            // initializing the superclass (when present)
            init_class! {
                string: "hello".into(),
                another_field: 26,
            }
        }

        // fields can have normal Rust visibility modifiers and be defined
        // anywhere in the class
        pub(crate) another_field: i32;

        pub fn set_string(&mut self, str: String) {
            self.string = str;
        }
    }
}

// object inheritance works across module and crate boundaries
mod object_one {
    use plusplus::{class, InConstruction};
    use reqwest::Response;
    class!{
        // ObjectOne is a subclass of ObjectZero. it inherits all of ObjectZero's methods and
        // fields, and can extend ObjectZero's methods with new behavior
        //
        // A subclass must derive all traits the parent class derives
        #[derive(Clone)]
        pub class ObjectOne: super::ObjectZero {
            string_one: String;
            reqwest_url: String;

            pub fn new() -> ObjectOne<InConstruction> {
                init_class! {
                    // initialize the superclass
                    superclass: super::ObjectZero::new(),

                    string_one: String::new(),
                    reqwest_url: "https://crouton.net/".into(),
                }
            }

            pub fn print_example(&self) {
                println!("hi from ObjectOne");
            }

            // you can also write async functions!
            pub async fn get_url(&self) -> reqwest::Result<Response> {
                reqwest::get(&self.reqwest_url).await
            }

            // you override a superclass's methods by declaring an Override block with the name
            // of the class you're overriding
            override super::ObjectZero {
                pub fn set_string(&mut self, str: String) {
                    // you prefix method invocations with the `super_` prefix to call the
                    // parent's implementation of the method. this allows you to extend methods
                    // with new behaviors!
                    self.super_set_string(str.clone());
                    self.string_one = str;
                }
            }
        }
    }

    // if you want to define methods that can't be overridden, just put them in a normal
    // impl block
    impl ObjectOne {
        pub fn string_one(&self) -> &str {
            &self.string_one
        }
    }
}
use object_one::ObjectOne;
use reqwest::Response;

class!{
    #[derive(Clone)]
    pub class ObjectTwo: ObjectOne {
        string_two: String;
        pub fn new(two: impl Into<String>) -> ObjectTwo<InConstruction> {
            init_class! {
                superclass: object_one::ObjectOne::new(),
                string_two: two.into(),
            }
        }

        // in order to override a class's method, you must create an override block
        // for the class that declared that method
        override ObjectOne {
            pub fn print_example(&self) {
                self.super_print_example();
                println!("hello from ObjectTwo");
            }

            pub async fn get_url(&self) -> reqwest::Result<Response> {
                let response = self.super_get_url().await?;
                println!("url get!!!");
                Ok(response)
            }
        }

        // you can override methods from any parent class in the class hierarchy
        override ObjectZero {
            pub fn set_string(&mut self, str: String) {
                self.super_set_string(str.clone());
                self.string_two = str;
            }
        }
    }
}

fn main() {
    // initialize an object. the `finish` method wraps the class in a `ClassBox`
    // and allows it to behave polymorphically.
    let object_two = ObjectTwo::new("hello!").finish();

    // prints:
    // ```
    // hi from ObjectOne
    // hello from ObjectTwo
    // ```
    object_two.print_example();
    println!();

    let object_as_one = object_two.upcast(); // is ClassBox<ObjectOne>

    // calling a class method from anywhere in the class hierarchy will always result in the
    // deepest implementation of that method getting executed. so, this also prints:
    // ```
    // hi from ObjectOne
    // hello from ObjectTwo
    // ```
    object_as_one.print_example();
    println!();

    // you can call a method with the `my_` prefix in order to bypass the method overload and
    // call that class's own implementation of the method. so this prints:
    // ```
    // hi from ObjectOne
    // ```
    object_as_one.my_print_example();
    println!();

    let mut object_as_base = object_as_one.upcast(); // is ClassBox<ObjectZero>

    // this call results in all three `set_string` methods getting invoked
    let new_str = "sets all strings!!";
    object_as_base.set_string(new_str.to_string());

    // you can't directly access a subclass's fields from a superclass. so, the following line
    // would not compile:
    // println!("{}" object_as_base.string_two);

    let object_as_two = object_as_base
        .downcast_to::<ObjectOne>().unwrap()
        .downcast_to::<ObjectTwo>().unwrap();

    // but, you can access a superclass's fields, within the limits set by visibilty rules
    assert_eq!(object_as_two.string, new_str);
    assert_eq!(object_as_two.string_one(), new_str);
    assert_eq!(object_as_two.string_two, new_str);

    let tokio_rt = tokio::runtime::Builder::new_current_thread().enable_io().build().unwrap();
    tokio_rt.block_on(async {
        let object_as_one = object_as_two.upcast();
        // method overloads work on async methods, too. this prints:
        // ```
        // url get!!!
        // ```
        let response = object_as_one.get_url().await.unwrap();

        // prints:
        // ```
        // <html>
        // <title> Crouton
        // </title>
        // <body bgcolor="white" text="black">
        // <img src="crouton.png" alt="Crouton">
        // </body>
        // </html>
        // ```
        // delightful....
        println!("{}", response.text().await.unwrap());
    });
}

```

More details, and how this works, can be found in the documentation.