1+ use crate :: commands:: bundle:: bundle;
2+
13use axum:: {
24 extract:: Query ,
35 response:: { IntoResponse , Response } ,
@@ -12,6 +14,15 @@ use tower_http::trace::{self, TraceLayer};
1214use tracing:: { info, Level } ;
1315use uuid:: Uuid ;
1416
17+ use anyhow:: Result ;
18+ use std:: path:: Path ;
19+
20+ use notify:: event:: { DataChange , EventKind , ModifyKind } ;
21+ use notify:: { Event , RecommendedWatcher , RecursiveMode , Watcher } ;
22+ use tokio:: sync:: mpsc;
23+
24+
25+
1526#[ derive( Deserialize ) ]
1627struct FlowQuery {
1728 name : String ,
@@ -34,7 +45,6 @@ struct SessionResponse {
3445}
3546
3647async fn read_flow ( name : & str ) -> Result < FlowResponse , String > {
37-
3848 let config = crate :: config:: Config :: from_file ( "./opacity.toml" ) . unwrap ( ) ;
3949
4050 let matched_flow = config
@@ -44,15 +54,19 @@ async fn read_flow(name: &str) -> Result<FlowResponse, String> {
4454 . find ( |flow| flow. alias == name)
4555 . ok_or_else ( || String :: from ( "Flow not found" ) ) ?;
4656
47- let script_path = PathBuf :: from ( config. settings . output_directory ) . join ( format ! ( "{}.bundle.lua" , name) ) ;
57+ let script_path =
58+ PathBuf :: from ( config. settings . output_directory ) . join ( format ! ( "{}.bundle.lua" , name) ) ;
4859 let script_content =
4960 fs:: read_to_string ( script_path) . map_err ( |_| String :: from ( "Script file not found" ) ) ?;
5061
5162 Ok ( FlowResponse {
5263 name : matched_flow. alias . clone ( ) ,
5364 min_sdk : match & matched_flow. min_sdk_version {
5465 None => {
55- info ! ( "No min SDK version found for flow {}; Defaulting to '1'" , name) ;
66+ info ! (
67+ "No min SDK version found for flow {}; Defaulting to '1'" ,
68+ name
69+ ) ;
5670 "1" . to_string ( )
5771 }
5872 Some ( min_sdk) => min_sdk. clone ( ) ,
@@ -67,10 +81,9 @@ async fn flows(Query(query): Query<FlowQuery>) -> Response {
6781 Err ( e) => {
6882 let ( status, message) = match e. as_str ( ) {
6983 "Flow not found" => ( axum:: http:: StatusCode :: NOT_FOUND , "Flow not found" ) ,
70- "Script file not found" => (
71- axum:: http:: StatusCode :: NOT_FOUND ,
72- "Script file not found" ,
73- ) ,
84+ "Script file not found" => {
85+ ( axum:: http:: StatusCode :: NOT_FOUND , "Script file not found" )
86+ }
7487 _ => (
7588 axum:: http:: StatusCode :: INTERNAL_SERVER_ERROR ,
7689 "Error processing flow request" ,
@@ -93,7 +106,33 @@ async fn sessions() -> Json<SessionResponse> {
93106 } )
94107}
95108
96- pub async fn serve ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
109+ async fn watch ( config_path : & str ) -> notify:: Result < ( ) > {
110+ let ( tx, mut rx) = mpsc:: channel :: < Event > ( 100 ) ;
111+
112+ let mut watcher: RecommendedWatcher = notify:: recommended_watcher ( move |res| {
113+ if let Ok ( event) = res {
114+ let _ = tx. try_send ( event) ;
115+ } else if let Err ( e) = res {
116+ eprintln ! ( "Watch error: {:?}" , e) ;
117+ }
118+ } ) ?;
119+
120+ watcher. watch ( Path :: new ( "src" ) , RecursiveMode :: Recursive ) ?;
121+ watcher. watch ( Path :: new ( config_path) , RecursiveMode :: NonRecursive ) ?;
122+ info ! ( "Watching all files in 'src' and '{}'" , config_path) ;
123+
124+ while let Some ( _event) = rx. recv ( ) . await {
125+ if _event. kind == EventKind :: Modify ( ModifyKind :: Data ( DataChange :: Content ) ) {
126+ if let Err ( err) = bundle ( config_path, true ) {
127+ tracing:: error!( "🟥 Rebundle failed: {:?}" , err)
128+ }
129+ }
130+ }
131+
132+ Ok ( ( ) )
133+ }
134+
135+ pub async fn serve ( config_path : & str , should_watch : & bool ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
97136 let port = 8080 ;
98137 let addr = SocketAddr :: from ( ( [ 0 , 0 , 0 , 0 ] , port) ) ;
99138
@@ -119,8 +158,21 @@ pub async fn serve() -> Result<(), Box<dyn std::error::Error>> {
119158
120159 info ! ( "Listening on port {}..." , port) ;
121160
122- let listener = tokio:: net:: TcpListener :: bind ( addr) . await ?;
123- axum:: serve ( listener, app. into_make_service ( ) ) . await ?;
161+ if * should_watch {
162+ tokio:: try_join!(
163+ async {
164+ let listener = tokio:: net:: TcpListener :: bind( addr) . await ?;
165+ axum:: serve( listener, app. into_make_service( ) ) . await ?;
166+ Ok :: <_, Box <dyn std:: error:: Error >>( ( ) )
167+ } ,
168+ async {
169+ watch( config_path) . await . map_err( |e| Box :: new( e) as Box <dyn std:: error:: Error >)
170+ }
171+ ) ?;
172+ } else {
173+ let listener = tokio:: net:: TcpListener :: bind ( addr) . await ?;
174+ axum:: serve ( listener, app. into_make_service ( ) ) . await ?;
175+ }
124176
125177 Ok ( ( ) )
126178}
0 commit comments