@@ -21,36 +21,17 @@ use golem_common::base_model::application::ApplicationName;
2121use golem_common:: base_model:: component:: ComponentName ;
2222use heck:: { ToKebabCase , ToSnakeCase } ;
2323use include_dir:: { Dir , DirEntry } ;
24- use itertools:: Itertools ;
25- use std:: borrow:: Cow ;
26- use std:: collections:: { BTreeMap , BTreeSet } ;
24+ use std:: collections:: BTreeMap ;
2725use std:: path:: { Path , PathBuf } ;
2826
2927const ON_DEMAND_COMMON_HASH_FILE_NAME : & str = ".golem-template-content-hash" ;
3028
31- #[ derive( Debug , Copy , Clone ) ]
32- enum TargetExistsResolveMode {
33- #[ allow( dead_code) ]
34- Skip ,
35- MergeOrSkip ,
36- Fail ,
37- MergeOrFail ,
38- }
39-
40- type MergeContents = Box < dyn FnOnce ( & [ u8 ] ) -> anyhow:: Result < Vec < u8 > > > ;
41-
42- enum TargetExistsResolveDecision {
43- Skip ,
44- Merge ( MergeContents ) ,
45- }
46-
4729pub trait TemplateGeneratorTargetFs {
4830 type Output ;
4931
50- fn ensure_dir ( & self , path : & Path ) -> anyhow :: Result < ( ) > ;
32+ fn exists ( & self , path : & Path ) -> bool ;
5133 fn write_file ( & mut self , path : & Path , contents : String ) -> anyhow:: Result < ( ) > ;
5234 fn finish ( self ) -> Self :: Output ;
53- fn is_in_memory ( & self ) -> bool ;
5435}
5536
5637#[ derive( Debug , Default ) ]
@@ -59,19 +40,15 @@ pub struct StdFs;
5940impl TemplateGeneratorTargetFs for StdFs {
6041 type Output = ( ) ;
6142
62- fn ensure_dir ( & self , path : & Path ) -> anyhow :: Result < ( ) > {
63- fs :: create_dir_all ( path) . map ( |_| ( ) )
43+ fn exists ( & self , path : & Path ) -> bool {
44+ path. exists ( )
6445 }
6546
6647 fn write_file ( & mut self , path : & Path , contents : String ) -> anyhow:: Result < ( ) > {
6748 fs:: write_str ( path, contents)
6849 }
6950
7051 fn finish ( self ) -> Self :: Output { }
71-
72- fn is_in_memory ( & self ) -> bool {
73- false
74- }
7552}
7653
7754#[ derive( Debug , Default ) ]
@@ -96,8 +73,8 @@ impl InMemoryFs {
9673impl TemplateGeneratorTargetFs for InMemoryFs {
9774 type Output = InMemoryFs ;
9875
99- fn ensure_dir ( & self , _path : & Path ) -> anyhow :: Result < ( ) > {
100- Ok ( ( ) )
76+ fn exists ( & self , path : & Path ) -> bool {
77+ self . files . contains_key ( path )
10178 }
10279
10380 fn write_file ( & mut self , path : & Path , contents : String ) -> anyhow:: Result < ( ) > {
@@ -108,10 +85,6 @@ impl TemplateGeneratorTargetFs for InMemoryFs {
10885 fn finish ( self ) -> Self :: Output {
10986 self
11087 }
111-
112- fn is_in_memory ( & self ) -> bool {
113- true
114- }
11588}
11689
11790#[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
@@ -130,7 +103,6 @@ struct GeneratorContext<'a> {
130103 component_name : Option < & ' a ComponentName > ,
131104 target_path : & ' a Path ,
132105 sdk_overrides : & ' a SdkOverrides ,
133- resolve_mode : TargetExistsResolveMode ,
134106}
135107
136108pub fn generate_commons_by_template < T : TemplateGeneratorTargetFs > (
@@ -152,7 +124,6 @@ pub fn generate_commons_by_template<T: TemplateGeneratorTargetFs>(
152124 component_name : None ,
153125 target_path,
154126 sdk_overrides,
155- resolve_mode : TargetExistsResolveMode :: MergeOrSkip ,
156127 } ,
157128 ) ?;
158129 Ok ( target. finish ( ) )
@@ -191,7 +162,6 @@ pub fn generate_on_demand_commons_by_template<T: TemplateGeneratorTargetFs>(
191162 component_name : None ,
192163 target_path,
193164 sdk_overrides,
194- resolve_mode : TargetExistsResolveMode :: Fail ,
195165 } ,
196166 ) ?;
197167
@@ -223,7 +193,31 @@ pub fn generate_component_by_template<T: TemplateGeneratorTargetFs>(
223193 component_name : Some ( component_name) ,
224194 target_path,
225195 sdk_overrides,
226- resolve_mode : TargetExistsResolveMode :: MergeOrFail ,
196+ } ,
197+ ) ?;
198+ Ok ( target. finish ( ) )
199+ }
200+
201+ pub fn generate_agent_by_template < T : TemplateGeneratorTargetFs > (
202+ template : & AppTemplate ,
203+ target_path : & Path ,
204+ application_name : & ApplicationName ,
205+ component_name : & ComponentName ,
206+ sdk_overrides : & SdkOverrides ,
207+ mut target : T ,
208+ ) -> anyhow:: Result < T :: Output > {
209+ if !template. metadata . is_agent ( ) {
210+ bail ! ( "Template {} is not an agent template" , template. name) ;
211+ }
212+
213+ generate_root_directory (
214+ & mut target,
215+ & GeneratorContext {
216+ template,
217+ application_name : Some ( application_name) ,
218+ component_name : Some ( component_name) ,
219+ target_path,
220+ sdk_overrides,
227221 } ,
228222 ) ?;
229223 Ok ( target. finish ( ) )
@@ -249,7 +243,6 @@ fn generate_directory<T: TemplateGeneratorTargetFs>(
249243 source : & Path ,
250244 target_path : & Path ,
251245) -> anyhow:: Result < ( ) > {
252- target. ensure_dir ( target_path) ?;
253246 for entry in templates_dir
254247 . get_dir ( source)
255248 . unwrap_or_else ( || panic ! ( "Could not find entry {source:?}" ) )
@@ -311,29 +304,18 @@ fn instantiate_file<T: TemplateGeneratorTargetFs>(
311304 target_path : & Path ,
312305 content_transforms : Vec < Transform > ,
313306) -> anyhow:: Result < ( ) > {
314- match get_resolved_contents ( target, ctx, dir, source, target_path) ? {
315- Some ( contents) => {
316- let contents = std:: str:: from_utf8 ( contents. as_ref ( ) ) . map_err ( |err| {
317- anyhow ! (
318- "Failed to decode as utf8, source: {}, err: {}" ,
319- source. display( ) ,
320- err
321- )
322- } ) ?;
323-
324- let rendered = if content_transforms. is_empty ( ) {
325- contents. to_string ( )
326- } else {
327- transform ( ctx, contents, & content_transforms)
328- } ;
329-
330- target. write_file ( target_path, rendered)
331- }
332- None => Err ( anyhow ! (
333- "Failed to resolve template contents for {}" ,
334- source. display( )
335- ) ) ,
307+ if target. exists ( target_path) {
308+ bail ! ( "Target {} already exists" , target_path. display( ) ) ;
336309 }
310+
311+ let contents = get_contents ( dir, source) ?;
312+ let rendered = if content_transforms. is_empty ( ) {
313+ contents. to_string ( )
314+ } else {
315+ transform ( ctx, contents, & content_transforms)
316+ } ;
317+
318+ target. write_file ( target_path, rendered)
337319}
338320
339321fn transform ( ctx : & GeneratorContext < ' _ > , str : impl AsRef < str > , transforms : & [ Transform ] ) -> String {
@@ -408,100 +390,9 @@ fn transform_file_name(ctx: &GeneratorContext<'_>, file_name: impl AsRef<str>) -
408390 transform ( ctx, file_name, & [ Transform :: ComponentName ] ) . replace ( "Cargo.toml._" , "Cargo.toml" )
409391}
410392
411- fn check_target < T : TemplateGeneratorTargetFs > (
412- target_kind : & T ,
413- target : & Path ,
414- resolve_mode : TargetExistsResolveMode ,
415- ) -> anyhow:: Result < Option < TargetExistsResolveDecision > > {
416- if target_kind. is_in_memory ( ) {
417- return Ok ( None ) ;
418- }
419-
420- if !target. exists ( ) {
421- return Ok ( None ) ;
422- }
423-
424- let get_merge = || -> anyhow:: Result < Option < TargetExistsResolveDecision > > {
425- let file_name = target
426- . file_name ( )
427- . ok_or_else ( || anyhow ! ( "Failed to get file name for target: {}" , target. display( ) ) )
428- . and_then ( |file_name| {
429- file_name. to_str ( ) . ok_or_else ( || {
430- anyhow ! (
431- "Failed to convert file name to string: {}" ,
432- file_name. to_string_lossy( )
433- )
434- } )
435- } ) ?;
436-
437- match file_name {
438- ".gitignore" => {
439- let target = target. to_path_buf ( ) ;
440- let current_content = fs:: read_to_string ( & target) ?;
441- Ok ( Some ( TargetExistsResolveDecision :: Merge ( Box :: new (
442- move |new_content : & [ u8 ] | -> anyhow:: Result < Vec < u8 > > {
443- Ok ( current_content
444- . lines ( )
445- . chain (
446- std:: str:: from_utf8 ( new_content) . map_err ( |err| {
447- anyhow ! (
448- "Failed to decode new content for merge as utf8, target: {}, err: {}" ,
449- target. display( ) ,
450- err
451- )
452- } ) ?. lines ( ) ,
453- )
454- . collect :: < BTreeSet < & str > > ( )
455- . iter ( )
456- . join ( "\n " )
457- . into_bytes ( ) )
458- } ,
459- ) ) ) )
460- }
461- _ => Ok ( None ) ,
462- }
463- } ;
464-
465- let target_already_exists = || {
466- Err ( anyhow ! ( format!(
467- "Target ({}) already exists!" ,
468- target. display( )
469- ) ) )
470- } ;
471-
472- match resolve_mode {
473- TargetExistsResolveMode :: Skip => Ok ( Some ( TargetExistsResolveDecision :: Skip ) ) ,
474- TargetExistsResolveMode :: MergeOrSkip => match get_merge ( ) ? {
475- Some ( merge) => Ok ( Some ( merge) ) ,
476- None => Ok ( Some ( TargetExistsResolveDecision :: Skip ) ) ,
477- } ,
478- TargetExistsResolveMode :: Fail => target_already_exists ( ) ,
479- TargetExistsResolveMode :: MergeOrFail => match get_merge ( ) ? {
480- Some ( merge) => Ok ( Some ( merge) ) ,
481- None => target_already_exists ( ) ,
482- } ,
483- }
484- }
485-
486- fn get_contents < ' a > ( dir : & Dir < ' a > , source : & ' a Path ) -> anyhow:: Result < & ' a [ u8 ] > {
487- Ok ( dir
488- . get_file ( source)
393+ fn get_contents < ' a > ( dir : & Dir < ' a > , source : & ' a Path ) -> anyhow:: Result < & ' a str > {
394+ dir. get_file ( source)
489395 . ok_or_else ( || anyhow ! ( "Could not find entry {}" , source. display( ) ) ) ?
490- . contents ( ) )
491- }
492-
493- fn get_resolved_contents < ' a , T : TemplateGeneratorTargetFs > (
494- target_kind : & T ,
495- ctx : & GeneratorContext < ' a > ,
496- dir : & Dir < ' a > ,
497- source : & ' a Path ,
498- target : & ' a Path ,
499- ) -> anyhow:: Result < Option < Cow < ' a , [ u8 ] > > > {
500- match check_target ( target_kind, target, ctx. resolve_mode ) ? {
501- None => Ok ( Some ( Cow :: Borrowed ( get_contents ( dir, source) ?) ) ) ,
502- Some ( TargetExistsResolveDecision :: Skip ) => Ok ( None ) ,
503- Some ( TargetExistsResolveDecision :: Merge ( merge) ) => {
504- Ok ( Some ( Cow :: Owned ( merge ( get_contents ( dir, source) ?) ?) ) )
505- }
506- }
396+ . contents_utf8 ( )
397+ . ok_or_else ( || anyhow ! ( "File contents are not valid UTF-8: {}" , source. display( ) ) )
507398}
0 commit comments