diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig b/vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig new file mode 100644 index 00000000..b586161f --- /dev/null +++ b/vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig @@ -0,0 +1,156 @@ +/// A single entry within our SSH entry cache. Our SSH entry cache +/// stores which hosts we've sent our terminfo to so that we don't have +/// to send it again. It doesn't store any sensitive information. +const Entry = @This(); + +const std = @import("std"); + +hostname: []const u8, +timestamp: i64, +terminfo_version: []const u8, + +pub fn parse(line: []const u8) ?Entry { + const trimmed = std.mem.trim(u8, line, " \t\r\n"); + if (trimmed.len == 0) return null; + + // Parse format: hostname|timestamp|terminfo_version + var iter = std.mem.tokenizeScalar(u8, trimmed, '|'); + const hostname = iter.next() orelse return null; + const timestamp_str = iter.next() orelse return null; + const terminfo_version = iter.next() orelse "xterm-ghostty"; + const timestamp = std.fmt.parseInt(i64, timestamp_str, 10) catch |err| { + std.log.warn( + "Invalid timestamp in cache entry: {s} err={}", + .{ timestamp_str, err }, + ); + return null; + }; + + return .{ + .hostname = hostname, + .timestamp = timestamp, + .terminfo_version = terminfo_version, + }; +} + +pub const FormatError = std.Io.Writer.Error; + +pub fn format(self: Entry, writer: *std.Io.Writer) FormatError!void { + try writer.print( + "{s}|{d}|{s}\n", + .{ self.hostname, self.timestamp, self.terminfo_version }, + ); +} + +pub fn isExpired(self: Entry, expire_days_: ?u32) bool { + const expire_days = expire_days_ orelse return false; + const now = std.time.timestamp(); + const age_days = @divTrunc(now -| self.timestamp, std.time.s_per_day); + return age_days > expire_days; +} + +test "cache entry expiration" { + const testing = std.testing; + const now = std.time.timestamp(); + + const fresh_entry: Entry = .{ + .hostname = "test.com", + .timestamp = now - std.time.s_per_day, // 1 day old + .terminfo_version = "xterm-ghostty", + }; + try testing.expect(!fresh_entry.isExpired(90)); + + const old_entry: Entry = .{ + .hostname = "old.com", + .timestamp = now - (std.time.s_per_day * 100), // 100 days old + .terminfo_version = "xterm-ghostty", + }; + try testing.expect(old_entry.isExpired(90)); + + // Test never-expire case + try testing.expect(!old_entry.isExpired(null)); +} + +test "cache entry expiration exact boundary" { + const testing = std.testing; + const now = std.time.timestamp(); + + // Exactly at expiration boundary + const boundary_entry: Entry = .{ + .hostname = "example.com", + .timestamp = now - (std.time.s_per_day * 30), + .terminfo_version = "xterm-ghostty", + }; + try testing.expect(!boundary_entry.isExpired(30)); + try testing.expect(boundary_entry.isExpired(29)); +} + +test "cache entry expiration large timestamp" { + const testing = std.testing; + const now = std.time.timestamp(); + + const boundary_entry: Entry = .{ + .hostname = "example.com", + .timestamp = now + (std.time.s_per_day * 30), + .terminfo_version = "xterm-ghostty", + }; + try testing.expect(!boundary_entry.isExpired(30)); +} + +test "cache entry parsing valid formats" { + const testing = std.testing; + + const entry = Entry.parse("example.com|1640995200|xterm-ghostty").?; + try testing.expectEqualStrings("example.com", entry.hostname); + try testing.expectEqual(@as(i64, 1640995200), entry.timestamp); + try testing.expectEqualStrings("xterm-ghostty", entry.terminfo_version); + + // Test default terminfo version + const entry_no_version = Entry.parse("test.com|1640995200").?; + try testing.expectEqualStrings( + "xterm-ghostty", + entry_no_version.terminfo_version, + ); + + // Test complex hostnames + const complex_entry = Entry.parse("user@server.example.com|1640995200|xterm-ghostty").?; + try testing.expectEqualStrings( + "user@server.example.com", + complex_entry.hostname, + ); +} + +test "cache entry parsing invalid formats" { + const testing = std.testing; + + try testing.expect(Entry.parse("") == null); + + // Invalid format (no pipe) + try testing.expect(Entry.parse("v1") == null); + + // Missing timestamp + try testing.expect(Entry.parse("example.com") == null); + + // Invalid timestamp + try testing.expect(Entry.parse("example.com|invalid") == null); + + // Empty terminfo should default + try testing.expect(Entry.parse("example.com|1640995200|") != null); +} + +test "cache entry parsing malformed data resilience" { + const testing = std.testing; + + // Extra pipes should not break parsing + try testing.expect(Entry.parse("host|123|term|extra") != null); + + // Whitespace handling + try testing.expect(Entry.parse(" host|123|term ") != null); + try testing.expect(Entry.parse("\n") == null); + try testing.expect(Entry.parse(" \t \n") == null); + + // Extremely large timestamp + try testing.expect( + Entry.parse("host|999999999999999999999999999999999999999999999999|xterm-ghostty") == null, + ); +} |
