Guarding:開源的多語言架構(gòu)守護(hù)工具
Guarding 簡介
Guarding 是一個可以用于 Java、JavaScript、Rust、Go 等語言的架構(gòu)守護(hù)工具。受 ArchUnit 的啟發(fā),借助于易于理解的 DSL,來編寫守護(hù)規(guī)則。支持 Windows、macOS、GNU/Linux 系統(tǒng)。
使用
簡單來說,就是我們可以使用一個易于閱讀的 DSL 來編寫架構(gòu)規(guī)則。而這些個架構(gòu)規(guī)則,可以用于主流的語言。如下是使用 Guarding 編寫的規(guī)則示例:
- 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("");
從上面的示例里,你可以發(fā)現(xiàn) :
- 如果你熟悉 ArchUnit 的話,就能很快的上手 Guarding 的編寫。當(dāng)然了,還有很多語法還在開發(fā)中。
- Guarding 可以很支持中文。但是,我覺得這中英文模板切換就是個問題。
- Guarding 可以支持更多的語法,如針對于 Rust 或者 Golang, class 可以換成 struct。
運(yùn)行
運(yùn)行起 Guarding 也非常簡單,只需要 guarding . 就可以了。
安裝
當(dāng)然了,安裝也非常簡單,直接從 GitHub 下載:https://github.com/inherd/guarding ?;蛘呤牵绻阌?Rust 的環(huán)境的話,那么你可以直接: cargo install guarding。
擴(kuò)展
那么,如何擴(kuò)展 Guarding 呢?
Guarding 架構(gòu)
下圖是 Guarding 的處理流程:
Guarding Architecture
- 簡單來說,Guarding 的程序?yàn)槿糠郑?/li>
- Guarding 規(guī)則解析器。
- 多語言解析器。使用 Treesitter 作為解析工具,配合 S 表達(dá)式進(jìn)行解析。
Guarding 規(guī)則執(zhí)行器。
多語言源碼解析
在語言解析這事上吧,我又經(jīng)歷了一系列的嘗試。
解析方式選型
基于 Antlr 的標(biāo)準(zhǔn)語言解析。起先在設(shè)計(jì) Guarding 的時候,我是打算使用類似于 Coca 的方式,基于 Antlr 官方維護(hù)的一個三方貢獻(xiàn)的語法庫。而對于我來說,這是一種舊的解析方式,所以我使用它的可能性不大。
基于 Ctags 的語法分析。另外一種選擇是使用在設(shè)計(jì) Modeling 的時候,引入的是 Ctags。Ctags 是一個用于從程序源代碼樹產(chǎn)生索引文件(或tag文件),從而便于文本編輯器來實(shí)現(xiàn)快速定位的實(shí)用工具。而使用 Ctags 需要引入二進(jìn)制的包。于是,首先我嘗試構(gòu)建了 ctags-sys,隨后還需要編寫 ctags 長長解析方式,時間成本有點(diǎn)高。
基于 LSP 的語法分析。我短暫的評估過采用 LSP (Language Server Protocol )的方式,但是使用 LSP 意味著:引入更多的語言相關(guān)的依賴。所以,依舊是不可行的路線。
直至,在完善 Uncode 的一些設(shè)計(jì)時,發(fā)現(xiàn)有 Tree-sitter 能實(shí)現(xiàn)相關(guān)的功能。Tree-sitter 早先是在 Atom 編輯器中引入的一個試驗(yàn)性功能。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]
隨后,我們就可以編寫對應(yīng)的查詢(query)語法樹 S 表達(dá)式(S-expression):
- (class_specifier
- name: ((type_identifier) @class-name)
- )
S 表達(dá)式會從語法樹中區(qū)別到對應(yīng)的節(jié)點(diǎn),將節(jié)點(diǎn)信息賦給變量,如這里的 @class-name。
你可以從 TreeSitter 官方提供的在線 Playground 嘗試:https://tree-sitter.github.io/tree-sitter/playground
Guarding 語法解析與設(shè)計(jì)
Guarding 使用的是 Rust 語言開發(fā)的,由于之前已經(jīng)用過了 Lalrpop、Antlr 等解析器,所以這次我們采用的解析器是:pest。雖然,我沒有細(xì)究過,這幾個不同的解析器在學(xué)術(shù)上的差距,我一般只會按需選擇我用得少的。如下是 guarding.pest 的部分代碼示例:
- normal_rule = {
- rule_level ~ ("(" ~ scope ~ ")")? ~ (use_symbol ~ expression)? ~ should? ~ only? ~ operator ~ assert ~ ";"?
- }
- rule_level = {
- "package" |
- "class" |
- "struct" |
- "function" |
- "file"
- }
在 docs 和 examples 里,有 Guarding 的語法開發(fā)過程中的記錄和關(guān)鍵詞信息。
包路徑解析
值得一提的是包路徑解析,所以我們的包解析方式參考的是 ArchUnit 的設(shè)計(jì)方式。
如何使用 Guarding 進(jìn)行架構(gòu)守護(hù)
Guarding 采用的是 Rust 語言,所以二進(jìn)制是直接支持所有的主流操作系統(tǒng)。其次,我們采用的是 CLI 方式,因此可以在任何階段中采用,如:
- 在本地結(jié)合 Git Hook 進(jìn)行代碼預(yù)提交檢查。
- 在持續(xù)集成階段,配合流水線工作使用。
- 結(jié)合 IDEA / 編輯器插件進(jìn)行實(shí)時檢查(還沒有實(shí)現(xiàn))。
當(dāng)然了,這個是適用于單個團(tuán)隊(duì)的處理方式。對于更大規(guī)模的團(tuán)隊(duì)來說,可以采用:
- 模板繼承的方式(當(dāng)然,還沒有實(shí)現(xiàn))
其它
歡迎加入 Guarding 的開發(fā):https://github.com/inherd/guarding
本文轉(zhuǎn)載自微信公眾號「phodal」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系phodal公眾號。