aimx/values/
instance.rs

1use crate::{
2    aim::{Context, WorkflowLike, Workspace, Writer, WriterLike},
3    expressions::Reference, reference::parse_reference,
4};
5use std::{
6    fmt,
7    sync::{Arc, RwLock}
8};
9use anyhow::Result;
10use nom::{
11    IResult, Parser,
12    character::complete::{char, multispace0},
13    sequence::{delimited, preceded},
14};
15
16#[derive(Debug, Clone)]
17enum InstanceState {
18    /// Context is not loaded; holds the [`Reference`] used to load it.
19    Unloaded { source: Arc<Reference>, target: Arc<Reference> },
20    /// Context is loaded; holds the mutable context.
21    Loaded { context: Arc<RwLock<Context>> },
22}
23
24#[derive(Debug, Clone)]
25pub struct Instance {
26    /// Internal state protected by read-write lock
27    inner: Arc<RwLock<InstanceState>>,
28}
29
30impl Instance {
31    pub fn new( source: Arc<Reference>, target: Arc<Reference>) -> Self {
32        Instance {
33            inner: Arc::new(RwLock::new(InstanceState::Unloaded {
34                source,
35                target,
36            })),
37        }
38    }
39/*
40    pub fn handle() -> Arc<str> {
41        // Generate a random identifier handle with leading underscore and fixed number
42        // of digits that has a high probability of being unique
43        const RAND_LENGTH: usize = 5; // 2 x 5 random alphanumeric characters = 62^10 possible combinations
44        const ALPHANUMERIC_CHARS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
45        
46        let mut rng = rand::rng();
47        let mut handle = String::with_capacity(2 + (2 * RAND_LENGTH));
48
49        handle.push('_');
50        for _ in 0..RAND_LENGTH {
51            let idx = rng.random_range(0..ALPHANUMERIC_CHARS.len());
52            handle.push(ALPHANUMERIC_CHARS[idx] as char);
53        }
54        for _ in 0..RAND_LENGTH {
55            let idx = rng.random_range(0..ALPHANUMERIC_CHARS.len());
56            handle.push(ALPHANUMERIC_CHARS[idx] as char);
57        }
58        handle.push('_');
59        
60        Arc::from(handle)
61    }
62*/
63
64    pub fn locate(handle: &str) -> Result<Arc<RwLock<Context>>> {
65        let locator = Reference::context();
66        let instance = Workspace::locate_instance(&locator, handle)?;
67        instance.get_context_lock()
68    }
69
70    pub fn get_context_lock(&self) -> Result<Arc<RwLock<Context>>> {
71        // Fast path: try read lock
72        let read_guard = self.inner.read()
73            .expect("Lock poisoned in get_context_lock() instance read.");
74        
75        match &*read_guard {
76            InstanceState::Loaded { context } => {
77                Ok(context.clone())
78            }
79            InstanceState::Unloaded { source: _, target: _} => {
80                // Release read lock before acquiring write lock
81                drop(read_guard);
82                
83                // Slow path: acquire write lock
84                let mut write_guard = self.inner.write()
85                    .expect("Lock poisoned in get_context_lock() instance write.");
86                
87                // Double-check pattern
88                match &*write_guard {
89                    InstanceState::Loaded { context } => {
90                        Ok(context.clone())
91                    }
92                    InstanceState::Unloaded { source, target } => {
93                        // Load context
94                        let ctx = Context::open(source.clone(), target.clone())?;
95                        let context = Arc::new(RwLock::new(ctx));
96                        
97                        // Update state
98                        *write_guard = InstanceState::Loaded {
99                            context: context.clone(),
100                        };
101
102                        Ok(context)
103                    }
104                }
105            }
106        }
107    }
108
109    pub fn compact(&self) {
110        // Check if loaded
111        let read_guard = self.inner.read()
112            .expect("Lock poisoned in compact() instance read.");
113
114        if let InstanceState::Loaded { context: _ } = &*read_guard {
115            drop(read_guard);                
116            let mut write_guard = self.inner.write()
117                .expect("Lock poisoned in compact() instance write.");
118
119            // Double-check pattern
120            if let InstanceState::Loaded { context } = &*write_guard {
121                let rwlock_context = context.clone();
122                let mut context_guard = rwlock_context.write()
123                    
124                        .expect("Lock poisoned in compact() context write.");
125                if !context_guard.target().is_touched() {
126                    let source = context_guard.source().locator();
127                    let target = context_guard.target().locator();
128                    // Flush
129                    *write_guard = InstanceState::Unloaded { source, target };
130                }
131            }
132        }
133    }
134
135    /// Saves the instance's target workflow and any related state.
136    pub fn save_all(&self) -> Result<()> {
137        // Fast path: try read lock
138        let read_guard = self.inner.read()
139            .expect("Lock poisoned in save_all() instance read.");
140        
141        match &*read_guard {
142            InstanceState::Loaded { context } => {
143                let mut write_guard = context.write()
144                    .expect("Lock poisoned in save_all() context write.");
145                write_guard.save_all()
146            }
147            // Lets assume that if its unloaded its already been saved
148            _ => Ok(()),
149        }
150    }
151
152    pub fn print(&self, writer: &mut Writer) {
153        let read_guard = self.inner.read()
154            .expect("Lock poisoned in print() instance read.");
155        match &*read_guard {
156            InstanceState::Loaded { context } => {
157                let context_guard = context.read()
158                    .expect("Lock poisoned in print() context read.");
159                context_guard.print(writer);
160            }
161            InstanceState::Unloaded { source, target} => {
162                source.print(writer);
163                writer.write_str(", ");
164                target.print(writer);
165            }
166        }
167    }
168}
169
170pub fn parse_instance(input: &str) -> IResult<&str, Instance> {
171    let (input, (source, target)) = delimited(
172        multispace0,
173        // Inner content: source, target
174        (
175            parse_reference,
176            preceded(
177                (multispace0, char(','), multispace0),
178                parse_reference,
179            ),
180        ),
181        multispace0,
182    ).parse(input)?;
183
184    Ok((input, Instance::new(source, target)))
185}
186
187impl PartialEq for Instance {
188    /// Equality is based on shared internal `Arc` identity, not workflow content.
189    fn eq(&self, other: &Self) -> bool {
190        Arc::ptr_eq(&self.inner, &other.inner)
191    }
192}
193
194impl WriterLike for Instance {
195    fn write(&self, writer: &mut Writer) {
196        self.print(writer);
197    }
198}
199
200impl fmt::Display for Instance {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        write!(f, "{}", self.to_stringized())
203    }
204}