1#![deny(unused_must_use)]
2#[macro_export]
161macro_rules! declare_machine {
162
163 (@inner next $new_state:ident{$($new_el:ident:$new_el_val:expr),*}) => (
165 $new_state{$($new_el:$new_el_val),*}
166 );
167
168 (@inner next $new_state:ident) => (
170 $new_state{}
171 );
172
173 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
175 {
176 declare_machine!(@inner context $sel $cur);
177 $callback;
178 $cur.leave($glob_context).unwrap();
179 Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
180 }
181 );
182
183 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;) => (
185 {
186 declare_machine!(@inner context $sel $cur);
187 $callback;
188 Some(States::__SameState__)
189 }
190 );
191
192 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident; ;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
194 {
195 declare_machine!(@inner context $sel $cur);
196 $cur.leave($glob_context).unwrap();
197 Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
198 }
199 );
200
201 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident ; ;) => (
203 Some(States::__SameState__)
204 );
205
206 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
208 {
209 declare_machine!(@inner context $sel $cur);
210 $callback;
211 $cur.leave($glob_context).unwrap();
212 Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
213 }
214 );
215
216 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;) => (
218 {
219 declare_machine!(@inner context $sel $cur);
220 $callback;
221 Some(States::__SameState__)
222 }
223 );
224
225 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident; ;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
227 {
228 declare_machine!(@inner context $sel $cur);
229 $cur.leave($glob_context).unwrap();
230 Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
231 }
232 );
233
234 (@inner command @$glob_context:ident@ $sel:ident:$cur:ident ; ;) => (
236 Some(States::__SameState__)
237 );
238
239 (@inner context $ss:ident $sel:ident)=>(let $sel = $ss;);
240 (@inner context $ss:ident )=>();
241
242 (@inner >> $($sel:ident)* @$glob_context:ident@ $income:block) => (
244 fn enter(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
245 declare_machine!(@inner context self $($sel)*);
246 $income
247 Ok(())
248 }
249 );
250 (@inner << $($sel:ident)* @$glob_context:ident@ $outcome:block) => (
251 fn leave(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
252 declare_machine!(@inner context self $($sel)*);
253 $outcome
254 Ok(())
255 }
256 );
257 (@inner >> $($sel:ident)* @$glob_context:ident@ ) => (
258 fn enter(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
259 Ok(())
260 }
261 );
262 (@inner << $($sel:ident)* @$glob_context:ident@ ) => (
263 fn leave(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
264 Ok(())
265 }
266 );
267
268 (@inner params $state:ident {$($el:ident:$typ:ty);*}) => (
270 #[derive(Debug)]
271 #[derive(PartialEq)]
272 #[derive(Copy)]
273 #[derive(Clone)]
274 pub struct $state {pub $($el:$typ),*}
275 );
276 (@inner params $state:ident) => (
277 #[derive(Debug)]
278 #[derive(PartialEq)]
279 #[derive(Copy)]
280 #[derive(Clone)]
281 pub struct $state {}
282 );
283 (@inner initial $initial:ident{$($init_field:ident:$init_val:expr),*}) => ($initial{$($init_field: $init_val),*});
284 (@inner initial $initial:ident) => ($initial{});
285
286 (@cmd_processor $sel:ident @$glob_context:ident@ ($($cmd:ident $($callback:block)* => $($new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*)*;)*))=>(
287 fn do_job(&mut self, cmd: & Commands, $glob_context: &mut MachineContext) -> Option<States> {
288 match *cmd {
289 $(Commands::$cmd => {declare_machine!(@inner command @$glob_context@ self:$sel;$($callback)*;$($new_state$({$($new_el:$new_el_val),*})*)*)})*
290 _ => None
291 }
292 }
293 );
294
295 (@state $gc_name:ident; $($state:ident @ $sel:ident ; $($income:block)*; ($job:tt); $($outcome:block)*@),*) => (
296 $(
297 impl CanDoJob for $state {
298 declare_machine!(@cmd_processor $sel @$gc_name@ $job);
299 declare_machine!(@inner >> $sel @$gc_name@ $($income)*);
300 declare_machine!(@inner << $sel @$gc_name@ $($outcome)*);
301 }
302 )*
303 );
304 (@state ; $($state:ident @ $sel:ident ; $($income:block)*; ($job:tt); $($outcome:block)* @),*) => (
305 $(
306 impl CanDoJob for $state {
307 declare_machine!(@cmd_processor $sel @__@ $job);
308 declare_machine!(@inner >> $sel @__@ $($income)*);
309 declare_machine!(@inner << $sel @__@ $($outcome)*);
310 }
311 )*
312 );
313
314 (@state $gc_name:ident; $($state:ident@; $($income:block)*; ($job:tt); $($outcome:block)*@),*) => (
315 $(
316 impl CanDoJob for $state {
317 declare_machine!(@cmd_processor ___ @$gc_name@ $job);
318 declare_machine!(@inner >> ___ @$gc_name@ $($income)*);
319 declare_machine!(@inner << ___ @$gc_name@ $($outcome)*);
320 }
321 )*
322 );
323 (@state ; $($state:ident@; $($income:block)*; ($job:tt); $($outcome:block)*@),*) => (
324 $(
325 impl CanDoJob for $state {
326 declare_machine!(@cmd_processor ___ @__@ $job);
327 declare_machine!(@inner >> ___ @__@ $($income)*);
328 declare_machine!(@inner << ___ @__@ $($outcome)*);
329 }
330 )*
331 );
332
333(
336 $machine:ident $($gc_name:ident{$($context_field:ident:$context_type:ty),*})* ($initial:ident$({$($init_field:ident:$init_val:expr),*})*)
337 states[$($states:ident),*]
338 commands[$($commands:ident),*]
339
340 $(($state:ident $($sel:ident)*$({$($el:ident:$typ:ty);*})*:
341 $(>> $income:block)*
342 $(<< $outcome:block)*
343 $($cmd:ident $($callback:block)* => $($new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*)*;)*
344 ))*
345) => (
346 #[allow(non_snake_case)]
347 #[allow(unused_imports)]
348 #[allow(dead_code)]
349 #[allow(unused_variables)]
350 mod $machine {
351 use super::*;
352 trait CanDoJob {
353 fn do_job(&mut self, cmd: &Commands, global_context: &mut MachineContext) -> Option<States>;
354 fn leave(&mut self, &mut MachineContext) -> Result<(), ()>;
355 fn enter(&mut self, &mut MachineContext) -> Result<(), ()>;
356 }
357
358 $(
359 declare_machine!(@inner params $state $({$($el:$typ);*})*);
360 )*
361
362 declare_machine!(@state $($gc_name)*;$($state @ $($sel)* ; $($income)*; (($($cmd $($callback)* => $($new_state $({$($new_el:$new_el_val),*})*)*;)*)); $($outcome)*@),*);
363
364 #[derive(Debug)]
365 #[derive(PartialEq)]
366 #[derive(Copy)]
367 #[derive(Clone)]
368 pub enum States {
369 __SameState__,
370 $($states {context: $states}),*
371 }
372
373 #[derive(Debug)]
374 #[derive(PartialEq)]
375 pub enum Commands {
376 $($commands),*
377 }
378
379 #[derive(Clone)]
380 pub struct MachineContext {$($(pub $context_field: $context_type),*)*}
381
382 pub struct Machine {
383 state: States,
384 context: MachineContext
385 }
386 pub fn new($($($context_field: $context_type),*)*) -> Machine {
387 let mut context = declare_machine!(@inner initial $initial $({$($init_field: $init_val),*})*);
388 let mut machine_context = MachineContext{$($($context_field: $context_field),*)*};
389 context.enter(&mut machine_context).unwrap();
390 Machine{state: States::$initial{context: context}, context: machine_context}
391 }
392
393 impl Machine {
394 pub fn execute(&mut self, cmd: & Commands) -> Result<(),()>{
395 match {
396 match self.state {
397 States::__SameState__ => None,
398 $(States::$state{ ref mut context } => context.do_job(cmd, &mut self.context)),*
399 }
400 } {
401 Some(x) => {
402 match x {
403 States::__SameState__ => {},
404 _ => {
405 self.change_state(x)
406 }
407 };Ok(())
408 },
409 None => {println!("Wrong operation {:?} for {:?} state!", cmd, self.state); Err(())}
410 }
411 }
412 fn change_state(&mut self, new_state: States) {
413 self.state = new_state;
414 match self.state {
415 States::__SameState__ => Ok(()),
416 $(States::$state{ ref mut context } => context.enter(&mut self.context)),*
417 }.unwrap();
418 }
419 pub fn get_current_state(&self) -> States {
420 self.state.clone()
421 }
422 pub fn get_inner_context(&self) -> MachineContext {
423 self.context.clone()
424 }
425 }
426 }
427)
428}
429
430#[cfg(test)]
431mod tests {
432 fn tes(x:i16) {
433 println!("x:{}",x);
434 }
435
436 declare_machine!(
437 Mach1 (New{x:0})
438
439 states[New,InConfig,Operational]
440 commands[Configure, ConfigureDone, Drop]
441
442 ( New context{x: i16}:
443 >> {println!("Enter {:?}", context)}
444 << {println!("Leave {:?}", context)}
445 Configure {println!("In New with context val: {}", context.x);} => InConfig{x:context.x+1, y:0};
446 ConfigureDone => New{x:0};
447 )
448 ( InConfig context{x:i16; y:i16}:
449 >> {println!("Enter {:?}", context)}
450 << {println!("Leave {:?}", context)}
451 ConfigureDone {tes(context.x)}=> Operational;
452 )
453 ( Operational context:
454 >> {println!("Enter {:?}", context)}
455 << {println!("Leave {:?}", context)}
456 ConfigureDone =>;
457 Drop => New{x:0};
458 )
459 );
460
461 declare_machine!(
462 Mach2 (State1)
463
464 states[State1,State2,State3]
465 commands[ToState1, ToState2, ToState3]
466
467 ( State1 :
468 ToState2 => State2;
469 )
470 ( State2 :
471 ToState3 => State3;
472 )
473 ( State3 :
474 ToState1 => State1;
475 )
476 );
477
478 #[test]
479 fn test1() {
480 let mut m = Mach1::new();
481 m.execute(&Mach1::Commands::Configure).unwrap();
482 m.execute(&Mach1::Commands::ConfigureDone).unwrap();
483 m.execute(&Mach1::Commands::Drop).unwrap();
484 m.execute(&Mach1::Commands::Configure).unwrap();
485 m.execute(&Mach1::Commands::ConfigureDone).unwrap();
486 m.execute(&Mach1::Commands::ConfigureDone).unwrap();
487 m.execute(&Mach1::Commands::ConfigureDone).unwrap();
488 }
489
490 #[test]
491 #[should_panic]
492 fn test2() {
493 let mut m = Mach2::new();
494 m.execute(&Mach2::Commands::ToState2).unwrap();
495 m.execute(&Mach2::Commands::ToState3).unwrap();
496 m.execute(&Mach2::Commands::ToState1).unwrap();
497 m.execute(&Mach2::Commands::ToState3).unwrap();
498 }
499
500 declare_machine!(
501 Mach3 glob_cont{id:i16}(State1{counter:0})
502
503 states[State1,State2,State3]
504 commands[ToState1, ToState2, ToState3]
505
506 ( State1 cont{counter:i16}:
507 >>{println!("Mach {} enter {:?}", glob_cont.id, cont);}
508 <<{cont.counter+=1; println!("Mach {} leave {:?}", glob_cont.id, cont);}
509 ToState2 => State2{counter: cont.counter};
510 )
511 ( State2 cont{counter:i16}:
512 >>{println!("Mach {} enter {:?}", glob_cont.id, cont);}
513 <<{cont.counter+=1; println!("Mach {} leave {:?}", glob_cont.id, cont);}
514 ToState3 => State3{counter: cont.counter};
515 )
516 ( State3 cont{counter:i16}:
517 >>{println!("Mach {} enter {:?}", glob_cont.id, cont);}
518 <<{cont.counter+=1; println!("Mach {} leave {:?}", glob_cont.id, cont);}
519 ToState1 => State1{counter: cont.counter};
520 )
521 );
522
523 #[test]
524 fn test3() {
525 let mut m = Mach3::new(0);
526 let mut m1 = Mach3::new(1);
527 m1.execute(&Mach3::Commands::ToState2).unwrap();
528 m.execute(&Mach3::Commands::ToState2).unwrap();
529 m.execute(&Mach3::Commands::ToState3).unwrap();
530 m1.execute(&Mach3::Commands::ToState3).unwrap();
531 }
532
533 #[derive(Clone)]
534 pub struct InnerMachineContext {
535 id: i16,
536 name: String,
537 counter: i16
538 }
539
540 declare_machine!(
541 Mach4 inner{st: InnerMachineContext} (State1)
542 states[State1,State2,State3]
543 commands[ToState1, ToState2, ToState3]
544
545 ( State1 :
546 << {println!("id={} name={} counter={}", inner.st.id, inner.st.name, inner.st.counter);}
547 ToState2 {inner.st.counter+=1;}=> State2;
548 )
549 ( State2 :
550 << {println!("id={} name={} counter={}", inner.st.id, inner.st.name, inner.st.counter);}
551 ToState3 {inner.st.counter+=1;}=> State3;
552 )
553 ( State3 :
554 << {println!("id={} name={} counter={}", inner.st.id, inner.st.name, inner.st.counter);}
555 ToState1 {inner.st.counter+=1;}=> State1;
556 )
557 );
558 #[test]
559 fn test4() {
560 let mut m = Mach4::new(InnerMachineContext{id:0, name: String::from("Mach 0"), counter: 0});
561 let mut m1 = Mach4::new(InnerMachineContext{id:1, name: String::from("Mach 1"), counter: 0});
562 let mut m2 = Mach4::new(InnerMachineContext{id:2, name: String::from("Mach 2"), counter: 0});
563 m.execute(&Mach4::Commands::ToState2).unwrap();
564 m.execute(&Mach4::Commands::ToState3).unwrap();
565 m1.execute(&Mach4::Commands::ToState2).unwrap();
566 m.execute(&Mach4::Commands::ToState1).unwrap();
567 m1.execute(&Mach4::Commands::ToState3).unwrap();
568 m2.execute(&Mach4::Commands::ToState2).unwrap();
569 m.execute(&Mach4::Commands::ToState2).unwrap();
570 m2.execute(&Mach4::Commands::ToState3).unwrap();
571 m1.execute(&Mach4::Commands::ToState1).unwrap();
572 }
573}