refactor: remove web-application config and SSE feature for CLI#226
refactor: remove web-application config and SSE feature for CLI#226reddevilmidzy merged 2 commits intomainfrom
Conversation
WalkthroughRemoves Axum-based HTTP server, configuration system, and SSE streaming. Deletes related dependencies and YAML configs. Refactors link check flow to compute and return a single summary result. Simplifies main to a small Tokio runtime invoking stream_link_checks with a fixed URL. Removes tests covering SSE and server behavior. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as Process Runner
participant Main as main()
participant RT as Tokio Runtime
participant LC as stream_link_checks()
participant Repo as Repo Scanner
participant Checker as Link Checker
User->>Main: start binary
Main->>RT: build and block_on
RT->>LC: stream_link_checks(repo_url, branch)
LC->>Repo: collect links
loop for each link
LC->>Checker: check(link)
Checker-->>LC: result
LC->>LC: update counters, log
end
alt success
LC-->>RT: LinkCheckSummaryEvent
RT-->>Main: summary
Main-->>User: exit 0
else error
LC-->>RT: Err(String)
RT-->>Main: error
Main-->>User: exit with error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Pull Request Test Coverage Report for Build 17921117767Details
💛 - Coveralls |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (5)
src/link_checker/sse.rs (2)
103-151: Simplify the stream pipeline with for_each_concurrent.Current map + buffer_unordered + for_each chain creates extra futures only to discard them. Use
for_each_concurrentfor clarity and less overhead.- stream::iter(links) - .map({ - let counters = Arc::clone(&counters); - move |link| { - let counters = Arc::clone(&counters); - async move { - let result = check_link(&link.url).await; - counters.increment_total(); - match &result { - LinkCheckResult::Valid => counters.increment_valid(), - LinkCheckResult::Invalid(_) => counters.increment_invalid(), - LinkCheckResult::Redirect(_) => counters.increment_redirect(), - LinkCheckResult::GitHubFileMoved(_) => counters.increment_moved(), - }; - let status = match &result { - LinkCheckResult::Valid => "valid", - LinkCheckResult::Invalid(_) => "invalid", - LinkCheckResult::Redirect(_) => "redirect", - LinkCheckResult::GitHubFileMoved(_) => "file_moved", - }; - let message: Option<String> = match result { - LinkCheckResult::Valid => None, - LinkCheckResult::Invalid(msg) => Some(msg), - LinkCheckResult::Redirect(url) => Some(format!("Redirected to: {url}")), - LinkCheckResult::GitHubFileMoved(msg) => Some(format!("Moved to: {msg}")), - }; - let message_str = message.as_deref().unwrap_or(""); - info!( - url = %link.url, - file_path = %link.file_path, - line_number = link.line_number as u32, - status = %status, - message = %message_str, - "link check" - ); - } - } - }) - .buffer_unordered(10) - .for_each(|_| async {}) - .await; + stream::iter(links) + .for_each_concurrent(10, { + let counters = Arc::clone(&counters); + move |link| { + let counters = Arc::clone(&counters); + async move { + let result = check_link(&link.url).await; + counters.increment_total(); + match &result { + LinkCheckResult::Valid => counters.increment_valid(), + LinkCheckResult::Invalid(_) => counters.increment_invalid(), + LinkCheckResult::Redirect(_) => counters.increment_redirect(), + LinkCheckResult::GitHubFileMoved(_) => counters.increment_moved(), + }; + let status = match &result { + LinkCheckResult::Valid => "valid", + LinkCheckResult::Invalid(_) => "invalid", + LinkCheckResult::Redirect(_) => "redirect", + LinkCheckResult::GitHubFileMoved(_) => "file_moved", + }; + let message: Option<String> = match result { + LinkCheckResult::Valid => None, + LinkCheckResult::Invalid(msg) => Some(msg), + LinkCheckResult::Redirect(url) => Some(format!("Redirected to: {url}")), + LinkCheckResult::GitHubFileMoved(msg) => Some(format!("Moved to: {msg}")), + }; + let message_str = message.as_deref().unwrap_or(""); + info!( + url = %link.url, + file_path = %link.file_path, + line_number = link.line_number as u32, + status = %status, + message = %message_str, + "link check" + ); + } + } + }) + .await;
151-160: Consider signaling failure in logs when invalid > 0.Add a warning/error when invalid links exist to make scanning logs easier.
Example:
let summary = counters.to_summary(); - info!( + let level = if summary.invalid > 0 { tracing::Level::WARN } else { tracing::Level::INFO }; + tracing::event!( + level, total = summary.total, valid = summary.valid, invalid = summary.invalid, redirect = summary.redirect, moved = summary.moved, "link check summary" );src/main.rs (3)
21-32: Use the summary to set the process exit code (CLI UX).Currently the result is discarded. Exit non‑zero when invalid links are found.
- rt.block_on(async { - stream_link_checks( - "https://github.com/reddevilmidzy/queensac".to_string(), - None, - ) - .await; - }); + let exit_code = rt.block_on(async { + let summary = stream_link_checks( + "https://github.com/reddevilmidzy/queensac".to_string(), + None, + ) + .await + #![allow(unused_must_use)]; + // If you adopt Result from sse.rs change above, handle it here: + // .map_err(|e| { error!(error=%e, "link check failed"); 1 })? + ; + if summary.invalid > 0 { 2 } else { 0 } + }); + std::process::exit(exit_code);
4-17: Minor: align copy to CLI (not “service”).Change startup log to reflect a CLI run.
- info!("Starting queensac service..."); + info!("Starting queensac link-check CLI...");
4-17: Optional: simplify with #[tokio::main].You can drop manual runtime construction.
-fn main() { +#[tokio::main(flavor = "current_thread")] +async fn main() { // Initialize tracing subscriber tracing_subscriber::fmt() .with_max_level(Level::INFO) .with_target(false) .with_thread_ids(true) .with_file(true) .with_line_number(true) .with_thread_names(true) .with_level(true) .with_ansi(true) .with_timer(KoreanTime) .pretty() .init(); - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("Failed to create Tokio runtime"); - rt.block_on(async { - stream_link_checks("https://github.com/reddevilmidzy/queensac".to_string(), None).await; - }); + let summary = stream_link_checks("https://github.com/reddevilmidzy/queensac".to_string(), None).await; + std::process::exit(if summary.invalid > 0 { 2 } else { 0 }); }Also applies to: 21-32
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
Cargo.toml(0 hunks)configuration/base.yaml(0 hunks)configuration/local.yaml(0 hunks)configuration/production.yaml(0 hunks)src/configuration.rs(0 hunks)src/lib.rs(0 hunks)src/link_checker/sse.rs(2 hunks)src/main.rs(2 hunks)src/startup.rs(0 hunks)tests/api/main.rs(0 hunks)tests/api/sse.rs(0 hunks)
💤 Files with no reviewable changes (9)
- src/configuration.rs
- src/lib.rs
- configuration/local.yaml
- tests/api/sse.rs
- configuration/production.yaml
- tests/api/main.rs
- src/startup.rs
- configuration/base.yaml
- Cargo.toml
🧰 Additional context used
🧬 Code graph analysis (2)
src/link_checker/sse.rs (2)
src/git/link_extractor.rs (1)
extract_links_from_repo_url(35-66)src/link_checker/link.rs (1)
check_link(35-75)
src/main.rs (1)
src/link_checker/sse.rs (1)
stream_link_checks(79-161)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
| async fn test_stream_link_checks_runs() { | ||
| let repo_url = "https://github.com/reddevilmidzy/kingsac".to_string(); | ||
| let branch = Some("main".to_string()); | ||
| let sse = stream_link_checks(repo_url, branch).await; | ||
| let mut stream = sse.into_response().into_body().into_data_stream(); | ||
|
|
||
| // 스트림에서 이벤트를 수집 | ||
| let mut events = Vec::new(); | ||
| let mut buffer = String::new(); | ||
|
|
||
| while let Some(chunk) = stream.next().await { | ||
| if let Ok(chunk) = chunk { | ||
| if let Ok(text) = String::from_utf8(chunk.to_vec()) { | ||
| buffer.push_str(&text); | ||
|
|
||
| if let Some(event_end) = buffer.find("\n\n") { | ||
| let event_str = buffer[..event_end].to_string(); | ||
| buffer = buffer[event_end + 2..].to_string(); | ||
| // "data: " 접두사를 제거하고 JSON 파싱 | ||
| if let Some(json_str) = event_str.strip_prefix("data: ") { | ||
| if let Ok(json) = serde_json::from_str::<Value>(json_str) { | ||
| events.push(json); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| assert!(!events.is_empty(), "No events were received"); | ||
|
|
||
| assert!(events.last().is_some(), "Last event should exist"); | ||
| if let Some(last_event) = events.last() { | ||
| assert!(last_event.get("total").is_some()); | ||
| assert!(last_event.get("total").unwrap().as_u64().unwrap() > 0); | ||
| assert!(last_event.get("valid").is_some()); | ||
| assert!(last_event.get("invalid").is_some()); | ||
| assert!(last_event.get("redirect").is_some()); | ||
| assert!(last_event.get("moved").is_some()); | ||
| } | ||
|
|
||
| assert!(events.first().is_some(), "First event should exist"); | ||
| if let Some(first_event) = events.first() { | ||
| assert!(first_event.get("url").is_some()); | ||
| assert!(first_event.get("file_path").is_some()); | ||
| assert!(first_event.get("line_number").is_some()); | ||
| assert!(first_event.get("status").is_some()); | ||
| } | ||
| let summary = stream_link_checks(repo_url, branch).await; | ||
| assert_eq!( | ||
| summary.total, | ||
| summary.valid + summary.invalid + summary.redirect + summary.moved | ||
| ); |
There was a problem hiding this comment.
Test is flaky: hits live GitHub and network. Gate or mock.
This will be slow and flaky in CI. Either mark ignored by default or inject a check function to avoid network.
Minimal mitigation:
- #[tokio::test]
+ #[tokio::test]
+ #[ignore = "uses live network and GitHub; move to integration or mock check_link"]If you want, I can refactor stream_link_checks to accept a check_fn: Fn(&str) -> Future<Output=LinkCheckResult> for deterministic unit tests.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async fn test_stream_link_checks_runs() { | |
| let repo_url = "https://github.com/reddevilmidzy/kingsac".to_string(); | |
| let branch = Some("main".to_string()); | |
| let sse = stream_link_checks(repo_url, branch).await; | |
| let mut stream = sse.into_response().into_body().into_data_stream(); | |
| // 스트림에서 이벤트를 수집 | |
| let mut events = Vec::new(); | |
| let mut buffer = String::new(); | |
| while let Some(chunk) = stream.next().await { | |
| if let Ok(chunk) = chunk { | |
| if let Ok(text) = String::from_utf8(chunk.to_vec()) { | |
| buffer.push_str(&text); | |
| if let Some(event_end) = buffer.find("\n\n") { | |
| let event_str = buffer[..event_end].to_string(); | |
| buffer = buffer[event_end + 2..].to_string(); | |
| // "data: " 접두사를 제거하고 JSON 파싱 | |
| if let Some(json_str) = event_str.strip_prefix("data: ") { | |
| if let Ok(json) = serde_json::from_str::<Value>(json_str) { | |
| events.push(json); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assert!(!events.is_empty(), "No events were received"); | |
| assert!(events.last().is_some(), "Last event should exist"); | |
| if let Some(last_event) = events.last() { | |
| assert!(last_event.get("total").is_some()); | |
| assert!(last_event.get("total").unwrap().as_u64().unwrap() > 0); | |
| assert!(last_event.get("valid").is_some()); | |
| assert!(last_event.get("invalid").is_some()); | |
| assert!(last_event.get("redirect").is_some()); | |
| assert!(last_event.get("moved").is_some()); | |
| } | |
| assert!(events.first().is_some(), "First event should exist"); | |
| if let Some(first_event) = events.first() { | |
| assert!(first_event.get("url").is_some()); | |
| assert!(first_event.get("file_path").is_some()); | |
| assert!(first_event.get("line_number").is_some()); | |
| assert!(first_event.get("status").is_some()); | |
| } | |
| let summary = stream_link_checks(repo_url, branch).await; | |
| assert_eq!( | |
| summary.total, | |
| summary.valid + summary.invalid + summary.redirect + summary.moved | |
| ); | |
| #[tokio::test] | |
| #[ignore = "uses live network and GitHub; move to integration or mock check_link"] | |
| async fn test_stream_link_checks_runs() { | |
| let repo_url = "https://github.com/reddevilmidzy/kingsac".to_string(); | |
| let branch = Some("main".to_string()); | |
| let summary = stream_link_checks(repo_url, branch).await; | |
| assert_eq!( | |
| summary.total, | |
| summary.valid + summary.invalid + summary.redirect + summary.moved | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/link_checker/sse.rs around lines 168 to 175, the test
test_stream_link_checks_runs is flaky because it performs live network requests
to GitHub; either mark the test as ignored by default (add #[ignore] above the
fn) or refactor stream_link_checks to accept an injected check function (e.g., a
parameter check_fn: impl Fn(&str) -> impl Future<Output = LinkCheckResult> +
Send + Sync) and update the test to pass a mock async check function that
returns deterministic LinkCheckResult values so the unit test no longer depends
on network.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/main.rs (1)
19-19: 로그 문구: service → CLI서버가 아닌 CLI 실행이므로 문구를 조정하면 혼동이 줄어듭니다.
- info!("Starting queensac service..."); + info!("Starting queensac CLI run...");src/link_checker/sse.rs (4)
78-82: 에러를String대신 구체 타입으로 반환 고려현재
Result<_, String>은 에러 컨텍스트를 잃습니다.git2::Error그대로 또는anyhow::Result를 권장합니다. 테스트/호출부 영향이 있으니 후속 PR로도 충분합니다.-) -> Result<LinkCheckSummaryEvent, String> { +) -> Result<LinkCheckSummaryEvent, git2::Error> {그리고 에러 반환부에서
Err(e)로 그대로 전달.
88-98: 구조화 로그로 에러 필드화에러 문자열 합치기 대신 필드로 남기면 검색/분석이 용이합니다.
- Err(e) => { - error!("Error processing repository: {}", e); - return Err(e.to_string()); - } + Err(e) => { + error!(error = %e, "Error processing repository"); + return Err(e.to_string()); + }
102-107: 불필요한Arc::clone중복
.map바깥 캡쳐 1회 + 내부 1회면 충분합니다. 가독성 위해 중복 제거 권장.- .map({ - let counters = Arc::clone(&counters); - move |link| { - let counters = Arc::clone(&counters); + .map({ + let counters = counters.clone(); + move |link| { + let counters = counters.clone();
144-146: 동시성 정도 상수/설정화매직넘버 10 대신 상수 또는 env/CLI로 주입하면 운영 시 튜닝이 쉽습니다. (본 PR 범위 밖이면 패스)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/link_checker/sse.rs(2 hunks)src/main.rs(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: reddevilmidzy
PR: reddevilmidzy/queensac#226
File: src/main.rs:21-32
Timestamp: 2025-09-22T15:50:19.498Z
Learning: User reddevilmidzy prefers to communicate in Korean and likes to keep PR scope focused and well-defined.
📚 Learning: 2025-09-22T15:50:19.498Z
Learnt from: reddevilmidzy
PR: reddevilmidzy/queensac#226
File: src/main.rs:21-32
Timestamp: 2025-09-22T15:50:19.498Z
Learning: User reddevilmidzy prefers to communicate in Korean and likes to keep PR scope focused and well-defined.
Applied to files:
src/main.rs
🧬 Code graph analysis (2)
src/main.rs (1)
src/link_checker/sse.rs (1)
stream_link_checks(79-158)
src/link_checker/sse.rs (2)
src/git/link_extractor.rs (1)
extract_links_from_repo_url(35-66)src/link_checker/link.rs (1)
check_link(35-75)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (6)
src/main.rs (2)
1-1: KoreanTime 경로 재검증 또는 re-export 필요크레이트 루트에서
KoreanTime이 re-export되지 않았다면 이 import는 실패합니다. 모듈 경로로 바꾸거나(queensac::domain::time::KoreanTime) 루트에서pub use하세요.다음 스크립트로 공개 re-export 여부를 확인해 주세요.
#!/bin/bash # KoreanTime / stream_link_checks re-export 확인 rg -nC2 -g 'src/**' $'^\\s*pub\\s+use\\b.*KoreanTime\\b|^\\s*pub\\s+(struct|type)\\s+KoreanTime\\b' rg -nC2 -g 'src/**' $'^\\s*pub\\s+use\\b.*stream_link_checks\\b|^\\s*pub\\s+(async\\s+)?fn\\s+stream_link_checks\\b'선호하시면 main.rs에서 바로 수정:
-use queensac::{KoreanTime, stream_link_checks}; +use queensac::stream_link_checks; +use queensac::domain::time::KoreanTime;
21-25: Tokio current_thread 선택 적절함CLI 단일 러너에
new_current_thread().enable_all()구성이 적합합니다. 👍src/link_checker/sse.rs (4)
67-75: 원자 카운터 요약 로드 OK
Relaxed로드/스토어는 카운팅 용도에 적절하며.await이후 최종 합산 시점에 충분합니다.
148-157: 요약 로그 + 반환 형태 적절최종 합산 후 요약 로그를 남기고
Ok(summary)반환하는 흐름이 명확합니다.
165-175: 실네트워크 의존 테스트는 flaky — 기본 무시 처리라이브 GitHub/네트워크 의존으로 CI 불안정합니다. 우선
#[ignore]를 권장합니다. (모의 주입 리팩터는 후속 이슈로)- #[tokio::test] + #[tokio::test] + #[ignore = "uses live network and GitHub; move to integration or mock check_link"] async fn test_stream_link_checks_runs() {
9-16: SSE 제거 후 남은 타입 정리 (LinkCheckEvent)rg 결과: 정의(src/link_checker/sse.rs)와 re-export(src/link_checker/mod.rs)에서만 발견되며 저장소 내부에 다른 사용처는 없습니다.
조치: src/link_checker/sse.rs에서 LinkCheckEvent 구조체 삭제 및 src/link_checker/mod.rs의 pub use에서 LinkCheckEvent 제거.
주의: 공개 API 변경이므로 외부 소비자(크레이트 사용자) 존재 여부와 CI/통합테스트를 확인하고, 필요 시 deprecated 단계·마이그레이션 안내를 추가.
| rt.block_on(async { | ||
| if let Err(e) = stream_link_checks( | ||
| "https://github.com/reddevilmidzy/queensac".to_string(), | ||
| None, | ||
| ) | ||
| .await | ||
| { | ||
| error!("Failed to stream link checks: {}", e); | ||
| } | ||
| }; | ||
|
|
||
| // Build application | ||
| let app = match Application::build(configuration).await { | ||
| Ok(app) => app, | ||
| Err(e) => { | ||
| error!("Failed to build application: {}", e); | ||
| std::process::exit(1); | ||
| } | ||
| }; | ||
|
|
||
| // Create socket address | ||
| let addr = SocketAddr::from(([127, 0, 0, 1], app.port)); | ||
| info!("Server listening on {}", addr); | ||
|
|
||
| // Start the server | ||
| let listener = match net::TcpListener::bind(addr).await { | ||
| Ok(listener) => listener, | ||
| Err(e) => { | ||
| error!("Failed to bind to address {}: {}", addr, e); | ||
| std::process::exit(1); | ||
| } | ||
| }; | ||
|
|
||
| info!("queensac service is running on http://{}", addr); | ||
|
|
||
| // Serve the application | ||
| if let Err(e) = axum::serve(listener, app.router).await { | ||
| error!("Server error: {}", e); | ||
| std::process::exit(1); | ||
| } | ||
| }); |
There was a problem hiding this comment.
실패 시 비영(非零) 종료코드로 종료하세요
현재는 에러를 로그만 남기고 종료코드 0으로 끝납니다. CI/스크립트 연계 시 실패를 감지하지 못합니다.
아래처럼 반환 코드를 받아 std::process::exit 하세요.
- rt.block_on(async {
- if let Err(e) = stream_link_checks(
- "https://github.com/reddevilmidzy/queensac".to_string(),
- None,
- )
- .await
- {
- error!("Failed to stream link checks: {}", e);
- }
- });
+ let exit_code = rt.block_on(async {
+ match stream_link_checks(
+ "https://github.com/reddevilmidzy/queensac".to_string(),
+ None,
+ )
+ .await {
+ Ok(_) => 0,
+ Err(e) => {
+ error!("Failed to stream link checks: {}", e);
+ 1
+ }
+ }
+ });
+ std::process::exit(exit_code);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| rt.block_on(async { | |
| if let Err(e) = stream_link_checks( | |
| "https://github.com/reddevilmidzy/queensac".to_string(), | |
| None, | |
| ) | |
| .await | |
| { | |
| error!("Failed to stream link checks: {}", e); | |
| } | |
| }; | |
| // Build application | |
| let app = match Application::build(configuration).await { | |
| Ok(app) => app, | |
| Err(e) => { | |
| error!("Failed to build application: {}", e); | |
| std::process::exit(1); | |
| } | |
| }; | |
| // Create socket address | |
| let addr = SocketAddr::from(([127, 0, 0, 1], app.port)); | |
| info!("Server listening on {}", addr); | |
| // Start the server | |
| let listener = match net::TcpListener::bind(addr).await { | |
| Ok(listener) => listener, | |
| Err(e) => { | |
| error!("Failed to bind to address {}: {}", addr, e); | |
| std::process::exit(1); | |
| } | |
| }; | |
| info!("queensac service is running on http://{}", addr); | |
| // Serve the application | |
| if let Err(e) = axum::serve(listener, app.router).await { | |
| error!("Server error: {}", e); | |
| std::process::exit(1); | |
| } | |
| }); | |
| let exit_code = rt.block_on(async { | |
| match stream_link_checks( | |
| "https://github.com/reddevilmidzy/queensac".to_string(), | |
| None, | |
| ) | |
| .await { | |
| Ok(_) => 0, | |
| Err(e) => { | |
| error!("Failed to stream link checks: {}", e); | |
| 1 | |
| } | |
| } | |
| }); | |
| std::process::exit(exit_code); |
🤖 Prompt for AI Agents
In src/main.rs around lines 26 to 35, the async block logs errors but still
returns exit code 0; change the control flow to capture the Result from
rt.block_on and, on Err(e), log the error and call std::process::exit with a
non-zero code (e.g., 1). Concretely, have rt.block_on return the Result from
stream_link_checks (or assign the block result to a variable), then if it is
Err(e) call error! with the error and immediately call std::process::exit(1) so
the process exits non-zero for CI/scripts to detect failure.
There was a problem hiding this comment.
이후 --args 받는 기능 구현할 때 반영할게욥
There was a problem hiding this comment.
@reddevilmidzy 네, 좋습니다! CLI 인자 파싱 기능과 함께 구현하시는 게 합리적인 접근이네요. PR 범위를 명확하게 유지하는 것이 좋은 접근 방식입니다. 👍
현재 PR은 웹 서버에서 CLI로의 전환에 집중하고, 향후 CLI 기능 개선 시 적절한 종료 코드 처리를 함께 구현하시면 될 것 같습니다.
🧠 Learnings used
Learnt from: reddevilmidzy
PR: reddevilmidzy/queensac#226
File: src/main.rs:21-32
Timestamp: 2025-09-22T15:50:19.498Z
Learning: User reddevilmidzy prefers to communicate in Korean and likes to keep PR scope focused and well-defined.
♟️ What’s this PR about?
axum 프레임워크를 걷어냈습니다. 이전에는 SSE로 링크 상태를 전송했는데, 현재는 로그만 출력하는 형태로 변경하였습니다.
🔗 Related Issues / PRs
part of #193
Summary by CodeRabbit