<commit_before>import React from 'react'
import View from 'app/components/rules_v2/rule'
import Menu from 'app/components/rules_v2/rulemenu'
import Loading from 'app/components/loading'
import i18n from 'app/utils/i18n'
import {object_is_equal, merge, map_set, map_get, concat} from 'app/utils'
import cache from 'app/utils/cache'
import rpc from 'app/rpc'
import Flash from 'app/flash'
import { goto } from 'app/utils/store'

import Description from 'app/components/rules_v2/edit/description'
import Service from 'app/components/rules_v2/edit/service'
import Trigger from 'app/components/rules_v2/edit/trigger'
import Params from 'app/components/rules_v2/edit/params'
import Condition from 'app/components/rules_v2/edit/condition'
import ActionEdit from 'app/components/rules_v2/edit/action'
import AddNode from 'app/components/rules_v2/edit/addnode'
import Error from 'app/components/error'

function find_action(actions, path){
  if (path.length==0)
    return actions
  let head = path[0]
  let rest = path.slice(1)
  return find_action(actions[path[0]], rest)
}

function update_rule(rule, path, value){
  if (path.length==0){
      switch (rule.type){
        case "action":
        case "condition":
          return merge(rule, value)
        case "add":
          return value
        default:
          console.error("Invalid rule leaf node for updating: %o", rule)
          return rule
      }
  }
  const [head, ...rest] = path
  if (!isNaN(head)){ // an index of an array
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    const item = update_rule(rule[head], rest, value)
    const ret = [...before, item, ...after]
    console.assert(ret.length==rule.length, ret.length, rule.length, rule, before, head, after)
    return ret
  }

  return merge(rule, {[head]: update_rule(rule[head], rest, value)})
}

function insert_add(rule, path){
  if (path.length == 1){
    return rule.concat({type: "add"})
  }
  const [head, ...rest] = path
  if (!isNaN(head)){
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    const item = insert_add(rule[head], rest)
    const ret = [...before, item, ...after]
    console.assert(ret.length==rule.length, ret.length, rule.length, rule, before, head, after)
    return ret
  }

  return {...rule, [head]: insert_add(rule[head], rest) }
}

function remove_step(rule, path){
  const [head, ...rest] = path
  if (path.length==1){ // can only remove from arrays. conditionals make no sense no then branch.
    console.assert(!isNaN(head), "%o is not a number", head)
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    return [...before, ...after]
  }
  const item = remove_step(rule[head], rest)
  if (!isNaN(head)){ // Replace (or remove) the element
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    const ret = [...before, item, ...after]
    console.assert(ret.length==rule.length, ret.length, rule.length, rule, before, head, after)
    return ret
  }
  else{ // is a conditional, change the branch
    return {...rule, [head]: item}
  }
}

function prepare_node_list(current){
  if (!current)
    return []
  if (current.type=="condition"){
    let ret=[[]] // condition is a terminal in itself
    for (let y of prepare_node_list(current.then)){
      ret.push(["then", ...y])
    }
    for (let y of prepare_node_list(current.else)){
      ret.push(["else", ...y])
    }
    return ret
  }
  if (current.type=="action"){
    return [[]] // this is a terminal, empty list to append at the end of the path
  }
  if (current.type=="add"){
    return [[]] // this is a terminal
  }
  let ret = []
  for (let x in current){
    for (let y of prepare_node_list(current[x])){
      ret.push([x, ...y])
    }
  }
  return ret
}

function get_next(current, path){
  const nl = prepare_node_list(current)
  let stop=false // mark stop on next iteration
  for (let i of nl){
    if (stop)
      return i
    if (object_is_equal(i,path))
      stop=true
  }
  return [0]
}
function get_prev(current, path){
  const nl = prepare_node_list(current)
  let prev="when:params"
  for (let i of nl){
    if (object_is_equal(i,path))
      return prev
    prev=i
  }
  return "when:params"
}

