diff options
| author | main <main@swarm.moe> | 2026-03-20 23:15:05 -0400 |
|---|---|---|
| committer | main <main@swarm.moe> | 2026-03-20 23:15:05 -0400 |
| commit | e325cd23f19378f543981071673c1d03be438fa5 (patch) | |
| tree | e89fdfd82b65f6b373b15da59c430a0ceb428afc /crates | |
| parent | bb92a05eb5446e07c6288e266bd06d7b5899eee5 (diff) | |
| download | libmcp-e325cd23f19378f543981071673c1d03be438fa5.zip | |
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/libmcp/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/libmcp/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/libmcp/src/projection.rs | 50 |
3 files changed, 52 insertions, 1 deletions
diff --git a/crates/libmcp/Cargo.toml b/crates/libmcp/Cargo.toml index c087ab8..0f12017 100644 --- a/crates/libmcp/Cargo.toml +++ b/crates/libmcp/Cargo.toml @@ -13,6 +13,7 @@ schemars.workspace = true serde.workspace = true serde_json.workspace = true thiserror.workspace = true +time.workspace = true tokio.workspace = true url.workspace = true diff --git a/crates/libmcp/src/lib.rs b/crates/libmcp/src/lib.rs index 049cffe..b167964 100644 --- a/crates/libmcp/src/lib.rs +++ b/crates/libmcp/src/lib.rs @@ -35,7 +35,7 @@ pub use normalize::{ }; pub use projection::{ FallbackJsonProjection, ProjectionError, ProjectionPolicy, SelectorProjection, SelectorRef, - StructuredProjection, SurfaceKind, SurfacePolicy, ToolProjection, + StructuredProjection, SurfaceKind, SurfacePolicy, TimestampText, ToolProjection, }; pub use render::{ DetailLevel, JsonPorcelainConfig, PathStyle, RenderConfig, RenderMode, TruncatedText, diff --git a/crates/libmcp/src/projection.rs b/crates/libmcp/src/projection.rs index a6216db..1e42f49 100644 --- a/crates/libmcp/src/projection.rs +++ b/crates/libmcp/src/projection.rs @@ -4,7 +4,9 @@ use crate::render::{DetailLevel, JsonPorcelainConfig, render_json_porcelain}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::fmt; use thiserror::Error; +use time::{OffsetDateTime, format_description::well_known::Rfc3339}; const OVERVIEW_CONCISE_CONFIG: JsonPorcelainConfig = JsonPorcelainConfig { max_lines: 10, @@ -117,6 +119,35 @@ pub struct SelectorRef { pub title: Option<String>, } +/// Uniform RFC3339 timestamp text for model-facing surfaces. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(transparent)] +pub struct TimestampText(String); + +impl TimestampText { + /// Returns the rendered timestamp string. + #[must_use] + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for TimestampText { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&self.0) + } +} + +impl From<OffsetDateTime> for TimestampText { + fn from(timestamp: OffsetDateTime) -> Self { + Self( + timestamp + .format(&Rfc3339) + .unwrap_or_else(|_| timestamp.unix_timestamp().to_string()), + ) + } +} + /// Anything that can produce a selector reference. pub trait SelectorProjection { /// Builds the selector reference. @@ -236,6 +267,7 @@ impl SurfacePolicy for FallbackJsonProjection { mod tests { use super::{StructuredProjection as _, SurfaceKind, SurfacePolicy as _}; use crate::{DetailLevel, SelectorProjection, SelectorRef, ToolProjection}; + use time::OffsetDateTime; #[derive(Clone, SelectorProjection)] struct HypothesisSelector { @@ -305,4 +337,22 @@ mod tests { }; assert!(porcelain.contains("slug: \"matched-lp-site-traces\"")); } + + #[test] + fn timestamp_text_serializes_as_rfc3339_string() { + let timestamp = OffsetDateTime::from_unix_timestamp(0); + assert!(timestamp.is_ok()); + let timestamp = match timestamp { + Ok(value) => value, + Err(_) => return, + }; + let rendered = super::TimestampText::from(timestamp); + let json = serde_json::to_value(&rendered); + assert!(json.is_ok()); + let json = match json { + Ok(value) => value, + Err(_) => return, + }; + assert_eq!(json, serde_json::json!("1970-01-01T00:00:00Z")); + } } |