File: since.cpp

package info (click to toggle)
watchman 4.9.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,992 kB
  • sloc: cpp: 27,459; python: 6,538; java: 3,404; php: 3,257; ansic: 2,803; javascript: 1,116; makefile: 671; ruby: 364; sh: 124; xml: 102; lisp: 4
file content (133 lines) | stat: -rw-r--r-- 4,013 bytes parent folder | download | duplicates (3)
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
126
127
128
129
130
131
132
133
/* Copyright 2013-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman.h"

#include "make_unique.h"

enum since_what { SINCE_OCLOCK, SINCE_CCLOCK, SINCE_MTIME, SINCE_CTIME };

static struct {
  enum since_what value;
  const char* label;
} allowed_fields[] = {
    {since_what::SINCE_OCLOCK, "oclock"},
    {since_what::SINCE_CCLOCK, "cclock"},
    {since_what::SINCE_MTIME, "mtime"},
    {since_what::SINCE_CTIME, "ctime"},
};

class SinceExpr : public QueryExpr {
  std::unique_ptr<ClockSpec> spec;
  enum since_what field;

 public:
  explicit SinceExpr(std::unique_ptr<ClockSpec> spec, enum since_what field)
      : spec(std::move(spec)), field(field) {}

  bool evaluate(struct w_query_ctx* ctx, const FileResult* file) override {
    time_t tval = 0;

    auto since = spec->evaluate(
        ctx->clockAtStartOfQuery.position(),
        ctx->lastAgeOutTickValueAtStartOfQuery);

    // Note that we use >= for the time comparisons in here so that we
    // report the things that changed inclusive of the boundary presented.
    // This is especially important for clients using the coarse unix
    // timestamp as the since basis, as they would be much more
    // likely to miss out on changes if we didn't.

    switch (field) {
      case since_what::SINCE_OCLOCK:
      case since_what::SINCE_CCLOCK: {
        const auto& clock =
            (field == since_what::SINCE_OCLOCK) ? file->otime() : file->ctime();
        if (since.is_timestamp) {
          return clock.timestamp >= since.timestamp;
        }
        if (since.clock.is_fresh_instance) {
          return file->exists();
        }
        return clock.ticks > since.clock.ticks;
      }
      case since_what::SINCE_MTIME:
        tval = file->stat().mtime.tv_sec;
        break;
      case since_what::SINCE_CTIME:
        tval = file->stat().ctime.tv_sec;
        break;
    }

    assert(since.is_timestamp);
    return tval >= since.timestamp;
  }

  static std::unique_ptr<QueryExpr> parse(w_query*, const json_ref& term) {
    auto selected_field = since_what::SINCE_OCLOCK;
    const char* fieldname = "oclock";

    if (!json_is_array(term)) {
      throw QueryParseError("\"since\" term must be an array");
    }

    if (json_array_size(term) < 2 || json_array_size(term) > 3) {
      throw QueryParseError("\"since\" term has invalid number of parameters");
    }

    const auto& jval = term.at(1);
    auto spec = ClockSpec::parseOptionalClockSpec(jval);
    if (!spec) {
      throw QueryParseError("invalid clockspec for \"since\" term");
    }
    if (spec->tag == w_cs_named_cursor) {
      throw QueryParseError("named cursors are not allowed in \"since\" terms");
    }

    if (term.array().size() == 3) {
      const auto& field = term.at(2);
      size_t i;
      bool valid = false;

      fieldname = json_string_value(field);
      if (!fieldname) {
        throw QueryParseError("field name for \"since\" term must be a string");
      }

      for (i = 0; i < sizeof(allowed_fields) / sizeof(allowed_fields[0]); ++i) {
        if (!strcmp(allowed_fields[i].label, fieldname)) {
          selected_field = allowed_fields[i].value;
          valid = true;
          break;
        }
      }

      if (!valid) {
        throw QueryParseError(
            "invalid field name \"", fieldname, "\" for \"since\" term");
      }
    }

    switch (selected_field) {
      case since_what::SINCE_CTIME:
      case since_what::SINCE_MTIME:
        if (spec->tag != w_cs_timestamp) {
          throw QueryParseError(
              "field \"",
              fieldname,
              "\" requires a timestamp value for comparison in \"since\" term");
        }
        break;
      case since_what::SINCE_OCLOCK:
      case since_what::SINCE_CCLOCK:
        /* we'll work with clocks or timestamps */
        break;
    }

    return watchman::make_unique<SinceExpr>(std::move(spec), selected_field);
  }
};
W_TERM_PARSER("since", SinceExpr::parse)

/* vim:ts=2:sw=2:et:
 */