feat: add JsonSchema to gpui

Sergey Onufrienko created

Change summary

crates/gpui/src/color.rs              | 21 ++++++++++-
crates/gpui/src/elements/container.rs | 18 ++++++++++
crates/gpui/src/elements/image.rs     |  3 +
crates/gpui/src/elements/label.rs     |  3 +
crates/gpui/src/elements/tooltip.rs   |  5 +-
crates/gpui/src/font_cache.rs         |  6 +--
crates/gpui/src/fonts.rs              | 52 ++++++++++++++++++++++------
crates/gpui/src/scene.rs              |  3 +
crates/settings/src/keymap_file.rs    | 14 +------
9 files changed, 90 insertions(+), 35 deletions(-)

Detailed changes

crates/gpui/src/color.rs 🔗

@@ -6,17 +6,19 @@ use std::{
 
 use crate::json::ToJson;
 use pathfinder_color::{ColorF, ColorU};
+use schemars::{
+    gen::SchemaGenerator,
+    schema::{InstanceType, Schema, SchemaObject},
+    JsonSchema,
+};
 use serde::{
     de::{self, Unexpected},
     Deserialize, Deserializer,
 };
 use serde_json::json;
-use ts_rs::TS;
 
 #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
 #[repr(transparent)]
-#[derive(TS)]
-#[ts(export, export_to = "theme/types/")]
 pub struct Color(ColorU);
 
 impl Color {
@@ -130,3 +132,16 @@ impl fmt::Debug for Color {
         self.0.fmt(f)
     }
 }
+
+impl JsonSchema for Color {
+    fn schema_name() -> String {
+        "Color".into()
+    }
+
+    fn json_schema(_: &mut SchemaGenerator) -> Schema {
+        let mut schema = SchemaObject::default();
+        schema.instance_type = Some(InstanceType::Integer.into());
+        schema.format = Some("uint".to_owned());
+        Schema::Object(schema)
+    }
+}

crates/gpui/src/elements/container.rs 🔗

@@ -12,6 +12,11 @@ use crate::{
     scene::{self, Border, CursorRegion, Quad},
     AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
 };
+use schemars::{
+    gen::SchemaGenerator,
+    schema::{InstanceType, Schema, SchemaObject},
+    JsonSchema,
+};
 use serde::Deserialize;
 use serde_json::json;
 
@@ -332,6 +337,19 @@ impl ToJson for ContainerStyle {
     }
 }
 
