blob: 3bf1714bbaf1f09545afbd27afcdefad084f9f6c [file] [log] [blame]
# Copyright (c) 2023 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from typing import Union
from matter_idl.generators import CodeGenerator, GeneratorStorage
from matter_idl.matter_idl_types import (AccessPrivilege, ApiMaturity, Attribute, AttributeQuality, AttributeStorage, Command,
CommandQuality, Event, EventPriority, EventQuality, FieldQuality, Idl, StructQuality,
StructTag)
def human_text_string(value: Union[StructTag, StructQuality, EventPriority, EventQuality, AccessPrivilege, AttributeQuality, CommandQuality, ApiMaturity, AttributeStorage]) -> str:
if type(value) is StructTag:
if value == StructTag.REQUEST:
return "request"
if value == StructTag.RESPONSE:
return "response"
elif type(value) is FieldQuality:
# mypy seems confused if using `FieldQuality.OPTIONAL in value`
# directly, so do a useless cast here
quality: FieldQuality = value
result = ""
if FieldQuality.OPTIONAL in quality:
result += "optional "
if FieldQuality.NULLABLE in quality:
result += "nullable "
if FieldQuality.FABRIC_SENSITIVE in quality:
result += "fabric_sensitive "
return result.strip()
elif type(value) is StructQuality:
result = ""
if value == StructQuality.FABRIC_SCOPED:
result += "fabric_scoped "
return result.strip()
elif type(value) is EventPriority:
if value == EventPriority.DEBUG:
return "debug"
if value == EventPriority.INFO:
return "info"
if value == EventPriority.CRITICAL:
return "critical"
elif type(value) is EventQuality:
result = ""
if EventQuality.FABRIC_SENSITIVE in value:
result += "fabric_sensitive "
return result.strip()
elif type(value) is AccessPrivilege:
if value == AccessPrivilege.VIEW:
return "view"
if value == AccessPrivilege.OPERATE:
return "operate"
if value == AccessPrivilege.MANAGE:
return "manage"
if value == AccessPrivilege.ADMINISTER:
return "administer"
elif type(value) is AttributeQuality:
result = ""
if AttributeQuality.TIMED_WRITE in value:
result += "timedwrite "
if AttributeQuality.WRITABLE not in value:
result += "readonly "
if AttributeQuality.NOSUBSCRIBE in value:
result += "nosubscribe "
return result
elif type(value) is CommandQuality:
result = ""
if CommandQuality.FABRIC_SCOPED in value:
result += "fabric "
if CommandQuality.TIMED_INVOKE in value:
result += "timed "
return result
elif type(value) is ApiMaturity:
if value == ApiMaturity.STABLE:
return ""
if value == ApiMaturity.PROVISIONAL:
return "provisional "
if value == ApiMaturity.INTERNAL:
return "internal "
if value == ApiMaturity.DEPRECATED:
return "deprecated "
elif type(value) is AttributeStorage:
if value == AttributeStorage.RAM:
return "ram"
if value == AttributeStorage.PERSIST:
return "persist"
if value == AttributeStorage.CALLBACK:
return "callback"
# wrong value in general
return "Unknown/unsupported: %r" % value
def event_access_string(e: Event) -> str:
"""Generates the access string required for an event. If string is non-empty it will
include a trailing space
"""
result = ""
if e.readacl != AccessPrivilege.VIEW:
result += "read: " + human_text_string(e.readacl)
if not result:
return ""
return f"access({result}) "
def command_access_string(c: Command) -> str:
"""Generates the access string required for a command. If string is non-empty it will
include a trailing space
"""
result = ""
if c.invokeacl != AccessPrivilege.OPERATE:
result += "invoke: " + human_text_string(c.invokeacl)
if not result:
return ""
return f"access({result}) "
def attribute_access_string(a: Attribute) -> str:
"""Generates the access string required for a struct. If string is non-empty it will
include a trailing space
"""
result = []
if a.readacl != AccessPrivilege.VIEW:
result.append("read: " + human_text_string(a.readacl))
if a.writeacl != AccessPrivilege.OPERATE:
result.append("write: " + human_text_string(a.writeacl))
if not result:
return ""
return f"access({', '.join(result)}) "
def render_default(value: Union[str, int, bool]) -> str:
"""
Renders a idl-style default.
Generally quotes strings and handles bools
"""
if type(value) is str:
# TODO: technically this should support escaping for quotes
# however currently we never needed this. Escaping can be
# added once we use this info
return f'"{value}"'
elif type(value) is bool:
if value:
return "true"
else:
return "false"
return str(value)
class IdlGenerator(CodeGenerator):
"""
Generation .matter idl files for a given IDL
"""
def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs):
super().__init__(storage, idl, fs_loader_searchpath=os.path.dirname(__file__))
self.jinja_env.filters['idltxt'] = human_text_string
self.jinja_env.filters['event_access'] = event_access_string
self.jinja_env.filters['command_access'] = command_access_string
self.jinja_env.filters['attribute_access'] = attribute_access_string
self.jinja_env.filters['render_default'] = render_default
# Easier whitespace management
self.jinja_env.trim_blocks = True
self.jinja_env.lstrip_blocks = True
def internal_render_all(self):
"""
Renders the output.
"""
# Header containing a macro to initialize all cluster plugins
self.internal_render_one_output(
template_path="MatterIdl.jinja",
output_file_name="idl.matter",
vars={
'idl': self.idl
}
)