Skip to content

Commit 099f9c5

Browse files
authored
feat: add async/await support to MemoryAdapter for Swift 6 concurrency (#53)
1 parent 14df6d7 commit 099f9c5

File tree

7 files changed

+589
-40
lines changed

7 files changed

+589
-40
lines changed

Package.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import PackageDescription
55

66
let package = Package(
77
name: "Casbin",
8+
platforms: [
9+
// Lowered minimums to explicitly support older OS versions.
10+
// Async/await overloads are gated with @available to remain compatible.
11+
.macOS(.v10_14),
12+
.iOS(.v12),
13+
.watchOS(.v6),
14+
.tvOS(.v12)
15+
],
816
products: [
917
// Products define the executables and libraries a package produces, and make them visible to other packages.
1018
.library(
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import NIO
2+
3+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
4+
extension FileAdapter {
5+
6+
// Generic bridge from EventLoopFuture to async/await
7+
private func awaitFuture<T>(_ future: EventLoopFuture<T>) async throws -> T {
8+
try await withCheckedThrowingContinuation { cont in
9+
future.whenComplete { result in
10+
cont.resume(with: result)
11+
}
12+
}
13+
}
14+
15+
// MARK: Load
16+
public func loadPolicy(m: Model) async throws {
17+
try await awaitFuture(self.loadPolicy(m: m))
18+
}
19+
20+
public func loadFilteredPolicy(m: Model, f: Filter) async throws {
21+
try await awaitFuture(self.loadFilteredPolicy(m: m, f: f))
22+
}
23+
24+
// MARK: Save / Clear
25+
public func savePolicy(m: Model) async throws {
26+
try await awaitFuture(self.savePolicy(m: m))
27+
}
28+
29+
public func clearPolicy() async throws {
30+
try await awaitFuture(self.clearPolicy())
31+
}
32+
33+
// MARK: No-op convenience (kept for API symmetry)
34+
public func addPolicy(sec: String, ptype: String, rule: [String]) async throws -> Bool {
35+
try await awaitFuture(self.addPolicy(sec: sec, ptype: ptype, rule: rule))
36+
}
37+
38+
public func addPolicies(sec: String, ptype: String, rules: [[String]]) async throws -> Bool {
39+
try await awaitFuture(self.addPolicies(sec: sec, ptype: ptype, rules: rules))
40+
}
41+
42+
public func removePolicy(sec: String, ptype: String, rule: [String]) async throws -> Bool {
43+
try await awaitFuture(self.removePolicy(sec: sec, ptype: ptype, rule: rule))
44+
}
45+
46+
public func removePolicies(sec: String, ptype: String, rules: [[String]]) async throws -> Bool {
47+
try await awaitFuture(self.removePolicies(sec: sec, ptype: ptype, rules: rules))
48+
}
49+
50+
public func removeFilteredPolicy(sec: String, ptype: String, fieldIndex: Int, fieldValues: [String]) async throws -> Bool {
51+
try await awaitFuture(self.removeFilteredPolicy(sec: sec, ptype: ptype, fieldIndex: fieldIndex, fieldValues: fieldValues))
52+
}
53+
}
54+
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import NIO
2+
3+
// Async/Await overloads for MemoryAdapter
4+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
5+
extension MemoryAdapter {
6+
7+
// MARK: Load
8+
public func loadPolicy(m: Model) async throws {
9+
for line in policy {
10+
let sec = line[0]
11+
let ptype = line[1]
12+
let rule = Array(line.dropFirst(2))
13+
if let ast = m.getModel()[sec]?[ptype] {
14+
ast.policy.append(rule)
15+
}
16+
}
17+
}
18+
19+
public func loadFilteredPolicy(m: Model, f: Filter) async throws {
20+
for line in policy {
21+
let sec = line[0]
22+
let ptype = line[1]
23+
let rule = Array(line.dropFirst(2))
24+
var isFiltered = false
25+
if sec == "p" {
26+
for (i,r) in f.p.enumerated() {
27+
if !r.isEmpty && r != rule[i] {
28+
isFiltered = true
29+
}
30+
}
31+
}
32+
if sec == "g" {
33+
for (i,r) in f.g.enumerated() {
34+
if !r.isEmpty && r != rule[i] {
35+
isFiltered = true
36+
}
37+
}
38+
}
39+
if !isFiltered {
40+
if let ast = m.getModel()[sec]?[ptype] {
41+
ast.policy.append(rule)
42+
}
43+
} else {
44+
self.isFiltered = true
45+
}
46+
}
47+
}
48+
49+
// MARK: Save / Clear
50+
public func savePolicy(m: Model) async throws {
51+
self.policy = []
52+
if let astMap = m.getModel()["p"] {
53+
for (ptype,ast) in astMap {
54+
for policy in ast.policy {
55+
var rule = policy
56+
rule.insert(ptype, at: 0)
57+
rule.insert("p", at: 0)
58+
self.policy.insert(rule)
59+
}
60+
}
61+
}
62+
if let astMap = m.getModel()["g"] {
63+
for (ptype,ast) in astMap {
64+
for policy in ast.policy {
65+
var rule = policy
66+
rule.insert(ptype, at: 0)
67+
rule.insert("g", at: 0)
68+
self.policy.insert(rule)
69+
}
70+
}
71+
}
72+
}
73+
74+
public func clearPolicy() async throws {
75+
self.policy = []
76+
self.isFiltered = false
77+
}
78+
79+
// MARK: Add
80+
public func addPolicy(sec: String, ptype: String, rule: [String]) async throws -> Bool {
81+
var rule = rule
82+
rule.insert(ptype, at: 0)
83+
rule.insert(sec, at: 0)
84+
return self.policy.insert(rule).inserted
85+
}
86+
87+
public func addPolicies(sec: String, ptype: String, rules: [[String]]) async throws -> Bool {
88+
var allAdded = true
89+
let rules: [[String]] = rules.map { r in
90+
var r = r
91+
r.insert(ptype, at: 0)
92+
r.insert(sec, at: 0)
93+
return r
94+
}
95+
for rule in rules {
96+
if policy.contains(rule) {
97+
allAdded = false
98+
return allAdded
99+
}
100+
}
101+
self.policy = self.policy.union(rules)
102+
return allAdded
103+
}
104+
105+
// MARK: Remove
106+
public func removePolicy(sec: String, ptype: String, rule: [String]) async throws -> Bool {
107+
var rule = rule
108+
rule.insert(ptype, at: 0)
109+
rule.insert(sec, at: 0)
110+
return policy.remove(rule) != nil
111+
}
112+
113+
public func removePolicies(sec: String, ptype: String, rules: [[String]]) async throws -> Bool {
114+
var allRemoved = true
115+
let rules: [[String]] = rules.map { r in
116+
var r = r
117+
r.insert(ptype, at: 0)
118+
r.insert(sec, at: 0)
119+
return r
120+
}
121+
// Atomic semantics: if any rule doesn't exist, do not remove any and return false
122+
for rule in rules {
123+
if !policy.contains(rule) {
124+
allRemoved = false
125+
break
126+
}
127+
}
128+
if allRemoved {
129+
for rule in rules {
130+
_ = self.policy.remove(rule)
131+
}
132+
}
133+
return allRemoved
134+
}
135+
136+
public func removeFilteredPolicy(sec: String, ptype: String, fieldIndex: Int, fieldValues: [String]) async throws -> Bool {
137+
if fieldValues.isEmpty {
138+
return false
139+
}
140+
var tmp: Set<[String]> = []
141+
var res = false
142+
for rule in policy {
143+
if sec == rule[0] && ptype == rule[1] {
144+
var matched = true
145+
for (i, fieldValue) in fieldValues.enumerated() {
146+
if !fieldValue.isEmpty && rule[fieldIndex + i + 2] != fieldValue {
147+
matched = false
148+
break
149+
}
150+
}
151+
if matched {
152+
res = true
153+
} else {
154+
tmp.insert(rule)
155+
}
156+
} else {
157+
tmp.insert(rule)
158+
}
159+
}
160+
self.policy = tmp
161+
return res
162+
}
163+
}

0 commit comments

Comments
 (0)