本節包含 Fuchsia 介面定義語言檔案的樣式相關資訊。
另請參閱 FIDL API 評量表。
名稱
為貓咪命名可不是件簡單的事,
這不是你放假時玩的遊戲;
--- T.S. Eliot
FIDL 中定義的名稱會用於在各譯文語言中產生 ID。某些語言會為各種形式的名稱賦予語意或傳統意義。舉例來說,在 Go 中,ID 的首字母是否大寫會控制 ID 的可見度。因此,許多語言後端會轉換程式庫中的名稱,使其更適合目標語言。本節的命名規則旨在兼顧 FIDL 來源的可讀性、各譯文語言的可用性,以及譯文語言之間的一致性。
避免使用常見的保留字詞,例如 goto。語言後端會將保留字轉換為非保留 ID,但這些轉換會降低這些語言的可用性。避免使用常見的保留字,可減少套用這些轉換的頻率。
雖然部分 FIDL 關鍵字也是譯文語言中的常見保留字 (例如 C 和 C++ 中的 struct),因此應避免使用,但其他 FIDL 關鍵字 (尤其是 request 和 handle) 通常具有描述性,可視情況使用。
名稱開頭或結尾不得使用底線。在某些語言中,開頭或結尾底線具有語意意義 (例如,開頭底線控制 Dart 中的可見性),在其他語言中則具有傳統意義 (例如,結尾底線通常用於 C++ 中的成員變數)。此外,FIDL 編譯器會使用開頭和結尾底線來混淆 ID,以避免衝突。
請使用 size 一詞來命名位元組數。使用 count 一詞來命名其他數量的數字 (例如結構體向量中的項目數量)。
案件定義
有時,決定如何分隔 ID 中的字詞不只有一種方法。我們的風格如下:
- 先從美式英文的原始詞組開始 (例如「Non-Null HTTP 用戶端」)
- 移除所有標點符號。("Non Null HTTP Client")
- 將所有內容轉換為小寫 (「non null HTTP 用戶端」)
- 請根據指定 ID 的適當樣式,執行下列其中一項操作:
- 將空格替換為底線 ('_'),以使用小寫蛇形命名法 (
non_null_http_client)。 - 如要使用大寫蛇形命名法 (
NON_NULL_HTTP_CLIENT),請將字母改為大寫,並以底線取代空格。 - 將每個字詞的第一個字母大寫,並將所有字詞合併,形成大駝峰式大小寫 (
NonNullHttpClient)。
- 將空格替換為底線 ('_'),以使用小寫蛇形命名法 (
用量
下表列出案例用途與元素的對應關係:
| 元素 | 外殼 | 範例 |
|---|---|---|
bits |
大駝峰式大小寫 | InfoFeatures |
| 位元欄位成員 | 大寫蛇形命名法 | WLAN_SNOOP |
const |
大寫蛇形命名法 | MAX_NAMES |
alias |
大駝峰式大小寫 | DeviceId |
protocol |
大駝峰式大小寫 | AudioRenderer |
| 通訊協定方法參數 | 小寫蛇形命名法 | enable_powersave |
| 通訊協定方法 | 大駝峰式大小寫 | GetBatteryStatus |
struct |
大駝峰式大小寫 | KeyboardEvent |
| struct 成員 | 小寫蛇形命名法 | child_pid |
table |
大駝峰式大小寫 | ComponentDecl |
| 資料表成員 | 小寫蛇形命名法 | num_rx |
union |
大駝峰式大小寫 | BufferFormat |
| 組合成員 | 小寫蛇形命名法 | vax_primary |
enum |
大駝峰式大小寫 | PixelFormat |
| 列舉成員 | 大寫蛇形命名法 | RGB_888 |
程式庫
程式庫名稱是以半形句號分隔的 ID 清單。程式庫名稱中除了最後一個部分以外,其他部分也稱為命名空間。名稱的每個元件都必須是小寫,且符合下列規則運算式:[a-z][a-z0-9]*。
我們之所以採用這些限制規則,是因為不同的目標語言對命名空間、程式庫或套件的限定方式有不同的限制。我們選擇了保守的最小公分母,讓 FIDL 能與目前的目標語言組合和未來可能的目標語言搭配運作。
ID 名稱:偏好有意義的功能角色
建議使用功能名稱 (例如 fuchsia.media),而非產品或產品代號名稱 (例如 fuchsia.amber 或 fuchsia.scenic)。如果產品在 Fuchsia 以外有外部存在,且通訊協定專屬於該產品,則適合使用產品名稱。舉例來說,fuchsia.cobalt 是比 fuchsia.metrics 更適合 Cobalt 介面通訊協定的名稱,因為其他指標實作 (例如 Firebase) 不太可能實作相同的通訊協定。
ID 名稱應與參與者扮演的特定角色相關,避免將存取控管編碼至名稱中。以角色為基礎的名稱具有描述性,且不會像以存取控管為基礎的名稱那樣快速過時,後者規定了外部定義的關係,會隨著平台發展而改變。舉例來說,如果 API 涉及 FocusChain 物件,適當的名稱應為 fuchsia.ui.focus,而非 fuchsia.ui.privileged;如果我們決定讓 FocusChain 物件更廣泛存取,則 fuchsia.ui.focus 不是有問題的名稱。請避免使用下列範例字詞:
constrainedlimitedoemprivateprivilegedprotectedspecialvendor
識別碼名稱應有意義,避免使用無意義的名稱。如果 fuchsia.foo.bar 和 fuchsia.foo.baz 共用多個概念,而您希望將這些概念分解到獨立的程式庫中,請考慮在 fuchsia.foo (而非 fuchsia.foo.common) 中定義這些概念。請避免使用下列字詞:
commonserviceutilbasef<letter>lzx<word>
頂層
避免重複使用資料庫名稱。舉例來說,在 fuchsia.process 程式庫中,啟動程序的通訊協定應命名為 Launcher,而非 ProcessLauncher,因為 process 名稱已出現在程式庫名稱中。在所有目標語言中,頂層名稱都會以某種方式由程式庫名稱限定範圍。
基本別名
原始別名不得重複使用封閉程式庫中的名稱。在所有譯文語言中,原始別名都會替換為基礎原始類型,因此不會造成名稱衝突。
alias vaddr = uint64;
常數
常數名稱不得與所屬程式庫的名稱重複。在所有目標語言中,常數名稱的範圍都由其封閉程式庫決定。
描述下限和上限的常數應分別使用 MIN_ 和 MAX_ 前置字元。
const MAX_NAMES uint64 = 32;
通訊協定
通訊協定是以 protocol 關鍵字指定。
通訊協定必須是名詞片語。一般來說,通訊協定會使用表示動作的名詞命名。例如,AudioRenderer 是名詞,表示通訊協定與音訊轉譯有關。同樣地,Launcher 是名詞,表示通訊協定與啟動某個項目有關。通訊協定也可以是被動名詞,尤其是與實作保留的某些狀態相關時。舉例來說,Directory 是名詞,表示通訊協定用於與實作項目保留的目錄互動。
通訊協定可使用物件導向設計模式命名。舉例來說,fuchsia.fonts.Provider 使用 provider 字尾,表示通訊協定提供字型 (而非代表字型本身)。同樣地,fuchsia.tracing.Controller 使用 controller 後置字串,表示通訊協定控制追蹤系統 (而非代表追蹤本身)。
Manager 這個名稱可做為範圍廣泛的通訊協定的最後手段名稱。例如:fuchsia.power.Manager。不過,請注意,「管理員」通訊協定往往會吸引大量鬆散相關的功能,這些功能可能更適合納入多個通訊協定。
通訊協定不得包含名稱 service.。所有通訊協定都會定義服務。這個詞彙沒有意義。舉例來說,fuchsia.tts.TtsService 有兩處違反這項評分標準。首先,Tts 前置字串與程式庫名稱重複。其次,禁止使用 Service 後置字串。
明確的「open/ajar/closed」修飾符
通訊協定應一律指定 open、ajar 或 closed,而非依賴預設值。也就是說,請一律使用 open protocol Foo {
...,而非 protocol Foo { ...。
方法
方法必須是動詞片語。
例如,GetBatteryStatus 和 CreateSession 是動詞片語,表示方法執行的動作。
事件發生時,系統會呼叫 listener 或 observer 通訊協定的方法,這些方法應加上 On 前置字元,並以過去式描述發生的事件。舉例來說,ViewContainerListener 通訊協定有名為 OnChildAttached 的方法。
活動
同樣地,事件 (即伺服器傳送至用戶端的來路不明訊息) 應加上 On 前置字元,並以過去式描述發生的事件。
例如,AudioCapturer 通訊協定有名為 OnPacketCaptured 的事件。
單一方法通訊協定
單一方法通訊協定的方法,應為定義該方法的通訊協定名詞片語的動詞片語,例如 Loader.Load、Getter.Get、Uploader.Upload。如果是 JobCreator 或 ProcessStopper 等限定名詞詞組,應使用不限定動詞詞組,即 JobCreator.Create 或 ProcessStopper.Stop。
如果通訊協定目前只有單一方法,但日後會發展成多方法,則不一定要遵循這項命名慣例。也就是說,如果通訊協定有已知擴充功能,但不適合採用建議的命名方式,則建議提早選擇其他名稱。如有疑慮,建議您遵循預設建議。
由於取代通訊協定比演進通訊協定更困難,如果 API 從未打算演進,但最終發現需要改用多方法通訊協定,建議您透過新增方法來演進現有通訊協定,並可能重新命名現有方法。
明確的「strict/flexible」修飾符
對於方法和事件,應一律指定 strict 或 flexible,而非依賴預設值。也就是說,請一律優先使用 flexible Foo();,而非僅使用 Foo()。
結構、聯集和資料表
結構體、聯集和資料表必須是名詞片語。舉例來說,Point 是定義空間位置的結構,而 KeyboardEvent 是定義鍵盤相關事件的結構。
Struct、union 和 table 成員
在可行情況下,建議使用單一字詞做為 struct、union 和 table 成員名稱 (單一字詞名稱在譯文語言中的呈現方式較一致)。不過,如果單一字詞會造成混淆或令人困惑,請不要害怕使用多個字詞。
成員名稱不得與封閉類型 (或程式庫) 的名稱重複,除非成員名稱在沒有封閉類型名稱的情況下會造成混淆。舉例來說,如果 KeyboardEvent 類型的成員包含事件傳送時間,則應命名為 time,而非 event_time,因為 event 這個名稱已出現在封閉類型的名稱中。在所有譯文語言中,成員名稱的範圍都由其封閉類型決定。
不過,如果類型 DeviceToRoom 將智慧型裝置與所在房間建立關聯,則可能需要成員 device_id 和 room_name,因為 id 和 name 模稜兩可。兩者都可能指裝置或會議室。
列舉
列舉必須是名詞片語。
舉例來說,PixelFormat 是列舉,定義了顏色在圖片中編碼為位元的方式。
列舉成員
列舉成員名稱不得與所屬類型 (或程式庫) 的名稱重複。舉例來說,PixelFormat 列舉的成員應命名為 ARGB,而非 PIXEL_FORMAT_ARGB,因為 PIXEL_FORMAT 名稱已出現在封閉類型的名稱中。在所有目標語言中,列舉成員名稱的範圍取決於其封閉類型。
Bitfields
位元欄位必須是名詞片語。
例如,InfoFeatures 是位元欄位,表示乙太網路介面上有哪些功能。
位元欄位成員
位元欄位成員不得重複使用封閉類型 (或程式庫) 的名稱。舉例來說,InfoFeatures 位元欄位的成員應命名為 WLAN,而非 INFO_FEATURES_WLAN,因為 INFO_FEATURES 名稱已出現在封閉類型的名稱中。在所有目標語言中,位元欄位成員名稱的範圍都由其封閉類型決定。
類型
明確的「strict/flexible」修飾符
對於接受 strict/flexible 修飾符 (bits、enum 和 union) 的類型,應一律指定這類修飾符,而非依賴預設值。也就是說,請一律使用 flexible bits ...,而非 bits ...。
機構
語法
- 使用 4 個空格縮排。
- 請勿使用分頁。
- 避免結尾空白字元。
bits、enum、protocol、struct、table和union建構函式的宣告與其他宣告之間,應以一個空白行 (兩個連續換行字元) 分隔。- 檔案結尾必須只有一個換行字元。
留言
註解使用 /// (三個正斜線)。程式庫中的註解也會顯示在產生的程式碼中,方便您根據程式庫編寫程式碼。我們稱之為註解「流向」譯文語言。
將註解放在要說明的內容上方。除了下列情況外,請使用合理的完整句子,並正確使用大小寫和句號。註解寬度限制為 80 個字元,除非無法避免較長的註解 (例如長網址)。
註解應以 Markdown 格式撰寫。我們採用 CommonMark 規格的 Markdown。部分工具可能會使用其他 Markdown 標準轉譯輸出內容;如果您的工具未使用 CommonMark,建議開發人員編寫與 CommonMark 和其工具相容的 Markdown。FIDL 元素的參照一律應使用程式碼字型。
有文件的實體是指任何附加註解的 FIDL 元素。在註解中首次參照任何記錄的實體時,應提供其完整名稱,格式為 [`<library>/<top level declaration>.<member>`] (例如 [`fuchsia.io/Node.clone`])。如果工具支援,這種格式可能會產生超連結。後續參照該記錄實體時,只要縮寫版本明確 (例如 clone),即可使用縮寫版本。不含括號的形式不會產生超連結。
文件註解的第一部分描述變數、欄位或類型,應為名詞片語,簡短說明文件實體的預期用途,包括無法從名稱和類型推論的資訊。說明應以句號結尾。說明不應重複提及記錄實體的名稱,或其特定類型的 FIDL 語言元素 (例如 struct 或 protocol)。
/// A representation of violins displayed on the screen.
type Widget = struct {
/// A monotonically increasing id, uniquely identifying the widget.
id uint64;
/// Location of the top left corner of the widget.
location Point;
};
以下是我們禁止的做法:
/// BAD: Widget is a representation of violins displayed on the screen.
/// BAD: struct Widget is a representation of violins displayed on the screen.
附加至通訊協定方法的說明文件註解,第一部分應簡短說明該方法的行為,開頭為動詞,並包含無法從名稱和類型推論的資訊。動詞應使用現在式、第三人稱單數代名詞,並使用直陳語氣 (這實際上意味著您應該假裝「it」這個字在動詞之前,且您正在陳述事實)。詞組應以句號結尾。
要求和回應參數應內嵌在對應的結構體或表格中。
如果通訊協定傳回錯誤值,應記錄在「Error」子節中:
## Error
Description of the error value.
完整範例如下:
/// An abstract representation of a [`fuchsia.io/Node`] whose layout is flat.
protocol File {
compose Node;
/// Acquires a [`fuchsia.mem/Buffer`] representing this file, if
/// there is one, with the requested access rights.
///
/// ## Rights
///
/// This method requires the following rights:
///
/// * [`fuchsia.io/OPEN_RIGHT_WRITABLE`] if `flags` includes
/// [`fuchsia.io/VMO_FLAG_WRITE`].
/// * [`fuchsia.io/OPEN_RIGHT_READABLE`] if `flags` includes
/// [`fuchsia.io/VMO_FLAG_READ`] or [`fuchsia.io/VMO_FLAG_EXEC`].
///
/// ## Error
///
/// Returns ZX_ERR_INVALID_ARGS if `flags` contains an invalid VMO flag.
/// Returns ZX_ERR_NOT_FOUND if the requested buffer does not exist.
///
/// * see [`fuchsia.mem/Buffer`]
/// [`fuchsia.mem/Buffer`]:
/// https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/fidl/fuchsia.mem/buffer.fidl
GetBuffer(struct {
/// A bit field composing any of `VMO_FLAG_READ`, `VMO_FLAG_WRITE`, or
/// `VMO_FLAG_EXEC`.
flags uint32;
}) -> (resource struct {
/// The requested `fuchsia.mem/Buffer`.
buffer box<fuchsia.mem.Buffer>;
}) error zx.Status;
};
如果類型或值是由某些外部可靠資料來源定義,則應加上註解,並參照外部事物。舉例來說,請參閱說明設定結構的 Wi-Fi 規格。同樣地,如果結構必須與 C 標頭中定義的 ABI 相符,請參照 C 標頭。
如要進一步瞭解註解應包含的內容,請參閱 API 說明文件評量表。
參照 FIDL 通訊協定或通訊協定方法
註解中提及 FIDL 通訊協定或其方法時,應遵循以下模式:
/// See fuchsia.library/ProtocolName.Method for more information.
如果參照的通訊協定與註解位於同一個程式庫,則可省略程式庫名稱:ProtocolName.Method。
同樣地,如果參照的方法與註解位於同一個通訊協定,則可省略程式庫名稱和通訊協定名稱:Method。
程式庫總覽
您可以在 library 陳述式中加入說明文件註解,提供程式庫總覽。FIDL 檔案開頭為「library」陳述式。例如:
/// Library containing example FIDL used throughout the Fuchsia documentation.
library fuchsia.examples.docs;
程式庫簡介應提供一般說明文件,定義程式庫。此外,也可能詳細介紹各種訊息、定義的通訊協定,以及訊息和通訊協定的搭配使用方式。
雖然程式庫可以細分為多個 FIDL 檔案,但只能有一個程式庫總覽。請參考下列程式庫總覽建議:
- 如果總覽內容簡短,且程式庫僅由單一檔案組成,您可以將總覽內容放在程式庫檔案頂端的
library陳述式中。 - 如果程式庫包含多個檔案,請建立獨立檔案
overview.fidl來記錄程式庫。「overview.fidl」檔案不應包含任何宣告、類型別名或通訊協定定義。
非流通留言
如果註解是給程式庫作者看的,請使用較簡單的註解 // (兩個正斜線),這類註解不會傳遞至譯文語言。
決定哪些應為一般 /// 註解,哪些應為非流通註解時,請注意下列事項。
一般留言:
- 參數、引數、函式的說明
- 使用須知
非流通註解:
- 內部「待辦事項」註解
- 著作權通知
- 導入作業詳細資料
這兩種註解樣式可以合併使用:
/// A widget displaying violins on the screen.
// TODO -- widgets should use UUIDs instead of sequential ids
type ViolinWidget = struct {
/// A monotonically increasing id, uniquely identifying the widget.
id uint64;
/// Location of the top left corner of the widget.
location Point;
};
檔案
程式庫由一或多個檔案組成。檔案會儲存在目錄階層中,並遵循下列慣例:
fidl/<library>/[<dir>/]*<file>.fidl
<library> 目錄的名稱是以點分隔的 FIDL 程式庫名稱。<dir> 子目錄為選用項目,通常不適用於檔案少於 12 個的程式庫。這個目錄結構與 Fuchsia SDK 中包含 FIDL 檔案的方式相符。
將程式庫分割成檔案,對程式庫的使用者不會造成技術影響。宣告 (包括通訊協定) 可以在整個程式庫中互相參照和自我參照,無論宣告出現在哪個檔案中。將程式庫分成多個檔案,盡可能提高可讀性。
- 建議使用 DAG 依附元件圖表,呈現程式庫中的檔案。
- 建議將相互參照的定義放在相近的位置,最好是在同一個檔案中。
- 如果是複雜的程式庫,建議在葉檔案中定義純資料類型或常數,並在主幹檔案中定義參照這些類型的通訊協定。