initial project scaffold

Rust workspace with nomina-core (rename engine) and nomina-app (Tauri v2 shell).
React/TypeScript frontend with tabbed rule panels, virtual-scrolled file list,
and Zustand state management. All 9 rule types implemented with 25 passing tests.
This commit is contained in:
2026-03-13 23:49:29 +02:00
commit 9dca2bedfa
69 changed files with 17462 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
pub mod rules;
pub mod pipeline;
pub mod filter;
pub mod metadata;
pub mod preset;
pub mod undo;
pub mod scanner;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum NominaError {
#[error("Invalid regex pattern: {pattern} - {reason}")]
InvalidRegex { pattern: String, reason: String },
#[error("File not found: {path}")]
FileNotFound { path: PathBuf },
#[error("Rename conflict: {count} files would produce the name '{name}'")]
NamingConflict { name: String, count: usize },
#[error("Invalid filename '{name}': {reason}")]
InvalidFilename { name: String, reason: String },
#[error("Filesystem error on '{path}': {source}")]
Filesystem {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("Preset parse error: {reason}")]
PresetError { reason: String },
#[error("BRU import error at line {line}: {reason}")]
BruImportError { line: usize, reason: String },
#[error("EXIF read error for '{path}': {reason}")]
ExifError { path: PathBuf, reason: String },
}
pub type Result<T> = std::result::Result<T, NominaError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RenameContext {
pub index: usize,
pub total: usize,
pub original_name: String,
pub extension: String,
pub path: PathBuf,
pub size: u64,
pub created: Option<DateTime<Utc>>,
pub modified: Option<DateTime<Utc>>,
pub date_taken: Option<DateTime<Utc>>,
pub parent_folder: String,
}
impl RenameContext {
pub fn dummy(index: usize) -> Self {
Self {
index,
total: 1,
original_name: String::new(),
extension: String::new(),
path: PathBuf::new(),
size: 0,
created: None,
modified: None,
date_taken: None,
parent_folder: String::new(),
}
}
}
pub trait RenameRule: Send + Sync {
fn apply(&self, filename: &str, context: &RenameContext) -> String;
fn display_name(&self) -> &str;
fn rule_type(&self) -> &str;
fn is_enabled(&self) -> bool;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileEntry {
pub path: PathBuf,
pub name: String,
pub stem: String,
pub extension: String,
pub size: u64,
pub is_dir: bool,
pub is_hidden: bool,
pub created: Option<DateTime<Utc>>,
pub modified: Option<DateTime<Utc>>,
pub accessed: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreviewResult {
pub original_path: PathBuf,
pub original_name: String,
pub new_name: String,
pub has_conflict: bool,
pub has_error: bool,
pub error_message: Option<String>,
}