fix(traits): transitive, minimal traits for types

Previously, I would just assign all useful traits to all types, no
matter on how they were actually used.
Now it builds all dependnecies and considers them when assigning
traits, which is as precise as we need it.

This is important to us as the `Json` type is just encodable, but
not decodable. Fortunately, we just have to encode it, but in theory
this makes it hard to embed any json in a known structure.
This commit is contained in:
Sebastian Thiel
2015-03-11 20:06:16 +01:00
parent e3ab233a6c
commit 00de2b187d
3 changed files with 1992 additions and 435 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@ ${struct};
<%
markers = schema_markers(s, c)
traits = ['Default', 'Clone']
if REQUEST_MARKER_TRAIT in markers:
traits.append('RustcEncodable')
if RESPONSE_MARKER_TRAIT in markers:
@@ -70,7 +71,7 @@ impl Default for ${s.id} {
impl ${marker_trait} for ${s.id} {}
% endfor
% if REQUEST_MARKER_TRAIT in markers:
% if REQUEST_MARKER_TRAIT in markers and 'properties' in s:
impl ${s.id} {
/// Return a comma separated list of members that are currently set, i.e. for which `self.member.is_some()`.
/// The produced string is suitable for use as a parts list that indicates the parts you are sending, and/or

View File

@@ -270,6 +270,7 @@ def _is_map_prop(p):
def _assure_unique_type_name(schemas, tn):
if tn in schemas:
tn += 'Nested'
assert tn not in schemas
return tn
# map a json type to an rust type
@@ -351,8 +352,22 @@ def is_pod_property(p):
# inherits this trait
def schema_markers(s, c):
res = set()
ids = s['parents'] + [s.id]
print ids
ids = [s.id]
used_by = s.used_by + s.parents
seen = set() # protect against loops, just to be sure ...
while used_by:
id = used_by.pop()
if id in seen:
continue
seen.add(id)
ids.append(id)
oid = c.schemas[id]
used_by.extend(oid.used_by)
used_by.extend(oid.parents)
# end gather usages
for sid in ids:
activities = c.sta_map.get(sid, dict())
if len(activities) == 0:
@@ -588,54 +603,58 @@ def new_context(schemas, resources):
# in order of traversal, [-1] is first parent, [0] is the root of them all
def build_schema_map():
# 'type' in t and t.type == 'object' and 'properties' in t or ('items' in t and 'properties' in t.items)
PKEY = 'parents'
UBKEY = 'used_by'
PARENT = 'parents'
USED_BY = 'used_by'
def assure_list(s, k):
if k not in s:
s[k] = list()
return s[k]
# end
def link_used(s, rs):
if TREF in s:
l = assure_list(all_schemas[s[TREF]], USED_BY)
if rs.id not in l:
l.append(rs.id)
all_schemas = deepcopy(schemas)
def recurse_properties(prefix, properties, parent_ids):
def recurse_properties(prefix, rs, s, parent_ids):
assure_list(s, USED_BY)
assure_list(s, PARENT).extend(parent_ids)
link_used(s, rs)
properties = s.get('properties', {rs.id: s})
for pn, p in properties.iteritems():
if TREF in p:
# they can be used in mulFinternaltiple spots - just brute-force copy all parents in there
# which should probably be renamed to used_by instead
pass
link_used(p, rs)
if is_nested_type_property(p):
ns = deepcopy(p)
ns.id = _assure_unique_type_name(schemas, nested_type_name(prefix, pn))
all_schemas[ns.id] = ns
ns[PKEY] = parent_ids
# To allow us recursing arrays, we simply put items one level up
if 'items' in p:
ns.update((k, deepcopy(v)) for k, v in p.items.iteritems())
if 'properties' in ns:
recurse_properties(prefix + canonical_type_name(pn), ns.properties, parent_ids + [ns.id])
recurse_properties(prefix + canonical_type_name(pn), ns, ns, parent_ids + [rs.id])
elif _is_map_prop(p):
# it's a hash, check its type
# TODO: does this code run ? Why is there a plain prefix
recurse_properties(prefix, {pn: p.additionalProperties}, parent_ids + [])
recurse_properties(prefix + canonical_type_name(pn), rs, p.additionalProperties, parent_ids + [])
elif 'items' in p:
# it's an array
recurse_properties(prefix, {pn: p.items}, parent_ids + [])
recurse_properties(prefix + canonical_type_name(pn), rs, p.items, parent_ids + [])
# end handle prop itself
# end for each property
# end utility
for s in schemas.values():
s[PKEY] = list() # roots never have parents
if UBKEY not in s:
s[UBKEY] = list()
if 'properties' not in s:
continue
recurse_properties(s.id, s.properties, [s.id])
for s in all_schemas.values():
recurse_properties(s.id, s, s, [])
# end for each schema
return all_schemas
# end utility
if schemas:
all_schemas = build_schema_map()
else:
all_schemas = dict()
all_schemas = schemas and build_schema_map() or dict()
if not resources:
return Context(dict(), dict(), dict(), dict(), all_schemas)
sta_map, fqan_map = build_activity_mappings(resources)
rta_map = dict()
rtc_map = dict()
@@ -643,6 +662,7 @@ def new_context(schemas, resources):
category, resource, activity = activity_split(an)
rta_map.setdefault(resource, list()).append(activity)
assert rtc_map.setdefault(resource, category) == category
# DEBUG
return Context(sta_map, fqan_map, rta_map, rtc_map, all_schemas)
# Expects v to be 'v\d+', throws otherwise