#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::Write;
use facet_core::{Def, Field, Shape, StructKind, Type, UserType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathStep {
Field(u32),
Index(u32),
Variant(u32),
MapKey,
MapValue,
OptionSome,
Deref,
}
#[derive(Debug, Clone, Default)]
pub struct Path {
steps: Vec<PathStep>,
}
impl Path {
pub const fn new() -> Self {
Self { steps: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
steps: Vec::with_capacity(capacity),
}
}
pub fn push(&mut self, step: PathStep) {
self.steps.push(step);
}
pub fn pop(&mut self) -> Option<PathStep> {
self.steps.pop()
}
pub fn steps(&self) -> &[PathStep] {
&self.steps
}
pub fn len(&self) -> usize {
self.steps.len()
}
pub fn is_empty(&self) -> bool {
self.steps.is_empty()
}
pub fn format_with_shape(&self, shape: &'static Shape) -> String {
let mut result = String::new();
let mut current_shape = shape;
for step in &self.steps {
match step {
PathStep::Field(idx) => {
let idx = *idx as usize;
if let Some(field_name) = get_field_name(current_shape, idx) {
if !result.is_empty() {
result.push('.');
}
result.push_str(field_name);
if let Some(field_shape) = get_field_shape(current_shape, idx) {
current_shape = field_shape;
}
}
}
PathStep::Index(idx) => {
write!(result, "[{}]", idx).unwrap();
if let Some(elem_shape) = get_element_shape(current_shape) {
current_shape = elem_shape;
}
}
PathStep::Variant(idx) => {
let idx = *idx as usize;
if let Some(variant_name) = get_variant_name(current_shape, idx) {
result.push_str("::");
result.push_str(variant_name);
if let Some(variant_shape) = get_variant_shape(current_shape, idx) {
current_shape = variant_shape;
}
}
}
PathStep::MapKey => {
result.push_str("[key]");
if let Some(key_shape) = get_map_key_shape(current_shape) {
current_shape = key_shape;
}
}
PathStep::MapValue => {
result.push_str("[value]");
if let Some(value_shape) = get_map_value_shape(current_shape) {
current_shape = value_shape;
}
}
PathStep::OptionSome => {
if let Some(inner_shape) = get_option_inner_shape(current_shape) {
current_shape = inner_shape;
}
}
PathStep::Deref => {
if let Some(inner_shape) = get_pointer_inner_shape(current_shape) {
current_shape = inner_shape;
}
}
}
}
if result.is_empty() {
result.push_str("<root>");
}
result
}
pub fn resolve_leaf_field(&self, shape: &'static Shape) -> Option<&'static Field> {
if self.steps.is_empty() {
return None;
}
let mut current_shape = shape;
let mut current_variant_idx: Option<usize> = None;
for step in &self.steps[..self.steps.len() - 1] {
match step {
PathStep::Field(idx) => {
let idx = *idx as usize;
current_shape =
get_field_shape_with_variant(current_shape, idx, current_variant_idx)?;
current_variant_idx = None;
}
PathStep::Index(_) => {
current_shape = get_element_shape(current_shape)?;
current_variant_idx = None;
}
PathStep::Variant(idx) => {
current_variant_idx = Some(*idx as usize);
}
PathStep::MapKey => {
current_shape = get_map_key_shape(current_shape)?;
current_variant_idx = None;
}
PathStep::MapValue => {
current_shape = get_map_value_shape(current_shape)?;
current_variant_idx = None;
}
PathStep::OptionSome => {
current_shape = get_option_inner_shape(current_shape)?;
current_variant_idx = None;
}
PathStep::Deref => {
current_shape = get_pointer_inner_shape(current_shape)?;
current_variant_idx = None;
}
}
}
if let Some(PathStep::Field(idx)) = self.steps.last() {
let idx = *idx as usize;
return get_field_with_variant(current_shape, idx, current_variant_idx);
}
None
}
}
fn get_field_with_variant(
shape: &Shape,
idx: usize,
variant_idx: Option<usize>,
) -> Option<&'static Field> {
match shape.ty {
Type::User(UserType::Struct(sd)) => sd.fields.get(idx),
Type::User(UserType::Enum(ed)) => {
let variant_idx = variant_idx?;
let variant = ed.variants.get(variant_idx)?;
variant.data.fields.get(idx)
}
_ => None,
}
}
fn get_field_shape_with_variant(
shape: &Shape,
idx: usize,
variant_idx: Option<usize>,
) -> Option<&'static Shape> {
get_field_with_variant(shape, idx, variant_idx).map(|f| f.shape())
}
fn get_field_name(shape: &Shape, idx: usize) -> Option<&'static str> {
match shape.ty {
Type::User(UserType::Struct(sd)) => sd.fields.get(idx).map(|f| f.name),
Type::User(UserType::Enum(_)) => {
None
}
_ => None,
}
}
fn get_field_shape(shape: &Shape, idx: usize) -> Option<&'static Shape> {
match shape.ty {
Type::User(UserType::Struct(sd)) => sd.fields.get(idx).map(|f| f.shape()),
_ => None,
}
}
fn get_element_shape(shape: &Shape) -> Option<&'static Shape> {
match shape.def {
Def::List(ld) => Some(ld.t()),
Def::Array(ad) => Some(ad.t()),
Def::Slice(sd) => Some(sd.t()),
_ => None,
}
}
fn get_variant_name(shape: &Shape, idx: usize) -> Option<&'static str> {
match shape.ty {
Type::User(UserType::Enum(ed)) => ed.variants.get(idx).map(|v| v.name),
_ => None,
}
}
fn get_variant_shape(shape: &Shape, idx: usize) -> Option<&'static Shape> {
match shape.ty {
Type::User(UserType::Enum(ed)) => {
let variant = ed.variants.get(idx)?;
if variant.data.kind == StructKind::Unit {
None
} else {
variant.data.fields.first().map(|f| f.shape())
}
}
_ => None,
}
}
fn get_map_key_shape(shape: &Shape) -> Option<&'static Shape> {
match shape.def {
Def::Map(md) => Some(md.k()),
_ => None,
}
}
fn get_map_value_shape(shape: &Shape) -> Option<&'static Shape> {
match shape.def {
Def::Map(md) => Some(md.v()),
_ => None,
}
}
fn get_option_inner_shape(shape: &Shape) -> Option<&'static Shape> {
match shape.def {
Def::Option(od) => Some(od.t()),
_ => None,
}
}
fn get_pointer_inner_shape(shape: &Shape) -> Option<&'static Shape> {
match shape.def {
Def::Pointer(pd) => pd.pointee(),
_ => None,
}
}
#[cfg(feature = "pretty")]
pub mod pretty;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_step_size() {
assert_eq!(core::mem::size_of::<PathStep>(), 8);
}
}