thread_flow/functions/
parse.rs1use async_trait::async_trait;
5use recoco::base::value::Value;
6use recoco::ops::factory_bases::SimpleFunctionFactoryBase;
7use recoco::ops::interface::{FlowInstanceContext, SimpleFunctionExecutor};
8use recoco::ops::sdk::{OpArgsResolver, SimpleFunctionAnalysisOutput};
9use serde::Deserialize;
10use std::sync::Arc;
11
12pub struct ThreadParseFactory;
14
15#[derive(Debug, Clone, Deserialize)]
17pub struct ThreadParseSpec {}
18
19#[async_trait]
20impl SimpleFunctionFactoryBase for ThreadParseFactory {
21 type Spec = ThreadParseSpec;
22 type ResolvedArgs = ();
23
24 fn name(&self) -> &str {
25 "thread_parse"
26 }
27
28 async fn analyze<'a>(
29 &'a self,
30 _spec: &'a Self::Spec,
31 _args_resolver: &mut OpArgsResolver<'a>,
32 _context: &FlowInstanceContext,
33 ) -> Result<SimpleFunctionAnalysisOutput<Self::ResolvedArgs>, recoco::prelude::Error> {
34 Ok(SimpleFunctionAnalysisOutput {
35 resolved_args: (),
36 output_schema: crate::conversion::get_thread_parse_output_schema(),
37 behavior_version: Some(1),
38 })
39 }
40
41 async fn build_executor(
42 self: Arc<Self>,
43 _spec: Self::Spec,
44 _resolved_args: Self::ResolvedArgs,
45 _context: Arc<FlowInstanceContext>,
46 ) -> Result<impl SimpleFunctionExecutor, recoco::prelude::Error> {
47 Ok(ThreadParseExecutor)
48 }
49}
50
51pub struct ThreadParseExecutor;
53
54#[async_trait]
55impl SimpleFunctionExecutor for ThreadParseExecutor {
56 async fn evaluate(&self, input: Vec<Value>) -> Result<Value, recoco::prelude::Error> {
57 let content = input
59 .first()
60 .ok_or_else(|| recoco::prelude::Error::client("Missing content"))?
61 .as_str()
62 .map_err(|e| recoco::prelude::Error::client(e.to_string()))?;
63
64 let lang_str = input
65 .get(1)
66 .ok_or_else(|| recoco::prelude::Error::client("Missing language"))?
67 .as_str()
68 .map_err(|e| recoco::prelude::Error::client(e.to_string()))?;
69
70 let path_str = input
71 .get(2)
72 .and_then(|v| v.as_str().ok())
73 .map(|v| v.to_string())
74 .unwrap_or_else(|| "unknown".to_string());
75
76 let lang = thread_language::from_extension_str(lang_str)
81 .or_else(|| {
82 let p = std::path::PathBuf::from(format!("dummy.{}", lang_str));
84 thread_language::from_extension(&p)
85 })
86 .ok_or_else(|| {
87 recoco::prelude::Error::client(format!("Unsupported language: {}", lang_str))
88 })?;
89
90 use thread_ast_engine::tree_sitter::LanguageExt;
92 let root = lang.ast_grep(content);
93
94 let fingerprint = thread_services::conversion::compute_content_fingerprint(content);
96
97 let path = std::path::PathBuf::from(&path_str);
99 let mut doc =
100 thread_services::conversion::root_to_parsed_document(root, path, lang, fingerprint);
101
102 thread_services::conversion::extract_basic_metadata(&doc)
104 .map(|metadata| {
105 doc.metadata = metadata;
106 })
107 .map_err(|e| {
108 recoco::prelude::Error::internal_msg(format!("Extraction error: {}", e))
109 })?;
110
111 use crate::conversion::serialize_parsed_doc;
115 serialize_parsed_doc(&doc)
116 }
117
118 fn enable_cache(&self) -> bool {
119 true
120 }
121
122 fn timeout(&self) -> Option<std::time::Duration> {
123 Some(std::time::Duration::from_secs(30))
124 }
125}