71 lines
2.1 KiB
Rust
71 lines
2.1 KiB
Rust
|
|
//! `Wiki` aggregate — per-wiki state, even when there's only one.
|
|||
|
|
//!
|
|||
|
|
//! v1.0 shipped with a single `Arc<RwLock<WorkspaceIndex>>` on `Backend`.
|
|||
|
|
//! Phase 11 lifts that into a `Wiki` aggregate so Phase 18 (multi-wiki)
|
|||
|
|
//! only changes config shape, not data flow. Phases 12–17 always operate
|
|||
|
|
//! on a `Wiki`; in practice the `wikis` `Vec` has one entry until 18.
|
|||
|
|
|
|||
|
|
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,
|
|||
|
|
pub config: WikiConfig,
|
|||
|
|
pub index: Arc<RwLock<WorkspaceIndex>>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Wiki {
|
|||
|
|
pub fn new(id: WikiId, config: WikiConfig) -> Self {
|
|||
|
|
let index = Arc::new(RwLock::new(WorkspaceIndex::new(Some(config.root.clone()))));
|
|||
|
|
Self { id, 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()
|
|||
|
|
}
|