gen-lsp-types 0.2.0

Library of Rust LSP types generated from the official metamodel
Documentation
# gen-lsp-types

<!-- vim: set spell: -->

Programmatically generated Rust type definitions for the
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/).

## Features

### 100% Accurate to the protocol*

**All** types are generated by the LSP Metamodel, and will be up-to-date on the
latest LSP changes.

\* If you notice that this is not the case, please
[file an issue](https://github.com/ribru17/gen-lsp-types/issues/new)!

### Comprehensive `derive` usage

Every type implements `serde`'s `Serialize` and `Deserialize` traits adhering to
the protocol, as well as `Debug`, `Clone`, and `PartialEq`. Additionally, the
generator will derive all of the following traits on every `enum`/`struct` that
supports them:

- `Copy`
- `Default`
- `Eq`
- `Hash`

Special types `Position` and `Range` also derive `Ord` and `PartialOrd`, and
string enumerations derive `From<String>` and `Display`.

All structures use [`derive-new`](https://github.com/nrc/derive-new) to generate
`::new()` constructors for convenience.

All "or" types in the spec (e.g. `bar: string | integer`) are represented as
untagged `enum`s, with intuitive naming based on the context that the "or" type
is defined in. Every "or" type `enum` implements `From` so it can be seamlessly
converted from its member types. E.g., for the above example, you can do:

```rust
// This...
let bar: Bar = 123.into();

// ...or this.
let bar2: Bar = String::from("baz").into();
```

### Distinguishes between `null` and "not present" properties

Unlike the original [`lsp-types`](https://github.com/gluon-lang/lsp-types)
crate, this one is able to encode properties as `null` **or** `undefined` (not
present). This is important because the spec **does differentiate the two from
each other**.

For example, for the following property which can be `null` **or** `undefined`:

```ts
interface InitializeParams extends WorkDoneProgressParams {
  // ...

  /**
   * The workspace folders configured in the client when the server starts.
   * This property is only available if the client supports workspace folders.
   * It can be `null` if the client supports workspace folders but none are
   * configured.
   *
   * @since 3.6.0
   */
  workspaceFolders?: WorkspaceFolder[] | null;
}
```

The generated Rust struct will be:

```rust
pub struct InitializeParams {
    // ...

    /// The workspace folders configured in the client when the server starts.
    ///
    /// This property is only available if the client supports workspace folders.
    /// It can be `null` if the client supports workspace folders but none are
    /// configured.
    ///
    /// @since 3.6.0
    #[serde(default, deserialize_with = "deserialize_some")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub workspace_folders: Option<WorkspaceFolders>,
}
```

...where `WorkspaceFolders` is an `enum`:

```rust
pub enum WorkspaceFolders {
    WorkspaceFolderList(Vec<WorkspaceFolder>),
    #[serde(rename = "null")]
    Null,
}
```

So `undefined` is represented as `workspace_folders = None`, and `null` is
represented as `workspace_folders = Some(WorkspaceFolders::Null)`.

> [!NOTE]
> If a property can only be one of `undefined` or `null`, not both, `None` will
> be used in both cases to denote those values, for convenience.

### Support for string literal properties

Structures with string literal properties will have those properties omitted
from their generated Rust types. E.g., for the following `DeleteFile`
definition:

```ts
/**
 * Delete file operation
 */
export interface DeleteFile {
  /**
   * This is a delete operation.
   */
  kind: 'delete';

  /**
   * The file to delete.
   */
  uri: DocumentUri;

  /**
   * Delete options.
   */
  options?: DeleteFileOptions;

  /**
   * An optional annotation identifier describing the operation.
   *
   * @since 3.16.0
   */
  annotationId?: ChangeAnnotationIdentifier;
}
```

The generated Rust struct looks like the following:

```rust
/// Delete file operation
pub struct DeleteFile {
    /// The file to delete.
    pub uri: Uri,
    /// Delete options.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub options: Option<DeleteFileOptions>,
    /// An optional annotation identifier describing the operation.
    ///
    /// @since 3.16.0
    #[serde(skip_serializing_if = "Option::is_none")]
    pub annotation_id: Option<ChangeAnnotationIdentifier>,
}
```

Note the `kind` property is not present. However, when serializing, it will
always be present with a value of `"delete"`. Deserializing only succeeds when
the field is present and has a `"delete"` value.

## Notable changes from `lsp-types`

[`lsp-types` controversially
switched to `fluent-uri`](https://github.com/gluon-lang/lsp-types/issues/284)
for better spec correctness, at the cost of decreased interoperability and
poorer DX. Previously, the `url` crate was used, which is significantly more
battle-tested, but isn't spec-compliant in all cases.

This library deliberately puts the URI encoding decision in the hands of the
downstream consumer. Developers migrating from the original `lsp-types` crate
will notice that the URI type is just a newtype wrapper over a `String`,
allowing them to internally represent URIs however they wish.

In the future, this crate might use feature flags to deserialize URIs as
`url::Url` or `fluent_uri::Uri` instead of the newtype wrapper, but I'd like to
hear some discussion on whether this is a bad idea first.

## Stability

Changes that are backwards compatible from a protocol perspective (e.g.
`foo: integer | string` -> `foo: integer | string | boolean`) are still breaking
from the point of view of this library. In the above example, an extra `enum`
variant would be added to the `Foo` type, which is a breaking change. Thus the
library will be perpetually kept at [zerover](https://0ver.org/) versioning,
like the original `lsp-types` crate.