thread-services 0.1.0

Service layer interfaces for Thread
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
// SPDX-FileCopyrightText: 2025 Knitli Inc. <knitli@knit.li>
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
// SPDX-License-Identifier: AGPL-3.0-or-later
#![allow(dead_code)]
//! # Service Layer Types - Abstraction Glue for Thread
//!
//! This module provides language-agnostic types that abstract over ast-grep functionality
//! while preserving all its powerful capabilities. The service layer acts as glue between
//! file-level ast-grep operations and codebase-level graph intelligence.
//!
//! ## Core Philosophy
//!
//! - **Preserve Power**: All ast-grep capabilities (Matcher, Replacer, Position) remain accessible
//! - **Bridge Levels**: Connect file-level AST operations to codebase-level relational intelligence
//! - **Enable Execution**: Abstract over different execution environments (rayon, cloud workers)
//! - **Commercial Ready**: Clear boundaries for commercial extensions
//!
//! ## Key Types
//!
//! - [`ParsedDocument`] - Wraps ast-grep Root while enabling cross-file intelligence
//! - [`CodeMatch`] - Extends NodeMatch with codebase-level context
//! - [`ExecutionScope`] - Defines execution boundaries (file, module, codebase)
//! - [`AnalysisContext`] - Carries execution and analysis context across service boundaries

use std::any::Any;
use std::path::PathBuf;
use thread_utilities::RapidMap;

// Conditionally import thread dependencies when available
#[cfg(feature = "ast-grep-backend")]
use thread_ast_engine::{Node, NodeMatch, Position, Root};

#[cfg(feature = "ast-grep-backend")]
pub use thread_ast_engine::source::Doc;

#[cfg(feature = "ast-grep-backend")]
use thread_ast_engine::pinned::PinnedNodeData;

#[cfg(feature = "ast-grep-backend")]
pub type PinnedNodeResult<D> = PinnedNodeData<D, Node<'static, D>>;

#[cfg(not(feature = "ast-grep-backend"))]
pub type PinnedNodeResult<D> = PinnedNodeData<D>;

/// Re-export key ast-grep types when available
#[cfg(feature = "ast-grep-backend")]
pub use thread_ast_engine::{
    Node as AstNode, NodeMatch as AstNodeMatch, Position as AstPosition, Root as AstRoot,
};

#[cfg(feature = "ast-grep-backend")]
pub use thread_language::{SupportLang, SupportLangErr};

#[cfg(not(feature = "ast-grep-backend"))]
pub trait Doc = Clone + 'static;

#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone)]
pub struct Root<D>(pub std::marker::PhantomData<D>);

#[cfg(not(feature = "ast-grep-backend"))]
impl<D: Doc> Root<D> {
    pub fn root<'a>(&'a self) -> Node<'a, D> {
        Node(std::marker::PhantomData)
    }

    pub fn generate(&self) -> String {
        String::new()
    }
}

#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone)]
pub struct Node<'a, D>(pub std::marker::PhantomData<&'a D>);

#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone)]
pub struct NodeMatch<'a, D>(pub std::marker::PhantomData<&'a D>);

#[cfg(not(feature = "ast-grep-backend"))]
impl<'a, D> std::ops::Deref for NodeMatch<'a, D> {
    type Target = Node<'a, D>;
    fn deref(&self) -> &Self::Target {
        unsafe { &*(self as *const Self as *const Node<'a, D>) }
    }
}

#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Position {
    pub row: usize,
    pub column: usize,
    pub index: usize,
}

#[cfg(not(feature = "ast-grep-backend"))]
impl Position {
    pub fn new(row: usize, column: usize, index: usize) -> Self {
        Self { row, column, index }
    }
}

#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone)]
pub struct PinnedNodeData<D>(pub std::marker::PhantomData<D>);

#[cfg(not(feature = "ast-grep-backend"))]
impl<D: Doc> PinnedNodeData<D> {
    pub fn new<F, T>(_root: &Root<D>, _f: F) -> Self
    where
        F: FnOnce(&Root<D>) -> T,
    {
        Self(std::marker::PhantomData)
    }
}

#[cfg(not(feature = "ast-grep-backend"))]
pub trait MatcherExt {}

#[cfg(not(feature = "ast-grep-backend"))]
impl<T> MatcherExt for T {}

// SupportLang enum stub when not using ast-grep-backend
#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SupportLang {
    Bash,
    C,
    Cpp,
    CSharp,
    Css,
    Go,
    Elixir,
    Haskell,
    Html,
    Java,
    JavaScript,
    Kotlin,
    Lua,
    Nix,
    Php,
    Python,
    Ruby,
    Rust,
    Scala,
    Swift,
    TypeScript,
    Tsx,
    Yaml,
}

