Skip to main content

recoco_core/builder/
plan.rs

1// ReCoco is a Rust-only fork of CocoIndex, by [CocoIndex](https://CocoIndex)
2// Original code from CocoIndex is copyrighted by CocoIndex
3// SPDX-FileCopyrightText: 2025-2026 CocoIndex (upstream)
4// SPDX-FileContributor: CocoIndex Contributors
5//
6// All modifications from the upstream for ReCoco are copyrighted by Knitli Inc.
7// SPDX-FileCopyrightText: 2026 Knitli Inc. (ReCoco)
8// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
9//
10// Both the upstream CocoIndex code and the ReCoco modifications are licensed under the Apache-2.0 License.
11// SPDX-License-Identifier: Apache-2.0
12
13use crate::base::schema::FieldSchema;
14use crate::base::spec::FieldName;
15use crate::prelude::*;
16
17use crate::ops::interface::*;
18use std::time::Duration;
19use utils::fingerprint::{Fingerprint, Fingerprinter};
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
22pub struct AnalyzedLocalFieldReference {
23    /// Must be non-empty.
24    pub fields_idx: Vec<u32>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
28pub struct AnalyzedFieldReference {
29    pub local: AnalyzedLocalFieldReference,
30    /// How many levels up the scope the field is at.
31    /// 0 means the current scope.
32    #[serde(skip_serializing_if = "u32_is_zero")]
33    pub scope_up_level: u32,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
37pub struct AnalyzedLocalCollectorReference {
38    pub collector_idx: u32,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
42pub struct AnalyzedCollectorReference {
43    pub local: AnalyzedLocalCollectorReference,
44    /// How many levels up the scope the field is at.
45    /// 0 means the current scope.
46    #[serde(skip_serializing_if = "u32_is_zero")]
47    pub scope_up_level: u32,
48}
49
50#[derive(Debug, Clone, Serialize)]
51pub struct AnalyzedStructMapping {
52    pub fields: Vec<AnalyzedValueMapping>,
53}
54
55#[derive(Debug, Clone, Serialize)]
56#[serde(tag = "kind")]
57pub enum AnalyzedValueMapping {
58    Constant { value: value::Value },
59    Field(AnalyzedFieldReference),
60    Struct(AnalyzedStructMapping),
61}
62
63#[derive(Debug, Clone)]
64pub struct AnalyzedOpOutput {
65    pub field_idx: u32,
66}
67
68/// Tracks which affects value of the field, to detect changes of logic.
69#[derive(Debug, Clone)]
70pub struct FieldDefFingerprint {
71    /// Name of sources that affect value of the field.
72    pub source_op_names: HashSet<String>,
73    /// Fingerprint of the logic that affects value of the field.
74    pub fingerprint: Fingerprint,
75}
76
77impl Default for FieldDefFingerprint {
78    fn default() -> Self {
79        Self {
80            source_op_names: HashSet::new(),
81            fingerprint: Fingerprinter::default().into_fingerprint(),
82        }
83    }
84}
85
86pub struct AnalyzedImportOp {
87    pub name: String,
88    pub executor: Box<dyn SourceExecutor>,
89    pub output: AnalyzedOpOutput,
90    pub primary_key_schema: Box<[FieldSchema]>,
91    pub refresh_options: spec::SourceRefreshOptions,
92
93    pub concurrency_controller: concur_control::CombinedConcurrencyController,
94}
95
96pub struct AnalyzedFunctionExecInfo {
97    pub enable_cache: bool,
98    pub timeout: Option<Duration>,
99    pub behavior_version: Option<u32>,
100
101    /// Fingerprinter of the function's behavior.
102    pub fingerprinter: Fingerprinter,
103    /// To deserialize cached value.
104    pub output_type: schema::ValueType,
105}
106
107pub struct AnalyzedTransformOp {
108    pub name: String,
109    pub op_kind: String,
110    pub inputs: Vec<AnalyzedValueMapping>,
111    pub function_exec_info: AnalyzedFunctionExecInfo,
112    pub executor: Box<dyn SimpleFunctionExecutor>,
113    pub output: AnalyzedOpOutput,
114}
115
116pub struct AnalyzedForEachOp {
117    pub name: String,
118    pub local_field_ref: AnalyzedLocalFieldReference,
119    pub op_scope: AnalyzedOpScope,
120    pub concurrency_controller: concur_control::ConcurrencyController,
121}
122
123pub struct AnalyzedCollectOp {
124    pub name: String,
125    pub has_auto_uuid_field: bool,
126    pub input: AnalyzedStructMapping,
127    pub input_field_names: Vec<FieldName>,
128    pub collector_schema: Arc<schema::CollectorSchema>,
129    pub collector_ref: AnalyzedCollectorReference,
130    /// Pre-computed mapping from collector field index to input field index.
131    pub field_index_mapping: Vec<Option<usize>>,
132    /// Fingerprinter of the collector's schema. Used to decide when to reuse auto-generated UUIDs.
133    pub fingerprinter: Fingerprinter,
134}
135
136pub enum AnalyzedPrimaryKeyDef {
137    Fields(Vec<usize>),
138}
139
140pub struct AnalyzedExportOp {
141    pub name: String,
142    pub input: AnalyzedLocalCollectorReference,
143    pub export_target_factory: Arc<dyn TargetFactory + Send + Sync>,
144    pub export_context: Arc<dyn Any + Send + Sync>,
145    pub primary_key_def: AnalyzedPrimaryKeyDef,
146    pub primary_key_schema: Box<[FieldSchema]>,
147    /// idx for value fields - excluding the primary key field.
148    pub value_fields: Vec<u32>,
149    /// If true, value is never changed on the same primary key.
150    /// This is guaranteed if the primary key contains auto-generated UUIDs.
151    pub value_stable: bool,
152    /// Fingerprinter of the output value.
153    pub output_value_fingerprinter: Fingerprinter,
154    pub def_fp: FieldDefFingerprint,
155}
156
157pub struct AnalyzedExportTargetOpGroup {
158    pub target_factory: Arc<dyn TargetFactory + Send + Sync>,
159    pub target_kind: String,
160    pub op_idx: Vec<usize>,
161}
162
163pub enum AnalyzedReactiveOp {
164    Transform(AnalyzedTransformOp),
165    ForEach(AnalyzedForEachOp),
166    Collect(AnalyzedCollectOp),
167}
168
169pub struct AnalyzedOpScope {
170    pub reactive_ops: Vec<AnalyzedReactiveOp>,
171    pub collector_len: usize,
172    pub scope_qualifier: String,
173}
174
175pub struct ExecutionPlan {
176    pub legacy_fingerprint: Vec<Fingerprint>,
177    pub import_ops: Vec<AnalyzedImportOp>,
178    pub op_scope: AnalyzedOpScope,
179    pub export_ops: Vec<AnalyzedExportOp>,
180    pub export_op_groups: Vec<AnalyzedExportTargetOpGroup>,
181}
182
183pub struct TransientExecutionPlan {
184    pub input_fields: Vec<AnalyzedOpOutput>,
185    pub op_scope: AnalyzedOpScope,
186    pub output_value: AnalyzedValueMapping,
187}
188
189fn u32_is_zero(v: &u32) -> bool {
190    *v == 0
191}