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
//! This crate provides the ability to annotate structs with a `#[derive(Inspectable)]`,
//! which opens a web interface (by default on port 5676) where you can visually edit the values of your struct live.
//!
//! Your struct will then be available to you as a bevy resource.
//!
//! ## Example
//! ```rust
//! use bevy_contrib_inspector::Inspectable;
//!
//! #[derive(Inspectable, Default)]
//! struct Data {
//!     should_render: bool,
//!     text: String,
//!     #[inspectable(min = 42.0, max = 100.0)]
//!     size: f32,
//! }
//! ```
//! Add the [`InspectorPlugin`] to your App.
//! ```rust,no_run
//! use bevy_contrib_inspector::InspectorPlugin;
//! # use bevy::prelude::*;
//!
//! # #[derive(bevy_contrib_inspector::Inspectable, Default)] struct Data {}
//! fn main() {
//!     App::build()
//!         .add_default_plugins()
//!         .add_plugin(InspectorPlugin::<Data>::new())
//!         .add_system(your_system.system())
//!         // ...
//!         .run();
//! }
//!
//! fn your_system(data: Res<Data>, mut query: Query<...>) { /* */ }
//! ```
//! To automatically open the webbrowser when starting, run your program using `BEVY_INSPECTOR_OPEN=1 cargo run`.
//!
//! ## Attributes
//! When deriving the [`Inspectable`] trait, you can set options such like the port the server will run on like so:
//! ```rust
//! # struct O { a: u8, b: u8, c: u8 }
//! # #[derive(Default)] struct Type {}
//! # impl bevy_contrib_inspector::AsHtml for Type {
//! #     type Err = ();
//! #     type Options = O;
//! #     const DEFAULT_OPTIONS: Self::Options = O { a: 0, b: 0, c: 0 };
//! #     fn as_html(_: bevy_contrib_inspector::as_html::SharedOptions<Self>, _: Self::Options, _: &'static str) -> String { todo!() }
//! #     fn parse(_: &str) -> Result<Self, Self::Err> { todo!() }
//! # }
//! #
//! # use bevy_contrib_inspector::Inspectable;
//! #[derive(Inspectable, Default)]
//! #[inspectable(port = 1234)]
//! struct Data {
//!    #[inspectable(a = 1, b = 2, c = 3)]
//!    field: Type,
//! }
//! ```
//! The attribute on the struct will accept fields of the type [`InspectableOptions`],
//! while the attributes on the fields accept those of their [`<Type as AsHtml>::Options`](as_html::AsHtml).
mod html_impls;
mod inspector_server;
mod plugin;

/// derives [AsHtml](trait.AsHtml.html)
pub use bevy_contrib_inspector_derive::AsHtml;
/// derives [Inspectable](trait.Inspectable.html)
pub use bevy_contrib_inspector_derive::Inspectable;

pub use plugin::InspectorPlugin;

/// This trait describes how a struct should be rendered in HTML.
/// It is meant to be derived, see the [crate-level docs](index.html) for that.
pub trait Inspectable: Send + Sync + 'static {
    /// The HTML code which will be rendered from the webserver.
    fn html() -> String;
    /// When recieving a PUT request, its body will be parsed as `$field:$value`.
    /// The update function is supposed to parse the value into its correct type and set it on `self`.
    fn update(&mut self, field: &str, value: &str);
    /// Describes things like the webserver's port. Can be set with a `#[inspector(option = value)]` on the struct.
    fn options() -> InspectableOptions {
        InspectableOptions::default()
    }
}

/// The `InspectableOptions` control parameters like the webserver's port.
///
/// They can be set when deriving the trait using `#[inspector(option = value)], as described in the [Attributes](index.html#attributes) section.
pub struct InspectableOptions {
    pub port: u16,
}
impl Default for InspectableOptions {
    fn default() -> Self {
        InspectableOptions { port: 5676 }
    }
}

/// Attribute-Options for the [AsHtml] trait.
pub mod as_html {
    pub use crate::html_impls::*;

    pub struct SharedOptions<T> {
        pub label: std::borrow::Cow<'static, str>,
        pub default: T,
    }

    pub use crate::AsHtml;
}

/// controls how a type is rendered in HTML
///
/// It also specifies how the type is parsed from a string
/// and what attributes you can apply to it using `#[inspector(min = 1, max = 2)]`
pub trait AsHtml: Sized {
    /// The parse error type
    type Err;
    /// The attibutes you can set for a field
    type Options;
    /// Default options for the `Options`-type
    const DEFAULT_OPTIONS: Self::Options;

    /// HTML that needs to go to the top of the page, e.g. <script /> tags used in [`as_html`].
    fn header() -> &'static str {
        ""
    }
    /// HTML that needs to go to the bottom of the page, e.g. initializer js glue.
    fn footer() -> &'static str {
        ""
    }

    /// The actual html content.
    ///
    /// The `options`-parameter gives you access to the attributes passed when deriving [`Inspectable`].
    /// When there is new input, the submit function should be called with it,
    /// for example like this:
    ///
    /// `format!("<input oninput="{}('new data arrived')" />, submit_fn)`.
    fn as_html(
        shared: as_html::SharedOptions<Self>,
        options: Self::Options,
        submit_fn: &'static str,
    ) -> String;

    /// specifies how the type should be parsed
    fn parse(value: &str) -> Result<Self, Self::Err>;
}