FIDL 樣式指南

本節包含 Fuchsia Interface Definition Language 檔案的樣式相關資訊。

另請參閱 FIDL API 評分量表

名稱

《Catsing of Cats》才比較艱難,
這不只是您的節慶遊戲之一;
--- T.S. Eliot

FIDL 中定義的名稱會用來產生每個指定語言的 ID。 某些語言會在各種形式的名稱中附加語意或傳統意義。例如,在 Go 中,ID 的初始字母是否使用大寫控制顯示設定。因此,許多語言後端都會轉換程式庫中的名稱,使其更適合目標語言。本節中的命名規則是 FIDL 來源中可讀性、每種譯文語言的可用性,以及跨目標語言一致性之間的平衡。

避免使用一般保留字詞,例如 goto。語言後端會將保留的字詞轉換為非保留 ID,但會降低這些語言的可用性。避免使用一般保留字詞,可減少套用這些轉換的頻率。

儘管部分 FIDL 關鍵字也是以目標語言保留的字詞 (例如 C 和 C++ 中的 struct),因此最好避免使用,因此其他 FIDL 關鍵字 (尤其是 requesthandle) 通常是描述性字詞,因此可能適合使用。

名稱開頭或結尾不得為底線。在某些語言中,開頭或結尾的底線具有語意含義 (例如 Dart 開頭的底線控制瀏覽權限),而其他語言則採用傳統的意義 (例如在 C++ 中常使用結尾的底線)。此外,FIDL 編譯器會使用前置和結尾的底線來避免衝突。

使用字詞 size 來命名位元組數。您可以使用 count 字詞來命名其他一些數量,例如結構向量中的項目數量。

案例定義

有時候,您可以透過多種方式決定如何分隔 ID 中的字詞。我們的樣式如下:

  • 以美式英文的原始詞組開頭 (例如「非空值 HTTP 用戶端」)
  • 請移除所有標點符號。(「非空值 HTTP 用戶端」)
  • Make everything lowercase ("non null http client")
  • 根據指定 ID 的適用樣式,執行下列任一操作:
    • 將空格替換為小寫的蛇形 (non_null_http_client) 時,以底線取代空格 (「_」)。
    • 如果是大寫的蛇大寫 (NON_NULL_HTTP_CLIENT),請以底線取代空格。
    • 將每個字詞的第一個字母大寫,並將所有字詞合併成大寫駝峰式大小寫 (NonNullHttpClient)。

用量

下表把這個案例的使用情形對應到元素:

元素 編織紋 範例
bits 大寫駝峰式大小寫 InfoFeatures
Bitfield 成員 防蛇罩 WLAN_SNOOP
const 防蛇罩 MAX_NAMES
alias 大寫駝峰式大小寫 DeviceId
protocol 大寫駝峰式大小寫 AudioRenderer
通訊協定方法參數 低蛇保護殼 enable_powersave
通訊協定方法 大寫駝峰式大小寫 GetBatteryStatus
struct 大寫駝峰式大小寫 KeyboardEvent
結構成員 低蛇保護殼 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.amberfuchsia.scenic)。當產品在 Fuchsia 以外的外部位置,且通訊協定適用於該產品時,即適合產品名稱。舉例來說,fuchsia.cobalt 是 Cobalt 介面通訊協定的名稱,而不是 fuchsia.metrics,因為其他指標實作項目 (例如Firebase) 不太可能採用相同的通訊協定。

ID 名稱應與參與者遊玩的特定角色有關;避免在名稱中加入編碼存取權控管。以角色為基礎的名稱具有描述性,不會像以存取權控管為依據的名稱一樣快速過時,這個控制項會指出外部定義的關係,而且隨時可能隨平台演變而改變。舉例來說,假如 API 涉及 FocusChain 物件,則適當的名稱會是 fuchsia.ui.focus,而不是 fuchsia.ui.privileged;如果我們決定讓 FocusChain 物件更容易存取,那麼 fuchsia.ui.focus 就不是問題名稱。以下是應避免的例子:

  • constrained
  • limited
  • oem
  • private
  • privileged
  • protected
  • special
  • vendor

ID 名稱應具有意義,請避免使用無意義的名稱。如果 fuchsia.foo.barfuchsia.foo.baz 共用許多概念,您如果想將概念解釋成獨立的程式庫,請考慮在 fuchsia.foo 中定義這些概念,而不要在 fuchsia.foo.common 中。應避免使用下列範例字詞:

  • common
  • service
  • util
  • base
  • f<letter>l
  • zx<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。不過請警告您,「manager」通訊協定通常會吸引大量相關且功能較不相關的功能,這些功能可能會更適合用於多個通訊協定。

