class Rufio::NativeScannerRubyCore
非同期スキャナークラス(Pure Ruby実装、ポーリングベース)
def cancel
def cancel @mutex.synchronize do @state = :cancelled if @state == :scanning end end
def close
def close @thread&.join if @thread&.alive? @thread = nil end
def file_type(stat)
def file_type(stat) if stat.directory? 'directory' elsif stat.symlink? 'symlink' elsif stat.file? 'file' else 'other' end end
def get_progress
def get_progress @mutex.synchronize do { current: @current_progress, total: @total_progress } end end
def get_results
def get_results @mutex.synchronize { @results.dup } end
def get_state
def get_state @mutex.synchronize { @state } end
def initialize
def initialize @thread = nil @state = :idle @results = [] @error = nil @current_progress = 0 @total_progress = 0 @mutex = Mutex.new end
def scan_async(path)
def scan_async(path) raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path) raise StandardError, "Scanner is already running" unless @state == :idle @mutex.synchronize do @state = :scanning @results = [] @error = nil @current_progress = 0 @total_progress = 0 end @thread = Thread.new do begin # ディレクトリをスキャン entries = [] Dir.foreach(path) do |entry| next if entry == '.' || entry == '..' # キャンセルチェック break if @state == :cancelled full_path = File.join(path, entry) stat = File.lstat(full_path) entries << { name: entry, type: file_type(stat), size: stat.size, mtime: stat.mtime.to_i, mode: stat.mode, executable: stat.executable?, hidden: entry.start_with?('.') } # 進捗を更新 @mutex.synchronize do @current_progress += 1 @total_progress = entries.length + 1 end end # 結果を保存 @mutex.synchronize do if @state == :cancelled @state = :cancelled else @results = entries @state = :done end end rescue StandardError => e @mutex.synchronize do @error = e @state = :failed end end end self end
def scan_fast_async(path, max_entries)
def scan_fast_async(path, max_entries) raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path) raise StandardError, "Scanner is already running" unless @state == :idle @mutex.synchronize do @state = :scanning @results = [] @error = nil @current_progress = 0 @total_progress = max_entries end @thread = Thread.new do begin entries = [] count = 0 Dir.foreach(path) do |entry| next if entry == '.' || entry == '..' break if count >= max_entries # キャンセルチェック break if @state == :cancelled full_path = File.join(path, entry) stat = File.lstat(full_path) entries << { name: entry, type: file_type(stat), size: stat.size, mtime: stat.mtime.to_i, mode: stat.mode, executable: stat.executable?, hidden: entry.start_with?('.') } count += 1 # 進捗を更新 @mutex.synchronize do @current_progress = count end end # 結果を保存 @mutex.synchronize do if @state == :cancelled @state = :cancelled else @results = entries @state = :done end end rescue StandardError => e @mutex.synchronize do @error = e @state = :failed end end end self end
def wait(timeout: nil)
def wait(timeout: nil) start_time = Time.now loop do state = get_state case state when :done return get_results when :failed raise @error || StandardError.new("Scan failed") when :cancelled raise StandardError, "Scan cancelled" end if timeout && (Time.now - start_time) > timeout raise StandardError, "Timeout" end sleep POLL_INTERVAL end end
def wait_with_progress(&block)
def wait_with_progress(&block) loop do state = get_state progress = get_progress yield(progress[:current], progress[:total]) if block_given? case state when :done return get_results when :failed raise @error || StandardError.new("Scan failed") when :cancelled raise StandardError, "Scan cancelled" end sleep POLL_INTERVAL end end