//! Derive and proc macros for magnus.
//!
//! ```
//! #[magnus::wrap(class = "RbPoint", free_immediately, size)]
//! struct Point {
//! x: isize,
//! y: isize,
//! }
//!
//! #[magnus::init]
//! fn init() -> Result<(), magnus::Error> {
//! magnus::define_class("RbPoint", magnus::class::object())?;
//! Ok(())
//! }
//! ```
use TokenStream;
use parse_macro_input;
/// Mark a function as the 'init' function to be run for a library when it is
/// `require`d by Ruby code.
///
/// The init function is used to define your Ruby modules & classes, bind
/// functions as Ruby methods, etc.
///
/// # Attributes
///
/// * `name = "..."` - sets the name of the init function exported for Ruby.
/// This default's to the current crate's name. The name will be prepended
/// with `Init_` and `-` will be replaced with `_`. This (minus the `Init_`
/// prefix) must match the name of the final `.so`/`.bundle` file.
///
/// # Examples
///
/// ```
/// fn distance(a: (f64, f64), b: (f64, f64)) -> f64 {
/// ((b.0 - a.0).powi(2) + (b.1 - a.1).powi(2)).sqrt()
/// }
///
/// #[magnus::init]
/// fn init() {
/// magnus::define_global_function("distance", magnus::function!(distance, 2));
/// }
/// ```
/// The init function can also return `Result<(), magnus::Error>`.
/// ```
/// use magnus::{class, define_module, function, method, prelude::*, Error};
///
/// #[magnus::wrap(class = "Euclid::Point", free_immediately, size)]
/// struct Point {
/// x: isize,
/// y: isize,
/// }
///
/// impl Point {
/// fn new(x: isize, y: isize) -> Self {
/// Self { x, y }
/// }
///
/// fn x(&self) -> isize {
/// self.x
/// }
///
/// fn y(&self) -> isize {
/// self.y
/// }
/// }
///
/// #[magnus::init]
/// fn init() -> Result<(), Error> {
/// let module = define_module("Euclid")?;
/// let class = module.define_class("Point", class::object())?;
/// class.define_singleton_method("new", function!(Point::new, 2))?;
/// class.define_method("x", method!(Point::x, 0))?;
/// class.define_method("y", method!(Point::y, 0))?;
/// Ok(())
/// }
/// ```
/// Setting the name.
/// ```
/// #[magnus::init(name = "example")]
/// fn init() {
/// ()
/// }
/// ```
/// Allow a Rust type to be passed to Ruby, automatically wrapped as a Ruby
/// object.
///
/// For more control over the wrapped object, see [`TypedData`].
///
/// # Attributes
///
/// * `class = "..."` - required, sets the Ruby class to wrap the Rust type.
/// Supports module paths, e.g. `Foo::Bar::Baz`.
/// * `name = "..."` - debug name for the type, must be unique. Defaults to the
/// class name.
/// * `free_immediately` - Drop the Rust type as soon as the Ruby object has
/// been garbage collected. This is only safe to set if the type's [`Drop`]
/// implmentation does not call Ruby.
/// * `size` - Report the [`std::mem::size_of_val`] of the type to Ruby, used
/// to aid in deciding when to run the garbage collector.
/// * `unsafe_generics` - The derived implementation of [`TypedData`] is not
/// guaranteed to be correct for types with generics. If you are sure it is
/// for your type this attribute can be used to override the compile time
/// error usually generated for types with generics.
///
/// # Variant Attributes
///
/// The `#[magnus(...)]` attribute can be set on enum variants with the
/// following values:
///
/// * `class = "..."` - sets the Ruby class to wrap the variant. Supports
/// module paths, e.g. `Foo::Bar::Baz`.
///
/// # Examples
///
/// ```
/// #[magnus::wrap(class = "RbPoint", free_immediately, size)]
/// struct Point {
/// x: isize,
/// y: isize,
/// }
///
/// // the `Point` struct is automatically wrapped in a Ruby `RbPoint` object
/// // when returned to Ruby.
/// fn point(x: isize, y: isize) -> Point {
/// Point { x, y }
/// }
///
/// // Ruby `RbPoint` objects are automatically unwrapped to references to the
/// // `Point` structs they are wrapping when this function is called from Ruby.
/// fn distance(a: &Point, b: &Point) -> f64 {
/// (((b.x - a.x).pow(2) + (b.y - a.y).pow(2)) as f64).sqrt()
/// }
///
/// #[magnus::init]
/// fn init() {
/// magnus::define_global_function("point", magnus::function!(point, 2));
/// magnus::define_global_function("distance", magnus::function!(distance, 2));
/// }
/// ```
///
/// With subclasses for enum variants:
///
/// ```
/// use std::f64::consts::PI;
///
/// use magnus::{class, define_class, function, method, prelude::*};
///
/// #[magnus::wrap(class = "Shape")]
/// enum Shape {
/// #[magnus(class = "Circle")]
/// Circle { r: f64 },
/// #[magnus(class = "Rectangle")]
/// Rectangle { x: f64, y: f64 },
/// }
///
/// impl Shape {
/// fn area(&self) -> f64 {
/// match self {
/// Shape::Circle { r } => PI * r * r,
/// Shape::Rectangle { x, y } => x * y,
/// }
/// }
/// }
///
/// #[magnus::init]
/// fn init() -> Result<(), magnus::Error> {
/// let shape = define_class("Shape", class::object())?;
/// shape.define_method("area", method!(Shape::area, 0))?;
///
/// let circle = define_class("Circle", shape)?;
/// circle.define_singleton_method("new", function!(|r| Shape::Circle { r }, 1))?;
///
/// let rectangle = define_class("Rectangle", shape)?;
/// rectangle.define_singleton_method("new", function!(|x, y| Shape::Rectangle { x, y }, 2))?;
///
/// Ok(())
/// }
/// ```
/// Derives `DataTypeFunctions` with default implementations, for simple uses
/// of [`TypedData`].
///
/// For cases where no custom `DataTypeFunctions` are required a default
/// implementation can be derived. The [`macro@wrap`] macro may be a simpler
/// alternative in this use case.
/// Derives `TypedData`, allowing the type to be passed to Ruby automatically
/// wrapped as a Ruby object.
///
/// For simple cases, see [`macro@wrap`].
///
/// # Attributes
///
/// The `#[magnus(...)]` attribute can be set with the following values:
///
/// * `class = "..."` - required, sets the Ruby class to wrap the Rust type.
/// Supports module paths, e.g. `Foo::Bar::Baz`.
/// * `name = "..."` - debug name for the type, must be unique. Defaults to the
/// class name.
/// * `free_immediately` - Drop the Rust type as soon as the Ruby object has
/// been garbage collected. This is only safe to set if the type's [`Drop`]
/// and `DataTypeFunctions::free` implementations do not call Ruby.
/// * `mark` - Enable Ruby calling the `DataTypeFunctions::mark` function.
/// * `size` - Enable Ruby calling the `DataTypeFunctions::size` function.
/// * `compact` - Enable Ruby calling the `DataTypeFunctions::compact` function.
/// * `wb_protected` - Enable the `wb_protected` flag.
/// * `frozen_shareable` - Enable the `frozen_shareable` flag.
/// * `unsafe_generics` - The derived implementation of [`TypedData`] is not
/// guaranteed to be correct for types with generics. If you are sure it is
/// for your type this attribute can be used to override the compile time
/// error usually generated for types with generics.
///
/// # Field Attributes
///
/// The `#[magnus(...)]` attribute can be set on struct fields with the
/// following values:
///
/// * `opaque_attr_reader` - For a Ruby value wrapped in `Opaque`, creates a
/// accessor method that returns the unwrapped Ruby value.
///
/// # Variant Attributes
///
/// The `#[magnus(...)]` attribute can be set on enum variants with the
/// following values:
///
/// * `class = "..."` - sets the Ruby class to wrap the variant. Supports
/// module paths, e.g. `Foo::Bar::Baz`.
///
/// # Examples
///
/// ```
/// use magnus::{DataTypeFunctions, TypedData};
///
/// #[derive(DataTypeFunctions, TypedData)]
/// #[magnus(class = "RbPoint", size, free_immediately)]
/// struct Point {
/// x: isize,
/// y: isize,
/// }
///
/// // the `Point` struct is automatically wrapped in a Ruby `RbPoint` object
/// // when returned to Ruby.
/// fn point(x: isize, y: isize) -> Point {
/// Point { x, y }
/// }
///
/// // Ruby `RbPoint` objects are automatically unwrapped to references to the
/// // `Point` structs they are wrapping when this function is called from Ruby.
/// fn distance(a: &Point, b: &Point) -> f64 {
/// (((b.x - a.x).pow(2) + (b.y - a.y).pow(2)) as f64).sqrt()
/// }
///
/// #[magnus::init]
/// fn init() {
/// magnus::define_global_function("point", magnus::function!(point, 2));
/// magnus::define_global_function("distance", magnus::function!(distance, 2));
/// }
/// ```
///
/// With subclasses for enum variants:
///
/// ```
/// use magnus::{class, define_class};
///
/// #[magnus::wrap(class = "Shape")]
/// enum Shape {
/// #[magnus(class = "Circle")]
/// Circle { r: f64 },
/// #[magnus(class = "Rectangle")]
/// Rectangle { x: f64, y: f64 },
/// }
///
/// #[magnus::init]
/// fn init() -> Result<(), magnus::Error> {
/// let shape = define_class("Shape", class::object())?;
/// define_class("Circle", shape)?;
/// define_class("Rectangle", shape)?;
/// Ok(())
/// }
/// ```
///
/// Defining a custom `DataType` function:
///
/// ```
/// use std::mem::size_of_val;
///
/// use magnus::{DataTypeFunctions, TypedData};
///
/// #[derive(TypedData)]
/// #[magnus(class = "Name", size, free_immediately)]
/// struct Name {
/// first: String,
/// last: String,
/// }
///
/// impl DataTypeFunctions for Name {
/// fn size(&self) -> usize {
/// size_of_val(&self.first) + size_of_val(&self.last)
/// }
/// }
/// ```
///
/// A struct containing Ruby values.
///
/// ```
/// use magnus::{
/// class, define_class, function, gc, method, prelude::*, typed_data::Obj, value::Opaque,
/// DataTypeFunctions, TypedData,
/// };
///
/// # #[magnus::wrap(class = "Point", free_immediately, size)]
/// # struct Point {
/// # x: isize,
/// # y: isize,
/// # }
/// #
/// #[derive(TypedData)]
/// #[magnus(class = "Line", free_immediately, mark)]
/// struct Line {
/// #[magnus(opaque_attr_reader)]
/// start: Opaque<Obj<Point>>,
/// #[magnus(opaque_attr_reader)]
/// end: Opaque<Obj<Point>>,
/// }
///
/// impl Line {
/// fn new(start: Obj<Point>, end: Obj<Point>) -> Self {
/// Self {
/// start: start.into(),
/// end: end.into(),
/// }
/// }
///
/// fn length(&self) -> f64 {
/// let start = self.start();
/// let end = self.end();
///
/// (((end.x - start.x).pow(2) + (end.y - start.y).pow(2)) as f64).sqrt()
/// }
/// }
///
/// impl DataTypeFunctions for Line {
/// fn mark(&self, marker: &gc::Marker) {
/// marker.mark(self.start);
/// marker.mark(self.end);
/// }
/// }
///
/// #[magnus::init]
/// fn init() -> Result<(), magnus::Error> {
/// let line = define_class("Line", class::object())?;
/// line.define_singleton_method("new", function!(Line::new, 2))?;
/// line.define_method("length", method!(Line::length, 0))?;
/// Ok(())
/// }
/// ```