Guarding:開源的多語言架構守護工具
Guarding 簡介
Guarding 是一個可以用于 Java、JavaScript、Rust、Go 等語言的架構守護工具。受 ArchUnit 的啟發,借助于易于理解的 DSL,來編寫守護規則。支持 Windows、macOS、GNU/Linux 系統。
使用
簡單來說,就是我們可以使用一個易于閱讀的 DSL 來編寫架構規則。而這些個架構規則,可以用于主流的語言。如下是使用 Guarding 編寫的規則示例:
- package(".")::file.len should < 200;
- package(".")::file.len should > 50;
- class("java.util.Map") only accessed(["com.phodal.pepper.refactor.staticclass"]);
- class(implementation "BaseParser")::len = 2
- class(implementation "BaseParser")::name should not contains "Lexer";
- struct("..myapp..")::function.name should contains("Model");
- struct("..myapp..")::function.name contains("");
從上面的示例里,你可以發現 :
- 如果你熟悉 ArchUnit 的話,就能很快的上手 Guarding 的編寫。當然了,還有很多語法還在開發中。
- Guarding 可以很支持中文。但是,我覺得這中英文模板切換就是個問題。
- Guarding 可以支持更多的語法,如針對于 Rust 或者 Golang, class 可以換成 struct。
運行
運行起 Guarding 也非常簡單,只需要 guarding . 就可以了。
安裝
當然了,安裝也非常簡單,直接從 GitHub 下載:https://github.com/inherd/guarding 。或者是,如果你有 Rust 的環境的話,那么你可以直接: cargo install guarding。
擴展
那么,如何擴展 Guarding 呢?
Guarding 架構
下圖是 Guarding 的處理流程:
Guarding Architecture
- 簡單來說,Guarding 的程序為三部分:
- Guarding 規則解析器。
- 多語言解析器。使用 Treesitter 作為解析工具,配合 S 表達式進行解析。
Guarding 規則執行器。
多語言源碼解析
在語言解析這事上吧,我又經歷了一系列的嘗試。
解析方式選型
基于 Antlr 的標準語言解析。起先在設計 Guarding 的時候,我是打算使用類似于 Coca 的方式,基于 Antlr 官方維護的一個三方貢獻的語法庫。而對于我來說,這是一種舊的解析方式,所以我使用它的可能性不大。
基于 Ctags 的語法分析。另外一種選擇是使用在設計 Modeling 的時候,引入的是 Ctags。Ctags 是一個用于從程序源代碼樹產生索引文件(或tag文件),從而便于文本編輯器來實現快速定位的實用工具。而使用 Ctags 需要引入二進制的包。于是,首先我嘗試構建了 ctags-sys,隨后還需要編寫 ctags 長長解析方式,時間成本有點高。
基于 LSP 的語法分析。我短暫的評估過采用 LSP (Language Server Protocol )的方式,但是使用 LSP 意味著:引入更多的語言相關的依賴。所以,依舊是不可行的路線。
直至,在完善 Uncode 的一些設計時,發現有 Tree-sitter 能實現相關的功能。Tree-sitter 早先是在 Atom 編輯器中引入的一個試驗性功能。Tree-sitter 支持 Rust、JavaScript、Python、Ruby、Haskell 語言。與 Haskell 和 Ruby 這種小眾語言比,Rust 這種小眾語言也就還行,哈哈。不過,從性能上來說,是這里面性能最好的。
解析示例
如下是一個簡單的 C++ 語言的 Class 示例:
- class MyClass {
- public:
- int myNum;
- string myString;
- };
TreeSitter 會將上述的 CPP 代碼解析成語法樹 (部分):
- translation_unit [0, 0] - [6, 0]
- class_specifier [0, 0] - [4, 1]
- name: type_identifier [0, 6] - [0, 13]
- body: field_declaration_list [0, 14] - [4, 1]
- access_specifier [1, 2] - [1, 9]
隨后,我們就可以編寫對應的查詢(query)語法樹 S 表達式(S-expression):
- (class_specifier
- name: ((type_identifier) @class-name)
- )
S 表達式會從語法樹中區別到對應的節點,將節點信息賦給變量,如這里的 @class-name。
你可以從 TreeSitter 官方提供的在線 Playground 嘗試:https://tree-sitter.github.io/tree-sitter/playground
Guarding 語法解析與設計
Guarding 使用的是 Rust 語言開發的,由于之前已經用過了 Lalrpop、Antlr 等解析器,所以這次我們采用的解析器是:pest。雖然,我沒有細究過,這幾個不同的解析器在學術上的差距,我一般只會按需選擇我用得少的。如下是 guarding.pest 的部分代碼示例:
- normal_rule = {
- rule_level ~ ("(" ~ scope ~ ")")? ~ (use_symbol ~ expression)? ~ should? ~ only? ~ operator ~ assert ~ ";"?
- }
- rule_level = {
- "package" |
- "class" |
- "struct" |
- "function" |
- "file"
- }
在 docs 和 examples 里,有 Guarding 的語法開發過程中的記錄和關鍵詞信息。
包路徑解析
值得一提的是包路徑解析,所以我們的包解析方式參考的是 ArchUnit 的設計方式。
如何使用 Guarding 進行架構守護
Guarding 采用的是 Rust 語言,所以二進制是直接支持所有的主流操作系統。其次,我們采用的是 CLI 方式,因此可以在任何階段中采用,如:
- 在本地結合 Git Hook 進行代碼預提交檢查。
- 在持續集成階段,配合流水線工作使用。
- 結合 IDEA / 編輯器插件進行實時檢查(還沒有實現)。
當然了,這個是適用于單個團隊的處理方式。對于更大規模的團隊來說,可以采用:
- 模板繼承的方式(當然,還沒有實現)
其它
歡迎加入 Guarding 的開發:https://github.com/inherd/guarding
本文轉載自微信公眾號「phodal」,可以通過以下二維碼關注。轉載本文請聯系phodal公眾號。