flowlang 0.3.27

A dataflow oriented programming meta-language in JSON supporting functions written in rust, python, javascript, java, and flow.
Documentation
use std::fs::File;
use std::path::*;
use std::io::Read;
use std::fs;
#[cfg(feature="serde_support")]
use serde_json::*;

use ndata::data::*;
use ndata::dataobject::*;
use ndata::dataarray::*;
use ndata::databytes::*;
use ndata::NDataConfig;

use crate::rand::*;

static mut STORE_PATH:Option<PathBuf> = None;

#[derive(Debug)]
pub struct DataStore {
  pub root: PathBuf,
}

impl DataStore {
  pub fn init(dir:&str) -> (&str, NDataConfig) {
    let d = Path::new(dir);
    unsafe { STORE_PATH = Some(d.to_path_buf()); }
    
    Rand::init();
    let q = ndata::init();
    
    let o = DataObject::new();
    let _x = o.incr();

    //RustCmd::init();

    (dir, q)
  }
  
  #[allow(dead_code)]
  pub fn mirror(q:(&str, NDataConfig)) {
    let d = Path::new(q.0);
    unsafe { STORE_PATH = Some(d.to_path_buf()); }
    
    Rand::init();
    ndata::mirror(q.1);

    //RustCmd::mirror();
  }
  
  #[allow(static_mut_refs)]
  pub fn new() -> DataStore {
    let path;
    unsafe {
      path = STORE_PATH.as_ref().unwrap();
    }
    return DataStore {
      root: path.to_path_buf(),
    };
  }
  
  #[allow(dead_code)]
  pub fn globals() -> DataObject {
    DataObject::get(0)
  }
  
  #[allow(dead_code)]
  pub fn gc() {
    DataObject::gc();
    DataArray::gc();
    DataBytes::gc();
  }
  
  pub fn lib_info(&self, lib:&str) -> DataObject {
    let path = self.root.join(lib).join("meta.json");
    let s = match path.exists() {
      true => self.read_file(path),
      false => "{}".to_string()
    };
    DataObject::from_string(&s)
  }
  
  #[allow(static_mut_refs)]
  pub fn get_lib_root(&self, lib:&str) -> PathBuf {
    let meta = self.lib_info(&lib);

    let root;
    if meta.has("root") {
    let r = meta.get_string("root");
    if r.starts_with("/") {
      root = Path::new(&r).to_owned();
    }
    else {
      root = self.root.parent().unwrap().join(r).to_owned();
    }
    }
    else {
    root = self.root.parent().unwrap().join("cmd").to_owned();
    }
    
    root
  }
  
  pub fn lookup_cmd_id(&self, lib:&str, ctl:&str, cmd:&str) -> String {
    let data = self.get_data(lib, "controls");
    let data = data.get_object("data").get_array("list");
    for c in data.objects() {
      let c = c.object();
      let n = c.get_string("name");
      if n == ctl {
        let ctlid = c.get_string("id");
        let ctldata = self.get_data(lib, &ctlid);
        let ctldata = ctldata.get_object("data");
        for cmddata in ctldata.get_array("cmd").objects() {
          let cmddata = cmddata.object();
          let n2 = cmddata.get_string("name");
          if n2 == cmd {
            return cmddata.get_string("id");
          }
        }
      }
    }
    panic!("No such command {}:{}:{}", lib, ctl, cmd);
  }
  
  #[allow(dead_code)]
  pub fn set_data(&self, db: &str, id: &str, data:DataObject) {
    let mut d = data.get_object("data");
    if d.has("attachmentkeynames") {
      let v = d.get_array("attachmentkeynames");
      for b in v.objects() {
        let b = &b.string();
        let aid = id.to_string()+"."+b;
        let f = self.get_data_file(db, &aid);
        let s = Data::as_string(d.get_property(b));
        fs::create_dir_all(f.parent().unwrap()).unwrap();
        fs::write(f, s).expect("Unable to write file");
        d.remove_property(b);
      }
    }
  
    let s = data.to_string();
    let f = self.get_data_file(db, id);
    fs::create_dir_all(f.parent().unwrap()).unwrap();
    fs::write(f, s).expect("Unable to write file");
  }
  
  #[cfg(feature="serde_support")]
  pub fn get_json(&self, db: &str, id: &str) -> Value {
    let path = self.get_data_file(db, id);
    let s = self.read_file(path);
    let mut data: Value = serde_json::from_str(&s).unwrap();
    let attachments: Value = data["data"]["attachmentkeynames"].to_owned();
    if attachments.is_array() {
      for a in attachments.as_array().unwrap().into_iter(){
        let b = &a.as_str().unwrap();
        let aid = id.to_string()+"."+b;
        let apath = self.get_data_file(db, &aid);
        if apath.exists(){
          let astr = self.read_file(apath);
          if astr.len() > 0 && astr[0..1].to_string() == "{" { // FIXME - Legacy hack
            data["data"][b] = serde_json::from_str(&astr).unwrap(); 
          } else {
            data["data"][b] = serde_json::Value::String(astr); 
          }
        }
      }
    }
    data
  }
  
  pub fn get_data(&self, db: &str, id: &str) -> DataObject {
    #[cfg(feature="serde_support")]
    {
      let data = self.get_json(db, id);
      return DataObject::from_json(data);
    }
    #[allow(unreachable_code)]
    {
      let path = self.get_data_file(db, id);
      let s = self.read_file(path);
      let data = DataObject::from_string(&s);
      let mut d = data.get_object("data");
      if d.has("attachmentkeynames") {
        let attachments: DataArray = d.get_array("attachmentkeynames");
        for a in attachments.objects(){
          let b = &a.string();
          let aid = id.to_string()+"."+b;
          let apath = self.get_data_file(db, &aid);
          let astr = self.read_file(apath);
          if astr.len() > 0 && astr[0..1].to_string() == "{" { // FIXME - Legacy hack
            d.put_object(b, DataObject::from_string(&astr)); 
          } else {
            d.put_string(b, &astr); 
          }
        }
      }
      return data;
    }
  }
  
  pub fn exists(&self, db: &str, id: &str) -> bool {
    self.get_data_file(db, id).exists()
  }
  
  pub fn get_data_file(&self, db: &str, id: &str) -> PathBuf {
    let mut path = self.root.join(db);
    path = self.get_sub_dir(path, id, 4, 4);
    path.push(id);
    path
  }
  
  fn get_sub_dir(&self, mut path: PathBuf, id: &str, chars: usize, levels: usize) -> PathBuf {
    let l:usize = chars * levels;
    let mut s = id.to_string();
    while s.len()<l {
      s = s + "_";
    }
    let mut i = 0;
    while i<levels{
      let n:usize = i * chars;
      let m:usize = n + chars;
      i = i + 1;
      let sub = &s[n..m];
      path.push(sub);
    }
    path
  }
  
  // FIXME - Replace with fs::read_to_string
  pub fn read_file(&self, path: PathBuf) -> String {
    if !path.exists() { println!("Missing file {:?}", path); }
    let mut f = File::open(path).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
  }
}