aimx/
api.rs

1//! Aim: v1 high-level API 
2//!
3//! This module defines the public-facing abstraction for AIMX. It is intentionally
4//! incomplete and will be wired up to existing workspace, workflow, AIM, and inference
5//! modules incrementally.
6
7use anyhow::{Result, anyhow};
8use std::{
9    path::{Path, PathBuf}, sync::Arc, time::Duration,
10};
11use crate::{
12    Cell, Reference, Sheet, aim::{LockManager, Row, Rule, WorkflowLike, Workspace}, reference::parse_identifier, values::{Instance, Node, Value}, info_library::{FunctionInfoRegistry, FunctionCard}
13};
14
15pub struct Aim {}
16
17impl Aim {
18    // -----------------------------------------------------------------------------
19    // Workspace File
20    // -----------------------------------------------------------------------------
21
22    /// Creates a new AIMX workspace at the specified path with default structure.
23    /// 
24    /// Initializes the foundational directory architecture and system workflow nodes
25    /// required for agentic workflow management. Creates both `.aim` and `.jnl` files
26    /// for persistent storage with MVCC versioning.
27    #[doc = include_str!("../docs/api/create.md")]
28    pub fn create(path: Arc<PathBuf>) -> Result<Sheet> {
29        Workspace::create(path)
30    }
31
32    /// Opens an existing AIMX workspace at the specified path.
33    #[doc = include_str!("../docs/api/open.md")]
34    pub fn open(path: &Path) -> Result<Sheet> {
35        Workspace::open(path)
36    }
37
38    /// Saves all pending changes to the current workspace.
39    #[doc = include_str!("../docs/api/save.md")]
40    pub fn save() -> Result<()> {
41        Workspace::save()
42    }
43
44    /// Saves all workflows within the workspace hierarchy.
45    #[doc = include_str!("../docs/api/save_all.md")]
46    pub fn save_all() -> Result<()> {
47        Workspace::save_all()
48    }
49
50    /// Prunes open read-only nodes from memory to free resources and optimize memory usage.
51    #[doc = include_str!("../docs/api/compact.md")]
52    pub fn compact() -> Result<()> {
53        Workspace::compact()
54    }
55
56    // -----------------------------------------------------------------------------
57    // Workspace Tree
58    // -----------------------------------------------------------------------------
59
60    /// Returns the global singleton reference to the workspace root.
61    #[doc = include_str!("../docs/api/root.md")]
62    pub fn root() -> Arc<Reference> {
63        Reference::workspace()
64    }
65
66    /// Retrieves a catalog of child node references for the specified workflow locator.
67    #[doc = include_str!("../docs/api/catalog.md")]
68    pub fn catalog(locator: Arc<Reference>) -> Result<Vec<Arc<Sheet>>> {
69        if &*locator =="_" {
70            Workspace::catalog()
71        } else {
72            let node = Workspace::locate_node(&locator)?;
73            let workflow = node.get_workflow()?;
74            let mut list = Vec::new();
75            for rule in workflow.iter_rules() {
76                // Construct a sheet to each node
77                 if let Some(node) = rule.get_node() {
78                    let sheet = Sheet::convert(&node)?;
79                    list.push(Arc::new(sheet));
80                }
81            }
82            Ok(list)
83        }
84    }
85
86    /// Adds a new workflow node to the workspace root with the specified inference configuration.
87    #[doc = include_str!("../docs/api/add.md")]    
88    pub fn add(identifier: &str, parameters: &str) -> Result<Arc<Reference>> {
89        Workspace::add_node(identifier, parameters)
90    }
91
92    /// Copies an existing workflow node to create a new node with a different identifier.
93    #[doc = include_str!("../docs/api/copy.md")]    
94    pub fn copy(from: &str, to: &str) -> Result<()> {
95        Workspace::copy_node(Arc::from(from), Arc::from(to))
96    }
97
98    /// Renames an existing workflow node in the workspace.
99    #[doc = include_str!("../docs/api/rename.md")]    
100    pub fn rename(from: &str, to: &str) -> Result<()> {
101        Workspace::rename_node(from, Arc::from(to))
102    }
103
104    /// Removes a workflow node from the workspace along with all associated files.
105    /// 
106    /// This function permanently deletes a workflow node and all its associated resources,
107    /// including the workflow file, journal file, and any child directories or files.
108    /// The removal operation is irreversible and comprehensive.
109    #[doc = include_str!("../docs/api/remove.md")]    
110    pub fn remove(identifier: &str) -> Result<()> {
111        Workspace::remove_node(identifier)
112    }
113
114    // -----------------------------------------------------------------------------
115    // Workspace Sheets
116    // -----------------------------------------------------------------------------
117
118    /// Retrieves metadata and structural information about a workflow as a [`Sheet`].
119    #[doc = include_str!("../docs/api/sheet.md")]    
120    pub fn sheet(locator: Arc<Reference>) -> Result<Sheet> {
121        if &*locator =="_" {
122            Workspace::sheet()
123        } else {
124            let node = Workspace::locate_node(&locator)?;
125            Sheet::convert(&node)
126        }
127    }
128
129    /// Lists all rules within a workflow as [`Cell`] objects.
130    #[doc = include_str!("../docs/api/list.md")]    
131    pub fn list(locator: Arc<Reference>) -> Result<Vec<Cell>> {
132        if &*locator == "_" {
133            // Handle workspace root case - get the workspace workflow itself
134            let workspace = Workspace::workflow()?;
135            let mut list = Vec::new();
136            for row in workspace.iter_rows() {
137                // Full Row/Rule abstraction
138                let cell = Cell::convert_row(row);
139                list.push(cell);
140            }
141            Ok(list)
142        } else {
143            let node = Workspace::locate_node(&locator)?;
144            let workflow = node.get_workflow()?;
145            let mut list = Vec::new();
146            for row in workflow.iter_rows() {
147                // Full Row/Rule abstraction
148                let cell = Cell::convert_row(row);
149                list.push(cell);
150            }
151            Ok(list)
152        }
153    }
154
155    pub fn exists(locator: Arc<Reference>) -> Result<bool> {
156        if locator.depth() == 1 {
157            let identifier = locator.identifier();
158            Workspace::contains_node(&identifier)
159        } else {
160            let parent = locator.get_parent()?;
161            let identifier = locator.identifier();
162            Aim::contains(Arc::new(parent), &identifier)
163        }
164    }
165
166    /// Checks if a workflow contains a rule with the specified identifier.
167    #[doc = include_str!("../docs/api/contains.md")]    
168    pub fn contains(locator: Arc<Reference>, identifier: &str) -> Result<bool> {
169        if &*locator == "_" {
170            Workspace::contains_node(identifier)
171        } else {
172            let node = Workspace::locate_node(&locator)?;
173            let workflow = node.get_workflow()?;
174            Ok(workflow.contains(identifier))
175        }
176    }
177
178    /// Imports workflow data from a CSV file into an existing workflow.
179    #[doc = include_str!("../docs/api/import.md")]
180    pub fn import(_locator: Arc<Reference>, _target_path: &Path) -> Result<()> {
181        todo!();
182    }
183
184    /// Exports workflow data to a CSV file from an existing workflow.
185    #[doc = include_str!("../docs/api/export.md")]
186    pub fn export(_locator: Arc<Reference>, _target_path: &Path) -> Result<()> {
187        todo!();
188    }
189
190    /// Print workflow to md string 
191    #[doc = include_str!("../docs/api/print.md")]
192    pub fn print(_locator: Arc<Reference>) -> Result<Arc<str>> {
193        todo!();
194    }
195
196    // -----------------------------------------------------------------------------
197    // Cell
198    // -----------------------------------------------------------------------------
199
200    /// Retrieves a specific cell (rule) from a workflow by its identifier.
201    #[doc = include_str!("../docs/api/cell.md")]
202    pub fn cell(locator: Arc<Reference>, identifier: Arc<str>) -> Result<Cell> {
203        let reference = locator.add_child(identifier)?;
204        Ok(if let Some(rule) = Workspace::locate_rule(&reference)? {
205            Cell::convert_rule(&rule)
206        }
207        else {
208            Cell::Empty
209        })
210    }
211
212    // -----------------------------------------------------------------------------
213    // Context
214    // -----------------------------------------------------------------------------
215
216    /// Acquires exclusive write access to a workflow for modifications.
217    #[doc = include_str!("../docs/api/acquire.md")]    
218    pub fn acquire(locator: Arc<Reference>) -> Result<Arc<str>> {
219        // Get the context locator reference
220        let context_locator = Reference::context();
221        // Acquire a write lock for the context workflow
222        let mutex = LockManager::try_get_mutex_with_timeout(context_locator.clone(), Duration::from_secs(1))?;
223        let _lock_guard = mutex.lock()
224            .expect("Lock poisoned during acquire().");
225        // Use the context locator to get the workspace context node
226        let context_node = Workspace::locate_node(&context_locator)?;
227        // Use the context node to get shared read-only workflow
228        let context_workflow = context_node.get_workflow_like()?;
229        // Convert the reference to an identifier
230        let instance_handle: Arc<str> = locator.handle();
231        // Check if the handle is in used already
232        if !context_workflow.contains(&instance_handle) {
233            // Make a mutable copy
234            let mut workflow = context_node.get_workflow_mut()?;
235            // Add the instance (source and target are the same workflow)
236            workflow.add_instance(instance_handle.clone(), locator.clone(), locator.clone())?;
237            context_node.set_workflow(workflow);
238            Ok(instance_handle)
239        } else {
240            Err(anyhow!("Workflow {} already in use", locator))
241        }
242    }
243
244    /// Saves the current state of a workflow instance to disk, making changes permanently visible.
245    #[doc = include_str!("../docs/api/snapshot.md")]    
246    pub fn snapshot(handle: &str) -> Result<()> {
247        let instance = Instance::locate(handle)?;
248        let mut write_guard = instance.write()
249            .expect("Lock poisoned in snapshot() instance write.");
250        write_guard.save()
251    }
252
253    /// Releases a workflow context handle and finalizes all pending changes.
254    #[doc = include_str!("../docs/api/release.md")]    
255    pub fn release(handle: &str) -> Result<()> {
256        Aim::snapshot(handle)?;
257
258        // Remove the instance from the `context` workflow
259        // Get the context locator reference
260        let context_locator = Reference::context();
261        // Acquire a write lock for the context workflow
262        let mutex = LockManager::try_get_mutex_with_timeout(context_locator.clone(), Duration::from_secs(1))?;
263        let _lock_guard = mutex.lock()
264            .expect("Lock poisoned during context release().");
265        // Use the context locator to get the workspace context node
266        let context_node = Workspace::locate_node(&context_locator)?;
267        // Use the context node to get a mutable copy of the workflow
268        let mut workflow = context_node.get_workflow_mut()?;
269        if let Some(index) = workflow.get_index(handle) {
270            workflow.remove_row(index);
271            context_node.set_workflow(workflow);
272            Ok(())
273        } else {
274            Err(anyhow!("Unable to release {}, missing instance", handle))
275        }
276    }
277
278    // -----------------------------------------------------------------------------
279    // Tree with Context
280    // -----------------------------------------------------------------------------
281
282    /// Adds a new child workflow node within an existing workflow using an active write context.
283    #[doc = include_str!("../docs/api/add_node.md")]    
284    pub fn add_node(locator: Arc<Reference>, identifier: Arc<str>, parameters: &str) -> Result<Arc<Reference>> {
285        let node = Node::parse(parameters)?;
286        let handle = Aim::acquire(locator)?;
287        match Instance::locate(&handle) {
288            Ok(context) => {
289                let result = {
290                    let mut write_guard = context.write()
291                        .expect("Lock poisoned in add_node() context write.");
292                    write_guard.target().add_node(identifier, node)
293                };
294                Aim::release(&handle)?;
295                result
296            }
297            Err(e) => {
298                Aim::release(&handle)?;
299                Err(e)
300            }
301        }
302    }
303
304    /// Copies an existing workflow node within a parent workflow to create a replica with a different identifier.
305    #[doc = include_str!("../docs/api/copy_node.md")]    
306    pub fn copy_node(locator: Arc<Reference>, from: Arc<Reference>, to: Arc<str>) -> Result<()> {
307        let node = Workspace::locate_node(&from)?;
308        let handle = Aim::acquire(locator)?;
309        match Instance::locate(&handle) {
310            Ok(context) => {
311                let result = {
312                    let mut write_guard = context.write()
313                        .expect("Lock poisoned in copy_node() context write.");
314                    write_guard.target().copy_node(&node, to)
315                };
316                Aim::release(&handle)?;
317                result
318            }
319            Err(e) => {
320                Aim::release(&handle)?;
321                Err(e)
322            }
323        }
324    }
325
326    /// Renames a workflow node within a parent workflow using an active write context.
327    #[doc = include_str!("../docs/api/rename_node.md")]    
328    pub fn rename_node(locator: Arc<Reference>, from: &str, to: Arc<str>) -> Result<()> {
329        let handle = Aim::acquire(locator)?;
330        match Instance::locate(&handle) {
331            Ok(context) => {
332                let result = {
333                    let mut write_guard = context.write()
334                        .expect("Lock poisoned in rename_node() context write.");
335                    write_guard.target().rename_node(from, to)
336                };
337                Aim::release(&handle)?;
338                result
339            }
340            Err(e) => {
341                Aim::release(&handle)?;
342                Err(e)
343            }
344        }
345    }
346
347    /// Removes a workflow node from within a parent workflow using an active write context.
348    #[doc = include_str!("../docs/api/remove_node.md")]    
349    pub fn remove_node(locator: Arc<Reference>, identifier: &str) -> Result<()> {
350       let handle = Aim::acquire(locator)?;
351        match Instance::locate(&handle) {
352            Ok(context) => {
353                let result = {
354                    let mut write_guard = context.write()
355                        .expect("Lock poisoned in remove_node() context write.");
356                    write_guard.target().delete_node(identifier)
357                };
358                Aim::release(&handle)?;
359                result
360            }
361            Err(e) => {
362                Aim::release(&handle)?;
363                Err(e)
364            }
365        }
366    }
367
368    // -----------------------------------------------------------------------------
369    // Cell [index] with Context
370    // -----------------------------------------------------------------------------
371
372    /// Retrieves the index position of a cell within a workflow using an active write context.
373    #[doc = include_str!("../docs/api/get_index.md")]    
374    pub fn get_index(handle: &str, identifier: &str) -> Result<Option<usize>> {
375        let context = Instance::locate(handle)?;
376        let mut write_guard = context.write()
377            .expect("Lock poisoned in get_index() context write.");
378        Ok(write_guard.target().get_index(identifier))
379    }
380
381    /// Retrieves a specific cell (rule) from a workflow by its zero-based index position within an active write context.
382    #[doc = include_str!("../docs/api/get_row.md")]
383    pub fn get_row(handle: &str, index: usize) -> Result<Cell> {
384        let context = Instance::locate(handle)?;
385        let mut write_guard = context.write()
386            .expect("Lock poisoned in get_row() context write.");
387        let row = write_guard.target().get_row(index);
388        Ok(Cell::convert_row(&row))
389    }
390
391    /// Updates a specific row within a workflow using an active write context.
392    #[doc = include_str!("../docs/api/set_row.md")]
393    pub fn set_row(handle: &str, index: usize, cell: Cell) -> Result<()> {
394        let context = Instance::locate(handle)?;
395        let mut write_guard = context.write()
396            .expect("Lock poisoned in set_row() context write.");
397        let row = Row::convert(cell);
398        write_guard.target().set_row(index, row)
399    }
400
401    /// Inserts a new row at the specified index within a workflow using an active write context.
402    #[doc = include_str!("../docs/api/insert_row.md")]
403    pub fn insert_row(handle: &str, index: usize, cell: Cell) -> Result<()> {
404        let context = Instance::locate(handle)?;
405        let mut write_guard = context.write()
406            .expect("Lock poisoned in insert_row() context write.");
407        let row = Row::convert(cell);
408        write_guard.target().insert_row(index, row)
409    }
410
411    /// Moves a rule from one row position to another within a workflow.
412    #[doc = include_str!("../docs/api/reposition_row.md")]
413    pub fn reposition_row(handle: &str, from: usize, to: usize) -> Result<()> {
414        let context = Instance::locate(handle)?;
415        let mut write_guard = context.write()
416            .expect("Lock poisoned in reposition_row() context write.");
417        write_guard.target().reposition_row(from, to)
418    }
419
420    /// Clears the content of a cell at the specified index while preserving its position.
421    #[doc = include_str!("../docs/api/clear_row.md")]
422    pub fn clear_row(handle: &str, index: usize) -> Result<Cell> {
423        let context = Instance::locate(handle)?;
424        let mut write_guard = context.write()
425            .expect("Lock poisoned in clear_row() context write.");
426        let row = write_guard.target().clear_row(index);
427        Ok(Cell::convert_row(&row))
428    }
429
430    /// Removes a specific row from a workflow permanently and returns the removed content.
431    #[doc = include_str!("../docs/api/remove_row.md")]
432    pub fn remove_row(handle: &str, index: usize) -> Result<Cell> {
433        let context = Instance::locate(handle)?;
434        let mut write_guard = context.write()
435            .expect("Lock poisoned in remove_row() context write.");
436        let row = write_guard.target().remove_row(index);
437        Ok(Cell::convert_row(&row))
438    }
439
440    // -----------------------------------------------------------------------------
441    // Cell Identifier with Context
442    // -----------------------------------------------------------------------------
443
444    /// Checks if a specific cell exists within a workflow using an active write context.
445    #[doc = include_str!("../docs/api/has_cell.md")]    
446    pub fn has_cell(handle: &str, identifier: &str) -> Result<bool> {
447        let context = Instance::locate(handle)?;
448        let mut write_guard = context.write()
449            .expect("Lock poisoned in has_cell() context write.");
450        Ok(write_guard.target().contains(identifier))
451    }
452
453    /// Retrieves a specific cell (rule) from a workflow using an active write context by its identifier.
454    #[doc = include_str!("../docs/api/get_cell.md")]
455    pub fn get_cell(handle: &str, identifier: &str) -> Result<Option<Cell>> {
456        let context = Instance::locate(handle)?;
457        let mut write_guard = context.write()
458            .expect("Lock poisoned in get_cell() context write.");
459        match write_guard.target().get_rule(identifier) {
460            Some(rule) => Ok(Some(Cell::convert_rule(&rule))),
461            _ => Ok(None),
462        }
463    }
464
465    /// Appends a new rule to or updates an existing rule within a workflow using an active write context.
466    #[doc = include_str!("../docs/api/append_or_update_cell.md")]
467    pub fn append_or_update_cell(handle: &str, cell: Cell) -> Result<()> {
468        let context = Instance::locate(handle)?;
469        let mut write_guard = context.write()
470            .expect("Lock poisoned in append_or_update_cell() context write.");
471        let row = Row::convert(cell);
472        write_guard.target().append_or_update(row);
473        Ok(())
474    }
475
476    /// Updates a specific cell (rule) within a workflow using an active write context.
477    #[doc = include_str!("../docs/api/update_cell.md")]
478    pub fn update_cell(handle: &str, identifier: Arc<str>, typedef: Arc<str>, formula: Arc<str>, value: Arc<str>) -> Result<()> {
479        let context = Instance::locate(handle)?;
480        let mut write_guard = context.write()
481            .expect("Lock poisoned in update_cell() context write.");
482        let rule = Rule::convert(identifier, typedef, formula, value);
483        write_guard.target().update_rule(Arc::new(rule))    
484    }
485
486    /// Updates only the value of an existing cell within a workflow using an active write context.
487    #[doc = include_str!("../docs/api/update_cell_value.md")]
488    pub fn update_cell_value(handle: &str, identifier: Arc<str>, value: Arc<str>) -> Result<()> {
489        let context = Instance::locate(handle)?;
490        let mut write_guard = context.write()
491            .expect("Lock poisoned in update_cell_value() context write.");
492        write_guard.target().update_value(&identifier, Value::convert(value))
493    }
494
495    /// Permanently removes a cell (rule) from a workflow by its identifier and returns the removed cell.
496    #[doc = include_str!("../docs/api/delete_cell.md")]    
497    pub fn delete_cell(handle: &str, identifier: Arc<str>) -> Result<Cell> {
498        let context = Instance::locate(handle)?;
499        let mut write_guard = context.write()
500            .expect("Lock poisoned in delete_cell() context write.");
501        let row = write_guard.target().delete_rule(&identifier);
502        Ok(Cell::convert_row(&row))
503    }
504
505    // -----------------------------------------------------------------------------
506    // Workflow inference
507    // -----------------------------------------------------------------------------
508
509    /// Starts a new workflow inference instance by creating a context instance that binds a source workflow template to an inference target.
510    #[doc = include_str!("../docs/api/start.md")]
511    pub fn start(_source: Arc<Reference>, _target: Option<Arc<Reference>>) -> Result<Arc<str>> {
512        // Generate a date-stamp identifier
513        // Generate a random handle identifier
514        // With the date-stamp, add new node to the `inference` workflow
515        // With the handle, add a new context instance to the `context` workflow
516        // Bine the `inference` to the context instance
517        // Return the handle
518        todo!();
519    }
520
521    /// Executes the next step in a workflow inference instance.
522    #[doc = include_str!("../docs/api/next.md")]
523    pub fn next(_handle: &str) -> Result<()> {
524        // TODO: Implementation
525        // 1. Locate the inference instance using the handle
526        // 2. Get the context lock for the instance
527        // 3. Run one step of evaluation using Context::run_evaluation
528        // 4. Handle branching logic and state transitions
529        // 5. Return success or error based on evaluation result
530        todo!("next() implementation pending");
531    }
532
533    /// Moves the workflow instance to the previous version in its journal history.
534    #[doc = include_str!("../docs/api/undo.md")]
535    pub fn undo(_handle: &str) -> Result<()> {
536        todo!();
537    }
538
539    /// Makes a previous version (currently loaded by undo) the current workflow instance.
540    #[doc = include_str!("../docs/api/proceed.md")]
541    pub fn proceed(_handle: &str) -> Result<()> {
542        todo!();
543    }
544
545    /// Re-applies changes that were previously undone using [`Aim::undo()`], effectively moving forward through the workflow's version history to restore previously rolled-back changes.
546    #[doc = include_str!("../docs/api/redo.md")]
547    pub fn redo(_handle: &str) -> Result<()> {
548        todo!();
549    }
550
551    /// Terminates and cleans up a workflow inference instance, removing all associated resources without saving changes.
552    #[doc = include_str!("../docs/api/cancel.md")]
553    pub fn cancel(_handle: &str) -> Result<()> {
554        todo!();
555    }
556
557    /// Retrieves all inference results from a workflow as result cells optimized for output display.
558    #[doc = include_str!("../docs/api/results.md")]    
559    pub fn results(locator: Arc<Reference>) -> Result<Vec<Cell>> {
560        let node = Workspace::locate_node(&locator)?;
561        let workflow = node.get_workflow()?;
562        let mut list = Vec::new();
563        for row in workflow.iter_rows() {
564            // Cell::Result(..) 
565            let cell = Cell::convert_result(row);
566            list.push(cell);
567        }
568        Ok(list)
569    }
570
571    // -----------------------------------------------------------------------------
572    // Helper
573    // -----------------------------------------------------------------------------
574
575    pub fn check_identifier(input: &str) -> Result<()> {
576        if input.is_empty() {
577            return Err(anyhow!("Empty identifier"));
578        }
579        match parse_identifier(input) {
580            Ok((remain, identifier)) => {
581                if remain.is_empty() {
582                    if identifier == "_" {
583                        Err(anyhow!("Invalid identifier {}", input))
584                    } else {
585                        Ok(())
586                    }
587                } else {
588                    Err(anyhow!("Invalid identifier {}~{}", input, remain))
589                }
590            }
591            Err(e) => Err(anyhow!("Parse error: {}", e)),
592        }
593    }
594
595    // -----------------------------------------------------------------------------
596    // Function Info Library Integration
597    // -----------------------------------------------------------------------------
598
599    /// Get all available function categories.
600    /// 
601    /// Returns a list of all function categories in the standard library.
602    /// Categories include: text, math, business, date, collection, statistical,
603    /// functional, set, task, and utility.
604    pub fn function_categories() -> Result<Vec<Arc<str>>> {
605        let registry = FunctionInfoRegistry::global();
606        Ok(registry.categories().into_iter().map(Arc::from).collect())
607    }
608
609    /// Get all functions in a specific category.
610    /// 
611    /// Returns a list of function identifiers for the specified category.
612    /// Returns an empty list if the category doesn't exist.
613    pub fn function_list(category: &str) -> Result<Vec<Arc<str>>> {
614        let registry = FunctionInfoRegistry::global();
615        let functions = registry.by_category(category);
616        Ok(functions.into_iter()
617            .map(|card| Arc::from(card.identifier))
618            .collect())
619    }
620
621    /// Get complete documentation for a specific function.
622    /// 
623    /// Returns a FunctionCard containing the function's signature, description,
624    /// examples, and other documentation. Returns an error if the function
625    /// is not found.
626    pub fn function_card(identifier: &str) -> Result<FunctionCard> {
627        let registry = FunctionInfoRegistry::global();
628        registry.get_required(identifier)
629            .map(|card| card.clone())
630            .map_err(|e| anyhow!(e))
631    }
632
633    /// Search functions by partial name or description.
634    /// 
635    /// Performs a case-insensitive search across function identifiers, brief
636    /// descriptions, and detailed descriptions. Returns matching functions
637    /// sorted by relevance.
638    pub fn function_search(partial: &str) -> Result<Vec<Arc<str>>> {
639        let registry = FunctionInfoRegistry::global();
640        let results = registry.search(partial);
641        Ok(results.into_iter()
642            .map(|card| Arc::from(card.identifier))
643            .collect())
644    }
645
646    /// Get signatures for multiple function identifiers.
647    /// 
648    /// Returns the function signatures for the provided list of identifiers.
649    /// Only functions that exist in the registry are included in the result.
650    pub fn function_signatures(identifier_list: Vec<Arc<str>>) -> Result<Vec<Arc<str>>> {
651        let registry = FunctionInfoRegistry::global();
652        
653        let signatures: Vec<_> = identifier_list
654            .into_iter()
655            .filter_map(|id| registry.get(&id))
656            .map(|card| Arc::from(card.signature))
657            .collect();
658            
659        Ok(signatures)
660    }
661}