carla 0.14.1

Rust client library for Carla simulator
Documentation
use super::ActorBlueprint;
use crate::error::ffi::with_ffi_error;
use autocxx::prelude::*;
use carla_sys::carla_rust::client::{FfiBlueprintLibrary, copy_actor_blueprint};
use cxx::{SharedPtr, let_cxx_string};
use derivative::Derivative;
use static_assertions::assert_impl_all;

/// Provides blueprints used to spawn actors, Corresponds to [`carla.BlueprintLibrary`](https://carla.readthedocs.io/en/0.9.14/python_api/#carla.BlueprintLibrary) in the Python API.
///
/// The blueprint library contains templates for all spawnable actors in CARLA,
/// including vehicles, pedestrians, sensors, and props. Use blueprints to create
/// actors with [`World::spawn_actor()`](crate::client::World::spawn_actor).
///
/// # Blueprint Categories
///
/// - **Vehicles**: `vehicle.*` (cars, trucks, motorcycles, bicycles)
/// - **Sensors**: `sensor.*` (cameras, LiDAR, GNSS, IMU, collision)
/// - **Walkers**: `walker.pedestrian.*`
/// - **Props**: `static.prop.*`
///
/// # Examples
///
/// ```no_run
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use carla::client::Client;
///
/// let client = Client::connect("localhost", 2000, None)?;
/// let mut world = client.world()?;
/// let library = world.blueprint_library()?;
///
/// // Find a specific vehicle by ID
/// if let Some(tesla_bp) = library.find("vehicle.tesla.model3")? {
///     println!("Found Tesla Model 3 blueprint");
/// }
///
/// // Filter vehicles by wildcard pattern
/// let all_vehicles = library.filter("vehicle.*")?;
/// println!("Available vehicles: {}", all_vehicles.len());
///
/// // Filter Tesla vehicles specifically
/// let tesla_vehicles = library.filter("vehicle.tesla.*")?;
/// for bp in tesla_vehicles.iter() {
///     println!("Tesla: {}", bp.id());
/// }
///
/// // Get blueprint by index
/// if let Some(first_bp) = library.get(0)? {
///     println!("First blueprint: {}", first_bp.id());
/// }
/// # Ok(())
/// # }
/// ```
#[derive(Clone, Derivative)]
#[derivative(Debug)]
#[repr(transparent)]
pub struct BlueprintLibrary {
    #[derivative(Debug = "ignore")]
    inner: SharedPtr<FfiBlueprintLibrary>,
}

impl BlueprintLibrary {
    pub(crate) fn from_cxx(ptr: SharedPtr<FfiBlueprintLibrary>) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(Self { inner: ptr })
        }
    }

    /// Filters blueprints by wildcard pattern.
    ///
    /// Returns a new library containing only blueprints matching the pattern.
    /// Use `*` as a wildcard to match multiple characters.
    ///
    /// # Arguments
    ///
    /// * `pattern` - Wildcard pattern (e.g., `"vehicle.*"`, `"vehicle.tesla.*"`)
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// use carla::client::Client;
    ///
    /// let client = Client::connect("localhost", 2000, None)?;
    /// let world = client.world()?;
    /// let library = world.blueprint_library()?;
    ///
    /// // Get all vehicles
    /// let vehicles = library.filter("vehicle.*")?;
    ///
    /// // Get all sensors
    /// let sensors = library.filter("sensor.*")?;
    ///
    /// // Get all Tesla vehicles
    /// let teslas = library.filter("vehicle.tesla.*")?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn filter(&self, pattern: &str) -> crate::Result<Self> {
        let_cxx_string!(pattern = pattern);
        let ptr = with_ffi_error("filter", |e| self.inner.filter(&pattern, e))?;
        Ok(unsafe { Self::from_cxx(ptr).unwrap_unchecked() })
    }

    /// Filters blueprints by attribute name and value.
    ///
    /// Returns a new library containing only blueprints where the specified
    /// attribute matches the given value.
    ///
    /// # Arguments
    ///
    /// * `name` - Attribute name to filter by (e.g., `"number_of_wheels"`)
    /// * `value` - Attribute value to match (e.g., `"4"`)
    #[cfg(any(carla_version_0915, carla_version_0916, carla_version_0100))]
    pub fn filter_by_attribute(&self, name: &str, value: &str) -> crate::Result<Self> {
        let_cxx_string!(name = name);
        let_cxx_string!(value = value);
        let ptr = with_ffi_error("filter_by_attribute", |e| {
            self.inner.filter_by_attribute(&name, &value, e)
        })?;
        Ok(unsafe { Self::from_cxx(ptr).unwrap_unchecked() })
    }

    /// Finds a blueprint by its exact ID.
    ///
    /// # Arguments
    ///
    /// * `key` - Exact blueprint ID (e.g., `"vehicle.tesla.model3"`)
    ///
    /// # Returns
    ///
    /// The blueprint if found, or `None` if the ID doesn't exist.
    pub fn find(&self, key: &str) -> crate::Result<Option<ActorBlueprint>> {
        let_cxx_string!(key = key);
        let ptr = with_ffi_error("find", |e| self.inner.find(&key, e))?;
        unsafe {
            let Some(actor_bp) = ptr.as_ref() else {
                return Ok(None);
            };
            let actor_bp = copy_actor_blueprint(actor_bp).within_unique_ptr();
            Ok(Some(ActorBlueprint::from_cxx(actor_bp).unwrap_unchecked()))
        }
    }

    /// Gets the blueprint at the given index.
    ///
    /// # Arguments
    ///
    /// * `index` - Index in the library (0-based)
    ///
    /// # Returns
    ///
    /// The blueprint at the index, or `None` if out of bounds.
    pub fn get(&self, index: usize) -> crate::Result<Option<ActorBlueprint>> {
        let ptr = with_ffi_error("at", |e| self.inner.at(index, e))?;
        unsafe {
            let Some(actor_bp) = ptr.as_ref() else {
                return Ok(None);
            };
            let actor_bp = copy_actor_blueprint(actor_bp).within_unique_ptr();
            Ok(Some(ActorBlueprint::from_cxx(actor_bp).unwrap_unchecked()))
        }
    }

    /// Returns an iterator over all blueprints in the library.
    pub fn iter(&self) -> impl Iterator<Item = ActorBlueprint> + '_ {
        // SAFETY: Index is bounds-checked by (0..self.len()), so get() always returns Ok(Some(..))
        (0..self.len()).map(|idx| unsafe { self.get(idx).unwrap_unchecked().unwrap_unchecked() })
    }

    /// Returns the number of blueprints in the library.
    pub fn len(&self) -> usize {
        self.inner.size()
    }

    /// Returns true if the library contains no blueprints.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.inner.is_empty()
    }
}

assert_impl_all!(BlueprintLibrary: Send, Sync);