ad-astra 1.0.0

Embeddable scripting language platform Ad Astra. Main Crate.
Documentation
////////////////////////////////////////////////////////////////////////////////
// This file is part of "Ad Astra", an embeddable scripting programming       //
// language platform.                                                         //
//                                                                            //
// This work is proprietary software with source-available code.              //
//                                                                            //
// To copy, use, distribute, or contribute to this work, you must agree to    //
// the terms of the General License Agreement:                                //
//                                                                            //
// https://github.com/Eliah-Lakhin/ad-astra/blob/master/EULA.md               //
//                                                                            //
// The agreement grants a Basic Commercial License, allowing you to use       //
// this work in non-commercial and limited commercial products with a total   //
// gross revenue cap. To remove this commercial limit for one of your         //
// products, you must acquire a Full Commercial License.                      //
//                                                                            //
// If you contribute to the source code, documentation, or related materials, //
// you must grant me an exclusive license to these contributions.             //
// Contributions are governed by the "Contributions" section of the General   //
// License Agreement.                                                         //
//                                                                            //
// Copying the work in parts is strictly forbidden, except as permitted       //
// under the General License Agreement.                                       //
//                                                                            //
// If you do not or cannot agree to the terms of this Agreement,              //
// do not use this work.                                                      //
//                                                                            //
// This work is provided "as is", without any warranties, express or implied, //
// except where such disclaimers are legally invalid.                         //
//                                                                            //
// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
// All rights reserved.                                                       //
////////////////////////////////////////////////////////////////////////////////

use crate::{
    analysis::symbols::{FnSymbol, LiteralSymbol, ModuleSymbol, StructSymbol},
    exports::Struct,
    runtime::{ComponentHint, PackageMeta, ScriptType, TypeFamily, TypeHint, TypeMeta},
    semantics::Tag,
};

/// An extended metadata about Rust and Script semantics.
///
/// The meaning of this object depends on the context of its creation. For
/// example, if the Description came from the
/// [CompletionItem](crate::analysis::CompletionItem), the object describes
/// completion candidate metadata. If the Description came from the
/// [IdentSymbol::ty](crate::analysis::symbols::IdentSymbol::ty), the
/// description focuses on the type of the script's identifiers.
///
/// In this description object, the Ad Astra analyzer attempts to collect the
/// most detailed and useful metadata for the end user that the analyzer can
/// heuristically infer about the analyzed entity.
///
/// Usually, you don't need to create this object manually. It is generated by
/// the API of this crate.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Description {
    /// A type description of the analyzed entity.
    ///
    /// If the analyzer is unable to infer the type, the value is
    /// [TypeHint::dynamic].
    pub type_hint: TypeHint,

    /// If the analyzed entity is explicitly defined in the source code of the
    /// script (e.g., a script function or script struct), this field provides
    /// access to the script construction metadata. Otherwise, `impl_symbol` is
    /// [ModuleSymbol::Nil].
    pub impl_symbol: ModuleSymbol,

    /// Rust documentation related to the analyzed entity.
    ///
    /// In contrast to the `type_hint`'s [TypeHint::doc], this documentation
    /// pertains directly to the analyzed item and is usually more informative
    /// for the end user. For example, if the analyzer is examining a Rust
    /// struct field, the `doc` field could provide Rustdoc documentation
    /// specific to that field, which differs from the `type_hint` documentation
    /// that contains only the Rustdoc documentation of the field's type.
    pub doc: Option<&'static str>,
}

impl Description {
    #[inline(always)]
    pub(super) fn dynamic() -> Self {
        let type_hint = TypeHint::dynamic();
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Nil,
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn nil() -> Self {
        let type_hint = TypeHint::nil();
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Nil,
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn number_family(symbol: LiteralSymbol) -> Self {
        let type_hint = TypeHint::Family(TypeFamily::number());
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Literal(symbol),
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn bool_type(symbol: LiteralSymbol) -> Self {
        let type_hint = TypeHint::Type(<bool>::type_meta());
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Literal(symbol),
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn string_family(symbol: LiteralSymbol) -> Self {
        let type_hint = TypeHint::Family(<str>::type_meta().family());
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Literal(symbol),
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn fn_family(symbol: FnSymbol) -> Self {
        let type_hint = TypeHint::Family(TypeFamily::fn_family());
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Fn(symbol),
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn fn_type(arity: usize, symbol: FnSymbol) -> Self {
        let Some(ty) = TypeMeta::script_fn(arity) else {
            return Self::fn_family(symbol);
        };

        let type_hint = TypeHint::Type(ty);
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Fn(symbol),
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn struct_family(symbol: StructSymbol) -> Self {
        let type_hint = TypeHint::Family(<Struct>::type_meta().family());
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Struct(symbol),
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn from_tag(tag: Tag) -> Self {
        let type_hint = tag.type_hint();
        let doc = type_hint.doc();
        let impl_symbol = ModuleSymbol::from(tag);

        Self {
            type_hint,
            impl_symbol,
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn from_component(component: &ComponentHint) -> Self {
        let type_hint = component.ty;
        let doc = component.doc.or(component.ty.doc());

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Nil,
            doc,
        }
    }

    #[inline(always)]
    pub(super) fn from_package(package: &'static PackageMeta) -> Self {
        let type_hint = TypeHint::Type(package.ty());
        let doc = type_hint.doc();

        Self {
            type_hint,
            impl_symbol: ModuleSymbol::Nil,
            doc,
        }
    }
}