summaryrefslogtreecommitdiffstats
path: root/tools/gn/file_template.cc
blob: 8b5d09f24006b74a400c7d700750d5651291075e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "tools/gn/file_template.h"

#include "tools/gn/filesystem_utils.h"

const char FileTemplate::kSource[] = "{{source}}";
const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";

FileTemplate::FileTemplate(const Value& t, Err* err) {
  ParseInput(t, err);
}

FileTemplate::FileTemplate(const std::vector<std::string>& t) {
  for (size_t i = 0; i < t.size(); i++)
    ParseOneTemplateString(t[i]);
}

FileTemplate::~FileTemplate() {
}

void FileTemplate::Apply(const Value& sources,
                         const ParseNode* origin,
                         std::vector<Value>* dest,
                         Err* err) const {
  if (!sources.VerifyTypeIs(Value::LIST, err))
    return;
  dest->reserve(sources.list_value().size() * templates_.container().size());

  // Temporary holding place, allocate outside to re-use- buffer.
  std::vector<std::string> string_output;

  const std::vector<Value>& sources_list = sources.list_value();
  for (size_t i = 0; i < sources_list.size(); i++) {
    if (!sources_list[i].VerifyTypeIs(Value::STRING, err))
      return;

    ApplyString(sources_list[i].string_value(), &string_output);
    for (size_t out_i = 0; out_i < string_output.size(); out_i++)
      dest->push_back(Value(origin, string_output[i]));
  }
}

void FileTemplate::ApplyString(const std::string& str,
                               std::vector<std::string>* output) const {
  // Compute all substitutions needed so we can just do substitutions below.
  // We skip the LITERAL one since that varies each time.
  std::string subst[Subrange::NUM_TYPES];
  if (types_required_[Subrange::SOURCE])
    subst[Subrange::SOURCE] = str;
  if (types_required_[Subrange::NAME_PART])
    subst[Subrange::NAME_PART] = FindFilenameNoExtension(&str).as_string();

  output->resize(templates_.container().size());
  for (size_t template_i = 0;
       template_i < templates_.container().size(); template_i++) {
    const Template& t = templates_[template_i];
    (*output)[template_i].clear();
    for (size_t subrange_i = 0; subrange_i < t.container().size();
         subrange_i++) {
      if (t[subrange_i].type == Subrange::LITERAL)
        (*output)[template_i].append(t[subrange_i].literal);
      else
        (*output)[template_i].append(subst[t[subrange_i].type]);
    }
  }
}

void FileTemplate::ParseInput(const Value& value, Err* err) {
  switch (value.type()) {
    case Value::STRING:
      ParseOneTemplateString(value.string_value());
      break;
    case Value::LIST:
      for (size_t i = 0; i < value.list_value().size(); i++) {
        if (!value.list_value()[i].VerifyTypeIs(Value::STRING, err))
          return;
        ParseOneTemplateString(value.list_value()[i].string_value());
      }
      break;
    default:
      *err = Err(value, "File template must be a string or list.",
                 "A sarcastic comment about your skills goes here.");
  }
}

void FileTemplate::ParseOneTemplateString(const std::string& str) {
  templates_.container().resize(templates_.container().size() + 1);
  Template& t = templates_[templates_.container().size() - 1];

  size_t cur = 0;
  while (true) {
    size_t next = str.find("{{", cur);

    // Pick up everything from the previous spot to here as a literal.
    if (next == std::string::npos) {
      if (cur != str.size())
        t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur)));
      break;
    } else if (next > cur) {
      t.container().push_back(
          Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
    }

    // Decode the template param.
    if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) {
      t.container().push_back(Subrange(Subrange::SOURCE));
      types_required_[Subrange::SOURCE] = true;
      cur = next + arraysize(kSource) - 1;
    } else if (str.compare(next, arraysize(kSourceNamePart) - 1,
                           kSourceNamePart) == 0) {
      t.container().push_back(Subrange(Subrange::NAME_PART));
      types_required_[Subrange::NAME_PART] = true;
      cur = next + arraysize(kSourceNamePart) - 1;
    } else {
      // If it's not a match, treat it like a one-char literal (this will be
      // rare, so it's not worth the bother to add to the previous literal) so
      // we can keep going.
      t.container().push_back(Subrange(Subrange::LITERAL, "{"));
      cur = next + 1;
    }
  }
}