11//! HTTP cookie printing and parsing.
22
33const std = @import ("std" );
4+
45const Datetime = @import ("Datetime.zig" );
56
67const Cookie = @This ();
78
9+ expires : ? Expiration = null ,
10+ same_site : ? SameSite = null ,
11+ domain : ? []const u8 = null ,
12+ max_age : ? u64 = null ,
13+ partitioned : ? bool = null ,
14+ http_only : ? bool = null ,
15+ path : ? []const u8 = null ,
16+ secure : ? bool = null ,
17+ value : []const u8 = "" ,
18+ name : []const u8 ,
19+
820pub const Error = error {
921 MissingPair ,
1022 EmptyName ,
@@ -22,55 +34,44 @@ pub const SameSite = enum {
2234 Lax ,
2335};
2436
25- expires : ? Expiration = null ,
26- same_site : ? SameSite = null ,
27- domain : ? []const u8 = null ,
28- max_age : ? u64 = null ,
29- partitioned : ? bool = null ,
30- http_only : ? bool = null ,
31- path : ? []const u8 = null ,
32- secure : ? bool = null ,
33- value : []const u8 = "" ,
34- name : []const u8 ,
35-
3637/// Set expires field from HTTP datetime value.
3738pub fn setExpires (self : * Cookie , value : []const u8 ) Datetime.Error ! void {
38- self .expires = .{ .datetime = try Datetime .parse (value ) };
39+ self .expires = .{ .datetime = try .parse (value ) };
3940}
4041
4142/// Turn into permanent cookie.
4243pub fn makePermanent (self : * Cookie ) void {
4344 self .max_age = 20 * 365 * std .time .s_per_day ;
44- self .expires = .{ .datetime = Datetime .fromTimestamp (std .time .timestamp () + self .max_age ) };
45+ self .expires = .{ .datetime = .fromTimestamp (std .time .timestamp () + self .max_age ) };
4546}
4647
4748/// Turn into removal cookie.
4849pub fn makeRemoval (self : * Cookie ) void {
4950 self .value = "" ;
5051 self .max_age = 0 ;
51- self .expires = .{ .datetime = Datetime .fromTimestamp (std .time .timestamp () + 365 * std .time .s_per_day ) };
52+ self .expires = .{ .datetime = .fromTimestamp (std .time .timestamp () + 365 * std .time .s_per_day ) };
5253}
5354
5455/// Parse cookie from string, specifying whether name and value need escaping.
5556pub fn parse (cookie_str : []const u8 ) Error ! Cookie {
5657 var attr_iter = std .mem .tokenizeScalar (u8 , cookie_str , ';' );
57- const name_value = attr_iter .next () orelse return error .MissingPair ;
58- const name_value_idx = std .mem .indexOfScalar (u8 , name_value , '=' ) orelse return error .MissingPair ;
59- var name = std .mem .trim (u8 , name_value [0.. name_value_idx ], std .ascii .whitespace [0 .. ] );
60- var value = std .mem .trim (u8 , name_value [name_value_idx + 1 .. ], std .ascii .whitespace [0 .. ] );
58+ const name_value = attr_iter .next () orelse return Error .MissingPair ;
59+ const name_value_idx = std .mem .indexOfScalar (u8 , name_value , '=' ) orelse return Error .MissingPair ;
60+ var name = std .mem .trim (u8 , name_value [0.. name_value_idx ], & std .ascii .whitespace );
61+ var value = std .mem .trim (u8 , name_value [name_value_idx + 1 .. ], & std .ascii .whitespace );
6162
6263 if (name .len == 0 ) {
63- return error .EmptyName ;
64+ return Error .EmptyName ;
6465 }
6566
66- var cookie = Cookie { .name = name , .value = value };
67+ var cookie : Cookie = . { .name = name , .value = value };
6768
6869 outer : while (attr_iter .next ()) | attr | {
6970 if (std .mem .indexOfScalar (u8 , attr , '=' )) | idx | {
70- name = std .mem .trim (u8 , attr [0.. idx ], std .ascii .whitespace [0 .. ] );
71- value = std .mem .trim (u8 , attr [idx + 1 .. ], std .ascii .whitespace [0 .. ] );
71+ name = std .mem .trim (u8 , attr [0.. idx ], & std .ascii .whitespace );
72+ value = std .mem .trim (u8 , attr [idx + 1 .. ], & std .ascii .whitespace );
7273 } else {
73- name = std .mem .trim (u8 , attr , std .ascii .whitespace [0 .. ] );
74+ name = std .mem .trim (u8 , attr , & std .ascii .whitespace );
7475 value = "" ;
7576 }
7677
@@ -104,23 +105,23 @@ pub fn parse(cookie_str: []const u8) Error!Cookie {
104105 } else if (std .ascii .eqlIgnoreCase (name , "partitioned" )) {
105106 cookie .partitioned = true ;
106107 } else if (std .ascii .eqlIgnoreCase (name , "expires" )) {
107- cookie .expires = .{ .datetime = try Datetime .parse (value ) };
108+ cookie .expires = .{ .datetime = try .parse (value ) };
108109 }
109110 }
110111
111112 return cookie ;
112113}
113114
114115/// Print cookie to writer.
115- pub fn format (self : Cookie , comptime _ : [] const u8 , _ : std.fmt.FormatOptions , writer : anytype ) ! void {
116+ pub fn format (self : Cookie , writer : * std.io.Writer ) std.io.Writer.Error ! void {
116117 try writer .print ("{s}={s}" , .{ self .name , self .value });
117118
118119 if (self .http_only ) | _ | {
119120 try writer .writeAll ("; HttpOnly" );
120121 }
121122
122123 if (self .same_site ) | same_site | {
123- try writer .print ("; SameSite={s }" , .{@tagName ( same_site ) });
124+ try writer .print ("; SameSite={t }" , .{same_site });
124125 }
125126
126127 if (self .partitioned ) | _ | {
@@ -148,48 +149,48 @@ pub fn format(self: Cookie, comptime _: []const u8, _: std.fmt.FormatOptions, wr
148149
149150 if (self .expires ) | expires | {
150151 if (std .meta .activeTag (expires ) == .datetime ) {
151- try writer .print ("; Expires={s }" , .{expires .datetime });
152+ try writer .print ("; Expires={f }" , .{expires .datetime });
152153 }
153154 }
154155}
155156
156157test format {
157- try std .testing .expectFmt ("foo=bar" , "{}" , .{Cookie { .name = "foo" , .value = "bar" }});
158- try std .testing .expectFmt ("foo=bar; HttpOnly" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .http_only = true }});
159- try std .testing .expectFmt ("foo=bar; Max-Age=10" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .max_age = 10 }});
160- try std .testing .expectFmt ("foo=bar; Secure" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .secure = true }});
161- try std .testing .expectFmt ("foo=bar; Path=/" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .path = "/" }});
162- try std .testing .expectFmt ("foo=bar; Domain=ziglang.org" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .domain = "ziglang.org" }});
163- try std .testing .expectFmt ("foo=bar; SameSite=Strict" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .same_site = .Strict }});
164- try std .testing .expectFmt ("foo=bar; SameSite=Lax" , "{}" , .{Cookie { .name = "foo" , .value = "bar" , .same_site = .Lax }});
165-
166- var cookie = Cookie { .name = "foo" , .value = "bar" , .same_site = .None };
167- try std .testing .expectFmt ("foo=bar; SameSite=None; Secure" , "{}" , .{cookie });
158+ try std .testing .expectFmt ("foo=bar" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" }});
159+ try std .testing .expectFmt ("foo=bar; HttpOnly" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .http_only = true }});
160+ try std .testing .expectFmt ("foo=bar; Max-Age=10" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .max_age = 10 }});
161+ try std .testing .expectFmt ("foo=bar; Secure" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .secure = true }});
162+ try std .testing .expectFmt ("foo=bar; Path=/" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .path = "/" }});
163+ try std .testing .expectFmt ("foo=bar; Domain=ziglang.org" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .domain = "ziglang.org" }});
164+ try std .testing .expectFmt ("foo=bar; SameSite=Strict" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .same_site = .Strict }});
165+ try std .testing .expectFmt ("foo=bar; SameSite=Lax" , "{f }" , .{Cookie { .name = "foo" , .value = "bar" , .same_site = .Lax }});
166+
167+ var cookie : Cookie = . { .name = "foo" , .value = "bar" , .same_site = .None };
168+ try std .testing .expectFmt ("foo=bar; SameSite=None; Secure" , "{f }" , .{cookie });
168169
169170 cookie .partitioned = true ;
170- try std .testing .expectFmt ("foo=bar; SameSite=None; Partitioned; Secure" , "{}" , .{cookie });
171+ try std .testing .expectFmt ("foo=bar; SameSite=None; Partitioned; Secure" , "{f }" , .{cookie });
171172
172173 cookie .same_site = null ;
173- try std .testing .expectFmt ("foo=bar; Partitioned; Secure" , "{}" , .{cookie });
174+ try std .testing .expectFmt ("foo=bar; Partitioned; Secure" , "{f }" , .{cookie });
174175
175176 cookie .secure = false ;
176- try std .testing .expectFmt ("foo=bar; Partitioned; Secure" , "{}" , .{cookie });
177+ try std .testing .expectFmt ("foo=bar; Partitioned; Secure" , "{f }" , .{cookie });
177178
178179 cookie .secure = null ;
179- try std .testing .expectFmt ("foo=bar; Partitioned; Secure" , "{}" , .{cookie });
180+ try std .testing .expectFmt ("foo=bar; Partitioned; Secure" , "{f }" , .{cookie });
180181
181182 cookie .partitioned = null ;
182- try std .testing .expectFmt ("foo=bar" , "{}" , .{cookie });
183+ try std .testing .expectFmt ("foo=bar" , "{f }" , .{cookie });
183184
184- cookie = Cookie { .name = "foo" , .value = "bar" , .same_site = .None , .secure = false };
185- try std .testing .expectFmt ("foo=bar; SameSite=None" , "{}" , .{cookie });
185+ cookie = . { .name = "foo" , .value = "bar" , .same_site = .None , .secure = false };
186+ try std .testing .expectFmt ("foo=bar; SameSite=None" , "{f }" , .{cookie });
186187
187188 cookie .secure = true ;
188- try std .testing .expectFmt ("foo=bar; SameSite=None; Secure" , "{}" , .{cookie });
189+ try std .testing .expectFmt ("foo=bar; SameSite=None; Secure" , "{f }" , .{cookie });
189190
190- cookie = Cookie { .name = "foo" , .value = "bar" };
191+ cookie = . { .name = "foo" , .value = "bar" };
191192 try cookie .setExpires ("Mon, 08 Feb 2016 07:28:00 GMT" );
192- try std .testing .expectFmt ("foo=bar; Expires=Mon, 08 Feb 2016 07:28:00 GMT" , "{}" , .{cookie });
193+ try std .testing .expectFmt ("foo=bar; Expires=Mon, 08 Feb 2016 07:28:00 GMT" , "{f }" , .{cookie });
193194}
194195
195196test parse {
@@ -215,9 +216,9 @@ test parse {
215216 try std .testing .expectEqualStrings (cookie .value , "bar" );
216217 try std .testing .expectEqual (cookie .expires .? .datetime , try Datetime .parse ("Mon, 08 Feb 2016 07:28:00 GMT" ));
217218
218- try std .testing .expectError (error .MissingPair , parse ("bar" ));
219- try std .testing .expectError (error .EmptyName , parse ("=bar" ));
220- try std .testing .expectError (error .EmptyName , parse (" =bar" ));
219+ try std .testing .expectError (Error .MissingPair , parse ("bar" ));
220+ try std .testing .expectError (Error .EmptyName , parse ("=bar" ));
221+ try std .testing .expectError (Error .EmptyName , parse (" =bar" ));
221222
222223 cookie = try parse ("foo=bar=baz" );
223224 try std .testing .expectEqualStrings (cookie .name , "foo" );
0 commit comments