pilota-build 0.13.10

Compile thrift and protobuf idl into rust code at compile-time.
Documentation
use std::sync::Arc;

use faststr::FastStr;
use itertools::Itertools;

use crate::{Context, DefId, IdentName, db::RirDatabase, rir::NodeKind, symbol::Symbol};

pub trait PathResolver: Sync + Send {
    fn path_for_def_id(&self, cx: &Context, def_id: DefId) -> Arc<[Symbol]> {
        fn calc_node_path(cx: &Context, def_id: DefId, segs: &mut Vec<Symbol>) {
            let node = cx.node(def_id).unwrap();

            match node.kind {
                NodeKind::Item(_) => {}
                _ => calc_node_path(cx, node.parent.unwrap(), segs),
            }

            let name = match node.kind {
                NodeKind::Item(item) => match &*item {
                    crate::rir::Item::Mod(_) => return,
                    _ => cx.rust_name(def_id),
                },
                _ => cx.rust_name(def_id),
            };

            segs.push(name);
        }

        let mut segs = Vec::from(&*self.mod_prefix(cx, def_id));

        calc_node_path(cx, def_id, &mut segs);

        Arc::from(segs)
    }
    fn mod_prefix(&self, cx: &Context, def_id: DefId) -> Arc<[Symbol]>;

    fn related_path(&self, p1: &[Symbol], p2: &[Symbol]) -> FastStr;
}

pub struct DefaultPathResolver;

impl PathResolver for DefaultPathResolver {
    fn mod_prefix(&self, cx: &Context, def_id: DefId) -> Arc<[Symbol]> {
        fn calc_item_path(cx: &Context, def_id: DefId, segs: &mut Vec<Symbol>) {
            let node = cx.node(def_id).unwrap();
            match node.parent {
                Some(parent) => {
                    tracing::debug!("the parent of {:?} is {:?} ", def_id, parent);
                    calc_item_path(cx, parent, segs)
                }
                _ => {
                    let file = cx.file(node.file_id).unwrap();
                    let package = &file.package;
                    if package.len() != 1 || !package.first().unwrap().0.is_empty() {
                        segs.extend(package.iter().map(|s| (&*s.0).mod_ident().into()))
                    }
                }
            }

            if let NodeKind::Item(item) = node.kind {
                if let crate::rir::Item::Mod(_) = &*item {
                    segs.push(cx.rust_name(def_id));
                }
            }
        }

        let mut segs = Default::default();

        calc_item_path(cx, def_id, &mut segs);

        Arc::from(segs)
    }

    fn related_path(&self, p1: &[Symbol], p2: &[Symbol]) -> FastStr {
        if p1 == p2 {
            return p2.last().unwrap().clone().0;
        }
        let mut i = 0;
        while i < p1.len() && i < p2.len() && p1[i] == p2[i] {
            i += 1
        }
        let mut segs = vec![];

        #[derive(Debug)]
        enum Kind {
            Super,
            Ident(Symbol),
        }

        let path = (0..p1.len() - i)
            .map(|_| Kind::Super)
            .chain((i..p2.len()).map(|i| Kind::Ident(p2[i].clone())))
            .collect::<Vec<_>>();

        let _length = path.len();

        for k in path.into_iter() {
            segs.push(match k {
                Kind::Super => "super".into(),
                Kind::Ident(ident) => ident.to_string(),
            });
        }
        segs.into_iter().join("::").into()
    }
}

pub struct WorkspacePathResolver;

impl PathResolver for WorkspacePathResolver {
    fn mod_prefix(&self, cx: &Context, def_id: DefId) -> Arc<[Symbol]> {
        let mut item_def_id = def_id;
        while !matches!(cx.node(item_def_id).unwrap().kind, NodeKind::Item(_)) {
            item_def_id = cx.node(item_def_id).unwrap().parent.unwrap()
        }

        let info = cx.workspace_info();
        let prefix = match info.location_map.get(&item_def_id) {
            location @ Some(super::context::DefLocation::Fixed(_, prefix)) => {
                let mut path = Vec::with_capacity(prefix.len() + 1);
                path.push(cx.crate_name(location.unwrap()).into());
                path.extend(prefix.iter().cloned());
                path
            }
            Some(super::context::DefLocation::Dynamic) => {
                [cx.config.common_crate_name.clone().into()]
                    .iter()
                    .chain(DefaultPathResolver.mod_prefix(cx, def_id).iter())
                    .cloned()
                    .collect_vec()
            }
            None => {
                panic!(
                    "no location found for \"{}\" in file \"{}\"",
                    cx.rust_name(item_def_id),
                    cx.file(cx.node(item_def_id).unwrap().file_id)
                        .unwrap()
                        .package
                        .join("/")
                )
            }
        };

        Arc::from(prefix)
    }

    fn related_path(&self, p1: &[Symbol], p2: &[Symbol]) -> FastStr {
        if p2[0] == p1[0] {
            DefaultPathResolver.related_path(p1, p2)
        } else {
            format!("::{}", p2.iter().map(|s| s.to_string()).join("::")).into()
        }
    }
}