1 // Copyright 2022 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include "pw_assert/check.h" 17 #include "pw_containers/intrusive_list.h" 18 #include "pw_containers/vector.h" 19 #include "pw_metric/metric.h" 20 #include "pw_status/status.h" 21 #include "pw_tokenizer/tokenize.h" 22 23 namespace pw::metric::internal { 24 25 class MetricWriter { 26 public: 27 virtual ~MetricWriter() = default; 28 virtual Status Write(const Metric& metric, const Vector<Token>& path) = 0; 29 }; 30 31 // Walk a metric tree recursively; passing metrics with their path (names) to a 32 // MetricWriter that can consume them. 33 class MetricWalker { 34 public: MetricWalker(MetricWriter & writer)35 MetricWalker(MetricWriter& writer) : writer_(writer) {} 36 Walk(const IntrusiveList<Metric> & metrics)37 Status Walk(const IntrusiveList<Metric>& metrics) { 38 for (const auto& m : metrics) { 39 ScopedName scoped_name(m.name(), *this); 40 PW_TRY(writer_.Write(m, path_)); 41 } 42 return OkStatus(); 43 } 44 Walk(const IntrusiveList<Group> & groups)45 Status Walk(const IntrusiveList<Group>& groups) { 46 for (const auto& g : groups) { 47 PW_TRY(Walk(g)); 48 } 49 return OkStatus(); 50 } 51 Walk(const Group & group)52 Status Walk(const Group& group) { 53 ScopedName scoped_name(group.name(), *this); 54 PW_TRY(Walk(group.children())); 55 PW_TRY(Walk(group.metrics())); 56 return OkStatus(); 57 } 58 59 private: 60 // Exists to safely push/pop parent groups from the explicit stack. 61 struct ScopedName { ScopedNameScopedName62 ScopedName(Token name, MetricWalker& rhs) : walker(rhs) { 63 // Metrics are too deep; bump path_ capacity. 64 PW_ASSERT(walker.path_.size() < walker.path_.capacity()); 65 walker.path_.push_back(name); 66 } ~ScopedNameScopedName67 ~ScopedName() { walker.path_.pop_back(); } 68 MetricWalker& walker; 69 }; 70 71 Vector<Token, /*capacity=*/4> path_; 72 MetricWriter& writer_; 73 }; 74 75 } // namespace pw::metric::internal 76