interthread 3.1.0

Auto implementation of the Actor Model
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

use crate::model::{method::ModelMethod, method::ModelOutput};
use quote::{quote,ToTokens};
use syn::{Path,Signature};
use proc_macro_error::abort;
use proc_macro2::TokenStream;


pub fn met_new_note_help<T: ToTokens>(name: &T) -> (String,String) {
    let name = quote!{#name}.to_string().replace(" ","");

    let note = format!(
        "The object {name:?} must implement a public or restricted method named 'new' \
        that returns Self or {name}. If the function may fail to return \
        an instance of {name}, name it 'try_new' \
        and return a 'Result<{name}>' or 'Option<{name}'. 
        It is recommended to follow the standard Rust naming \
        convention and use 'try_new', but it is not mandatory.\n"
    );

    let help = format!("
    the following are possible method signatures:
    
    - returning Type
    
        pub fn new (arg, ...) -> Self
        pub(crate) fn new (arg, ...) -> Self
        pub fn new (arg, ...) -> {name} 
    
    - returning Option <Type>  
        
        pub fn try_new(arg, ...) -> Option<Self>
        pub fn try_new(arg, ...) -> Option<{name}>
                                or
                 -> custom::module::Option<{name}>
        
    - returning Result <Type>  
    
        pub fn try_new(arg, ...) -> Result<Self,...>
        pub fn try_new(arg, ...) -> Result<{name},...>
                                or
                    -> custom::module::Result<{name}>
                 -> custom::module::Result<{name}, E>

    ");
    return (note,help);
}


pub fn met_new_found<T: ToTokens>(sig: &Signature, name: &T, bit: TokenStream, res_opt: ModelOutput) -> (String,String,String){
    let sig_name      = sig.ident.to_string();
    let act_name      = quote!{ #name }.to_string();
    let mut bit_str   = bit.to_string();
    if bit_str == ""{
        bit_str = " ".to_string();
    }
    let msg = if res_opt.is_none() {

        if &sig_name == "new"{
            format!("'{act_name}::{sig_name}' expected to return \
            'Self' or '{act_name}'. \nFound: {bit_str:?} .")
        }
        else {
            format!("'{act_name}::{sig_name}' expected to return \
            'Result<Self>' or 'Result<{act_name}>' or 'Option<Self>' or \
            'Option<{act_name}>'. \nFound: {bit_str:?} .")
        }
    }
    else {
        //result 
        if res_opt.is_result(){
            format!("'{act_name}::{sig_name}' expected to return \
            'Self' or '{act_name}' wrapped in a 'Result' type. \nFound: {bit_str:?} .")
        }

        //option 
        else {
            format!("'{act_name}::{sig_name}' expected to return \
            'Self' or '{act_name}' wrapped in a 'Option' type. \nFound: {bit_str:?} .")
        }
    };
    let (note,help) = crate::error::met_new_note_help(name);

    (msg,note,help)
}

pub fn met_new_not_instance<T: ToTokens>(sig: &Signature, name: &T, bit: TokenStream, res_opt: ModelOutput) -> (String,String,String){
    let sig_name = sig.ident.to_string();
    let act_name = quote!{#name}.to_string();
    let bit_str  = bit.to_string();
    
    let msg = {
        //result 
        if res_opt.is_result(){
            format!("'{act_name}::{sig_name}' expected to return \
            Result<'{act_name}'>. \nFound: {bit_str:?} .")
        }

        //option 
        else {
            format!("'{act_name}::{sig_name}' expected to return \
            Option<'{act_name}'>. \nFound: {bit_str:?} .")
        }
    };
    let (note,help) = crate::error::met_new_note_help(name);
    (msg,note,help)
} 

pub fn abort_async_no_lib(met: &ModelMethod){
    let msg = format!("'async' methods present but the runtime (lib) is not specified.");
    abort!( &met.get_met().sig.asyncness, msg; help=crate::error::AVAIL_LIB );
}


pub fn unknown_attr_arg( aa: &str, path: &Path ){

    let path_str = quote!{#path}.to_string().replace(" ","");
    let msg = format!("Unknown argument option  -  '{}'  for '{}' ", path_str,aa);

    match aa {

        val if val == "actor"   => abort!(path, msg ;help = AVAIL_ACTOR  ),                   
        val if val == "expand"  => abort!(path, msg ;help = AVAIL_EXPAND ),
        val if val == "example" => abort!(path, msg ;help = AVAIL_EXAMPLE),
        val if val == "edit"    => abort!(path, msg ;help = AVAIL_EDIT   ),
        val if val == "family"  => abort!(path, msg ;help = AVAIL_FAMILY ),

        _ => (),
    }
}

pub fn error_name_type(n: &syn::Path, t: &str ) -> String {

    let path_str = quote::quote!{#n}.to_string().replace(" ","");
    return format!("Expected a  < {} >  value for attribute argument '{}'.", t,path_str  );
}

pub static AVAIL_EXAMPLE: &'static str = "

#[interthread::example( 
   
    (   
        main

        path = \"path/to/file.rs\" 

        expand(actor,family) *
    )
)]


*  -  default       
";

pub static AVAIL_EXPAND: &'static str = "
Argument 'expand' takes a tuple of ident options.

Available ident options are: 

                        actor 
                        family 

Examples of expected usage:

    expand(actor), 
    expand(family), 
*   expand(actor,family) 


* - default 
";

pub static AVAIL_LIB:&'static str = "
\navailable 'lib' options:

*   \"std\"
    \"smol\"
    \"tokio\"
    \"async_std\"


*  -  default
";

pub static AVAIL_EDIT: &'static str = "
\navailable 'edit' options:
         
     Struct        Options        
         
    'script'    ( 
                 def        
                 imp(name, ..)
                 trt(name, ..)
                )  

    'live'      ( 
                  def
                  imp(name, ..)
                  trt(name, ..)
                ) 

def  - Struct definition 
imp  - Struct methods 
trt  - Struct traits
name - method/trait name

    When employing the `imp` or `trt` option without providing a tuple list, \
the macro interprets it as a request to include all method/trait names.
    Similarly, for `script` or `live` a statement `edit(live)` implies `edit(live(def, imp, trt))`, \
a statement just `edit` implies `edit(live,script)` !
";

pub static AVAIL_DEBUT: &'static str = "
\navailable 'debut' options:
    `debut`
";

pub static AVAIL_ACTOR: &'static str = "
#[interthread::actor( 
    
     channel = 0 * 
               n (usize)

         lib = \"std\" *
               \"smol\"
               \"tokio\"
               \"async_std\"

        edit( 
             script(..)
             live(..)
            ) 

        file = \"path/to/current/file.rs\"
        
        name = \"\" 

        show

     include|exclude 
        
       debut

    interact
)]

*  -  default 
";


pub static HELP_EDIT_FILE_ACTOR: &'static str = "
The 'file' identifier within the 'edit' argument customizes writing \
behavior. It allows you selectively write portions of the \
model to the file,  enabling edition of other parts while excluding those \
that have already been modified.

Here are two key guidelines to keep in mind when using the 'file' identifier:

1. Options `script` and `live`, along with their suboptions `def`, `imp`, and `trt`, \
as well as their respective arguments (the names of methods/traits), can only \
be declared once within their respective scopes.

2. While multiple 'file' declarations are allowed, nesting \
them is not permitted.

Example 1:
edit( script, live(file(def), imp))
                   ^^^^
   write:   live(def)
   exclude: script, live(imp)

Example 2:
edit( script(imp), file(live(def, imp)))
                   ^^^^
   write:   live(def, imp)
   exclude: script(imp)

Example 3:
edit( live(file(def), imp(try_new, file(try_old))))
           ^^^^                    ^^^^
   write:   live(def,imp(try_old))
   exclude: live(imp(try_new))

Special case: `edit(file)` is similar to `edit(file(script, live))`, \
but the former entirely writes to the file and excludes the macro,\
while the latter only writes to the file, persisting in the form of \
`edit(script, live)`.
";


pub static EXPECT_LIST: &'static str = "Expected a list!";
pub static EXPECT_IDENT: &'static str = "Expected an identifier. Please pass only a single identifier without any namespace or path.";

pub static NESTED_FILE: &'static str  = "Nested `file` option!"; 
pub static NOTE_SPECIAL_FILE_EDIT: &'static str  = 
"`edit(file)` is the only scenario where the file argument \
can be specified as an identifier. In all other cases, it must be \
used in the list form `file(..)`."; 

pub static HELP_SPECIAL_FILE_EDIT: &'static str  =
"`edit(file)` serves as a special writable equivalent to `edit`. \
This notation will directly replace the macro with the actual \
generated code. However, in explicit notation like \
`edit(file(script, live))`, the macro will persist in the 
file as `edit(script, live)`, despite that the whole model \
is written to the file.";


pub static REQ_FILE: &'static str  =
r#"Expected a 'file' argument `file = "path/to/current/file.rs"`."#;



pub static FILTER_CONURENT_USE_OF_OPTIONS: &'static str = "Unexpected. Concurrent use of 'include' and 'exclude' options.";

pub static FILTER_OPTION_USE_HELP: &'static str =
"The 'actor' offers two filtering options: 'include' and 'exclude'. 
When applied to the set {a, b, c, d}, these options function as follows:

    include(a, c) -> {a, c}
    exclude(a, c) -> {b, d}

Only one option can be applied at a time.";

pub static PARAMETERS_ALLOWED_PATTERN_NOTE: &'static str ="
The model will accept the following patterns in method parameters:
    1) Ident ( variable name ) - 'foo : Type'
    2) Tuple - '(a, b, ..) : (Type, Type)' 
    3) Array - '[a,b,..] : [Type; n]'
    4) Struct  - 'Struct { a, b, .. } : MyStruct'
    5) Tuple Struct - 'Struct(a, b, ..) : MyTupleStruct'

