feat: initial nuwiki Rust workspace (core, lsp, ls)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
//! `Wiki` aggregate — per-wiki state, even when there's only one.
|
||||
//!
|
||||
//! The single `Arc<RwLock<WorkspaceIndex>>` on `Backend` is lifted into a
|
||||
//! `Wiki` aggregate so multi-wiki support only changes config shape, not
|
||||
//! data flow. Handlers always operate on a `Wiki`; in practice the `wikis`
|
||||
//! `Vec` has one entry until multi-wiki support lands.
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use tower_lsp::lsp_types::Url;
|
||||
|
||||
use crate::config::WikiConfig;
|
||||
use crate::index::WorkspaceIndex;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
|
||||
pub struct WikiId(pub u32);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Wiki {
|
||||
pub id: WikiId,
|
||||
// `Arc` so cloning a `Wiki` (done on every handler entry that picks a
|
||||
// wiki out of the list) is a refcount bump, not a deep copy of the whole
|
||||
// config — color_dic, templates, header strings, and so on.
|
||||
pub config: Arc<WikiConfig>,
|
||||
pub index: Arc<RwLock<WorkspaceIndex>>,
|
||||
}
|
||||
|
||||
impl Wiki {
|
||||
pub fn new(id: WikiId, config: WikiConfig) -> Self {
|
||||
let index = WorkspaceIndex::new(Some(config.root.clone()))
|
||||
.with_diary_rel_path(Some(config.diary_rel_path.clone()))
|
||||
.with_file_extension(Some(config.file_extension.clone()));
|
||||
let index = Arc::new(RwLock::new(index));
|
||||
Self {
|
||||
id,
|
||||
config: Arc::new(config),
|
||||
index,
|
||||
}
|
||||
}
|
||||
|
||||
/// True when `uri` resolves to a file under this wiki's root.
|
||||
pub fn contains(&self, uri: &Url) -> bool {
|
||||
let Ok(path) = uri.to_file_path() else {
|
||||
return false;
|
||||
};
|
||||
path.starts_with(&self.config.root)
|
||||
}
|
||||
|
||||
/// Length of the wiki's root path (component count) — used to break
|
||||
/// ties in nested-root layouts.
|
||||
pub fn root_depth(&self) -> usize {
|
||||
self.config.root.components().count()
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &Path {
|
||||
&self.config.root
|
||||
}
|
||||
}
|
||||
|
||||
/// Pick the wiki whose root is the longest prefix of `uri`. Returns the
|
||||
/// wiki id, not a guard, so callers can release the read lock before
|
||||
/// awaiting.
|
||||
pub fn resolve_uri_to_wiki(wikis: &[Wiki], uri: &Url) -> Option<WikiId> {
|
||||
wikis
|
||||
.iter()
|
||||
.filter(|w| w.contains(uri))
|
||||
.max_by_key(|w| w.root_depth())
|
||||
.map(|w| w.id)
|
||||
}
|
||||
|
||||
/// Build the initial `Vec<Wiki>` from a list of `WikiConfig`s. Always
|
||||
/// assigns ids by position so `WikiId(0)` is the default wiki.
|
||||
pub fn build_wikis(configs: &[WikiConfig]) -> Vec<Wiki> {
|
||||
configs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, cfg)| Wiki::new(WikiId(i as u32), cfg.clone()))
|
||||
.collect()
|
||||
}
|
||||
Reference in New Issue
Block a user