gritpack-searchlib 0.1.1

Compiler-facing resolver and grammar integration surface for Gritpack
Documentation
#![allow(dead_code)]

//! Compiler-facing resolver and grammar integration surface for Gritpack.
//!
//! This crate is the handoff boundary for compiler, language-server, and other
//! language-tooling integrations. It intentionally exposes the narrow read-side
//! resolver API, the build-side grammar publication helpers, and the shared
//! grammar model types.
//!
//! # Who This Is For
//!
//! `gritpack-searchlib` is for compiler and toolchain authors who need to work
//! against a compiled Gritpack project snapshot.
//!
//! The primary workflow is:
//!
//! 1. define grammar metadata in [`grammar`]
//! 2. publish that metadata into an existing project snapshot with [`build`]
//! 3. reopen the snapshot through [`resolver`]
//! 4. start from authoritative participating files
//! 5. use grammar-aware narrowing only as an optimization layer
//!
//! # Public Modules
//!
//! - [`build`] publishes grammar specifications and external-driver payloads
//!   into a project snapshot
//! - [`grammar`] defines the build-side grammar model and query-time capability
//!   contract
//! - [`resolver`] exposes the read-only resolver API over compiled snapshot state
//!
//! # Correctness Model
//!
//! The key rule for consumers is:
//!
//! - participating files are the correctness baseline
//! - grammar-aware queries are narrowing optimizations over that baseline
//!
//! If a narrowing answer is non-authoritative, callers should fall back to the
//! broader participating-file set before making semantic decisions.
//!
//! # Quick Start
//!
//! ```no_run
//! use gritpack_searchlib::grammar::GrammarId;
//! use gritpack_searchlib::resolver::{
//!     LoadedProjectResolver, ModuleQuery, PackageScope, ParticipatingFileQuery,
//!     QuerySelection, ReferenceQuery,
//! };
//!
//! # async fn demo() -> Result<(), gritpack_searchlib::CliError> {
//! let resolver = LoadedProjectResolver::open(std::path::Path::new("/workspace/project")).await?;
//!
//! let participating = resolver.participating_files(&ParticipatingFileQuery {
//!     package_scope: PackageScope::AnyDependency,
//! });
//! assert!(participating.authoritative);
//!
//! let grammar = GrammarId {
//!     dialect: "lumen/1.0.0".to_string(),
//!     name: "modules".to_string(),
//!     version: 1,
//! };
//!
//! match resolver.files_for_query(
//!     &grammar,
//!     &ReferenceQuery::Modules(vec![ModuleQuery {
//!         package_scope: PackageScope::AnyDependency,
//!         name: "demo::thing".to_string(),
//!     }]),
//! )? {
//!     Some(QuerySelection::Files(selection)) if selection.authoritative => {
//!         for path in selection.file_paths {
//!             println!("candidate={path}");
//!         }
//!     }
//!     _ => {}
//! }
//! # Ok(())
//! # }
//! ```

use std::path::{Component, Path, PathBuf};

use sha2::{Digest, Sha256};

pub mod build;
mod environment;
pub mod grammar;
mod layout;
mod models;
pub mod resolver;
mod runtime;
mod state;
mod support;

pub use models::CliError;

fn sanitize_relative_package_path(path: &Path) -> Result<PathBuf, CliError> {
	let mut result = PathBuf::new();
	for component in path.components() {
		match component {
			Component::Normal(part) => result.push(part),
			Component::CurDir => {}
			_ => {
				return Err(CliError::Message(format!(
					"refusing to use unsafe package path: {}",
					path.display()
				)))
			}
		}
	}
	Ok(result)
}

fn sanitize_archive_path(path: &Path) -> Result<PathBuf, CliError> {
	let mut result = PathBuf::new();
	for component in path.components() {
		match component {
			Component::Normal(part) => result.push(part),
			Component::CurDir => {}
			_ => {
				return Err(CliError::Message(format!(
					"refusing to unpack unsafe archive path: {}",
					path.display()
				)))
			}
		}
	}
	Ok(result)
}

fn sha256_hex(bytes: &[u8]) -> String {
	let mut hasher = Sha256::new();
	hasher.update(bytes);
	format!("{:x}", hasher.finalize())
}