Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 0 additions & 56 deletions crates/frilvault-core/src/cache/vault_context.rs

This file was deleted.

4 changes: 2 additions & 2 deletions crates/frilvault-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
pub mod cache;
pub mod constants;
pub mod error;
pub mod note;
pub mod parser;
pub mod runtime;
pub mod storage;
pub mod workspace;

pub use cache::*;
pub use error::*;
pub use note::*;
pub use parser::*;
pub use runtime::*;
pub use storage::*;
pub use workspace::*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};

use crate::NoteFile;

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct NoteCache {
files: HashMap<PathBuf, NoteFile>,
}
Expand Down
113 changes: 113 additions & 0 deletions crates/frilvault-core/src/runtime/vault_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::path::{Path, PathBuf};

use crate::{
FrilVaultResult, NoteCache, NoteFile, NoteFileRecord, WorkspaceIndex, WorkspaceIndexRepository,
YamlNoteRepository,
};

/// Runtime container for FrilVault.
///
/// VaultContext owns shared runtime resources:
///
/// - repositories
/// - caches
/// - indexes
///
/// Services should use VaultContext instead of
/// accessing repositories directly.
#[derive(Clone)]
pub struct VaultContext {
pub note_repository: YamlNoteRepository,
pub workspace_index_repository: WorkspaceIndexRepository,
pub note_cache: NoteCache,
}

impl VaultContext {
pub fn new(
note_repository: YamlNoteRepository,
workspace_index_repository: WorkspaceIndexRepository,
) -> Self {
Self {
note_repository,
workspace_index_repository,
note_cache: NoteCache::default(),
}
}

pub fn load_notes(&mut self, source_file: &Path) -> FrilVaultResult<NoteFile> {
// 1. CACHE HIT
if let Some(cached) = self.note_cache.get(source_file) {
return Ok(cached.clone());
}

// 2. REPOSITORY LOAD
let note_file = self.note_repository.load_by_source_file(source_file)?;

// 3. CACHE STORE
self.note_cache
.insert(source_file.to_path_buf(), note_file.clone());

Ok(note_file)
}

pub fn invalidate_notes(&mut self, source_file: &Path) {
self.note_cache.invalidate(source_file);
}

pub fn rebuild_index(&self) -> FrilVaultResult<WorkspaceIndex> {
self.workspace_index_repository.rebuild()
}

pub fn contains_cached_notes(&self, source_file: &Path) -> bool {
self.note_cache.contains(source_file)
}

pub fn list_all_note_files(&self) -> FrilVaultResult<Vec<NoteFileRecord>> {
self.note_repository.list_all_note_files()
}

pub fn scan_workspace_files(&self) -> FrilVaultResult<Vec<String>> {
let mut files = Vec::new();

self.collect_workspace_files(self.workspace_index_repository.workspace_root(), &mut files)?;

Ok(files)
}

pub fn resolve_note_path(&self, source_file: &str) -> PathBuf {
self.note_repository.resolve_note_path(source_file)
}

fn collect_workspace_files(
&self,
directory: &Path,
files: &mut Vec<String>,
) -> FrilVaultResult<()> {
for entry in std::fs::read_dir(directory)? {
let entry = entry?;
let file_type = entry.file_type()?;
let path = entry.path();

if file_type.is_symlink() {
continue;
}

if file_type.is_dir() {
if path.file_name().and_then(|n| n.to_str()) == Some(".vault") {
continue;
}

self.collect_workspace_files(&path, files)?;
continue;
}

let relative = path
.strip_prefix(self.workspace_index_repository.workspace_root())
.map_err(|_| crate::FrilVaultError::SourcePathOutsideWorkspace)?;

files.push(relative.to_string_lossy().to_string());
}

Ok(())
}
}
5 changes: 5 additions & 0 deletions crates/frilvault-core/src/tests/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ pub fn create_test_workspace_service(workspace_root: &Path) -> WorkspaceService

WorkspaceService::new(vault_context, repository)
}

pub fn create_test_index_repository(workspace_root: &Path) -> WorkspaceIndexRepository {
let resolver = PathResolver::new(workspace_root);
WorkspaceIndexRepository::new(resolver)
}
3 changes: 3 additions & 0 deletions crates/frilvault-core/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ mod workspace_index_repository_test;

#[cfg(test)]
mod vault_context_test;

#[cfg(test)]
mod repair_engin_test;
102 changes: 102 additions & 0 deletions crates/frilvault-core/src/tests/repair_engin_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::{fs, path::Path};
use uuid::Uuid;

use crate::{
AddNoteInput, FileMove, LineAnchor, NoteAnchor, NoteService, PathResolver, VaultContext,
WorkspaceIndexRepository, YamlNoteRepository, repair_engine::RepairEngine,
};

#[test]
fn repair_engine_moves_note_files() {
let workspace_root = std::env::temp_dir().join(format!("frilvault-test-{}", Uuid::new_v4()));

fs::create_dir_all(&workspace_root).unwrap();

let resolver = PathResolver::new(&workspace_root);

let note_repository = YamlNoteRepository::new(resolver.clone());

let index_repository = WorkspaceIndexRepository::new(resolver.clone());

let vault_context = VaultContext::new(note_repository, index_repository);

let mut service = NoteService::new(vault_context.clone());

// 1. create note for original file
service
.add_note(AddNoteInput {
source_file: "src/main.rs".into(),
anchor: NoteAnchor::Line(LineAnchor { line: 1, column: 1 }),
content: "test note".to_string(),
})
.unwrap();

// 2. simulate repair move
let moves = vec![FileMove {
from: "src/main.rs".to_string(),
to: "src/main_renamed.rs".to_string(),
confidence: 1.0,
}];

let mut engine = RepairEngine { vault_context };

let repaired = engine.apply_moves(moves).unwrap();

assert_eq!(repaired, 1);

let old_path = resolver.note_path_for_source_file("src/main.rs");

let new_path = resolver.note_path_for_source_file("src/main_renamed.rs");

assert!(!old_path.exists());
assert!(new_path.exists());

fs::remove_dir_all(workspace_root).unwrap();
}

#[test]
fn repair_engine_invalidates_cache_correctly() {
let workspace_root =
std::env::temp_dir().join(format!("frilvault-test-{}", uuid::Uuid::new_v4()));

fs::create_dir_all(&workspace_root).unwrap();

let resolver = PathResolver::new(&workspace_root);

let note_repository = YamlNoteRepository::new(resolver.clone());

let index_repository = WorkspaceIndexRepository::new(resolver.clone());

let mut vault_context = VaultContext::new(note_repository, index_repository);

// preload cache
let _ = vault_context.load_notes("src/main.rs".as_ref());

assert!(vault_context.note_cache.contains(Path::new("src/main.rs")));

let mut engine = RepairEngine { vault_context };

let moves = vec![FileMove {
from: "src/main.rs".to_string(),
to: "src/main_renamed.rs".to_string(),
confidence: 1.0,
}];

let _ = engine.apply_moves(moves).unwrap();

assert!(
!engine
.vault_context
.note_cache
.contains(Path::new("src/main.rs"))
);

assert!(
engine
.vault_context
.note_cache
.contains(Path::new("src/main_renamed.rs"))
);

fs::remove_dir_all(workspace_root).unwrap();
}
Loading