FIDL 樣式指南

本節包含 Fuchsia 介面定義語言檔案的樣式相關資訊。

另請參閱 FIDL API 評量表

名稱

貓咪命名是件難事,
不只是節慶遊戲而已;
--- T.S. 艾略特

FIDL 中定義的名稱會用於在各目標語言中產生 ID。某些語言會為各種形式的名稱附加語意或傳統意義。舉例來說,在 Go 中,識別碼開頭字母是否大寫會控管識別碼的可見度。因此,許多語言後端會轉換媒體庫中的名稱,使其更適合目標語言。本節的命名規則旨在兼顧 FIDL 來源的可讀性、各目標語言的可用性,以及目標語言之間的一致性。

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

部分 FIDL 關鍵字也是目標語言中常見的保留字 (例如 C 和 C++ 中的 struct),因此應避免使用。不過,其他 FIDL 關鍵字 (尤其是 requesthandle) 通常具有描述性,可視情況使用。

名稱開頭或結尾不得有底線。在某些語言中,開頭或結尾底線具有語意意義 (例如,開頭底線可控制 Dart 中的可見度),在其他語言中則具有慣例意義 (例如,結尾底線通常用於 C++ 中的成員變數)。此外,FIDL 編譯器會使用開頭和結尾底線來混淆處理 ID,避免發生衝突。

使用 size 字詞命名位元組數。使用 count 這個字詞來命名其他數量 (例如結構體向量中的項目數量)。

案件定義

有時候,決定如何劃分 ID 中的字詞不只一種方式。我們的風格如下:

  • 先以美式英文輸入原始片語 (例如 "Non-Null HTTP Client")
  • 移除所有標點符號。("Non Null HTTP Client")
  • 將所有內容設為小寫 (「non null http client」)
  • 請根據適用於指定 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
結構體成員 小寫蛇形命名法 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 是比 fuchsia.metrics 更適合 Cobalt 介面通訊協定的名稱,因為其他指標實作 (例如 Firebase) 都不太可能實作相同的通訊協定。

識別碼名稱應與參與者扮演的特定角色相關,請避免將存取權控管編碼至名稱中。以角色為依據的名稱具有描述性,且不會像以存取權控管為依據的名稱一樣快速過時,因為存取權控管會規定外部定義的關係,而這類關係會隨著平台演進而變更。舉例來說,如果 API 涉及 FocusChain 物件,適當的名稱應為 fuchsia.ui.focus,而非 fuchsia.ui.privileged;如果我們決定讓 FocusChain 物件更廣為人知,則 fuchsia.ui.focus 並非有問題的名稱。請避免使用下列範例字詞:

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

識別項名稱應有意義,請避免使用無意義的名稱。如果 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。不過請注意,「管理員」通訊協定往往會吸引大量鬆散相關的功能,這些功能或許更適合納入多個通訊協定。

通訊協定不得包含 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 是位元欄位,用於指出乙太網路介面上的功能。

位元欄位成員

位元欄位成員不得重複使用封閉型別 (或程式庫) 的名稱。 舉例來說,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)。不含方括號的表單不會產生超連結。

描述變數、欄位或型別的文件註解第一部分應為名詞片語,簡要說明所記錄實體的預期用途,包括無法從名稱和型別推斷的資訊。說明應以句號結尾。說明不應重述記錄實體的名稱,或其特定類型的 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」一字,且您正在陳述事實)。詞組結尾應加上句點。

要求和回應參數應內嵌在對應的結構體或表格中。

如果通訊協定傳回錯誤值,應在「錯誤」小節中記錄:

## 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 陳述式中提供程式庫總覽,做為說明文件註解。「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;
};

檔案

程式庫包含一或多個檔案。檔案會儲存在目錄階層中,並遵循下列慣例:

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

<library> 目錄的命名方式是使用以半形句號分隔的 FIDL 程式庫名稱。<dir> 子目錄為選用項目,通常不會用於檔案少於十幾個的程式庫。這個目錄結構與 Fuchsia SDK 中 FIDL 檔案的納入方式相符。

將程式庫劃分為多個檔案,對程式庫的消費者沒有技術影響。無論宣告出現在哪個檔案中,程式庫中的宣告 (包括通訊協定) 都可以互相參照,也可以參照自身。將程式庫分成多個檔案,盡可能提高可讀性。

  • 建議使用程式庫中檔案的 DAG 依附元件圖表。
  • 建議將相互參照的定義文字彼此靠近,最好是在同一個檔案中。
  • 如果是複雜的程式庫,建議在葉片檔案中定義純資料型別或常數,並在主幹檔案中定義參照這些型別的通訊協定。