From 65883bf166ee3a14cfa336099532d3fb78bdbefb Mon Sep 17 00:00:00 2001 From: Mist Date: Sun, 27 Oct 2024 18:32:54 +0800 Subject: [PATCH] [Feat] support create snapshot from JSON config --- Cargo.lock | 5 +- core/Cargo.toml | 1 + core/src/config.rs | 273 ++++++++-------------------- core/src/snapshot/image_snapshot.rs | 6 +- 4 files changed, 88 insertions(+), 197 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2dcbf8..bc5140b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,6 +356,7 @@ dependencies = [ "regex", "rgb", "serde", + "serde_json", "syntect", "thiserror", "tiny-skia", @@ -1355,9 +1356,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", diff --git a/core/Cargo.toml b/core/Cargo.toml index 1f2c7bc..a58426c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,3 +21,4 @@ derive_builder = "0.20.2" chrono = "0.4.38" base64 = "0.22.1" once_cell = "1.20.2" +serde_json = "1.0.132" diff --git a/core/src/config.rs b/core/src/config.rs index 9f7853b..da686ed 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -10,16 +10,41 @@ use crate::{ pub const DEFAULT_WINDOW_MARGIN: f32 = 90.; -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Debug)] +#[serde(untagged)] pub enum DimensionValue { Num(f32), Max, } -#[derive(Clone, Serialize, Deserialize)] +impl<'de> Deserialize<'de> for DimensionValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(untagged)] + enum AnyType { + Num(f32), + Max(String), + } + + Ok(match AnyType::deserialize(deserializer)? { + AnyType::Num(num) => DimensionValue::Num(num), + AnyType::Max(max) if max == "max" => DimensionValue::Max, + _ => { + return Err(serde::de::Error::custom( + "The value of DimensionValue should be a number or 'max'", + )) + } + }) + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Point { - pub(crate) x: T, - pub(crate) y: T, + pub x: T, + pub y: T, } pub type GradientPoint = Point; @@ -39,7 +64,7 @@ impl Point { } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct LinearGradientStop { position: f32, color: String, @@ -67,20 +92,22 @@ impl From for GradientStop { } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct LinearGradient { pub start: GradientPoint, pub end: GradientPoint, pub stops: Vec, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(untagged)] pub enum Background { Solid(String), Gradient(LinearGradient), } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct TitleConfig { #[builder(setter(into))] pub title: String, @@ -92,7 +119,7 @@ pub struct TitleConfig { pub color: String, } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] pub struct Margin { #[builder(setter(into, strip_option), default = DEFAULT_WINDOW_MARGIN)] pub x: f32, @@ -101,7 +128,8 @@ pub struct Margin { pub y: f32, } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct Breadcrumbs { #[builder(setter(into, strip_option), default = None)] pub separator: Option, @@ -113,13 +141,14 @@ pub struct Breadcrumbs { pub color: String, } -#[derive(Clone, Builder, Default, Serialize, Deserialize)] +#[derive(Clone, Builder, Default, Serialize, Deserialize, Debug)] pub struct Border { #[builder(setter(into))] pub color: String, } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct Window { #[builder(setter(into, strip_option), default = Margin {x : DEFAULT_WINDOW_MARGIN, y: DEFAULT_WINDOW_MARGIN})] pub margin: Margin, @@ -137,13 +166,15 @@ pub struct Window { pub shadow: f32, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(untagged)] pub enum HighlightLine { Single(u32, String), Range(u32, u32, String), } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct LineNumber { #[builder(setter(into))] pub start_number: u32, @@ -152,9 +183,11 @@ pub struct LineNumber { pub color: String, } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct Code { #[builder(setter(into))] + #[serde(default)] pub content: String, #[builder(setter(into), default = String::from("CaskaydiaCove Nerd Font"))] @@ -169,6 +202,13 @@ pub struct Code { #[builder(setter(into), default = String::from("base16-ocean.dark"))] pub theme: String, + /// Breadcrumbs is a useful and unique feature of CodeSnap, it can help users to understand the + /// code location in the project. If the `has_breadcrumbs` is true, CodeSnap will display the + /// `file_path` on top of the code. + /// + /// The code snapshot is different from normal screenshots, it should provide more information + /// about the code, such as the file path, the line number and highlight code line, these + /// information can help users to understand the code better. #[builder(setter(into, strip_option), default = None)] pub breadcrumbs: Option, @@ -176,6 +216,7 @@ pub struct Code { pub line_number: Option, #[builder(setter(into), default = vec![])] + #[serde(default)] pub highlight_lines: Vec, /// The `language` will be used to determine the syntax highlighting to use for generating @@ -187,7 +228,10 @@ pub struct Code { pub file_path: Option, } -#[derive(Serialize, Deserialize, Clone, Builder)] +/// Draw a watermark below the code, you can use this to add a logo or any other text +/// The watermark is designed as a place for users to provide personalize label +#[derive(Serialize, Deserialize, Clone, Builder, Debug)] +#[serde(rename_all = "camelCase")] pub struct Watermark { #[builder(setter(into))] pub content: String, @@ -199,8 +243,9 @@ pub struct Watermark { pub color: String, } -#[derive(Clone, Builder, Serialize, Deserialize)] +#[derive(Clone, Builder, Serialize, Deserialize, Debug)] #[builder(name = "CodeSnap", build_fn(validate = "Self::validate"))] +#[serde(rename_all = "camelCase")] pub struct SnapshotConfig { #[builder(setter(into, strip_option), default = WindowBuilder::default().build().unwrap())] pub window: Window, @@ -215,6 +260,7 @@ pub struct SnapshotConfig { /// CodeSnap default generate triple size snapshot image, /// you can use this config to change the scale factor. #[builder(default = 3)] + #[serde(default = "default_scale_factor")] pub scale_factor: u8, /// CodeSnap use Syntect as the syntax highlighting engine, you can provide a custom theme @@ -235,193 +281,30 @@ pub struct SnapshotConfig { #[builder(setter(into, strip_option), default = None)] pub fonts_folder: Option, - /// CodeSnap have pre-defined background themes, you can use the theme name to specify the - /// background theme you want to use. - /// - /// Currently available themes: - /// - summer - /// - bamboo - /// - peach - /// - grape - /// - dusk - /// - sea #[builder(setter(into), default = BAMBOO.clone())] pub background: Background, - // /// Draw a MacOS style window bar - // #[builder(default = true)] - // pub mac_window_bar: bool, - // - // /// Draw a watermark below the code, you can use this to add a logo or any other text - // /// The watermark is designed as a place for users to provide personalize label - // #[builder(setter(into, strip_option), default = None)] - // pub watermark: Option, - // - // /// Config title of the code window - // #[builder(setter(into, strip_option), default = None)] - // pub title: Option, - // - // /// Config font family you like to use for the code, default is "CaskaydiaCove Nerd Font" - // #[builder(setter(into), default = String::from("CaskaydiaCove Nerd Font"))] - // pub code_font_family: String, - // - // /// Config font family you like to use for the watermark, default is "Pacifico" - // #[builder(setter(into), default = String::from("Pacifico"))] - // pub watermark_font_family: String, - // - // /// The `code_file_path` will be read by Syntect, which will use the file content to detect which - // /// language highlighting to use for generating the snapshot. - // /// If the `language` field is provided, CodeSnap will prioritize the value provided by - // /// `language` for syntax highlighting. - // /// - // /// This config is useful for users who use CodeSnap editor plugins, in this case, the - // /// `code_file_path` should always have a value which from the editor. - // /// - // /// But if you want to generate a snapshot from a string of code, you should always use the - // /// `language` field to provide the language of the code or just let CodeSnap detect the - // /// language by itself. - // #[builder(setter(into, strip_option), default = None)] - // pub code_file_path: Option, - // - // /// The `language` will be used to determine the syntax highlighting to use for generating - // /// the snapshot. - // #[builder(setter(into, strip_option), default = None)] - // pub language: Option, - // - // /// Config the path where the snapshot will be saved, if the path is not provided, call `save` - // /// method will return an error. - // /// - // /// If you set `save_path` to a folder path, CodeSnap will save the snapshot to the folder with - // /// a random name, it's looks like `CodeSnap_1997-04-22_01:12:00.png` (or svg). - // /// - // /// For Linux and MacOS users: - // /// You can use `~` and `$HOME` in the path to represent the home directory, CodeSnap will - // /// replace them with the value of the `HOME` environment variable. - // #[builder(setter(into, strip_option), default = None)] - // pub save_path: Option, - // - - // - // /// CodeSnap use Syntect as the syntax highlighting engine, you can provide a custom theme - // /// for code highlighting and background. - // /// The theme is load from the `themes_folder`(if not provided, CodeSnap load the default - // /// themes), you can use the theme name to specify the theme you want to use. - // /// - // /// See `themes_folder` config for more detail. - // #[builder(setter(into), default = String::from("base16-ocean.dark"))] - // pub theme: String, - // - // /// CodeSnap have pre-defined background themes, you can use the theme name to specify the - // /// background theme you want to use. - // /// - // /// Currently available themes: - // /// - summer - // /// - bamboo - // /// - peach - // /// - grape - // /// - dusk - // /// - sea - // #[builder(setter(into), default = String::from("bamboo"))] - // pub bg_theme: String, - // - // /// Except for the pre-defined background themes, you can also provide a custom solid background - // /// the color should be a hex color string, e.g. "#ffffff" - // #[builder(setter(into, strip_option), default = None)] - // pub bg_color: Option, - // - // /// The `file_path` will displayed in the snapshot as breadcrumbs - // #[builder(setter(into), default = String::from(""))] - // pub file_path: String, - // - // /// The separator of the breadcrumbs, default is "/" - // #[builder(setter(into), default = String::from("/"))] - // pub breadcrumbs_separator: String, - // - // /// Breadcrumbs is a useful and unique feature of CodeSnap, it can help users to understand the - // /// code location in the project. If the `has_breadcrumbs` is true, CodeSnap will display the - // /// `file_path` on top of the code. - // /// - // /// Also see the `file_path` config. - // /// - // /// The code snapshot is different from normal screenshots, it should provide more information - // /// about the code, such as the file path, the line number and highlight code line, these - // /// information can help users to understand the code better. - // #[builder(default = false)] - // pub has_breadcrumbs: bool, - // - // /// The `start_line_number` is used to specify the start line number of the code, if you use - // /// CodeSnap in editor plugins, the start line number will be the line number of the code in - // /// the editor. - // /// - // /// If the `start_line_number` is provided, CodeSnap will display the "line number" in the - // /// snapshot, otherwise, CodeSnap will not display the "line number". - // #[builder(setter(into, strip_option), default = None)] - // pub start_line_number: Option, - // - // /// CodeSnap can highlight multiple lines of code, to help users to understand the code better. - // /// The `highlight_start_line_number` specify the start line number of the highlight code. - // /// - // /// Please notice that the `highlight_start_line_number` and `highlight_end_line_number` should - // /// be provided together, and `highlight_start_line_number` should always <= - // /// `highlight_end_line_number`, otherwise, CodeSnap will throw a panic. - // /// - // /// Also see `highlight_end_line_number` config. - // #[builder(setter(into, strip_option), default = None)] - // pub highlight_start_line_number: Option, - // - // /// CodeSnap can highlight multiple lines of code, to help users to understand the code better. - // /// The `highlight_end_line_number` specify the end line number of the highlight code. - // /// - // /// Please notice that the `highlight_start_line_number` and `highlight_end_line_number` should - // /// be provided together, and `highlight_start_line_number` should always <= - // /// `highlight_end_line_number`, otherwise, CodeSnap will throw a panic. - // /// - // /// Also see `highlight_start_line_number` config. - // #[builder(setter(into, strip_option), default = None)] - // pub highlight_end_line_number: Option, - // - // /// The `min_width` is used to specify the minimum width of the code window, default is 350. - // #[builder(setter(into, strip_option), default = Some(350.))] - // pub min_width: Option, - // - // #[builder(setter(into, strip_option), default = Some(Margin { x: DEFAULT_EDITOR_MARGIN, y: DEFAULT_EDITOR_MARGIN }))] - // pub window_margin: Option, - // - // /// The `bg_x_padding` is used to specify the horizontal padding of the background - // /// default is `82`. If you want to set the same padding for both horizontal and vertical, - // /// you can use the `bg_padding` config. - // // #[builder(default = VIEW_WATERMARK_PADDING)] - // // pub bg_x_padding: f32, - // - // /// The `bg_y_padding` is used to specify the vertcal padding of the background - // /// default is `82`. If you want to set the same padding for both horizontal and vertical, - // /// you can use the `bg_padding` config. - // /// - // /// Please notice that if the `bg_y_padding` is less than 82., the watermark will be hidden. - // // #[builder(default = VIEW_WATERMARK_PADDING)] - // // pub bg_y_padding: f32, - // - // /// The `bg_padding` is used to specify same padding for both horizontal and vertical of the - // /// background, if you want to set different padding for horizontal and vertical, you can use - // /// the `bg_x_padding` and `bg_y_padding` config. - // // #[builder(setter(into, strip_option), default = None)] - // // pub bg_padding: Option, - // - // /// CodeSnap generate code snapshot image with scale `3.0` by default, you can use this config to } impl CodeSnap { fn validate(&self) -> Result<(), String> { if let Some(scale_factor) = self.scale_factor { - return match scale_factor { - scale_factor if scale_factor < 1 => { - Err("The scale factor must be greater than 1".to_string()) - } - _ => Ok(()), - }; + if scale_factor < 1 { + return Err("The scale factor must be greater than 1".to_string()); + } + } + + if let Some(ref code) = self.code { + if code.content.is_empty() { + return Err("The content of the code should not be empty".to_string()); + } } Ok(()) } + + pub fn from_config(config: &str) -> Result { + serde_json::from_str::(config) + } } impl SnapshotConfig { @@ -446,3 +329,7 @@ impl SnapshotConfig { ASCIISnapshot::from_config(self.clone()) } } + +fn default_scale_factor() -> u8 { + 3 +} diff --git a/core/src/snapshot/image_snapshot.rs b/core/src/snapshot/image_snapshot.rs index 569e420..2b5d457 100644 --- a/core/src/snapshot/image_snapshot.rs +++ b/core/src/snapshot/image_snapshot.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::{ - config::{SnapshotConfig, DEFAULT_WINDOW_MARGIN}, + config::{Border, SnapshotConfig, DEFAULT_WINDOW_MARGIN}, utils::{ clipboard::Clipboard, color::RgbaColor, path::parse_file_name, theme_provider::ThemeProvider, @@ -113,7 +113,9 @@ impl ImageSnapshot { let border_rgba_color: RgbaColor = config .window .border - .unwrap_or_default() + .unwrap_or(Border { + color: String::from("#ffffff30"), + }) .color .as_str() .into();