windjammer 0.48.0

A simple language inspired by Go, Ruby, and Elixir that transpiles to Rust - 80% of Rust's power with 20% of the complexity
Documentation
// Simple .gitignore parser for wjfind

use std::fs
use std::path
use std::path::Path
use std::collections::HashSet

@derive(Clone)
pub struct GitignoreRules {
    patterns: Vec<string>,
}

impl GitignoreRules {
    pub fn new() -> Self {
        GitignoreRules {
            patterns: vec![],
        }
    }
    
    pub fn load_from_directory(dir: string) -> Result<Self, string> {
        let gitignore_path = path::join(Path::new(&dir), ".gitignore")
        
        if !fs::exists(&gitignore_path) {
            return Ok(GitignoreRules::new())
        }
        
        let contents = fs::read_to_string(&gitignore_path)?
        let mut pattern_list = vec![]
        
        for line in contents.lines() {
            let trimmed = line.trim()
            
            // Skip empty lines and comments
            if trimmed.is_empty() || trimmed.starts_with("#") {
                continue
            }
            
            pattern_list.push(trimmed.to_string())
        }
        
        Ok(GitignoreRules {
            patterns: pattern_list,
        })
    }
    
    pub fn is_ignored(&self, path: &string) -> bool {
        // Get the file/dir name from the path
        let name = path::file_name(Path::new(path))
            .unwrap_or(path.as_ref())
            .to_string()
        
        for pattern in self.patterns {
            if self.matches_pattern(&name, pattern) 
                || self.matches_pattern(path, pattern) {
                return true
            }
        }
        
        false
    }
    
    fn matches_pattern(&self, name: &string, pattern: &string) -> bool {
        // Simple matching (not full gitignore spec, but good enough)
        
        // Exact match
        if name == pattern {
            return true
        }
        
        // Directory match (ends with /)
        if pattern.ends_with("/") {
            let dir_pattern = pattern.trim_end_matches("/")
            if name == dir_pattern {
                return true
            }
        }
        
        // Wildcard match (*)
        if pattern.contains("*") {
            return self.wildcard_match(name, pattern)
        }
        
        // Extension match (*.ext)
        if pattern.starts_with("*.") {
            let ext = pattern.trim_start_matches("*.")
            if name.ends_with(&format!(".{}", ext)) {
                return true
            }
        }
        
        // Path contains pattern
        if name.contains(pattern) {
            return true
        }
        
        false
    }
    
    fn wildcard_match(&self, name: &string, pattern: &string) -> bool {
        // Simple wildcard matching (* matches any characters)
        let parts: Vec<&str> = pattern.split('*').collect()
        
        if parts.is_empty() {
            return false
        }
        
        // Check first part
        if !parts[0].is_empty() && !name.starts_with(parts[0]) {
            return false
        }
        
        // Check last part
        if parts.len() > 1 {
            let last = &parts[parts.len() - 1]
            if !last.is_empty() && !name.ends_with(last) {
                return false
            }
        }
        
        // Check middle parts (simplified - just check they exist in order)
        let mut pos = 0
        for (i, part) in parts.iter().enumerate() {
            if part.is_empty() {
                continue
            }
            
            if i == 0 {
                pos = part.len()
                continue
            }
            
            if let Some(idx) = name[pos..].find(part) {
                pos += idx + part.len()
            } else {
                return false
            }
        }
        
        true
    }
}

// Global cache of gitignore rules per directory
pub struct GitignoreCache {
    cache: std::collections::HashMap<string, GitignoreRules>,
}

impl GitignoreCache {
    pub fn new() -> Self {
        GitignoreCache {
            cache: std::collections::HashMap::new(),
        }
    }
    
    pub fn get_rules(&mut self, dir: &string) -> GitignoreRules {
        if let Some(rules) = self.cache.get(dir) {
            return rules.clone()
        }
        
        let rules = GitignoreRules::load_from_directory(dir.to_string())
            .unwrap_or_else(|_| GitignoreRules::new())
        
        self.cache.insert(dir.to_string(), rules.clone())
        rules
    }
}