use crate::server::{ServerState, utils};
use anyhow::Context;
use lsp_types::{Location, Uri};
use std::{
fs,
path::{Path, PathBuf},
};
#[derive(Debug, Clone)]
pub(crate) struct ProjectContext<'a> {
state: &'a ServerState,
root: PathBuf,
achitekfile: PathBuf,
achitekfile_uri: Option<Uri>,
}
impl<'a> ProjectContext<'a> {
pub(crate) fn for_uri(state: &'a ServerState, uri: &Uri) -> Option<Self> {
let path = utils::file_path_from_uri(uri)?;
Self::for_path(state, &path)
.or_else(|| {
is_achitekfile_path(&path)
.then(|| Self::from_achitekfile_uri(state, path.clone(), uri.clone()))?
})
.or_else(|| {
utils::is_tera_path(&path).then(|| Self::for_template_path(state, &path))?
})
}
pub(crate) fn for_template_path(state: &'a ServerState, template_path: &Path) -> Option<Self> {
Self::for_path(state, template_path).or_else(|| {
let achitekfile = utils::find_achitekfile_for_template(template_path)?;
Self::from_achitekfile_path(state, achitekfile)
})
}
pub(crate) fn root(&self) -> &Path {
&self.root
}
pub(crate) fn achitekfile_path(&self) -> &Path {
&self.achitekfile
}
pub(crate) fn achitekfile_uri(&self) -> anyhow::Result<Uri> {
if let Some(uri) = &self.achitekfile_uri {
Ok(uri.clone())
} else {
utils::path_to_uri(&self.achitekfile)
}
}
pub(crate) fn achitekfile_source(&self) -> anyhow::Result<String> {
let uri = self.achitekfile_uri()?;
self.source_for(&uri, &self.achitekfile, "Achitekfile")
}
pub(crate) fn template_source(&self, uri: &Uri, path: &Path) -> anyhow::Result<String> {
self.source_for(uri, path, "template")
}
pub(crate) fn scan_template_references(
&self,
prompt_name: &str,
) -> anyhow::Result<Vec<Location>> {
utils::scan_references(&self.root, prompt_name)
}
fn for_path(state: &'a ServerState, path: &Path) -> Option<Self> {
let project = state.workspace.project_for_path(path)?;
Some(Self {
state,
root: project.root().to_path_buf(),
achitekfile: project.achitekfile().to_path_buf(),
achitekfile_uri: None,
})
}
fn from_achitekfile_path(state: &'a ServerState, achitekfile: PathBuf) -> Option<Self> {
let root = achitekfile.parent()?.to_path_buf();
Some(Self {
state,
root,
achitekfile,
achitekfile_uri: None,
})
}
fn from_achitekfile_uri(
state: &'a ServerState,
achitekfile: PathBuf,
uri: Uri,
) -> Option<Self> {
let root = achitekfile.parent()?.to_path_buf();
Some(Self {
state,
root,
achitekfile,
achitekfile_uri: Some(uri),
})
}
fn source_for(&self, uri: &Uri, path: &Path, label: &str) -> anyhow::Result<String> {
self.state
.documents
.get(uri.as_str())
.map(|document| Ok(document.text.clone()))
.unwrap_or_else(|| {
fs::read_to_string(path)
.with_context(|| format!("failed to read {label} `{}`", path.display()))
})
}
}
fn is_achitekfile_path(path: &Path) -> bool {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| name.eq_ignore_ascii_case("achitekfile"))
}