#[cfg(not(feature = "ast-grep-backend"))]
impl SupportLang {
    pub fn from_path(_path: &std::path::Path) -> Option<Self> {
        // Simple stub implementation
        Some(Self::Rust)
    }
}

#[cfg(not(feature = "ast-grep-backend"))]
#[derive(Debug, Clone)]
pub struct SupportLangErr(pub String);

#[cfg(not(feature = "ast-grep-backend"))]
impl std::fmt::Display for SupportLangErr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[cfg(not(feature = "ast-grep-backend"))]
impl std::error::Error for SupportLangErr {}

/// A parsed document that wraps ast-grep Root with additional codebase-level metadata.
///
/// This type preserves all ast-grep functionality while adding context needed for
/// cross-file analysis and graph intelligence. It acts as the bridge between
/// file-level AST operations and codebase-level relational analysis.
#[derive(Debug)]
pub struct ParsedDocument<D: Doc> {
    /// The underlying ast-grep Root - preserves all ast-grep functionality
    pub ast_root: Root<D>,

    /// Source file path for this document
    pub file_path: PathBuf,

    /// Language of this document
    pub language: SupportLang,

    /// Content fingerprint for deduplication and change detection (blake3 hash)
    pub content_fingerprint: recoco_utils::fingerprint::Fingerprint,

    /// Codebase-level metadata (symbols, imports, exports, etc.)
    pub metadata: DocumentMetadata,

    /// Internal storage for ast-engine types (type-erased for abstraction)
    pub(crate) internal: Box<dyn Any + Send + Sync>,
}

impl<D: Doc> ParsedDocument<D> {
    /// Create a new ParsedDocument wrapping an ast-grep Root
    pub fn new(
        ast_root: Root<D>,
        file_path: PathBuf,
        language: SupportLang,
        content_fingerprint: recoco_utils::fingerprint::Fingerprint,
    ) -> Self {
        Self {
            ast_root,
            file_path,
            language,
            content_fingerprint,
            metadata: DocumentMetadata::default(),
            internal: Box::new(()),
        }
    }

    /// Get the root node - preserves ast-grep API
    pub fn root(&self) -> Node<'_, D> {
        self.ast_root.root()
    }

    /// Get the underlying ast-grep Root for full access to capabilities
    pub fn ast_grep_root(&self) -> &Root<D> {
        &self.ast_root
    }

    /// Get mutable access to ast-grep Root for replacements
    pub fn ast_grep_root_mut(&mut self) -> &mut Root<D> {
        &mut self.ast_root
    }

    /// Create a pinned version for cross-thread/FFI usage
    pub fn pin_for_threading(&self) -> PinnedNodeResult<D> {
        #[cfg(feature = "ast-grep-backend")]
        return PinnedNodeData::new(self.ast_root.clone(), |r| r.root());

        #[cfg(not(feature = "ast-grep-backend"))]
        return PinnedNodeData::new(&self.ast_root, |_| ());
    }

    /// Generate the source code (preserves ast-grep replacement functionality)
    pub fn generate(&self) -> String {
        #[cfg(feature = "ast-grep-backend")]
        {
            use thread_ast_engine::source::Content;
            let root_node = self.root();
            let doc = root_node.get_doc();
            let range = root_node.range();
            let bytes = doc.get_source().get_range(range);
            D::Source::encode_bytes(bytes).into_owned()
        }
        #[cfg(not(feature = "ast-grep-backend"))]
        self.ast_root.generate()
    }

    /// Get document metadata for codebase-level analysis
    pub fn metadata(&self) -> &DocumentMetadata {
        &self.metadata
    }

    /// Get mutable document metadata
    pub fn metadata_mut(&mut self) -> &mut DocumentMetadata {
        &mut self.metadata
    }
}

/// A pattern match that extends ast-grep NodeMatch with codebase-level context.
///
/// Preserves all NodeMatch functionality while adding cross-file relationship
/// information needed for graph intelligence.
#[derive(Debug)]
pub struct CodeMatch<'tree, D: Doc> {
    /// The underlying ast-grep NodeMatch - preserves all matching functionality
    pub node_match: NodeMatch<'tree, D>,

    /// Additional context for codebase-level analysis
    pub context: MatchContext,

    /// Cross-file relationships (calls, imports, inheritance, etc.)
    pub relationships: Vec<CrossFileRelationship>,
}