通訊協定不得包含名稱 service.。所有通訊協定均定義服務。 字詞沒有意義。例如,fuchsia.tts.TtsService 有兩種方式違反這個評分量表。首先,Tts 前置字串與程式庫名稱重複。其次,Service 尾碼遭到停權。

明確的「open/ajar/closed」修飾符

如果是通訊協定,請一律指定 openajarclosed,而非使用預設值。也就是說,一律偏好 open protocol Foo { ...protocol Foo { ...

方法

方法必須是動詞片語。

舉例來說,GetBatteryStatusCreateSession 都是動詞片語,用來指出方法會執行的動作。

發生事件時呼叫的 listenerobserver 通訊協定方法應加上 On 前置字串,並描述過去發生的事件。例如,ViewContainerListener 通訊協定有一個名為 OnChildAttached 的方法。

事件

同樣地,事件 (亦即從伺服器傳送至用戶端的來路不明的訊息) 都應加上 On 前置字串,並描述過去發生的事件。

舉例來說,AudioCapturer 通訊協定有名為 OnPacketCaptured 的事件。

單一方法通訊協定

單一方法通訊協定的方法應為通訊協定所定義名詞的動詞片語,例如 Loader.LoadGetter.GetUploader.Upload。如果是限定名詞的詞組 (例如 JobCreatorProcessStopper),則應使用不限定的動詞詞組,也就是 JobCreator.CreateProcessStopper.Stop

僅是單一方法但打算隨著時間發展到多方法的通訊協定,但不一定需要遵循此命名慣例;亦即,如果已知有通訊協定的已知延伸名稱,建議命名,系統可能會優先選擇其他名稱。如有疑問,建議您按照預設建議操作。

由於取代通訊協定比發展通訊協定困難,因此如果 API 從未打算發展,但最終卻發現必須移至多方法通訊協定,建議您加入方法來改進現有的通訊協定,但可能需要重新命名現有方法。

明確的「strict/flexible」修飾符

對於方法和事件,應一律指定 strictflexible,而非依賴預設值。也就是說,一律偏好 flexible Foo();Foo()

結構體、聯集和表格

結構體、聯集和資料表必須為名詞詞組。舉例來說,Point 是定義空格中位置的結構,KeyboardEvent 則是定義鍵盤相關事件的結構。

結構體、聯集和資料表成員

寫實時,請優先使用結構體、聯集和資料表成員名稱 (單一字詞名稱在各目標語言中呈現的一致)。然而,如果單一字詞容易混淆或令人混淆,請放心使用多個字詞。

成員名稱不得與封閉類型 (或程式庫) 重複名稱,除非成員名稱不明確,而沒有涵蓋類型中的名稱。舉例來說,包含事件送達時間的 KeyboardEvent 類型成員應命名為 time,而不是 event_time,因為名稱 event 已出現在涵蓋類型的名稱中。在所有譯文語言中,成員名稱都是依所屬類型限定範圍。

但是,DeviceToRoom 類型可將智慧型裝置與所在房間建立關聯,可能需要含有成員 device_idroom_name,因為 idname 不明確。這兩種裝置都可以指裝置或房間。

列舉

列舉必須是名詞片語。

舉例來說,PixelFormat 是一個列舉,會定義在圖片中將顏色編碼為位元的方式。

列舉成員

列舉成員名稱不得與包含類型 (或程式庫) 的名稱重複。舉例來說,PixelFormat 列舉的成員應命名為 ARGB 而非 PIXEL_FORMAT_ARGB,因為 PIXEL_FORMAT 的名稱已出現在涵蓋類型的名稱中。在所有目標語言中,列舉成員名稱是依據其所屬類型設定範圍。

位元欄位

Bitfields 必須是名詞片語。

舉例來說,InfoFeatures 是一個位元欄位,用來指出乙太網路介面上有哪些功能。

Bitfield 成員

位元欄位成員不得與包含類型 (或程式庫) 的名稱重複,舉例來說,InfoFeatures 位元欄位的成員應命名為 WLAN,而非 INFO_FEATURES_WLAN,因為 INFO_FEATURES 的名稱已出現在涵蓋類型的名稱中。在所有目標語言中,位元欄位成員名稱皆是依據其所屬類型限定範圍。

類型

明確的「strict/flexible」修飾符

如果類型接受 strict/flexible 修飾符 (bitsenumunion),請一律指定這類修飾符,而非依賴預設值。也就是說,一律偏好 flexible bits ...bits ...

機構

語法

  • 使用 4 個空格縮排。
  • 一律不使用分頁。
  • 請勿在結尾加上空白字元。
  • bitsenumprotocolstructtableunion 建構項目與其他宣告分開,並以一行空白字元 (兩個連續的換行字元) 分隔。
  • 檔案結尾只有一個換行字元。

留言

註解使用 /// (三個正斜線)。程式庫中的註解也會顯示在產生的程式碼中,方便您在對程式庫編寫程式碼時簡化開發作業。我們會說「流動」對目標語言的留言。

請將評論置於說明內容上方。除了下列情況以外,請使用合理完整的句子,大小寫和半形句號正確無誤。除非無法避免較長的註解 (例如較長的網址),否則註解寬度上限為 80 個半形字元。

留言應以 Markdown 編寫。我們的 Markdown 採用的是 CommonMark 規格。部分工具可能會使用其他 Markdown 標準算繪輸出內容;如果工具未使用 CommonMark,則建議開發人員編寫與 CommonMark 及其工具相容的 Markdown。FIDL 元素的參照應一律採用程式碼字型。

已記錄的實體是指任何附加註解的 FIDL 元素。對於註解中任何記錄的實體,應以其完整名稱提供參照,格式為 [`<library>/<top level declaration>.<member>`] (例如:[`fuchsia.io/Node.clone`])。如果工具支援,這份表單可以產生超連結。該記錄實體的後續參照可使用縮寫版本,只要縮寫版本不明確 (例如clone)。不含括號的表單不會產生超連結。

要求參數、回應參數和錯誤類型應記錄為以下形式的清單:

+ request `param1` <description>
+ request `param2` <description>
- response `param1` <description>
- response `param2` <description>
* error <description>

要求、回應和錯誤必須依序顯示。一組指定參數也必須遵循參數清單中宣告的順序。如果參數名稱只出現在其中一個要求或回應參數清單中,則可省略「request」和「response」這兩個字詞。

說明變數、欄位或類型的文件註解的第一部分應為名詞片語,其中簡要說明記錄實體的預期用途,包括無法從名稱和類型衍生的資訊。說明的結尾應為句號。說明不得重述記錄實體的名稱,或特定類型 FIDL 語言元素 (例如structprotocol)。

/// 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」這個字,而您要發表事實陳述)。詞組的結尾應為句號。

完整範例:

/// 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`].
    ///
    /// + request `flags` a bit field composing any of
    ///     `VMO_FLAG_READ`, `VMO_FLAG_WRITE`, or `VMO_FLAG_EXEC`.
    /// - response `buffer` the requested `fuchsia.mem/Buffer`, or
    ///     null if there was an error, or the buffer does not exist.
    /// * error a zx_status value indicating success or failure.
    /// * see [`fuchsia.mem/Buffer`]
    /// [`fuchsia.mem/Buffer`]:
    ///    https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/fidl/fuchsia.io/
    GetBuffer(struct {
        flags uint32;
    }) -> (resource struct {
        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 陳述式中,以說明文件註解的形式提供程式庫總覽。「library」陳述式會啟動 FIDL 檔案。例如:

/// 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;
};

Files

程式庫由一或多個檔案組成,檔案會以下列慣例儲存在目錄階層中:

fidl/<library>/[<dir>/]*<file>.fidl

系統會使用 FIDL 程式庫的名稱以以點分隔的名稱命名 <library> 目錄。<dir> 子目錄是選用項目,通常不用於檔案少於十個檔案的程式庫。此目錄結構符合 Fuchsia SDK 中 FIDL 檔案的方式。

將程式庫分割成檔案,不會對程式庫使用者造成技術影響。任何宣告 (包括通訊協定) 都可以在程式庫中參照彼此,而無論這些宣告在哪個檔案都是如此。將程式庫分割成檔案,盡可能提高可讀性。

  • 偏好程式庫中檔案的 DAG 依附元件圖表。
  • 最好將共同參照的定義保持相近的文字,最好在同一個檔案中。
  • 如果是複雜的程式庫,最好在分葉檔案中定義純資料類型或常數,並在主幹檔案中定義這些類型一併參照的通訊協定。