";

pub static INTER_VARIABLE_SUPPORTED_PATTERN_NOTE: &'static str ="
The ONLY pattern supported for `inter variable` is 'ident' (a variable name 'foo : Type')!";

pub fn double_decl(s: &str) -> String {
    format!("Double declaration of `{s}` option.")
}

pub fn direct_send(script_name: &syn::Ident, variant: &syn::Ident) -> TokenStream {
    let msg = format!("'{script_name}::{variant}.direct'. Sending on a closed channel.");
    quote!{.unwrap_or_else(|_error| core::panic!( #msg ))}
}

pub static INTERACT_VARS_HELP: &str = "
    The `interact` option is designed to provide the model with \
comprehensive non-blocking functionality, along with convenient \
internal getter calls to access the state of the `live` instance.\
Please consult the documentation for correct usage.
";

pub static INTER_SEND_RECV_RESTRICT_NOTE : &'static str =
"   Using method arguments named `inter_send` or `inter_recv` will \
interfere with the model's internal variables. To proceed with \
these names, explicitly opt in by providing the argument `interact` \
to the macro (see option `interact` in documentation). Otherwise, \
consider renaming the arguments.";

pub static CONCURRENT_INTER_SEND_RECV : &'static str =
"   Concurrent use of `inter_send` and `inter_recv`.\
Please make sure to access only one end of the channel, not both simultaneously.";

pub static NOT_ACCESSIBLE_CHANNEL_END: &'static str =
"   `inter_send` and `inter_recv` variables cannot be accessed in methods that return a type.";

pub static EXPECTED_IDENTIFIER_SHOW: &'static str = "Expected an identifier ( show ).";

pub fn var_name_conflict<V,P>( var: V, part: P ) -> String 
where 
    V: ToString,
    P: ToString,
{
    format!("   Naming conflict: `{}`. Please choose a different \
    {} name.", var.to_string(),part.to_string())
} 


pub static AVAIL_FAMILY: &'static str = "
#[interthread::family( 
    
  ~ channel = 0 * 
              n (usize)

        lib = \"std\" *
              \"tokio\"
              \"async_std\"

        edit( 
             live(..)
            ) 

        Mutex | RwLock *
               
        file = \"path/to/current/file.rs\"
        
        name = \"\" 

        show

        debut

        actor(  
                first_name = \"\" 

                edit( 
                    script(..)
                    live(..)
                    ) 

                include|exclude 

                show

                interact

                channel ~
            )

)]

~  -  override 
*  -  default 
";


pub static NOT_ALLOW_FAMILY_DIRECT_MUT_REF: &'static str = 
"Mutable references (`mut`) are not allowed for crate's 'actor' convention.

Expected:
    ( actor : &  Arc<Mutex<Actor>> ) 
    ( actor : &  Arc<RwLock<Actor>> ) 

";

pub static NOT_ALLOW_FAMILY_IN_SMOL: &'static str = "The 'family' macro is only supported for the following runtimes: 'std' (standard), 'tokio' and 'async_std'.";

pub static TY_SEND_OPTIONS: &'static str = "The 'ty' argument accepts two options: 'Send' (the default) or '!Send'.";
pub static NOT_SEND_RESTRICTION: &'static str = 
"
The 'ty' argument accepts two options: 'Send' (the default) or '!Send'.
If you intend to build a '!Send' actor, please note the following restrictions:

    1) '!Send' actors can only be ordinary actors — they are not supported within a family context.
    2) '!Send' actors are only supported for 'lib = \"std\"' - they are not supported within any 'async' context.
    3) All arguments passed to the actor’s 'new' method must themselves be 'Send'.
"
;

pub static NOT_SEND_METHOD_ERROR: &'static str = "
Self-consuming methods, as well as static methods that ultimately 
become self-consuming after macro expansion, are not allowed for `!Send` actors.
";

pub static NOT_SEND_METHOD_NOTE: &'static str = "
When using `!Send`, the actor must remain on a single thread.
However, consuming `self` in this method would cause the actor to be moved,
which violates the `!Send` constraint.
";