Description
The Cache-Control
header is just interesting enough that I feel it isn't quite obvious which of the following designs make more sense.
List of Directives
This is how it was in hyper 0.11.x. Essentially, it's just a comma separated list of CacheDirectives
, and not any smarter than that.
pub struct CacheControl {
directives: Vec<CacheDirective>,
}
Example construction:
let cc = CacheControl::new(vec![
CacheDirective::NO_TRANSFORM,
CacheDirective::max_age(Duration::from_secs(300)),
]);
This matches the spec more directly, as its ABNF claims to just be a list of directives, and clients may not understand newer directives. However, with the more conservative API (to allow adding new directive support without being a breaking change), the CacheDirective::Ext
is no longer exported, so servers/clients couldn't really use "newer" directives anyways, without updating this crate.
The downsides are that this results in an extra allocation of a Vec
, and is more complicated for checking if a certain directive is set (one has to iterate looking for a specific directive).
Set of Fields
This would be a plain old Rust object with fields representing known directives.
pub struct CacheControl {
no_store: bool,
no_cache: bool,
max_age: Option<Seconds>,
// ...
}
This would mean that an allocation of a mostly useless Vec
can be removed, and accessors like cc.is_no_cache()
can be added cheaply.
The struct could be made even smaller by using bitflags
for all the boolean fields.
Lazy (FlatCsv
)
Some other fields are currently just lazily parsed when they would otherwise be a list of strings. This saves on allocations/copies (and makes encoding super cheap) at the expense of needing to parse on any lookup. For CacheControl
, it seems feasible that you'd need to check for various directives. You could do so once in a single for dir in cc.iter()
, but subsequent ones would repeat all the work.
I think I'm leaning towards the Set of Fields option.