sqruff_lib/core/rules/
reference.rs

1use smol_str::SmolStr;
2
3pub fn object_ref_matches_table(
4    possible_references: &[Vec<SmolStr>],
5    targets: &[Vec<SmolStr>],
6) -> bool {
7    // Simple case: If there are no references, assume okay.
8    if possible_references.is_empty() {
9        return true;
10    }
11
12    // Simple case: Reference exactly matches a target.
13    for pr in possible_references {
14        if targets.contains(pr) {
15            return true;
16        }
17    }
18
19    // Tricky case: If one is shorter than the other, check for a suffix match.
20    for pr in possible_references {
21        for t in targets {
22            if (pr.len() < t.len() && pr == &t[t.len() - pr.len()..])
23                || (t.len() < pr.len() && t == &pr[pr.len() - t.len()..])
24            {
25                return true;
26            }
27        }
28    }
29
30    false
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn test_object_ref_matches_table() {
39        let test_cases = vec![
40            // Empty list of references is always true
41            (vec![], vec![vec!["abc".into()]], true),
42            // Simple cases: one reference, one target
43            (
44                vec![vec!["agent1".into()]],
45                vec![vec!["agent1".into()]],
46                true,
47            ),
48            (
49                vec![vec!["agent1".into()]],
50                vec![vec!["customer".into()]],
51                false,
52            ),
53            // Multiple references. If any match, good.
54            (
55                vec![vec!["bar".into()], vec!["user_id".into()]],
56                vec![vec!["bar".into()]],
57                true,
58            ),
59            (
60                vec![vec!["foo".into()], vec!["user_id".into()]],
61                vec![vec!["bar".into()]],
62                false,
63            ),
64            // Multiple targets. If any reference matches, good.
65            (
66                vec![vec!["table1".into()]],
67                vec![
68                    vec!["table1".into()],
69                    vec!["table2".into()],
70                    vec!["table3".into()],
71                ],
72                true,
73            ),
74            (
75                vec![vec!["tbl2".into()]],
76                vec![vec!["db".into(), "sc".into(), "tbl1".into()]],
77                false,
78            ),
79            (
80                vec![vec!["tbl2".into()]],
81                vec![vec!["db".into(), "sc".into(), "tbl2".into()]],
82                true,
83            ),
84            // Multipart references and targets. Checks for a suffix match.
85            (
86                vec![vec!["Arc".into(), "tbl1".into()]],
87                vec![vec!["db".into(), "sc".into(), "tbl1".into()]],
88                false,
89            ),
90            (
91                vec![vec!["sc".into(), "tbl1".into()]],
92                vec![vec!["db".into(), "sc".into(), "tbl1".into()]],
93                true,
94            ),
95            (
96                vec![vec!["cb".into(), "sc".into(), "tbl1".into()]],
97                vec![vec!["db".into(), "sc".into(), "tbl1".into()]],
98                false,
99            ),
100            (
101                vec![vec!["db".into(), "sc".into(), "tbl1".into()]],
102                vec![vec!["db".into(), "sc".into(), "tbl1".into()]],
103                true,
104            ),
105            (
106                vec![vec!["public".into(), "agent1".into()]],
107                vec![vec!["agent1".into()]],
108                true,
109            ),
110            (
111                vec![vec!["public".into(), "agent1".into()]],
112                vec![vec!["public".into()]],
113                false,
114            ),
115        ];
116
117        for (possible_references, targets, expected) in test_cases {
118            assert_eq!(
119                object_ref_matches_table(&possible_references, &targets),
120                expected
121            );
122        }
123    }
124}