Skip to content

錯誤處理

navi_vm::Error 列舉涵蓋了編譯時和執行時的錯誤。

錯誤類型

rust
pub enum Error {
    Compile(CompileErrors),          // 解析、類型檢查或編譯錯誤
    Exception(RuntimeError),         // 執行時異常(如 runtime.error())
    MissingScriptType,               // 沒有 indicator()/strategy()/library() 呼叫
    InputValueNotFound(usize),       // with_input_value 中的無效輸入 ID
    SetInputValue(usize, String),    // 輸入值類型不匹配
    UnsupportedTimeFrame(TimeFrame), // 腳本不支援此時間週期
    SessionNotAllowed(TradeSession), // 交易時段類型未啟用
    DataProvider(String),            // DataProvider 回傳的錯誤
    LibraryScriptNotExecutable,      // 嘗試執行程式庫
}

處理錯誤

模式配對

rust
async fn run(source: &str) {
    let mut instance = match Instance::builder(source, TimeFrame::days(1), "NASDAQ:AAPL")
        .build().await
    {
        Ok(inst) => inst,
        Err(Error::Compile(errors)) => {
            // 帶原始碼位置顯示編譯錯誤(彩色)
            eprintln!("{}", errors.display());
            return;
        }
        Err(Error::MissingScriptType) => {
            eprintln!("Script must call indicator(), strategy(), or library()");
            return;
        }
        Err(Error::Exception(exception)) => {
            // 全域範疇執行時錯誤(如頂層程式碼中的 runtime.error())
            eprintln!("{}", exception.display());
            return;
        }
        Err(e) => {
            eprintln!("Build error: {}", e);
            return;
        }
    };

    // 執行
    match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
        Ok(()) => {}
        Err(Error::Exception(exception)) => {
            eprintln!("Runtime exception: {}", exception.display());
        }
        Err(e) => {
            eprintln!("Execution error: {}", e);
        }
    }
}

編譯錯誤

編譯錯誤包含原始碼 span,用於精確的錯誤報告:

rust
match Instance::builder(source, tf, "NASDAQ:AAPL").build().await {
    Err(Error::Compile(errors)) => {
        // 使用 codespan-reporting 渲染所有診斷(彩色)
        eprintln!("{}", errors.display());

        // 或純文字(無 ANSI 顏色)
        eprintln!("{}", errors.display().set_colorize(false));

        // 存取單個診斷
        for diag in errors.diagnostics() {
            eprintln!("  {}", diag.message);
        }
    }
    _ => {}
}

執行時異常

執行時異常發生在腳本執行期間(如 runtime.error() 呼叫、索引越界):

rust
match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
    Err(Error::Exception(exception)) => {
        // 顯示帶原始檔案上下文和呼叫堆疊的異常
        eprintln!("{}", exception.display());

        // 直接存取錯誤 span
        if let Some(span) = exception.spans.first() {
            eprintln!("  at line {}", span.start.line);
        }
    }
    _ => {}
}

編譯警告

警告不會阻止執行,但指示潛在問題。 Instance::warnings() 回傳一個 Warnings 值,已包含原始檔案, 可以直接渲染:

rust
let instance = Instance::builder(source, tf, "NASDAQ:AAPL").build().await?;

let warnings = instance.warnings();
if !warnings.is_empty() {
    // 使用 codespan-reporting 渲染為黃色警告
    eprintln!("{}", warnings.display());
}

警告也會作為 Event::Warningrun() 的事件流中發出:

rust
let mut handle = instance.run(symbol, timeframe);
while let Some(result) = handle.next_event().await {
    match result? {
        Event::Warning(warnings) => {
            eprintln!("{}", warnings.display());
        }
        _ => {}
    }
}

最佳實踐

  1. 始終處理編譯錯誤 — 向使用者顯示帶原始碼上下文的錯誤
  2. 也要處理 build() 的異常build() 執行全域範疇以發現腳本元資料(indicator()input.*() 等),因此頂層程式碼中的任何執行時錯誤(如 runtime.error()、無效的函式參數)都會在執行開始之前以 Error::Exception 的形式出現
  3. 遇到執行時錯誤立即停止 — Navi 逐 K 線計算,每根 K 線的結果依賴前一根的狀態(var 變數、series 歷史參照如 close[1] 等)。如果某根 K 線計算出錯,後續所有結果都將無效,因此應立即終止
  4. 處理事件流中的事件 — 日誌事件可能包含有用的診斷資訊;消費 run() 回傳的流或使用 run_to_end() 丟棄事件
  5. 建構後檢查警告 — 它們通常指示已棄用的功能或潛在的 bug
rust

async fn run_safely(
    instance: &mut Instance,
    symbol: &str,
    timeframe: TimeFrame,
) -> Result<(), Error> {
    let warnings = instance.warnings();
    if !warnings.is_empty() {
        eprintln!("{}", warnings.display());
    }

    let mut handle = instance.run(symbol, timeframe);
    while let Some(result) = handle.next_event().await {
        match result? {
            Event::Log(log) => {
                eprintln!("[{:?}] {}", log.level, log.message);
            }
            Event::Alert(alert) => {
                println!("Alert: {}", alert.message);
            }
            _ => {}
        }
    }
    Ok(())
}

下一步

基於 MIT 許可證發佈。