Skip to content

Commit 3902094

Browse files
committed
refactor: update spec formatting logic to use SpecFormatter trait
1 parent 945cb51 commit 3902094

File tree

3 files changed

+68
-137
lines changed

3 files changed

+68
-137
lines changed

python/cocoindex/flow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ def _render_spec(self, verbose: bool = False) -> Tree:
493493
return tree
494494

495495
def _get_spec(self, verbose: bool = False) -> list[tuple[str, str, int]]:
496-
return self._lazy_engine_flow().get_spec(format_mode="verbose" if verbose else "concise")
496+
return self._lazy_engine_flow().get_spec(output_mode="verbose" if verbose else "concise")
497497

498498
def _get_schema(self) -> list[tuple[str, str, str]]:
499499
return self._lazy_engine_flow().get_schema()

src/base/spec.rs

Lines changed: 62 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,34 @@ use crate::prelude::*;
22

33
use super::schema::{EnrichedValueType, FieldSchema};
44
use serde::{Deserialize, Serialize};
5-
use serde_json::Value;
65
use std::fmt;
76
use std::ops::Deref;
87

9-
// Define SpecFormatMode enum for type-safe formatting
8+
/// OutputMode enum for displaying spec info in different granularity
109
#[derive(Debug, Clone, Copy, PartialEq)]
11-
pub enum SpecFormatMode {
10+
pub enum OutputMode {
1211
Concise,
1312
Verbose,
1413
}
1514

16-
impl SpecFormatMode {
17-
pub fn from_str(s: &str) -> Result<Self, String> {
15+
impl OutputMode {
16+
pub fn from_str(s: &str) -> Self {
1817
match s.to_lowercase().as_str() {
19-
"concise" => Ok(SpecFormatMode::Concise),
20-
"verbose" => Ok(SpecFormatMode::Verbose),
21-
_ => Err(format!("Invalid format mode: {}", s)),
18+
"concise" => OutputMode::Concise,
19+
"verbose" => OutputMode::Verbose,
20+
_ => unreachable!(
21+
"Invalid format mode: {}. Expected 'concise' or 'verbose'.",
22+
s
23+
),
2224
}
2325
}
2426
}
2527

28+
/// Formatting spec per output mode
29+
pub trait SpecFormatter {
30+
fn format(&self, mode: OutputMode) -> String;
31+
}
32+
2633
#[derive(Debug, Clone, Serialize, Deserialize)]
2734
#[serde(tag = "kind")]
2835
pub enum SpecString {
@@ -233,69 +240,31 @@ pub struct OpSpec {
233240
pub spec: serde_json::Map<String, serde_json::Value>,
234241
}
235242

236-
impl OpSpec {
237-
pub fn format_concise(&self) -> String {
238-
let mut parts = vec![];
239-
for (key, value) in self.spec.iter() {
240-
match value {
241-
Value::String(s) => parts.push(format!("{}={}", key, s)),
242-
Value::Array(arr) => {
243-
let items = arr
244-
.iter()
245-
.filter_map(|v| v.as_str())
246-
.collect::<Vec<_>>()
247-
.join(",");
248-
if !items.is_empty() {
249-
parts.push(format!("{}={}", key, items));
250-
}
251-
}
252-
Value::Object(obj) => {
253-
if let Some(model) = obj.get("model").and_then(|v| v.as_str()) {
254-
parts.push(format!("{}={}", key, model));
255-
}
256-
}
257-
_ => {}
258-
}
259-
}
260-
if parts.is_empty() {
261-
self.kind.clone()
262-
} else {
263-
format!("{}({})", self.kind, parts.join(", "))
264-
}
265-
}
266-
267-
pub fn format_verbose(&self) -> String {
268-
let spec_str = serde_json::to_string_pretty(&self.spec)
269-
.map(|s| {
270-
let lines: Vec<&str> = s.lines().collect();
271-
if lines.len() < s.lines().count() {
272-
lines
273-
.into_iter()
274-
.chain(["..."])
275-
.collect::<Vec<_>>()
276-
.join("\n ")
277-
} else {
278-
lines.join("\n ")
279-
}
280-
})
281-
.unwrap_or("#serde_error".to_string());
282-
format!("{}({})", self.kind, spec_str)
283-
}
284-
285-
pub fn format(&self, mode: SpecFormatMode) -> String {
243+
impl SpecFormatter for OpSpec {
244+
fn format(&self, mode: OutputMode) -> String {
286245
match mode {
287-
SpecFormatMode::Concise => self.format_concise(),
288-
SpecFormatMode::Verbose => self.format_verbose(),
246+
OutputMode::Concise => self.kind.clone(),
247+
OutputMode::Verbose => {
248+
let spec_str = serde_json::to_string_pretty(&self.spec)
249+
.map(|s| {
250+
let lines: Vec<&str> = s.lines().collect();
251+
if lines.len() < s.lines().count() {
252+
lines
253+
.into_iter()
254+
.chain(["..."])
255+
.collect::<Vec<_>>()
256+
.join("\n ")
257+
} else {
258+
lines.join("\n ")
259+
}
260+
})
261+
.unwrap_or("#serde_error".to_string());
262+
format!("{}({})", self.kind, spec_str)
263+
}
289264
}
290265
}
291266
}
292267

293-
impl fmt::Display for OpSpec {
294-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295-
write!(f, "{}", self.format_concise())
296-
}
297-
}
298-
299268
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
300269
pub struct SourceRefreshOptions {
301270
pub refresh_interval: Option<std::time::Duration>,
@@ -319,19 +288,16 @@ pub struct ImportOpSpec {
319288
pub refresh_options: SourceRefreshOptions,
320289
}
321290

322-
impl ImportOpSpec {
323-
pub fn format(&self, mode: SpecFormatMode) -> String {
324-
let source = match mode {
325-
SpecFormatMode::Concise => self.source.format_concise(),
326-
SpecFormatMode::Verbose => self.source.format_verbose(),
327-
};
291+
impl SpecFormatter for ImportOpSpec {
292+
fn format(&self, mode: OutputMode) -> String {
293+
let source = self.source.format(mode);
328294
format!("source={}, refresh={}", source, self.refresh_options)
329295
}
330296
}
331297

332298
impl fmt::Display for ImportOpSpec {
333299
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334-
write!(f, "{}", self.format(SpecFormatMode::Concise))
300+
write!(f, "{}", self.format(OutputMode::Concise))
335301
}
336302
}
337303

@@ -341,31 +307,22 @@ pub struct TransformOpSpec {
341307
pub op: OpSpec,
342308
}
343309

344-
impl TransformOpSpec {
345-
pub fn format(&self, mode: SpecFormatMode) -> String {
310+
impl SpecFormatter for TransformOpSpec {
311+
fn format(&self, mode: OutputMode) -> String {
346312
let inputs = self
347313
.inputs
348314
.iter()
349315
.map(ToString::to_string)
350316
.collect::<Vec<_>>()
351317
.join(",");
352-
let op_str = match mode {
353-
SpecFormatMode::Concise => self.op.format_concise(),
354-
SpecFormatMode::Verbose => self.op.format_verbose(),
355-
};
318+
let op_str = self.op.format(mode);
356319
match mode {
357-
SpecFormatMode::Concise => format!("op={}, inputs={}", op_str, inputs),
358-
SpecFormatMode::Verbose => format!("op={}, inputs=[{}]", op_str, inputs),
320+
OutputMode::Concise => format!("op={}, inputs={}", op_str, inputs),
321+
OutputMode::Verbose => format!("op={}, inputs=[{}]", op_str, inputs),
359322
}
360323
}
361324
}
362325

363-
impl fmt::Display for TransformOpSpec {
364-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365-
write!(f, "{}", self.format(SpecFormatMode::Concise))
366-
}
367-
}
368-
369326
#[derive(Debug, Clone, Serialize, Deserialize)]
370327
pub struct ForEachOpSpec {
371328
/// Mapping that provides a table to apply reactive operations to.
@@ -377,21 +334,17 @@ impl ForEachOpSpec {
377334
pub fn get_label(&self) -> String {
378335
format!("Loop over {}", self.field_path)
379336
}
337+
}
380338

381-
pub fn format(&self, mode: SpecFormatMode) -> String {
339+
impl SpecFormatter for ForEachOpSpec {
340+
fn format(&self, mode: OutputMode) -> String {
382341
match mode {
383-
SpecFormatMode::Concise => self.get_label(),
384-
SpecFormatMode::Verbose => format!("field={}", self.field_path),
342+
OutputMode::Concise => self.get_label(),
343+
OutputMode::Verbose => format!("field={}", self.field_path),
385344
}
386345
}
387346
}
388347

389-
impl fmt::Display for ForEachOpSpec {
390-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391-
write!(f, "field={}", self.field_path)
392-
}
393-
}
394-
395348
#[derive(Debug, Clone, Serialize, Deserialize)]
396349
pub struct CollectOpSpec {
397350
/// Field values to be collected.
@@ -405,17 +358,17 @@ pub struct CollectOpSpec {
405358
pub auto_uuid_field: Option<FieldName>,
406359
}
407360

408-
impl CollectOpSpec {
409-
pub fn format(&self, mode: SpecFormatMode) -> String {
361+
impl SpecFormatter for CollectOpSpec {
362+
fn format(&self, mode: OutputMode) -> String {
410363
let uuid = self.auto_uuid_field.as_deref().unwrap_or("none");
411364
match mode {
412-
SpecFormatMode::Concise => {
365+
OutputMode::Concise => {
413366
format!(
414367
"collector={}, input={}, uuid={}",
415368
self.collector_name, self.input, uuid
416369
)
417370
}
418-
SpecFormatMode::Verbose => {
371+
OutputMode::Verbose => {
419372
format!(
420373
"scope={}, collector={}, input=[{}], uuid={}",
421374
self.scope_name, self.collector_name, self.input, uuid
@@ -425,12 +378,6 @@ impl CollectOpSpec {
425378
}
426379
}
427380

428-
impl fmt::Display for CollectOpSpec {
429-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430-
write!(f, "{}", self.format(SpecFormatMode::Concise))
431-
}
432-
}
433-
434381
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
435382
pub enum VectorSimilarityMetric {
436383
CosineSimilarity,
@@ -493,29 +440,20 @@ pub struct ExportOpSpec {
493440
pub setup_by_user: bool,
494441
}
495442

496-
impl ExportOpSpec {
497-
pub fn format(&self, mode: SpecFormatMode) -> String {
498-
let target_str = match mode {
499-
SpecFormatMode::Concise => self.target.format_concise(),
500-
SpecFormatMode::Verbose => self.target.format_verbose(),
501-
};
443+
impl SpecFormatter for ExportOpSpec {
444+
fn format(&self, mode: OutputMode) -> String {
445+
let target_str = self.target.format(mode);
502446
let base = format!(
503447
"collector={}, target={}, {}",
504448
self.collector_name, target_str, self.index_options
505449
);
506450
match mode {
507-
SpecFormatMode::Concise => base,
508-
SpecFormatMode::Verbose => format!("{}, setup_by_user={}", base, self.setup_by_user),
451+
OutputMode::Concise => base,
452+
OutputMode::Verbose => format!("{}, setup_by_user={}", base, self.setup_by_user),
509453
}
510454
}
511455
}
512456

513-
impl fmt::Display for ExportOpSpec {
514-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515-
write!(f, "{}", self.format(SpecFormatMode::Concise))
516-
}
517-
}
518-
519457
#[derive(Debug, Clone, Serialize, Deserialize)]
520458
#[serde(tag = "action")]
521459
pub enum ReactiveOpSpec {
@@ -524,25 +462,19 @@ pub enum ReactiveOpSpec {
524462
Collect(CollectOpSpec),
525463
}
526464

527-
impl ReactiveOpSpec {
528-
pub fn format(&self, mode: SpecFormatMode) -> String {
465+
impl SpecFormatter for ReactiveOpSpec {
466+
fn format(&self, mode: OutputMode) -> String {
529467
match self {
530468
ReactiveOpSpec::Transform(t) => format!("Transform: {}", t.format(mode)),
531469
ReactiveOpSpec::ForEach(fe) => match mode {
532-
SpecFormatMode::Concise => format!("{}", fe.get_label()),
533-
SpecFormatMode::Verbose => format!("ForEach: {}", fe.format(mode)),
470+
OutputMode::Concise => format!("{}", fe.get_label()),
471+
OutputMode::Verbose => format!("ForEach: {}", fe.format(mode)),
534472
},
535473
ReactiveOpSpec::Collect(c) => format!("Collect: {}", c.format(mode)),
536474
}
537475
}
538476
}
539477

540-
impl fmt::Display for ReactiveOpSpec {
541-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542-
write!(f, "{}", self.format(SpecFormatMode::Concise))
543-
}
544-
}
545-
546478
#[derive(Debug, Clone, Serialize, Deserialize)]
547479
pub struct ReactiveOpScope {
548480
pub name: ScopeName,

src/py/mod.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::prelude::*;
22

33
use crate::base::schema::{FieldSchema, ValueType};
4-
use crate::base::spec::SpecFormatMode;
54
use crate::base::spec::VectorSimilarityMetric;
65
use crate::base::spec::{NamedSpec, ReactiveOpSpec};
6+
use crate::base::spec::{OutputMode, SpecFormatter};
77
use crate::execution::query;
88
use crate::lib_context::{clear_lib_context, get_auth_registry, init_lib_context};
99
use crate::ops::interface::{QueryResult, QueryResults};
@@ -198,10 +198,9 @@ impl Flow {
198198
})
199199
}
200200

201-
#[pyo3(signature = (format_mode="concise"))]
202-
pub fn get_spec(&self, format_mode: &str) -> PyResult<Vec<(String, String, u32)>> {
203-
let mode = SpecFormatMode::from_str(format_mode)
204-
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e))?;
201+
#[pyo3(signature = (output_mode="concise"))]
202+
pub fn get_spec(&self, output_mode: &str) -> PyResult<Vec<(String, String, u32)>> {
203+
let mode = OutputMode::from_str(output_mode);
205204
let spec = &self.0.flow.flow_instance;
206205
let mut result = Vec::with_capacity(
207206
1 + spec.import_ops.len()
@@ -234,7 +233,7 @@ impl Flow {
234233
fn walk(
235234
op: &NamedSpec<ReactiveOpSpec>,
236235
indent: u32,
237-
mode: SpecFormatMode,
236+
mode: OutputMode,
238237
out: &mut Vec<(String, String, u32)>,
239238
) {
240239
out.push((

0 commit comments

Comments
 (0)