0.6 rust重构01
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
use crate::common::{ActorPayload, AffectedObject, SourcePayload, TargetRef};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommandEnvelope<T> {
|
||||
pub name: String,
|
||||
pub command_id: String,
|
||||
pub idempotency_key: Option<String>,
|
||||
pub actor: ActorPayload,
|
||||
pub source: SourcePayload,
|
||||
pub target: Option<TargetRef>,
|
||||
pub payload: T,
|
||||
pub reason: Option<String>,
|
||||
pub refs: Vec<String>,
|
||||
pub dry_run: bool,
|
||||
pub validate_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommandResult {
|
||||
pub ok: bool,
|
||||
pub command_id: String,
|
||||
pub event_ids: Vec<String>,
|
||||
pub affected_objects: Vec<AffectedObject>,
|
||||
pub revision: Option<u64>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CreateWorkspace {
|
||||
pub name: String,
|
||||
pub slug: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CreatePage {
|
||||
pub title: String,
|
||||
pub parent_page_id: Option<String>,
|
||||
pub position: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UpdatePageTitle {
|
||||
pub page_id: String,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UpdatePageStats {
|
||||
pub page_id: String,
|
||||
pub word_count: i64,
|
||||
pub character_count: i64,
|
||||
pub block_count: i64,
|
||||
pub todo_total: i64,
|
||||
pub todo_done: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UpdatePageOptions {
|
||||
pub page_id: String,
|
||||
pub wide_layout: Option<bool>,
|
||||
pub small_text: Option<bool>,
|
||||
pub show_heading_numbers: Option<bool>,
|
||||
pub show_toc: Option<bool>,
|
||||
pub show_structure: Option<bool>,
|
||||
pub protect_editing: Option<bool>,
|
||||
pub show_word_count: Option<bool>,
|
||||
pub collapse_backlinks: Option<bool>,
|
||||
pub page_font: Option<String>,
|
||||
pub layout_density: Option<String>,
|
||||
pub hide_child_pages: Option<bool>,
|
||||
pub show_block_ref_count: Option<bool>,
|
||||
pub embed_default_block_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InsertBlock {
|
||||
pub page_id: String,
|
||||
pub block_type: String,
|
||||
pub content: String,
|
||||
pub parent_block_id: Option<String>,
|
||||
pub prev_block_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UpdateBlock {
|
||||
pub block_id: String,
|
||||
pub patch_content: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MoveBlock {
|
||||
pub block_id: String,
|
||||
pub new_parent_block_id: Option<String>,
|
||||
pub new_page_id: Option<String>,
|
||||
pub prev_block_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DeleteBlock {
|
||||
pub block_id: String,
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ActorPayload {
|
||||
pub actor_type: String,
|
||||
pub actor_id: String,
|
||||
pub session_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SourcePayload {
|
||||
pub channel: String,
|
||||
pub client: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TargetRef {
|
||||
pub workspace_id: Option<String>,
|
||||
pub page_id: Option<String>,
|
||||
pub block_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RequestMeta {
|
||||
pub idempotency_key: Option<String>,
|
||||
pub validate_only: bool,
|
||||
pub dry_run: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AffectedObject {
|
||||
pub object_type: String,
|
||||
pub object_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ResponseMeta {
|
||||
pub request_id: String,
|
||||
pub trace_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OkPayload<T> {
|
||||
pub data: T,
|
||||
pub meta: ResponseMeta,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ErrorDetail {
|
||||
pub field: Option<String>,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ErrorPayload {
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub details: Vec<ErrorDetail>,
|
||||
pub retryable: bool,
|
||||
pub meta: ResponseMeta,
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AccessContext {
|
||||
pub tenant_id: Option<String>,
|
||||
pub workspace_id: String,
|
||||
pub actor_id: String,
|
||||
pub actor_type: String,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AccessDecision {
|
||||
Allow,
|
||||
Deny(AccessDenyReason),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AccessDenyReason {
|
||||
MissingTenant,
|
||||
MissingWorkspace,
|
||||
PermissionDenied,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum JobStatus {
|
||||
Pending,
|
||||
Running,
|
||||
Succeeded,
|
||||
Failed,
|
||||
RetryScheduled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct JobTicket {
|
||||
pub job_id: String,
|
||||
pub job_type: String,
|
||||
pub request_id: String,
|
||||
pub trace_id: String,
|
||||
pub workspace_id: String,
|
||||
pub status: JobStatus,
|
||||
}
|
||||
|
||||
impl JobTicket {
|
||||
pub fn new(
|
||||
job_id: impl Into<String>,
|
||||
job_type: impl Into<String>,
|
||||
request_id: impl Into<String>,
|
||||
trace_id: impl Into<String>,
|
||||
workspace_id: impl Into<String>,
|
||||
status: JobStatus,
|
||||
) -> Self {
|
||||
Self {
|
||||
job_id: job_id.into(),
|
||||
job_type: job_type.into(),
|
||||
request_id: request_id.into(),
|
||||
trace_id: trace_id.into(),
|
||||
workspace_id: workspace_id.into(),
|
||||
status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decide_access(context: &AccessContext) -> AccessDecision {
|
||||
if context.tenant_id.is_none() {
|
||||
return AccessDecision::Deny(AccessDenyReason::MissingTenant);
|
||||
}
|
||||
if context.workspace_id.trim().is_empty() {
|
||||
return AccessDecision::Deny(AccessDenyReason::MissingWorkspace);
|
||||
}
|
||||
AccessDecision::Allow
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn access_requires_tenant() {
|
||||
let context = AccessContext {
|
||||
tenant_id: None,
|
||||
workspace_id: "ws-1".to_string(),
|
||||
actor_id: "user-1".to_string(),
|
||||
actor_type: "human".to_string(),
|
||||
source: "react-next".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
decide_access(&context),
|
||||
AccessDecision::Deny(AccessDenyReason::MissingTenant)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access_allows_valid_context() {
|
||||
let context = AccessContext {
|
||||
tenant_id: Some("tenant-1".to_string()),
|
||||
workspace_id: "ws-1".to_string(),
|
||||
actor_id: "user-1".to_string(),
|
||||
actor_type: "human".to_string(),
|
||||
source: "react-next".to_string(),
|
||||
};
|
||||
assert_eq!(decide_access(&context), AccessDecision::Allow);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn job_ticket_keeps_trace_fields() {
|
||||
let ticket = JobTicket::new(
|
||||
"job-1",
|
||||
"rebuild-index",
|
||||
"req-1",
|
||||
"trace-1",
|
||||
"ws-1",
|
||||
JobStatus::Pending,
|
||||
);
|
||||
assert_eq!(ticket.trace_id, "trace-1");
|
||||
assert_eq!(ticket.status, JobStatus::Pending);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
pub mod command;
|
||||
pub mod common;
|
||||
pub mod governance;
|
||||
pub mod query;
|
||||
pub mod tool;
|
||||
|
||||
pub use command::{
|
||||
CommandEnvelope, CreatePage, CreateWorkspace, DeleteBlock, InsertBlock, MoveBlock,
|
||||
UpdateBlock, UpdatePageStats, UpdatePageTitle,
|
||||
};
|
||||
pub use common::{
|
||||
ActorPayload, AffectedObject, ErrorDetail, ErrorPayload, OkPayload, RequestMeta, ResponseMeta,
|
||||
SourcePayload, TargetRef,
|
||||
};
|
||||
pub use query::{GetPage, ListPageBlocks, QueryEnvelope, SearchBlocks, SearchPages};
|
||||
pub use tool::{InvocationKind, ToolInvocation};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn command_envelope_keeps_validate_only_flag() {
|
||||
let envelope = CommandEnvelope::<CreateWorkspace> {
|
||||
name: "create_workspace".into(),
|
||||
command_id: "cmd_1".into(),
|
||||
idempotency_key: Some("idem_1".into()),
|
||||
actor: ActorPayload {
|
||||
actor_type: "human".into(),
|
||||
actor_id: "user_1".into(),
|
||||
session_id: Some("session_1".into()),
|
||||
},
|
||||
source: SourcePayload {
|
||||
channel: "cli".into(),
|
||||
client: "mnote-cli".into(),
|
||||
},
|
||||
target: None,
|
||||
payload: CreateWorkspace {
|
||||
name: "demo".into(),
|
||||
slug: Some("demo".into()),
|
||||
},
|
||||
reason: Some("初始化工作区".into()),
|
||||
refs: vec![],
|
||||
dry_run: false,
|
||||
validate_only: true,
|
||||
};
|
||||
|
||||
assert!(envelope.validate_only);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct QueryEnvelope<T> {
|
||||
pub name: String,
|
||||
pub payload: T,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Pagination {
|
||||
pub limit: u32,
|
||||
pub cursor: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GetPage {
|
||||
pub page_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ListPageBlocks {
|
||||
pub page_id: String,
|
||||
pub pagination: Pagination,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SearchPages {
|
||||
pub query: String,
|
||||
pub workspace_id: Option<String>,
|
||||
pub pagination: Pagination,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SearchBlocks {
|
||||
pub query: String,
|
||||
pub page_id: Option<String>,
|
||||
pub pagination: Pagination,
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InvocationKind {
|
||||
Command,
|
||||
Query,
|
||||
Job,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ToolInvocation {
|
||||
pub tool: String,
|
||||
pub kind: InvocationKind,
|
||||
pub args_json: String,
|
||||
}
|
||||
Reference in New Issue
Block a user