bevy-remote-devtools-plugin 0.2.0

A toolset that allows you to debug / view any bevy application with a tauri based UI. This crate is only the plugin part.
Documentation
use std::convert::Infallible;

use bevy::{
    asset::HandleId,
    prelude::*,
    render::mesh::{Indices, Mesh, VertexAttributeValues},
};
use rweb::{
    reject::{custom, Reject},
    *,
};
use serde::Serialize;

use crate::{
    serialization::StringHandleId,
    sync::{execute_in_world, ExecutionChannel},
};

#[derive(Serialize, Debug)]
struct AssetOverview {
    name: String,
    id: StringHandleId,
    ty: AssetType,
}

#[derive(Serialize, Debug)]
enum AssetType {
    Mesh,
}

impl openapi::Entity for AssetOverview {
    fn type_name() -> rt::Cow<'static, str> {
        "AssetOverview".into()
    }
    fn describe(_comp_d: &mut openapi::ComponentDescriptor) -> openapi::ComponentOrInlineSchema {
        openapi::ComponentOrInlineSchema::Component {
            name: "AssetOverview".into(),
        }
    }
}

#[allow(dead_code)]
fn get_asset_name(server: &AssetServer, handle: HandleId) -> String {
    server
        .get_handle_path(handle)
        .map(|path| {
            path.label()
                .map(|p| p.to_string())
                .or_else(|| path.path().to_str().map(|p| p.to_string()))
        })
        .flatten()
        .unwrap_or_else(|| "Unknown".to_string())
}

#[get("/v1/assets")]
#[cors(origins("*"))]
pub(crate) async fn assets() -> Result<Json<Vec<AssetOverview>>, Infallible> {
    let assets = execute_in_world(ExecutionChannel::FrameEnd, |world| {
        let mut assets = Vec::new();
        if let Some(server) = world.get_resource::<AssetServer>() {
            if let Some(mesh_assets) = world.get_resource::<Assets<Mesh>>() {
                for (id, _) in mesh_assets.iter() {
                    assets.push(AssetOverview {
                        name: get_asset_name(server, id),
                        ty: AssetType::Mesh,
                        id: id.into(),
                    });
                }
            }
        }
        assets
    })
    .await;

    Ok(assets.into())
}

#[derive(Serialize, Schema)]
struct MeshAsset {
    vertices: Vec<[f32; 3]>,
    indices: Vec<u32>,
}

#[post("/v1/assets/mesh")]
#[cors(origins("*"))]
pub(crate) async fn get_asset_mesh(
    #[json] id: StringHandleId,
) -> Result<Json<MeshAsset>, rweb::Rejection> {
    let id: HandleId = id.into();
    let mesh = execute_in_world(ExecutionChannel::FrameEnd, move |world| {
        let meshes = world.get_resource::<Assets<Mesh>>();
        meshes.map(|meshes| meshes.get(id).cloned()).flatten()
    })
    .await;

    if let Some(mesh) = mesh {
        let vertices = if let Some(VertexAttributeValues::Float32x3(values)) =
            mesh.attribute(Mesh::ATTRIBUTE_POSITION)
        {
            values.clone()
        } else {
            return Err(custom(AssetMeshErrors::UnsupportedFormat));
        };

        let mut indices = Vec::new();
        match mesh.indices() {
            Some(Indices::U16(raw)) => indices.extend(raw.iter().map(|i| *i as u32)),
            Some(Indices::U32(raw)) => indices.extend(raw),
            _ => {
                return Err(custom(AssetMeshErrors::UnsupportedFormat));
            }
        }

        return Ok(MeshAsset { vertices, indices }.into());
    }

    Err(custom(AssetMeshErrors::NotFound))
}

#[allow(dead_code)]
#[derive(Debug)]
enum AssetMeshErrors {
    NotFound,
    UnsupportedFormat,
}
impl Reject for AssetMeshErrors {}