# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# 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.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========

# The content reads the warm file cache may answer: a cached whole-file
# value can serve them (sliced for ranged reads) without touching the
# backend, subject to the reconciler's consistency check.
DISPATCH_READ_OPS = frozenset({"read", "read_bytes"})

# Backend mutations that run the dispatcher's post-write bookkeeping:
# file-cache eviction, parent index invalidation, and overlay time
# clearing (plus the observed-mtime stamp for the content writes in
# ``STAMP_WRITE_OPS``).
DISPATCH_WRITE_OPS = frozenset({
    "write", "write_bytes", "append", "unlink", "create", "truncate", "mkdir",
    "rmdir", "rename"
})

# What the admission gates classify as a write (``OpsContext.write``).
# A superset of DISPATCH_WRITE_OPS: setattr mutates the mount but keeps
# its own overlay bookkeeping in ``_apply_setattr``, and symlink writes
# only the node table, so both need write admission without joining the
# post-write invalidation path.
POLICY_WRITE_OPS = DISPATCH_WRITE_OPS | frozenset({"setattr", "symlink"})

# Ops the node table itself answers: a symlink is namespace state with
# no backend behind it, so the door is the authority for both
# directions (create and readlink) rather than a router to a mount.
NAMESPACE_TABLE_OPS = frozenset({"symlink", "readlink"})

# Ops the node table answers when the path itself is a link, and only
# then. The name is the whole of what exists there, so forwarding one
# reaches a backend that has never heard of it: an unlink answered
# ENOENT with the link still in the table, and a rename moved nothing.
# ``stat`` joins them only under ``nofollow``, which is how a caller
# spells lstat; a following stat arrives already resolved to its target.
LINK_ENTRY_OPS = frozenset({"unlink", "rename", "stat"})

# Ops that create the path they name. A hidden target refuses these as
# EACCES rather than ENOENT, because "does not exist" is nonsense as
# the answer to a create the caller is spelling out; every other op on
# a hidden path answers ENOENT, the no-name-leak rule.
HIDDEN_CREATE_OPS = frozenset(
    {"write", "write_bytes", "append", "create", "mkdir", "symlink"})

# The attribute fields a setattr op can carry, in one place so the
# requested/residual split and the overlay write read the same names.
SETATTR_KEYS = ("mode", "uid", "gid", "atime", "mtime")
