scribe_selection/
bundler.rs1use crate::context::CodeContext;
6use scribe_core::Result;
7use serde::{Deserialize, Serialize};
8use serde_json::json;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct BundleOptions {
13 pub format: String,
15 pub include_metadata: bool,
17}
18
19impl Default for BundleOptions {
20 fn default() -> Self {
21 Self {
22 format: "json".to_string(),
23 include_metadata: true,
24 }
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct CodeBundle {
30 pub content: String,
32 pub metadata: HashMap<String, String>,
34}
35
36pub struct CodeBundler;
37
38impl CodeBundler {
39 pub fn new() -> Self {
40 Self
41 }
42
43 pub async fn bundle(
44 &self,
45 context: &CodeContext,
46 options: &BundleOptions,
47 ) -> Result<CodeBundle> {
48 let content = match options.format.as_str() {
49 "plain" => render_plain(context),
50 _ => render_json(context)?,
51 };
52
53 let metadata = if options.include_metadata {
54 build_metadata(context, options)
55 } else {
56 HashMap::new()
57 };
58
59 Ok(CodeBundle { content, metadata })
60 }
61}
62
63impl Default for CodeBundler {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69fn render_json(context: &CodeContext) -> Result<String> {
70 let files: Vec<_> = context
71 .files
72 .iter()
73 .map(|file| {
74 json!({
75 "path": file.path,
76 "token_estimate": file.token_estimate,
77 "contents": file.contents,
78 })
79 })
80 .collect();
81
82 Ok(serde_json::to_string_pretty(&json!({
83 "total_tokens": context.total_tokens,
84 "files": files,
85 }))?)
86}
87
88fn render_plain(context: &CodeContext) -> String {
89 let mut body = String::new();
90
91 for file in &context.files {
92 body.push_str("===== ");
93 body.push_str(&file.path);
94 body.push_str(" =====\n");
95
96 match &file.contents {
97 Some(contents) => body.push_str(contents),
98 None => body.push_str("[content not loaded]\n"),
99 }
100
101 body.push_str("\n\n");
102 }
103
104 body
105}
106
107fn build_metadata(context: &CodeContext, options: &BundleOptions) -> HashMap<String, String> {
108 let mut metadata = HashMap::new();
109 metadata.insert("format".to_string(), options.format.clone());
110 metadata.insert("file_count".to_string(), context.files.len().to_string());
111 metadata.insert("total_tokens".to_string(), context.total_tokens.to_string());
112 metadata
113}