use std::path::PathBuf;
use crate::index::parse::DocumentFormat;
use super::types::{IndexMode, IndexOptions};
#[derive(Debug, Clone)]
pub(crate) enum IndexSource {
Path(PathBuf),
Content {
data: String,
format: DocumentFormat,
},
Bytes {
data: Vec<u8>,
format: DocumentFormat,
},
}
impl IndexSource {
pub fn is_path(&self) -> bool {
matches!(self, IndexSource::Path(_))
}
pub fn is_content(&self) -> bool {
matches!(self, IndexSource::Content { .. })
}
pub fn is_bytes(&self) -> bool {
matches!(self, IndexSource::Bytes { .. })
}
}
#[derive(Debug, Clone)]
pub struct IndexContext {
pub(crate) sources: Vec<IndexSource>,
pub(crate) name: Option<String>,
pub(crate) options: IndexOptions,
}
impl IndexContext {
pub fn from_path(path: impl Into<PathBuf>) -> Self {
Self {
sources: vec![IndexSource::Path(path.into())],
name: None,
options: IndexOptions::default(),
}
}
pub fn from_paths(paths: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
Self {
sources: paths
.into_iter()
.map(|p| IndexSource::Path(p.into()))
.collect(),
name: None,
options: IndexOptions::default(),
}
}
pub fn from_dir(dir: impl Into<PathBuf>) -> Self {
let dir = dir.into();
let supported_extensions = ["md", "markdown", "pdf", "txt"];
let mut sources = Vec::new();
if let Ok(entries) = std::fs::read_dir(&dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if supported_extensions.contains(&ext.to_lowercase().as_str()) {
sources.push(IndexSource::Path(path));
}
}
}
}
Self {
sources,
name: None,
options: IndexOptions::default(),
}
}
pub fn from_content(content: impl Into<String>, format: DocumentFormat) -> Self {
Self {
sources: vec![IndexSource::Content {
data: content.into(),
format,
}],
name: None,
options: IndexOptions::default(),
}
}
pub fn from_bytes(bytes: Vec<u8>, format: DocumentFormat) -> Self {
Self {
sources: vec![IndexSource::Bytes {
data: bytes,
format,
}],
name: None,
options: IndexOptions::default(),
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_options(mut self, options: IndexOptions) -> Self {
self.options = options;
self
}
pub fn with_mode(mut self, mode: IndexMode) -> Self {
self.options.mode = mode;
self
}
pub fn len(&self) -> usize {
self.sources.len()
}
pub fn is_empty(&self) -> bool {
self.sources.is_empty()
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn options(&self) -> &IndexOptions {
&self.options
}
}
impl From<PathBuf> for IndexContext {
fn from(path: PathBuf) -> Self {
Self::from_path(path)
}
}
impl From<&std::path::Path> for IndexContext {
fn from(path: &std::path::Path) -> Self {
Self::from_path(path.to_path_buf())
}
}
impl From<&str> for IndexContext {
fn from(path: &str) -> Self {
Self::from_path(path)
}
}
impl From<String> for IndexContext {
fn from(path: String) -> Self {
Self::from_path(path)
}
}
impl std::fmt::Display for IndexSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IndexSource::Path(p) => write!(f, "path:{}", p.display()),
IndexSource::Content { format, .. } => write!(f, "content:{}", format.extension()),
IndexSource::Bytes { format, .. } => write!(f, "bytes:{}", format.extension()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_path() {
let ctx = IndexContext::from_path("./test.md");
assert_eq!(ctx.len(), 1);
assert!(ctx.name.is_none());
}
#[test]
fn test_from_paths() {
let ctx = IndexContext::from_paths(vec!["./a.md", "./b.pdf"]);
assert_eq!(ctx.len(), 2);
}
#[test]
fn test_from_content() {
let ctx = IndexContext::from_content("# Title", DocumentFormat::Markdown);
assert_eq!(ctx.len(), 1);
}
#[test]
fn test_from_bytes() {
let ctx = IndexContext::from_bytes(vec![1, 2, 3], DocumentFormat::Pdf);
assert_eq!(ctx.len(), 1);
}
#[test]
fn test_with_name() {
let ctx = IndexContext::from_path("./test.md").with_name("My Document");
assert_eq!(ctx.name(), Some("My Document"));
}
#[test]
fn test_with_mode() {
let ctx = IndexContext::from_path("./test.md").with_mode(IndexMode::Force);
assert_eq!(ctx.options.mode, IndexMode::Force);
}
#[test]
fn test_from_path_trait() {
let ctx = IndexContext::from(PathBuf::from("./test.md"));
assert_eq!(ctx.len(), 1);
}
}