//! 云文件浏览的 provider 抽象与工作区状态。默认 provider 未连接,不假装已有外部文件。 //! 当前源码仅定义注入接口,没有具体 CloudSourceProvider 实现或启动时安装逻辑。 //! 列出和打开文件的结果作为工具数据展示,错误保持在当前工具状态中供界面说明。 //! //! Cloud source integration seam. The host installs a provider when one exists; //! the default workspace stays disconnected and never fabricates or stores files. use anyhow::Result; #[derive(Clone, Debug)] /// provider 提供的一条文件元数据,供云文件列表显示。 pub struct SourceFile { // provider 定义的不透明文件身份,后续 open_file 使用;显示路径不必等于该身份。 pub id: String, // 面向用户显示的路径文本,不能据此推断为本机可直接读取的文件路径。 pub path: String, } #[derive(Clone, Debug)] /// 已经取得的源文件内容与标识,不代表它已经被执行。 pub struct OpenedSource { // provider 返回的展示路径,Sources 面板据此显示当前打开文件标题,不把它当作执行入口。 pub path: String, // 已读取的文本副本,仅绘制源码行;不覆盖当前已发布文档的 source 或触发热重载。 pub code: String, } /// 外部文件能力接口;只负责列出和读取,不与 VM 全局环境耦合。 // 由宿主注入具体实现;Send 允许转移线程,但方法使用 &mut self,不声明可并行共享调用。 pub trait CloudSourceProvider: std::fmt::Debug + Send { /// SourceWorkspace::refresh 同步调用,返回文件元数据;这里只声明接口,不规定网络协议或缓存策略。 fn list_files(&mut self) -> Result>; /// SourceWorkspace::open 同步调用,按不透明 ID 读取完整文本;读取结果不进入 VM 执行队列。 fn open_file(&mut self, id: &str) -> Result; } #[derive(Debug, Default)] /// Sources 云文件页面保留的 provider、列表、已打开内容与错误状态;不另存列表选中 ID。 pub struct SourceWorkspace { // None 表示未接入外部文件服务;默认工作区不提供虚构文件或隐式认证。 pub provider: Option>, // 最近一次 list_files 成功返回的完整列表,刷新失败仍保留供界面查看。 pub files: Vec, // 最近成功打开的内容;选择另一个文件失败时保持旧内容,不显示半读取结果。 pub opened: Option, // 最近调用错误的可显示文本,成功 refresh/open 会清空;默认未连接时初始为空。 pub error: String, } impl SourceWorkspace { /// 更新云文件列表并保存结果或错误,供 Sources 页面显示。 // developer_tools 处理 Action::CloudRefresh 时调用,build 仅根据保存状态绘制,不在绘制中请求 provider。 pub fn refresh(&mut self) { // 未连接时明确写错误并返回;已有文件列表/打开内容不会在失败路径中被清除。 let Some(provider) = &mut self.provider else { self.error = "No cloud file provider configured".into(); return; }; // 调用是同步的,调用方负责安排执行位置;成功才整体替换列表,失败保留旧列表供查看。 match provider.list_files() { // 只替换列表并清错,不自动重读或关闭已经打开的文件。 Ok(files) => { self.files = files; self.error.clear(); } Err(error) => self.error = error.to_string(), } } /// 更新选中文件的内容与打开状态,失败保持可读错误信息。 // 只读取内容而不执行代码;未连接时直接返回,不像 refresh 那样写入新的错误。 // developer_tools 处理 Action::CloudOpen(id) 时调用,id 来自 SourceFile,不用显示路径代替 provider 身份。 pub fn open(&mut self, id: &str) { let Some(provider) = &mut self.provider else { return; }; // 成功替换 opened 并清错;失败只更新 error,用户仍可查看之前成功打开的内容。 match provider.open_file(id) { // provider 返回完整内容后一次替换 opened,查看动作不会执行其中的 Endor 程序。 Ok(source) => { self.opened = Some(source); self.error.clear(); } Err(error) => self.error = error.to_string(), } } }