impl<'tree, D: Doc> CodeMatch<'tree, D> {
    /// Create a new CodeMatch wrapping an ast-grep NodeMatch
    pub fn new(node_match: NodeMatch<'tree, D>) -> Self {
        Self {
            node_match,
            context: MatchContext::default(),
            relationships: Vec::new(),
        }
    }

    /// Get the underlying NodeMatch for full ast-grep access
    pub fn ast_node_match(&self) -> &NodeMatch<'tree, D> {
        &self.node_match
    }

    /// Get the matched node (delegate to NodeMatch)
    pub fn node(&self) -> &Node<'tree, D> {
        &self.node_match
    }

    #[cfg(any(feature = "ast-grep-backend", feature = "matching"))]
    /// Get captured meta-variables (delegate to NodeMatch)
    pub fn get_env(&self) -> &thread_ast_engine::MetaVarEnv<'tree, D> {
        self.node_match.get_env()
    }

    /// Add cross-file relationship information
    pub fn add_relationship(&mut self, relationship: CrossFileRelationship) {
        self.relationships.push(relationship);
    }

    /// Get all cross-file relationships
    pub fn relationships(&self) -> &[CrossFileRelationship] {
        &self.relationships
    }
}

/// Metadata about a parsed document for codebase-level analysis
#[derive(Debug, Default, Clone)]
pub struct DocumentMetadata {
    /// Symbols defined in this document (functions, classes, variables)
    pub defined_symbols: RapidMap<String, SymbolInfo>,

    /// Symbols imported from other files
    pub imported_symbols: RapidMap<String, ImportInfo>,

    /// Symbols exported by this file
    pub exported_symbols: RapidMap<String, ExportInfo>,

    /// Function calls made in this document
    pub function_calls: Vec<CallInfo>,

    /// Type definitions and usages
    pub type_info: Vec<TypeInfo>,

    /// Language-specific metadata
    pub language_metadata: RapidMap<String, String>,
}

/// Information about a symbol definition
#[derive(Debug, Clone)]
pub struct SymbolInfo {
    pub name: String,
    pub kind: SymbolKind,
    pub position: Position,
    pub scope: String,
    pub visibility: Visibility,
}

/// Information about an import
#[derive(Debug, Clone)]
pub struct ImportInfo {
    pub symbol_name: String,
    pub source_path: String,
    pub import_kind: ImportKind,
    pub position: Position,
}

/// Information about an export
#[derive(Debug, Clone)]
pub struct ExportInfo {
    pub symbol_name: String,
    pub export_kind: ExportKind,
    pub position: Position,
}

/// Information about a function call
#[derive(Debug, Clone)]
pub struct CallInfo {
    pub function_name: String,
    pub position: Position,
    pub arguments_count: usize,
    pub is_resolved: bool,
    pub target_file: Option<PathBuf>,
}

/// Information about type usage
#[derive(Debug, Clone)]
pub struct TypeInfo {
    pub type_name: String,
    pub position: Position,
    pub kind: TypeKind,
    pub generic_params: Vec<String>,
}

/// Cross-file relationships for graph intelligence
#[derive(Debug, Clone)]
pub struct CrossFileRelationship {
    pub kind: RelationshipKind,
    pub source_file: PathBuf,
    pub target_file: PathBuf,
    pub source_symbol: String,
    pub target_symbol: String,
    pub relationship_data: RapidMap<String, String>,
}

/// Context for pattern matches
#[derive(Debug, Default, Clone)]
pub struct MatchContext {
    pub execution_scope: ExecutionScope,
    pub analysis_depth: AnalysisDepth,
    pub context_data: RapidMap<String, String>,
}

/// Execution scope for analysis operations
#[derive(Debug, Clone, Default)]
pub enum ExecutionScope {
    /// Single file analysis
    #[default]
    File,
    /// Module or directory level
    Module(PathBuf),
    /// Entire codebase
    Codebase,
    /// Custom scope with specific files
    Custom(Vec<PathBuf>),
}

/// Depth of analysis to perform
#[derive(Debug, Clone, Default)]
pub enum AnalysisDepth {
    /// Syntax-only analysis
    Syntax,
    /// Include local dependencies
    #[default]
    Local,
    /// Include external dependencies
    Deep,
    /// Complete codebase analysis
    Complete,
}