function decorate_actions(actions){
  if (actions.length!=undefined){<commit_msg>Improved resilence to bad formed rules<commit_after>import React from 'react'
import View from 'app/components/rules_v2/rule'
import Menu from 'app/components/rules_v2/rulemenu'
import Loading from 'app/components/loading'
import i18n from 'app/utils/i18n'
import {object_is_equal, merge, map_set, map_get, concat} from 'app/utils'
import cache from 'app/utils/cache'
import rpc from 'app/rpc'
import Flash from 'app/flash'
import { goto } from 'app/utils/store'

import Description from 'app/components/rules_v2/edit/description'
import Service from 'app/components/rules_v2/edit/service'
import Trigger from 'app/components/rules_v2/edit/trigger'
import Params from 'app/components/rules_v2/edit/params'
import Condition from 'app/components/rules_v2/edit/condition'
import ActionEdit from 'app/components/rules_v2/edit/action'
import AddNode from 'app/components/rules_v2/edit/addnode'
import Error from 'app/components/error'

function find_action(actions, path){
  if (path.length==0)
    return actions
  let head = path[0]
  let rest = path.slice(1)
  return find_action(actions[path[0]], rest)
}

function update_rule(rule, path, value){
  if (path.length==0){
      switch (rule.type){
        case "action":
        case "condition":
          return merge(rule, value)
        case "add":
          return value
        default:
          console.error("Invalid rule leaf node for updating: %o", rule)
          return rule
      }
  }
  const [head, ...rest] = path
  if (!isNaN(head)){ // an index of an array
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    const item = update_rule(rule[head], rest, value)
    const ret = [...before, item, ...after]
    console.assert(ret.length==rule.length, ret.length, rule.length, rule, before, head, after)
    return ret
  }

  return merge(rule, {[head]: update_rule(rule[head], rest, value)})
}

function insert_add(rule, path){
  if (path.length == 1){
    return rule.concat({type: "add"})
  }
  const [head, ...rest] = path
  if (!isNaN(head)){
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    const item = insert_add(rule[head], rest)
    const ret = [...before, item, ...after]
    console.assert(ret.length==rule.length, ret.length, rule.length, rule, before, head, after)
    return ret
  }

  return {...rule, [head]: insert_add(rule[head], rest) }
}

function remove_step(rule, path){
  const [head, ...rest] = path
  if (path.length==1){ // can only remove from arrays. conditionals make no sense no then branch.
    console.assert(!isNaN(head), "%o is not a number", head)
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    return [...before, ...after]
  }
  const item = remove_step(rule[head], rest)
  if (!isNaN(head)){ // Replace (or remove) the element
    const before=(head>0) ? rule.slice(0, head) : []
    const after=rule.slice(head+1)
    const ret = [...before, item, ...after]
    console.assert(ret.length==rule.length, ret.length, rule.length, rule, before, head, after)
    return ret
  }
  else{ // is a conditional, change the branch
    return {...rule, [head]: item}
  }
}

function prepare_node_list(current){
  if (!current)
    return []
  if (current.type=="condition"){
    let ret=[[]] // condition is a terminal in itself
    for (let y of prepare_node_list(current.then)){
      ret.push(["then", ...y])
    }
    for (let y of prepare_node_list(current.else)){
      ret.push(["else", ...y])
    }
    return ret
  }
  if (current.type=="action"){
    return [[]] // this is a terminal, empty list to append at the end of the path
  }
  if (current.type=="add"){
    return [[]] // this is a terminal
  }
  let ret = []
  for (let x in current){
    for (let y of prepare_node_list(current[x])){
      ret.push([x, ...y])
    }
  }
  return ret
}

function get_next(current, path){
  const nl = prepare_node_list(current)
  let stop=false // mark stop on next iteration
  for (let i of nl){
    if (stop)
      return i
    if (object_is_equal(i,path))
      stop=true
  }
  return [0]
}
function get_prev(current, path){
  const nl = prepare_node_list(current)
  let prev="when:params"
  for (let i of nl){
    if (object_is_equal(i,path))
      return prev
    prev=i
  }
  return "when:params"
}

function decorate_actions(actions){
  if (!actions)
    return []
  if (actions.length!=undefined){
{'fraction_non_alphanumeric': 0.14682133515296258, 'fraction_numerical': 0.007505970658478335, 'mean_word_length': 3.33629191321499, 'pattern_counts': {'":': 6, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '663592', 'n_tokens_mistral': 2934, 'n_tokens_neox': 2921, 'n_words': 825}