+impl JsonSchema for ContainerStyle {
+    fn schema_name() -> String {
+        "ContainerStyle".into()
+    }
+
+    fn json_schema(_: &mut SchemaGenerator) -> Schema {
+        let mut schema = SchemaObject::default();
+        schema.instance_type = Some(InstanceType::Integer.into());
+        schema.format = Some("uint".to_owned());
+        Schema::Object(schema)
+    }
+}
+
 #[derive(Clone, Copy, Debug, Default)]
 pub struct Margin {
     pub top: f32,

crates/gpui/src/elements/image.rs 🔗

@@ -8,6 +8,7 @@ use crate::{
     scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View,
     ViewContext,
 };
+use schemars::JsonSchema;
 use serde::Deserialize;
 use std::{ops::Range, sync::Arc};
 
@@ -21,7 +22,7 @@ pub struct Image {
     style: ImageStyle,
 }
 
-#[derive(Copy, Clone, Default, Deserialize)]
+#[derive(Copy, Clone, Default, Deserialize, JsonSchema)]
 pub struct ImageStyle {
     #[serde(default)]
     pub border: Border,

crates/gpui/src/elements/label.rs 🔗

@@ -10,6 +10,7 @@ use crate::{
     text_layout::{Line, RunStyle},
     Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
 };
+use schemars::JsonSchema;
 use serde::Deserialize;
 use serde_json::json;
 use smallvec::{smallvec, SmallVec};
@@ -20,7 +21,7 @@ pub struct Label {
     highlight_indices: Vec<usize>,
 }
 
-#[derive(Clone, Debug, Deserialize, Default)]
+#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
 pub struct LabelStyle {
     pub text: TextStyle,
     pub highlight_text: Option<TextStyle>,

crates/gpui/src/elements/tooltip.rs 🔗

@@ -9,6 +9,7 @@ use crate::{
     Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View,
     ViewContext,
 };
+use schemars::JsonSchema;
 use serde::Deserialize;
 use std::{
     cell::{Cell, RefCell},
@@ -33,7 +34,7 @@ struct TooltipState {
     debounce: RefCell<Option<Task<()>>>,
 }
 
-#[derive(Clone, Deserialize, Default)]
+#[derive(Clone, Deserialize, Default, JsonSchema)]
 pub struct TooltipStyle {
     #[serde(flatten)]
     pub container: ContainerStyle,
@@ -42,7 +43,7 @@ pub struct TooltipStyle {
     pub max_text_width: Option<f32>,
 }
 
-#[derive(Clone, Deserialize, Default)]
+#[derive(Clone, Deserialize, Default, JsonSchema)]
 pub struct KeystrokeStyle {
     #[serde(flatten)]
     container: ContainerStyle,

crates/gpui/src/font_cache.rs 🔗

@@ -7,16 +7,14 @@ use crate::{
 use anyhow::{anyhow, Result};
 use ordered_float::OrderedFloat;
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
+use schemars::JsonSchema;
 use std::{
     collections::HashMap,
     ops::{Deref, DerefMut},
     sync::Arc,
 };
-use ts_rs::TS;
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
-#[derive(TS)]
-#[ts(export, export_to = "theme/types/")]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, JsonSchema)]
 pub struct FamilyId(usize);
 
 struct Family {

crates/gpui/src/fonts.rs 🔗

@@ -15,11 +15,8 @@ use schemars::JsonSchema;
 use serde::{de, Deserialize, Serialize};
 use serde_json::Value;
 use std::{cell::RefCell, sync::Arc};
-use ts_rs::TS;
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
-#[derive(TS)]
-#[ts(export, export_to = "theme/types/")]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, JsonSchema)]
 pub struct FontId(pub usize);
 
 pub type GlyphId = u32;
@@ -62,22 +59,52 @@ pub struct Features {
     pub zero: Option<bool>,
 }
 
-#[derive(Clone, Debug)]
-#[derive(TS)]
-#[ts(export, export_to = "theme/types/")]
+#[derive(Clone, Debug, JsonSchema)]
 pub struct TextStyle {
     pub color: Color,
     pub font_family_name: Arc<str>,
     pub font_family_id: FamilyId,
     pub font_id: FontId,
     pub font_size: f32,
+    #[serde(with = "PropertiesDef")]
     pub font_properties: Properties,
     pub underline: Underline,
 }
 
-#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[derive(JsonSchema)]
+#[serde(remote = "Properties")]
+pub struct PropertiesDef {
+    /// The font style, as defined in CSS.
+    pub style: StyleDef,
+    /// The font weight, as defined in CSS.
+    pub weight: WeightDef,
+    /// The font stretchiness, as defined in CSS.
+    pub stretch: StretchDef,
+}
+
+#[derive(JsonSchema)]
+#[serde(remote = "Style")]
+pub enum StyleDef {
+    /// A face that is neither italic not obliqued.
+    Normal,
+    /// A form that is generally cursive in nature.
+    Italic,
+    /// A typically-sloped version of the regular face.
+    Oblique,
+}
+
+#[derive(JsonSchema)]
+#[serde(remote = "Weight")]
+pub struct WeightDef(pub f32);
+
+#[derive(JsonSchema)]
+#[serde(remote = "Stretch")]
+pub struct StretchDef(pub f32);
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, JsonSchema)]
 pub struct HighlightStyle {
     pub color: Option<Color>,
+    #[serde(with = "WeightDef")]
     pub weight: Option<Weight>,
     pub italic: Option<bool>,
     pub underline: Option<Underline>,
@@ -86,15 +113,18 @@ pub struct HighlightStyle {
 
 impl Eq for HighlightStyle {}
 
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-#[derive(TS)]
-#[ts(export, export_to = "theme/types/")]
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
 pub struct Underline {
     pub color: Option<Color>,
+    #[serde(with = "OrderedFloatDef::<f32>")]
     pub thickness: OrderedFloat<f32>,
     pub squiggly: bool,
 }
 
+#[derive(JsonSchema)]
+#[serde(remote = "OrderedFloat")]
+pub struct OrderedFloatDef<T>(pub T);
+
 #[allow(non_camel_case_types)]
 #[derive(Deserialize)]
 enum WeightJson {

crates/gpui/src/scene.rs 🔗

@@ -3,6 +3,7 @@ mod mouse_region;
 
 #[cfg(debug_assertions)]
 use collections::HashSet;
+use schemars::JsonSchema;
 use serde::Deserialize;
 use serde_json::json;
 use std::{borrow::Cow, sync::Arc};
@@ -99,7 +100,7 @@ pub struct Icon {
     pub color: Color,
 }
 
-#[derive(Clone, Copy, Default, Debug)]
+#[derive(Clone, Copy, Default, Debug, JsonSchema)]
 pub struct Border {
     pub width: f32,
     pub color: Color,

crates/settings/src/keymap_file.rs 🔗

@@ -3,7 +3,7 @@ use anyhow::{Context, Result};
 use collections::BTreeMap;
 use gpui::{keymap_matcher::Binding, AppContext};
 use schemars::{
-    gen::{SchemaGenerator, SchemaSettings},
+    gen::SchemaSettings,
     schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
     JsonSchema,
 };
@@ -22,20 +22,10 @@ pub struct KeymapBlock {
     bindings: BTreeMap<String, KeymapAction>,
 }
 
-#[derive(Deserialize, Default, Clone)]
+#[derive(Deserialize, Default, Clone, JsonSchema)]
 #[serde(transparent)]
 pub struct KeymapAction(Box<RawValue>);
 
-impl JsonSchema for KeymapAction {
-    fn schema_name() -> String {
-        "KeymapAction".into()
-    }
-
-    fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        Schema::Bool(true)
-    }
-}
-
 #[derive(Deserialize)]
 struct ActionWithData(Box<str>, Box<RawValue>);