drone-micropython-core 0.1.1

MicroPython for Drone.
use alloc::alloc;
use cmd::{Cmd, CmdKind, CmdRes};
use core::alloc::Layout;
use core::ptr;
use core::sync::atomic::AtomicBool;
use drone_core::drv::{Driver, Resource};
use drone_core::ffi::c_void;
#[cfg(feature = "io")]
use drone_core::ffi::CStr;
use drone_core::stack_adapter::{Adapter, Context, Stack};
#[cfg(feature = "io")]
use exec::exec_path;
use exec::{exec_source, UncaughtException};
use futures::prelude::*;
use rt::{gc_init, mp_deinit, mp_init};

static CREATED: AtomicBool = AtomicBool::new(false);
static mut HEAP: *mut u8 = ptr::null_mut();

/// MicroPython session core driver.
#[derive(Driver)]
#[driver(forward)]
pub struct MpSessCore<T: MpSessResCore>(T);

/// MicroPython session core resource.
pub trait MpSessResCore: Resource<Source = Self> + Driver {
  /// Stack storage.
  type Stack: Stack<Cmd, CmdRes, Self::Req, Self::ReqRes>;

  /// Context handler.
  type Context: Context<Self::Req, Self::ReqRes>;

  /// Possible requests.
  type Req: Send + 'static;

  /// Possible request results.
  type ReqRes: Send + 'static;

  /// Possible errors.
  type Error: From<UncaughtException>;

  /// Stack size.
  const STACK_SIZE: usize;

  /// Heap size.
  const HEAP_SIZE: usize;

  /// Returns a mutable reference to the stack object.
  fn stack(&mut self) -> &mut Self::Stack;

  /// Runs a request `req` asynchronously.
  fn run_req<'sess>(
    &'sess mut self,
    req: Self::Req,
  ) -> Box<Future<Item = Self::ReqRes, Error = Self::Error> + 'sess>;

  /// Runs at the first entrance into command loop.
  fn init();
}

impl<T: MpSessResCore> Adapter for MpSessCore<T> {
  type Stack = T::Stack;
  type Context = T::Context;
  type Cmd = Cmd;
  type CmdRes = CmdRes;
  type Req = T::Req;
  type ReqRes = T::ReqRes;
  type Error = T::Error;

  const STACK_SIZE: usize = T::STACK_SIZE;

  fn singleton() -> Option<&'static AtomicBool> {
    Some(&CREATED)
  }

  fn init() {
    T::init();
    unsafe {
      HEAP = alloc::alloc(heap_layout(T::HEAP_SIZE));
      gc_init(HEAP as *mut c_void, HEAP.add(T::HEAP_SIZE) as *mut c_void);
      mp_init();
    }
  }

  fn deinit() {
    unsafe {
      mp_deinit();
      alloc::dealloc(HEAP, heap_layout(T::HEAP_SIZE));
    }
  }

  fn stack(&mut self) -> &mut Self::Stack {
    self.0.stack()
  }

  fn run_cmd(cmd: Cmd, _context: Self::Context) -> CmdRes {
    match cmd {
      Cmd(CmdKind::ExecSource { source }) => CmdRes {
        exec_source: unsafe { exec_source(&*source) },
      },
      #[cfg(feature = "io")]
      Cmd(CmdKind::ExecPath { path }) => CmdRes {
        exec_path: unsafe { exec_path(&*path) },
      },
    }
  }

  fn run_req<'sess>(
    &'sess mut self,
    req: Self::Req,
  ) -> Box<Future<Item = Self::ReqRes, Error = Self::Error> + 'sess> {
    self.0.run_req(req)
  }
}

impl<T: MpSessResCore> MpSessCore<T> {
  /// Executes a script contained in `source`.
  pub fn exec_source<'sess>(
    &'sess mut self,
    source: &'sess str,
  ) -> impl Future<Item = (), Error = T::Error> + 'sess {
    async(static move || unsafe {
      await!(self.cmd(Cmd(CmdKind::ExecSource { source })))?.exec_source?;
      Ok(())
    })
  }

  /// Executes a script located at `path`.
  #[cfg(feature = "io")]
  pub fn exec_path<'sess>(
    &'sess mut self,
    path: &'sess CStr,
  ) -> impl Future<Item = (), Error = T::Error> + 'sess {
    async(static move || unsafe {
      await!(self.cmd(Cmd(CmdKind::ExecPath { path })))?.exec_path?;
      Ok(())
    })
  }
}

fn heap_layout(size: usize) -> Layout {
  unsafe { Layout::from_size_align_unchecked(size, 1) }
}