/// Execution context that carries state across service boundaries
#[derive(Debug, Clone)]
pub struct AnalysisContext {
    /// Scope of the current analysis
    pub scope: ExecutionScope,

    /// Depth of analysis
    pub depth: AnalysisDepth,

    /// Base directory for relative path resolution
    pub base_directory: PathBuf,

    /// Include patterns for file filtering
    pub include_patterns: Vec<String>,

    /// Exclude patterns for file filtering
    pub exclude_patterns: Vec<String>,

    /// Maximum number of files to process
    pub max_files: Option<usize>,

    /// Parallel execution configuration
    pub execution_config: ExecutionConfig,

    /// Custom context data
    pub context_data: RapidMap<String, String>,
}

impl Default for AnalysisContext {
    fn default() -> Self {
        Self {
            scope: ExecutionScope::File,
            depth: AnalysisDepth::Local,
            base_directory: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
            include_patterns: vec!["**/*".to_string()],
            exclude_patterns: vec!["**/node_modules/**".to_string(), "**/target/**".to_string()],
            max_files: None,
            execution_config: ExecutionConfig::default(),
            context_data: thread_utilities::get_map(),
        }
    }
}

/// Configuration for execution environments
#[derive(Debug, Clone)]
pub struct ExecutionConfig {
    /// Parallel execution strategy
    pub strategy: ExecutionStrategy,

    /// Maximum number of concurrent operations
    pub max_concurrency: Option<usize>,

    /// Chunk size for batched operations
    pub chunk_size: Option<usize>,

    /// Timeout for individual operations
    pub operation_timeout: Option<std::time::Duration>,
}

impl Default for ExecutionConfig {
    fn default() -> Self {
        Self {
            strategy: ExecutionStrategy::Auto,
            max_concurrency: None,
            chunk_size: None,
            operation_timeout: None,
        }
    }
}

/// Execution strategy for different environments
#[derive(Debug, Clone, Default)]
pub enum ExecutionStrategy {
    /// Choose strategy automatically based on environment
    #[default]
    Auto,
    /// Single-threaded execution
    Sequential,
    /// Rayon-based parallel execution (for CLI)
    Rayon,
    /// Chunked execution for cloud workers
    Chunked,
    /// Custom execution strategy
    Custom(String),
}

// Enums for categorizing symbols and relationships

#[derive(Debug, Clone, PartialEq)]
pub enum SymbolKind {
    Function,
    Class,
    Interface,
    Variable,
    Constant,
    Type,
    Module,
    Namespace,
    Enum,
    Field,
    Property,
    Method,
    Constructor,
    Other(String),
}

#[derive(Debug, Clone, PartialEq)]
pub enum Visibility {
    Public,
    Private,
    Protected,
    Internal,
    Package,
    Other(String),
}

#[derive(Debug, Clone, PartialEq)]
pub enum ImportKind {
    Named,
    Default,
    Namespace,
    SideEffect,
    Dynamic,
    Other(String),
}

#[derive(Debug, Clone, PartialEq)]
pub enum ExportKind {
    Named,
    Default,
    Namespace,
    Reexport,
    Other(String),
}

#[derive(Debug, Clone, PartialEq)]
pub enum TypeKind {
    Primitive,
    Struct,
    Class,
    Interface,
    Union,
    Enum,
    Generic,
    Function,
    Array,
    Other(String),
}

#[derive(Debug, Clone, PartialEq)]
pub enum RelationshipKind {
    /// Function calls another function
    Calls,
    /// Module imports from another module
    Imports,
    /// Class inherits from another class
    Inherits,
    /// Interface implements another interface
    Implements,
    /// Type uses another type
    Uses,
    /// Module depends on another module
    DependsOn,
    /// Symbol references another symbol
    References,
    /// Custom relationship type
    Custom(String),
}

/// Range representing a span of text in source code
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Range {
    pub start: Position,
    pub end: Position,
}

impl Range {
    pub fn new(start: Position, end: Position) -> Self {
        Self { start, end }
    }

    /// Create a range from ast-grep positions
    pub fn from_ast_positions(start: Position, end: Position) -> Self {
        Self { start, end }
    }

    /// Check if this range contains a position
    pub fn contains(&self, pos: Position) -> bool {
        pos >= self.start && pos <= self.end
    }

    /// Check if this range overlaps with another range
    pub fn overlaps(&self, other: &Range) -> bool {
        self.start <= other.end && other.start <= self.end
    }
}