|
| 1 | +//! 原生文档观察绑定已有工作台,不初始化或签发关闭。 |
| 2 | +use serde_json::{Value, json}; |
| 3 | +use std::{ |
| 4 | + path::{Path, PathBuf}, |
| 5 | + sync::atomic::{AtomicU64, Ordering}, |
| 6 | + time::{Instant, SystemTime, UNIX_EPOCH}, |
| 7 | +}; |
| 8 | +static NEXT: AtomicU64 = AtomicU64::new(0); |
| 9 | +/// 定位已有工作台;显式范围越界在原生启动前拒绝。 |
| 10 | +pub(crate) fn resolve_root( |
| 11 | + source: &Path, |
| 12 | + explicit: Option<&Path>, |
| 13 | +) -> Result<Option<PathBuf>, String> { |
| 14 | + // 先拒绝链接/父跳转等不适用源码;不能按链接目标去写另一项目的工作台。 |
| 15 | + crate::plain_syntax_source::read_plain_source(source) |
| 16 | + .map_err(|_| "C/C++工作台源码范围不适用")?; |
| 17 | + let absolute = source.canonicalize().map_err(|_| "C/C++工作台源码不可读")?; |
| 18 | + if let Some(root) = explicit { |
| 19 | + let root = root.canonicalize().map_err(|_| "C/C++工作台根不可读")?; |
| 20 | + if !root.is_dir() || !absolute.starts_with(&root) { |
| 21 | + return Err("C/C++文档源码不属于显式工作台".into()); |
| 22 | + } |
| 23 | + return Ok(Some(root)); |
| 24 | + } |
| 25 | + Ok(absolute |
| 26 | + .parent() |
| 27 | + .into_iter() |
| 28 | + .flat_map(Path::ancestors) |
| 29 | + .find(|root| std::fs::symlink_metadata(root.join(".codeguard")).is_ok()) |
| 30 | + .map(Path::to_path_buf)) |
| 31 | +} |
| 32 | +/// 保存并同步局部观察;中断后不开始新的持久化阶段。 |
| 33 | +pub(crate) fn connect(root: &Path, tool: &Path, report: &mut Value, deadline: Instant) { |
| 34 | + report["schema_version"] = json!("0.2.0"); |
| 35 | + report["task_workflow_status"] = json!("partial"); |
| 36 | + let argv = report["verification_command"] |
| 37 | + .as_array_mut() |
| 38 | + .expect("固定复检命令为数组"); |
| 39 | + argv.pop(); |
| 40 | + argv.push(json!("--workspace")); |
| 41 | + argv.push(json!(root)); |
| 42 | + argv.push(json!("--format=json")); |
| 43 | + report["workbench"] = json!({"status":"incomplete","reason":"clang_documentation_workspace_unavailable","workspace_id":null,"run_id":null,"task_ids":[],"sync":null}); |
| 44 | + if codeguard_runtime::sigint_cancellation_requested() || Instant::now() >= deadline { |
| 45 | + report["workbench"]["reason"] = json!("request_interrupted"); |
| 46 | + return; |
| 47 | + } |
| 48 | + let Some(id) = crate::workspace_refresh::read_workspace_baseline(root) |
| 49 | + .ok() |
| 50 | + .flatten() |
| 51 | + .and_then(|b| b.workspace_id().map(str::to_owned)) |
| 52 | + else { |
| 53 | + return; |
| 54 | + }; |
| 55 | + let Some(path) = report["path"] |
| 56 | + .as_str() |
| 57 | + .and_then(|s| Path::new(s).strip_prefix(root).ok()) |
| 58 | + .and_then(Path::to_str) |
| 59 | + else { |
| 60 | + return; |
| 61 | + }; |
| 62 | + let run = format!( |
| 63 | + "clangdoc-{}-{}-{}", |
| 64 | + std::process::id(), |
| 65 | + NEXT.fetch_add(1, Ordering::Relaxed), |
| 66 | + SystemTime::now() |
| 67 | + .duration_since(UNIX_EPOCH) |
| 68 | + .map_or(0, |d| d.as_nanos()) |
| 69 | + ); |
| 70 | + let packet = json!({"schema_version":"0.1.0","report_type":"clang_documentation_workbench_observation","workspace_binding":"bound","workspace_id":id,"run_id":run,"path":path,"language":report["language"],"standard":report["documentation_configuration"]["standard"],"profile":"clang-documentation-v1","selected_tool":tool,"source_sha256":report["source_sha256"],"local_scan_complete":report["local_scan_complete"],"native":report["native"],"authority":"local_unverified","coverage_proven":false,"delivery_decision":"not_evaluated"}); |
| 71 | + report["workbench"]["workspace_id"] = json!(id); |
| 72 | + report["workbench"]["run_id"] = json!(run); |
| 73 | + if !crate::work_sync::c_family_comments_report::valid_shape(&packet) { |
| 74 | + report["workbench"]["reason"] = json!("clang_documentation_packet_invalid"); |
| 75 | + return; |
| 76 | + } |
| 77 | + if crate::work_sync::save_local_report(root, &packet).is_err() { |
| 78 | + report["workbench"]["reason"] = json!("clang_documentation_report_write_failed"); |
| 79 | + return; |
| 80 | + } |
| 81 | + if codeguard_runtime::sigint_cancellation_requested() || Instant::now() >= deadline { |
| 82 | + report["workbench"]["reason"] = json!("request_interrupted"); |
| 83 | + return; |
| 84 | + } |
| 85 | + match crate::work_sync::sync_local_workspace(root) { |
| 86 | + Ok(s) if s.failed_reports == 0 => { |
| 87 | + report["workspace_binding"] = json!("bound"); |
| 88 | + report["workbench"]["status"] = json!("synced_partial"); |
| 89 | + report["workbench"]["reason"] = Value::Null; |
| 90 | + report["workbench"]["sync"] = json!({"new_findings":s.new_findings,"new_blockers":s.new_blockers,"imported_reports":s.imported_reports,"already_consumed_reports":s.already_consumed_reports,"historical_findings":s.historical_findings}); |
| 91 | + report["workbench"]["task_ids"] = json!( |
| 92 | + crate::work_sync::c_family_comments_report::task_ids(root, &packet) |
| 93 | + ); |
| 94 | + if !codeguard_runtime::sigint_cancellation_requested() && Instant::now() < deadline { |
| 95 | + match crate::next_command::read_local_brief_for_checker( |
| 96 | + root, |
| 97 | + crate::work_sync::c_family_comments_report::checker(&packet), |
| 98 | + ) { |
| 99 | + Ok(next) => { |
| 100 | + report["next"] = if next["repair_brief"].is_null() { |
| 101 | + Value::Null |
| 102 | + } else { |
| 103 | + next |
| 104 | + } |
| 105 | + } |
| 106 | + Err(_) => { |
| 107 | + report["workbench"]["reason"] = |
| 108 | + json!("clang_documentation_next_unavailable") |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + } |
| 113 | + _ => report["workbench"]["reason"] = json!("clang_documentation_sync_failed"), |
| 114 | + } |
| 115 | +} |
0 commit comments