use arcstr::{literal, ArcStr};
use indexmap::IndexMap;
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{Deserialize, Serialize};
use crate::{model::{DataRef, Graph, NodeRef, SPath, StofData, SELF_STR_KEYWORD, SUPER_STR_KEYWORD}, runtime::{Val, Variable}};
pub const NOEXPORT_FIELD_ATTR: ArcStr = literal!("no-export");
pub const NOFIELD_FIELD_ATTR: ArcStr = literal!("no-field");
pub const PRIVATE_FIELD_ATTR: ArcStr = literal!("private");
pub const READ_ONLY_FIELD_ATTR: ArcStr = literal!("readonly");
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Field {
pub value: Variable,
pub attributes: FxHashMap<String, Val>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldDoc {
pub field: DataRef,
pub docs: String,
}
#[typetag::serde(name = "Field")]
impl StofData for Field {
fn core_data(&self) -> bool {
return true;
}
fn hard_node_ref(&self, node: &NodeRef) -> bool {
if let Some(nref) = self.value.try_obj() {
&nref == node
} else {
false
}
}
fn deep_copy(&self, graph: &mut Graph, context: Option<NodeRef>) -> Box::<dyn StofData> {
let copied = self.value.deep_copy(graph, context);
Box::new(Field {
value: copied,
attributes: self.attributes.clone(),
})
}
}
#[typetag::serde(name = "FieldDoc")]
impl StofData for FieldDoc {
fn core_data(&self) -> bool {
return true;
}
}
impl FieldDoc {
pub fn docs(graph: &Graph, node: &NodeRef) -> FxHashMap<DataRef, String> {
let mut docs = FxHashMap::default();
if let Some(node) = node.node(graph) {
for (_, dref) in &node.data {
if let Some(doc) = graph.get_stof_data::<Self>(dref) {
docs.insert(doc.field.clone(), doc.docs.clone());
}
}
}
docs
}
}
impl Field {
pub fn new(value: Variable, attrs: Option<FxHashMap<String, Val>>) -> Self {
let mut attributes = FxHashMap::default();
if let Some(attr) = attrs {
attributes = attr;
}
Self {
value,
attributes
}
}
pub fn can_set(&self) -> bool {
!self.attributes.contains_key(READ_ONLY_FIELD_ATTR.as_str())
}
pub fn is_private(&self) -> bool {
self.attributes.contains_key(PRIVATE_FIELD_ATTR.as_str())
}
pub fn field_from_path(graph: &mut Graph, path: &str, start: Option<NodeRef>) -> Option<DataRef> {
let mut spath = SPath::from(path);
if spath.path.is_empty() { return None; }
let field_name = spath.path.pop().unwrap();
if let Some(node) = SPath::node(&graph, spath, start) {
return Self::field(graph, &node, field_name.as_ref());
}
None
}
#[inline]
pub fn direct_field(graph: &Graph, node: &NodeRef, field_name: &str) -> Option<DataRef> {
if let Some(node) = node.node(graph) {
if let Some(dref) = node.data.get(field_name) {
if let Some(field) = graph.get_stof_data::<Self>(dref) {
if !field.value.dangling_obj(graph) {
return Some(dref.clone());
}
}
}
}
None
}
pub fn field(graph: &mut Graph, node: &NodeRef, field_name: &str) -> Option<DataRef> {
let mut created = None;
let mut self_parent = None;
let mut self_name = None;
if let Some(node) = node.node(&graph) {
if let Some(dref) = node.data.get(field_name) {
if let Some(field) = graph.get_stof_data::<Self>(dref) {
if !field.value.dangling_obj(graph) {
return Some(dref.clone());
}
}
}
for child in &node.children {
if let Some(child) = child.node(&graph) {
if child.name.as_ref() == field_name && child.is_field() {
let mut attrs = child.attributes.clone();
attrs.insert(NOEXPORT_FIELD_ATTR.to_string(), Val::Null); let var = Variable::new(graph, true, Val::Obj(child.id.clone()), false);
created = Some(Self::new(var, Some(attrs)));
break;
}
}
}
if created.is_none() {
let mut gp = None;
if let Some(parent) = &node.parent {
if let Some(parent) = parent.node(&graph) {
if (parent.name.as_ref() == field_name || field_name == &SUPER_STR_KEYWORD) && parent.is_field() {
if let Some(grand) = &parent.parent {
if grand.node_exists(&graph) {
gp = Some((grand.clone(), parent.name.clone()));
}
}
}
}
}
if let Some((gp, field_name)) = gp {
return Self::field(graph, &gp, field_name.as_ref());
}
if (node.name.as_ref() == field_name || field_name == &SELF_STR_KEYWORD) && node.is_field() {
if let Some(parent) = &node.parent {
if parent.node_exists(graph) {
self_parent = Some(parent.clone());
self_name = Some(node.name.clone());
}
}
}
}
}
if let Some(field) = created {
if let Some(dref) = graph.insert_stof_data(node, field_name, Box::new(field), None) {
return Some(dref);
}
}
if let Some(parent) = self_parent {
if let Some(name) = self_name {
return Self::field(graph, &parent, name.as_ref());
}
}
None
}
pub fn fields_len(graph: &Graph, node: &NodeRef) -> i64 {
let mut len = 0;
if let Some(node) = node.node(&graph) {
let mut seen = FxHashSet::default();
for (name, dref) in &node.data {
if let Some(field) = graph.get_stof_data::<Self>(dref) {
if !field.value.dangling_obj(graph) {
seen.insert(name.as_str());
len += 1;
}
}
}
for child in &node.children {
if let Some(child) = child.node(&graph) {
if child.is_field() && !seen.contains(child.name.as_ref()) {
len += 1;
seen.insert(child.name.as_ref());
}
}
}
}
len
}
pub fn fields_at(graph: &mut Graph, node: &NodeRef, index: usize) -> Option<(String, DataRef)> {
let mut current = 0;
let mut seen_names = FxHashSet::default();
let mut to_create = Vec::new();
if let Some(node) = node.node(&graph) {
for (name, dref) in &node.data {
if let Some(field) = graph.get_stof_data::<Self>(dref) {
if !field.value.dangling_obj(graph) {
if current == index {
return Some((name.clone(), dref.clone()));
}
current += 1;
seen_names.insert(name.as_str());
}
}
}
for child in &node.children {
if let Some(child) = child.node(&graph) {
if child.is_field() && !seen_names.contains(child.name.as_ref()) {
let mut attrs = child.attributes.clone();
attrs.insert(NOEXPORT_FIELD_ATTR.to_string(), Val::Null); let var = Variable::new(graph, true, Val::Obj(child.id.clone()), false);
to_create.push((child.name.clone(), Self::new(var, Some(attrs))));
}
}
}
}
let mut res = None;
for (name, field) in to_create {
if let Some(dref) = graph.insert_stof_data(node, name.clone(), Box::new(field), None) {
if current == index {
if res.is_none() { res = Some((name.to_string(), dref)); }
} else {
current += 1;
}
}
}
res
}
pub fn fields(graph: &mut Graph, node: &NodeRef) -> IndexMap<String, DataRef> {
let mut fields = IndexMap::default();
let mut to_create = Vec::new();
if let Some(node) = node.node(&graph) {
for (name, dref) in &node.data {
if let Some(field) = graph.get_stof_data::<Self>(dref) {
if !field.value.dangling_obj(graph) {
fields.insert(name.clone(), dref.clone());
}
}
}
for child in &node.children {
if let Some(child) = child.node(&graph) {
if child.is_field() && !fields.contains_key(child.name.as_ref()) {
let mut attrs = child.attributes.clone();
attrs.insert(NOEXPORT_FIELD_ATTR.to_string(), Val::Null); let var = Variable::new(graph, true, Val::Obj(child.id.clone()), false);
to_create.push((child.name.clone(), Self::new(var, Some(attrs))));
}
}
}
}
for (name, field) in to_create {
if let Some(dref) = graph.insert_stof_data(node, name.clone(), Box::new(field), None) {
fields.insert(name.to_string(), dref);
}
}
fields
}
}