use std::clone::Clone;
use std::string::ToString;
use nature_common::{Executor, Meta, NatureError, Protocol, Result};
use crate::{FlowSelector, MetaCache, MetaDao, RawRelation, RelationSettings};
use crate::models::relation_target::RelationTarget;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Relation {
pub from: String,
pub to: Meta,
pub selector: Option<FlowSelector>,
pub executor: Executor,
pub filter_before: Vec<Executor>,
pub filter_after: Vec<Executor>,
pub use_upstream_id: bool,
pub target: RelationTarget,
pub delay: i32,
pub delay_on_pare: (i32, u8),
pub id_bridge: bool,
}
impl Iterator for Relation {
type Item = Relation;
fn next(&mut self) -> Option<Self::Item> {
Some(self.clone())
}
}
impl Relation {
pub async fn from_raw<MC, M>(val: RawRelation, meta_cache_getter: &MC, meta_getter: &M) -> Result<Relation>
where MC: MetaCache, M: MetaDao
{
let settings = match serde_json::from_str::<RelationSettings>(&val.settings) {
Ok(s) => s,
Err(e) => {
let msg = format!("{}'s setting format error: {:?}", val.get_string(), e);
warn!("{}", &msg);
return Err(NatureError::VerifyError(msg));
}
};
let selector = &settings.selector;
let m_to = Relation::check_converter(&val.to_meta, meta_cache_getter, meta_getter, &settings).await?;
let rtn = match settings.executor {
Some(e) => {
if e.protocol == Protocol::Auto {
let err = format!("{} Protocol::Auto can not be used by user. ", val.get_string());
return Err(NatureError::VerifyError(err));
}
Relation {
from: val.from_meta.to_string(),
to: m_to,
selector: selector.clone(),
executor: e,
filter_before: settings.filter_before,
filter_after: settings.filter_after,
use_upstream_id: settings.use_upstream_id,
target: settings.target.clone(),
delay: settings.delay,
delay_on_pare: settings.delay_on_para,
id_bridge: settings.id_bridge,
}
}
None => Relation {
from: val.from_meta.to_string(),
to: m_to.clone(),
selector: selector.clone(),
executor: Executor::new_auto(),
filter_before: settings.filter_before,
filter_after: settings.filter_after,
use_upstream_id: settings.use_upstream_id,
target: settings.target.clone(),
delay: settings.delay,
delay_on_pare: settings.delay_on_para,
id_bridge: settings.id_bridge,
}
};
debug!("load {}", val.get_string());
Ok(rtn)
}
async fn check_converter<MC, M>(meta_to: &str, meta_cache_getter: &MC, meta_getter: &M, settings: &RelationSettings) -> Result<Meta>
where MC: MetaCache, M: MetaDao
{
let m_to = meta_cache_getter.get(meta_to, meta_getter).await?;
if let Some(ts) = &settings.target.states {
if let Some(x) = &ts.add {
Relation::check_state(&m_to, x)?
};
if let Some(x) = &ts.remove {
Relation::check_state(&m_to, x)?
};
}
Ok(m_to)
}
fn check_state(m_to: &Meta, x: &Vec<String>) -> Result<()> {
let b = x.iter().filter(|one| { !m_to.has_state_name(one) }).collect::<Vec<&String>>();
if b.len() > 0 {
return Err(NatureError::VerifyError(format!("[to meta] did not defined state : {:?} ", b)));
}
Ok(())
}
pub fn relation_string(&self) -> String {
format!("{}->{}", self.from, self.to.meta_string()).to_owned()
}
}
#[cfg(test)]
mod test_from_raw {
use tokio::runtime::Runtime;
use nature_common::Protocol;
use crate::RawMeta;
use super::*;
#[test]
fn master_should_have_relation() {
let raw = RawRelation {
from_meta: "B:from:1".to_string(),
to_meta: "B:to:1".to_string(),
settings: "{}".to_string(),
flag: 1,
};
let mg = MetaMock {};
let mut rt = Runtime::new().unwrap();
let rtn = rt.block_on(Relation::from_raw(raw, &MetaCacheMasterMock {}, &mg)).unwrap();
assert_eq!(rtn.executor.protocol, Protocol::Auto);
}
#[test]
fn setting_error_test() {
let raw = RawRelation {
from_meta: "B:from:1".to_string(),
to_meta: "B:to:1".to_string(),
settings: "dd".to_string(),
flag: 1,
};
let mg = MetaMock {};
let mut rt = Runtime::new().unwrap();
let rtn = rt.block_on(Relation::from_raw(raw, &MetaCacheMock {}, &mg));
assert_eq!(rtn.err().unwrap().to_string().contains("relation[B:from:1 ---> B:to:1]"), true);
}
#[test]
fn one_group_is_ok() {
let settings = RelationSettings {
selector: None,
executor: Some(Executor {
protocol: Protocol::LocalRust,
url: "url_one".to_string(),
settings: "".to_string(),
}),
filter_before: vec![],
filter_after: vec![],
use_upstream_id: false,
target: Default::default(),
delay: 0,
delay_on_para: (0, 0),
id_bridge: false,
};
let raw = RawRelation {
from_meta: "B:from:1".to_string(),
to_meta: "B:to:1".to_string(),
settings: serde_json::to_string(&settings).unwrap(),
flag: 1,
};
let mg = MetaMock {};
let mut rt = Runtime::new().unwrap();
let rtn = rt.block_on(Relation::from_raw(raw, &MetaCacheMock {}, &mg));
assert_eq!(rtn.is_ok(), true);
}
#[derive(Copy, Clone)]
struct MetaCacheMasterMock;
#[async_trait]
impl MetaCache for MetaCacheMasterMock {
async fn get<M>(&self, m: &str, _getter: &M) -> Result<Meta> where M: MetaDao {
if m.eq("B:to:1") {
let mut rtn = Meta::from_string(m).unwrap();
let _ = rtn.set_setting(r#"{"master":"B:from:1"}"#);
Ok(rtn)
} else {
Meta::from_string(m)
}
}
}
#[derive(Copy, Clone)]
struct MetaCacheMock;
#[async_trait]
impl MetaCache for MetaCacheMock {
async fn get<M>(&self, meta_str: &str, _getter: &M) -> Result<Meta> where M: MetaDao {
Meta::from_string(meta_str)
}
}
#[derive(Copy, Clone)]
struct MetaMock;
#[async_trait]
impl MetaDao for MetaMock {
async fn get(&self, m: &str) -> Result<Option<RawMeta>> {
Ok(Some(RawMeta::from(Meta::from_string(m)?)))
}
async fn insert(&self, _define: &RawMeta) -> Result<usize> {
unimplemented!()
}
async fn update_flag(&self, _meta_str: &str, _flag_f: i32) -> Result<usize> {
unimplemented!()
}
async fn delete(&self, _m: &Meta) -> Result<usize> {
unimplemented!()
}
}
}